.. meta::
   :description: Learn how to use ThinLinc Web Access behind a reverse
                 proxy.

.. _reverse_proxy:

Reverse proxy
-------------

Running :ref:`ThinLinc Web Access <tlwebaccess>` behind a reverse proxy
is a way to integrate ThinLinc into existing infrastructure or meet
organizational requirements. This chapter explains the requirements
ThinLinc Web Access places on a reverse proxy to function correctly.

In a ThinLinc cluster, users start at the ThinLinc master server, but their
actual sessions run on individual agents to which ThinLinc Web Access
automatically redirects traffic. If a reverse proxy is used, it must be
configured to handle this routing and forward the connection to the correct
internal agent.

To support a proxy environment, specific ThinLinc parameters should
be updated. This involves enabling the :file:`X-Forwarded-For` header to
identify client IPs (see :ref:`forwarding_client_ip`) and updating
:servconf:`/webaccess/login_page` to match the public URL for the login
page.

Please note that this is a conceptual guide intended to illustrate the
routing logic. You must adapt the configuration to match your environment.

ThinLinc specific information
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

When a user starts a session, the master redirects the client to a
specific path in the format
:file:`/connect/<agent identifier>/`. This identifier is
determined by the :servconf:`/vsmagent/agent_hostname` (or the agent's
IP if unset). The proxy must capture this path to correctly route the
traffic to the agent service.

.. note::

   In a scenario where a large number of simultaneous ThinLinc sessions
   are directed through a single reverse proxy, the reverse proxy might
   become a network bottleneck. In such cases, make sure to take
   ThinLinc's bandwidth requirements into account in your reverse proxy
   architecture.

.. _reverse_proxy_basic_setup:

Basic proxy setup
~~~~~~~~~~~~~~~~~

Below is an example Nginx configuration for a setup with a single
ThinLinc server running both the master and agent services. All requests
to the root :file:`/` are forwarded to the ThinLinc backend.

Pay special attention to the path handler :file:`/connect/tl.internal.example.com/`.

.. code:: nginx

   server {
       listen 443 ssl;
       server_name tl.example.com;

       # ... (SSL configuration) ...

       proxy_read_timeout 999h;

       location / {
           proxy_pass https://tl.internal.example.com:300/;
           proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection $http_connection;
       }

       location /connect/tl.internal.example.com/ {
           proxy_pass https://tl.internal.example.com:300/;
           proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection $http_connection;
       }
   }

ThinLinc Web Access relies on WebSocket connections to establish and
maintain user sessions. Therefore, your proxy must set the Upgrade and
Connection headers again. If this is not configured correctly, users
will be able to load the login page but unable to start a session.

Once the session is established, the proxy should allow the connection
to remain open for long periods, even when the user is idle. Default
proxy read timeouts are typically short and might abruptly disconnect
inactive users. To avoid this, it is recommended to increase the proxy's
read timeout.

.. note::

   Do not use the proxy timeout to enforce idle limits. If you wish to
   disconnect users after a specific period of inactivity, this should
   be configured within ThinLinc using the :option:`-MaxIdleTime`
   parameter. For more information, see
   :ref:`configuration_limiting_lifetime`.

Multi-agent setup
~~~~~~~~~~~~~~~~~

In a ThinLinc cluster with more than one agent, follow the instructions
in :ref:`reverse_proxy_basic_setup`, but replicate the agent-specific
path handler for every additional agent, as in the example below.

.. code:: nginx

    location /connect/agent1.internal.example.com/ {
        proxy_pass https://agent1.internal.example.com:300/;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $http_connection;
    }

Custom path configuration
~~~~~~~~~~~~~~~~~~~~~~~~~

You may also want to serve the entire ThinLinc Web Access interface from
a specific sub-path, such as :file:`/subpath/`, instead of from the
server root :file:`/`. This requires prefixing all of your ThinLinc path
handlers. The master's path becomes :file:`/subpath/` and the agent
paths will also be nested under it, such as
:file:`/subpath/connect/agent1.internal.example.com/`.

.. code:: nginx

   location /subpath/ {
       proxy_pass https://tl.internal.example.com:300/;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection $http_connection;
   }

   location /subpath/connect/tl.internal.example.com/ {
       proxy_pass https://tl.internal.example.com:300/;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection $http_connection;
   }

   location /subpath/connect/agent1.internal.example.com/ {
       proxy_pass https://agent1.internal.example.com:300/;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection $http_connection;
   }

Distributed agents
~~~~~~~~~~~~~~~~~~

In complex or geographically distributed environments, you may not want
to proxy all agent connections through a single entry point. A more
scalable approach is to use a main proxy for the login page, which then
redirects clients to their specific agent connections.

.. code:: nginx

   location ~ /connect/tl.sweden.example.com/(.*) {
       return 307 https://tl.sweden.example.com/$1;
   }

The redirection sends the client to a new hostname. This can be the
agent server directly, if it is exposed on the network. Alternatively,
it can be a dedicated proxy server configured to forward
the traffic to the internal agent as below.

.. code:: nginx

   server {
       listen 443 ssl;
       server_name tl.sweden.example.com;

       # ... (SSL configuration) ...

       location / {
           proxy_pass https://tl.internal.sweden.example.com:300/;
           proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection $http_connection;
       }
   }

The following configuration brings together all the concepts discussed
in this guide for a more advanced example.

.. code:: nginx

   server {
       listen 443 ssl;
       server_name tl.example.com;

       # ... (SSL configuration) ...

       proxy_read_timeout 999h;

       location /subpath/ {
           proxy_pass https://tl.internal.example.com:300/;
           proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection $http_connection;
       }

       location /subpath/connect/tl.internal.example.com/ {
           proxy_pass https://tl.internal.example.com:300/;
           proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection $http_connection;
       }

       location ~ /subpath/connect/tl.sweden.example.com/(.*) {
           return 307 https://tl.sweden.example.com/$1;
       }
   }

   server {
       listen 443 ssl;
       server_name tl.sweden.example.com;

       # ... (SSL configuration) ...

       location / {
           proxy_pass https://tl.internal.sweden.example.com:300/;
           proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection $http_connection;
       }
   }

