ThinLinc, FortiWeb and nginx

ThinLinc, FortiWeb and nginx


A post talking about combining ThinLinc (a truly amazing Linux based remote access solution), Fortinet FortiWeb and nginx as a reverse proxy.

The idea is relatively simple, I wanted to host a ThinLinc Web Access remote access possibility although through different possible ingressing sides. In fact, I needed to be able to hook on the tlwebaccess service from within my local networks while still having the same possibility although through public networks.

To complexify a bit, I wanted the public access to be achieved over an existing FQDN and using a dedicated /thinlinc path.

Here is an high level view of the wanted setup:

Let's start with the ThinLinc Server setup, you'll find all the needed documentation here: https://www.cendio.com/resources/docs/tag/

I've done the following modifications on my local setup once installed:‌

/opt/thinlinc/etc/conf.d/webaccess.hconf:

  • TCP:443 as it's bound port
  • Added both the locally trusted server.crt/.key

/opt/thinlinc/etc/conf.d/vsmagent.hconf:

  • agent_hostname=local.host.name (FQDN.local type of hostname)

The agent_hostname is used by ThinLinc in order to redirect any successfully authenticated user to the final ThinLinc destination while opening the connection. We will see further on why this might give some issues in the wanted setup.

Onto the nginx setup, all I wanted was a reverse proxy listening on a dedicated TCP port and handling some rewriting. Here is my setup:

pyjaman@tlhost:~$ cat /etc/nginx/nginx.conf
...
http {
        ...
        include /etc/nginx/conf.d/*.conf;
}
pyjaman@tlhost:~$
pyjaman@tlhost:~$ cat /etc/nginx/conf.d/nginx-ssl.conf 
server {
        listen              4443 ssl http2;
        server_name         localhost;
        ssl_certificate     /etc/ssl/server.crt;
        ssl_certificate_key /etc/ssl/server.key;
        ssl_protocols       TLSv1.2;
        ssl_ciphers         HIGH:!aNULL:!MD5;
        location /thinlinc/ {
        proxy_pass https://10.1.1.253:443/;
        proxy_redirect /main/ /thinlinc/main/;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Ssl on;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_cookie_path / /thinlinc/; 
        proxy_read_timeout 36000;
        proxy_send_timeout 36000;
        sub_filter ="/ ="/thinlinc/;
        sub_filter tlhost.local:443/agent public.fqdn.ch:443/thinlinc/agent;
        sub_filter 10.1.1.253 $host;
        sub_filter websocket/ thinlinc/websocket/;
        sub_filter_once off;
        } 
}

The above nginx reverse proxy setup will; make the service available on the wanted /thinlinc path as well as rewrite the ThinLinc sent "agent_hostname" with our wanted publicly reachable FQDN.

I took here inspirations on two distinct blog posts:

https://www.verboom.net/blog/index.html?single=20151019.0
https://www.bitbull.ch/wiki/index.php/Thinlinc_html5_webaccess_behind_nginx_reverse_proxy

After which, once both ThinLinc Web Access and the nginx reverse proxy are started, they shall bind TCP:443 as well as TCP:4443 on our ThinLinc host:

pyjaman@tlhost:~$ sudo ss -tnlp | grep 443
LISTEN 0      511          0.0.0.0:4443      0.0.0.0:*    users:(("nginx",pid=413,fd=5),("nginx",pid=412,fd=5),("nginx",pid=411,fd=5))
LISTEN 0      128                *:443             *:*    users:(("python3",pid=432,fd=3))      

Therefore, we shall be able to reach our TCP:443 internally, being redirected by ThinLinc toward the correct host – bizznizz as usual here. This while also being able to address our TCP:4443 nginx reverse proxy from the outside world through FortiWeb in my case, pretty neat.

I'll be assuming that FortiWeb is already in place, configured and able to possibly address back-end authentications systems, in my case a FortiAuthenticator appliance enabling PCI DSS 3.2 two-factor authentication.

In my setup, what was needed were a couple of items on FortiWeb; an HTTP Content Routing Policy, a Server Pool entry, a dedicated Web Protection Profile, a Site Publishing Policy and obviously the wanted authentication schemes you want to address, the GeoIP you want, host-name protection etc etc.

our HTTP Content Routing Policy
Our Site Publishing Rule
And our Server Pool entity, bound to our nginx reverse proxy on TCP:4443 while the publicly reachable resource is provided over FortiWeb and TCP:443

This will provide us with the possibility to address our ThinLinc enabled system from the Internet while leveraging Fortinet based multi-factor authentications. Here is a view of the user experience from the Internet:

Our FortiWeb Site Publishing "Sign In" invitation, followed by our 2FA request
The login form on the ThinLinc Web Access service (I couldn't yet correctly setup the FortiWeb authentication delegation... workin' on it)
And our remote WebSocket session on a Manjaro host

This while of course, HTTPS connection towards TCP:443 if addressing the ThinLinc host from our internal networks will provide the same results although without 2FA and thus reaching directly on the ThinLinc service.

Hope you've enjoyed this post, have a check at ThinLinc it's really a neat piece of software and so is FortiWeb.

Cheers,
Obuno

Image Credits: Eric Gagnon - https://www.artstation.com/artwork/8l9VGw

Show Comments