Reverse proxy

Running ThinLinc Web Access 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 X-Forwarded-For header to identify client IPs (see Forwarding client IP addresses) and updating /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 /connect/<agent identifier>/. This identifier is determined by the /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.

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 / are forwarded to the ThinLinc backend.

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

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 -MaxIdleTime parameter. For more information, see Limiting lifetime of ThinLinc sessions.

Multi-agent setup

In a ThinLinc cluster with more than one agent, follow the instructions in Basic proxy setup, but replicate the agent-specific path handler for every additional agent, as in the example below.

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 /subpath/, instead of from the server root /. This requires prefixing all of your ThinLinc path handlers. The master’s path becomes /subpath/ and the agent paths will also be nested under it, such as /subpath/connect/agent1.internal.example.com/.

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.

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.

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.

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;
    }
}