So.. you’ve finished installing and setting up all your docker containers, plugins ect. Radarr is doing its thing, Plex is chugging away… everything is just peachy! And now you want to be able to access the unraid GUI outside your network.

The safest method you can do this is by setting up a VPN. You can do that by installing the OpenVPN container but that won’t give you access if your server or the docker service crashes. So setting up the VPN on the router ect is much more recommended. BUT you’re not always in a situation where you can connect to a VPN. For example your work computer. Be it you don’t have the administrator rights to change your network settings or your company policy forbids it.. This is where Apache Guacamole is useful. By installing Apache Guacamole, centos – xfce – vnc Firefox vnc and letsencrypt we can gain access to the unraid GUI externally. And by setting up fail2ban and geo-block we can protect our self from bruteforce attempts at gaining access!

Installation

I use the jasonbean/guacamole container. Nothing special you need to think about. Add your custom port and select your appdata location. The default username and password is guacadmin Note: It isn’t mentioned in the documentation but if you are installing the container using docker run or compose you need to add -e 'OPT_MYSQL'='Y'

Search for VNC Web Browser in community applications and you will find a template by cheesemarathon for the consol/centos-xfce-vnc container. I have found that using the VNC Web Browser Container gives me constant connection errors and have switched to the Firefox container (jlesage/firefox) instead. Install the container and add your VNC Password if you want. Take note of the VNC port as you will need that later. Use the VNC_PASSWORD variable described here Do not add SSL in the container settings as it looks like Guacamole doesn’t support VNC with encryption.

To access all this we’ ll use the linuxserver/letsencrypt container from linuxserver. This container sets up an Nginx web server and reverse proxy with php support and a built-in letsencrypt client that automates free SSL server certificate generation and renewal processes. It also contains fail2ban for intrusion prevention. Before we start you need to acquire a domain. You can do that on duckdns or any other domain service. I’m using https://domains.google/ and I’m very happy with that.

If you have a dynamic ip-address you can setup the captinsano DDclient container and have that update your synthetic record. If you don’t want to pay for a custom domain, using the duckdns container will work just fine.

Forward your domain to your public IP address. After you’ve done that add your different ANAME/CNAME records e.g guacamole.yourdomain.com or unraid.yourdomain.com

@ = root domain (technicalramblings.com) and points to my external ip www = sub domain grafana = sub domain TTL: (Time to Live) How often a copy of the record stored in cache must be updated or discarded.

Installation

Container Port: 80 – Choose your desired host port. e.g 81 (You can’t set this to 80 as the unRAID web GUI uses that. ) Container Port: 443 – Set this to 444 or something else (On update 6.4 unraid will use port 443 if you setup https and it’s better to be ahead of time so it won’t cause any issues) Enter you email Add your domain e.g yourdomain.com Add your different sub domains e.g guacamole,guac ect Validation: Select your validation type. http will work in most cases (Unless your ISP blocks port 80) Container Path: /config Install the container config to your desired location. I recommend using an SSD.

Next is port forwarding. This is done on your router and you need to forward port 80 and 443 to the ports you chose in step 1 and 2. So if your servers IP is 192.168.1.2 and you have chosen that the container is on port 81, you need to forward all traffic on port 80 to port 81 on IP 192.168.1.2 And do the same for port 443 to 444. If you’re unsure how to do this on your router check out: Portforward.com

Next go to https://yourserverip:444 or http://yourserverip:81 If you now see the Nginx welcome page, it works. Also test if yourdomain.com redirects you to the nginx welcome page. Note: TTL differs from each provider, some has a minimum 60 minutes before DNS propagates and others have 1 minute. So it might take a while before https://yourdomain.com works.

Configuring Apache Guacamole

Browse to the Apache guacamole container and login. You can add a new admin user if you want. The default username and password is guacadmin

Go to Settings and click on Connections Click on New Connection Give the connection a name. I just called it Firefox. And Location is ROOT and Protocol is VNC

Set your Maximum number of connections, I use 3

Scroll down to Parameters – Network. Add your Hostname: Your Unraid IP Add your Port: This is the VNC port to the Firefox container (default is 7914) For Authentication input the password you set for the Firefox container. Click Save

You can now test the connection you have created. Click on your user name and select the connection. You should now be presented with the desktop of the VNC Web Browser container.

Tip: If you want to go back to settings just press ctrl + shift + alt to open the side menu. Here you can also copy text that you have copied within the VNC connection!

Here you can open Firefox and go to your unraid IP and log in.

Windows 10 VM with RDP

You can also easily add your Windows VM using RDP, this will also let you mount your shares so you can manage your files like you would at home. This is how I added my Windows 10 Pro VM:



The 3389 port is the RDP port.

Configuring Nginx

Go to your letsencrypt appdata location. Find the nginx folder and then edit the file called default or add a new .conf file in the site-conf folder. I recommend using notepad++ If you want to use this on a subdomain I recommend creating a guacamole.conf file instead and adding the nginx config to that. Below is an nginx config that will give you A+ ratings on securityheaders.io and ssllabs.com

server_name guacamole.domain.com; This is where you will add your domain name e.g guacamole.duckdns.org proxy_pass http://192.168.1.34:8089/; This is your IP and port to the Apache Guacamole container

READ THE COMMENT ON add_header X-Frame-Options AND add_header Content-Security-Policy IF YOU USE THIS ON A SUBDOMAIN YOU WANT TO IFRAME!

# GUACAMOLE CONTAINER server { listen 80; server_name guacamole.domain.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name guacamole.domain.com; ##SSL SETTINGS ## READ THE COMMENT ON add_header X-Frame-Options AND add_header Content-Security-Policy IF YOU USE THIS ON A SUBDOMAIN YOU WANT TO IFRAME! ## Certificates from LE container placement ssl_certificate /config/keys/letsencrypt/fullchain.pem; ssl_certificate_key /config/keys/letsencrypt/privkey.pem; ## Strong Security recommended settings per cipherli.st ssl_dhparam /config/nginx/dhparams.pem; # Bit value: 4096 ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384; ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0 ssl_session_timeout 10m; ## NOTE: The add_header Content-Security-Policy won't work with duckdns since you don't own the root domain. Just buy a domain. It's cheap ## Settings to add strong security profile (A+ on securityheaders.io/ssllabs.com) add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header X-Robots-Tag none; #SET THIS TO index IF YOU WANT GOOGLE TO INDEX YOU SITE! add_header Content-Security-Policy "frame-ancestors https://*.DOMAIN.COM https://DOMAIN.COM https://$server_name"; #Add your domains you want to enable iframing on add_header X-Frame-Options "allow-from https://DOMAIN.COM https://$server_name"; #Add your domains you want to enable iframing on. https://$server_name = sub.domain.com in this server block add_header Referrer-Policy "strict-origin-when-cross-origin"; add_header Feature-Policy "geolocation none;midi none;notifications none;push none;sync-xhr none;microphone none;camera none;magnetometer none;gyroscope none;speaker self;vibrate none;fullscreen self;payment none;"; #FEATURE POLICY: READ MORE HERE: https://scotthelme.co.uk/a-new-security-header-feature-policy/ proxy_cookie_path / "/; HTTPOnly; Secure"; ##NOTE: This may cause issues with unifi. Remove HTTPOnly; or create another ssl config for unifi. more_set_headers "Server: Classified"; more_clear_headers 'X-Powered-By'; ##END SSL SETTINGS location / { proxy_pass http://192.168.1.34:8089/; proxy_buffering off; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $http_connection; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_no_cache $cookie_session; } }

Configuring fail2ban

From github: Fail2Ban scans log files like /var/log/auth.log and bans IP addresses conducting too many failed login attempts. It does this by updating system firewall rules to reject new connections from those IP addresses, for a configurable amount of time. Fail2Ban comes out-of-the-box ready to read many standard log files, such as those for sshd and Apache, and is easily configured to read any log file of your choosing, for any error you wish. Luckily Fail2ban comes preinstalled with your letsencrypt container, so you only need to add the filter and edit the jail.local file! For this to work we need the letsencrypt container to be able to see the catalina.out file in the Apache Guacamole container.

Open the letsencrypt container settings.

Add a path from the letsencrypt container to the Apache Guacamole container. Name: guacamole fail2ban Container path: /guacamole or whatever you prefer Host path: Your path to the Apache Guacamole /log folder e.g /AppData/ApacheGuacamole/log/tomcat8 Access mode: Read only Description: fail2ban path into guacamole /log folder

container.

Go to the fail2ban folder inside the letsencrypt appdata folder and edit the jail.local file. For my config I have set the bantime to 86400 seconds (24h) The findtime is 600 seconds and maxretry is 3 At the end of the jail.local file add the following:

[guacamole-auth] enabled = true port = http,https filter = guacamole-auth logpath = /guacamole/catalina.out ignoreip = 192.168.1.0/24

The ignore IP is so that fail2ban won’t ban your local IP. Check out https://www.aelius.com/njh/subnet_sheet.html if you are wondering what your CIDR notation is. Most often it will be /24 (netmask 255.255.255.0)

To find your netmask run ipconfig /all on windows or ifconfig | grep netmask on linux.

is so that fail2ban won’t ban your local IP. Check out https://www.aelius.com/njh/subnet_sheet.html if you are wondering what your is. Most often it will be (netmask 255.255.255.0) To find your netmask run on windows or on linux. The logpath is the container path you created in step 2. And catalina.out the Guacamole log.

Now, there is already a filter (guacamole.conf) for Guacamole in the filter.d folder inside the fail2ban folder. But that filter won’t work unless we make a change to it. Copy the guacamole.conf file and rename it guacamole-auth.conf

In the guacamole-auth.conf file change:

failregex = ^.*

WARNING: Authentication attempt from <HOST> for user "[^"]*" failed\.$

to

failregex = \bAuthentication attempt from \[<HOST>(?:,.*)?\] for user ".*" failed\.

Restart the letsencrypt container. If you get the error below in the fail2ban.log file you can comment the 3 date pattern lines in the guacamole-auth.conf file.

2018-06-12 21:36:49,121 fail2ban.filter [351]: ERROR Error during seek to start time in "/guacamole/catalina.out" 2018-06-12 21:36:49,121 fail2ban.filterpoll [351]: ERROR Caught unhandled exception in main cycle: TypeError('an integer is required',)

Comment these lines by adding # in front.

#datepattern = ^%%b %%d, %%ExY %%I:%%M:%%S %%p #^WARNING:()** #{^LN-BEG}

Remember to restart the container anytime you make a change in the conf file.

Banned

The fail2ban.log file should output something like this:

2018-06-12 21:39:07,529 fail2ban.jail [350]: INFO Jail 'guacamole-auth' started 2018-06-12 21:39:30,779 fail2ban.filter [350]: INFO [guacamole-auth] Ignore 192.168.1.1 by ip 2018-06-12 21:39:44,801 fail2ban.filter [350]: INFO [guacamole-auth] Found 77.16.72.179 - 2018-06-12 21:39:44 2018-06-12 21:39:57,420 fail2ban.filter [350]: INFO [guacamole-auth] Found 77.16.72.179 - 2018-06-12 21:39:57 2018-06-12 21:40:00,025 fail2ban.filter [350]: INFO [guacamole-auth] Found 77.16.72.179 - 2018-06-12 21:39:59 2018-06-12 21:40:00,196 fail2ban.actions [350]: NOTICE [guacamole-auth] Ban 77.16.72.179

Unbanning

If you managed to ban yourself or a friend banned themself you can do this to unban. Bash into the container with: docker exec -it letsencrypt bash Enter fail2ban interactive mode: fail2ban-client -i Check the status of the jail: status guacamole-auth Output

fail2ban> status guacamole-auth Status for the jail: guacamole-auth |- Filter | |- Currently failed: 0 | |- Total failed: 3 | `- File list: /guacamole/catalina.out `- Actions |- Currently banned: 1 |- Total banned: 1 `- Banned IP list: 77.16.72.179

unban with: set guacamole-auth unbanip 77.16.72.179 If you already know the IP you want to unban you can just type this: docker exec -it letsencrypt fail2ban-client set guacamole-auth unbanip 77.16.72.179

Adding geo-blocking

Purpose

Restrict access based on the user’s geographical location

Installation

If you are using the letsencrypt container the nginx module is already installed. If not you can take a look at the howtoforge guide. That said the container doesn’t come with the GeoIP database.

The database can be found here: https://dev.maxmind.com/geoip/geoip2/geolite2/ and its the country database we’ll be using for this guide.

Download the database and extract the .mmdb file to the folder of you choice. I’ll be using /config/geolite2/ for my setup.

NGINX

In your nginx.conf file add the following in the http { block



geoip2 /config/geolite2/GeoLite2-Country.mmdb { auto_reload 1d; $geoip2_data_country_code country iso_code; } geo $lan-ip { default no ; 192.168 . 1.0 / 24 yes; } map $geoip2_data_country_code $allowed_country { default no ; <YOUR-COUNTRY-CODE> yes; }

Instead of "YOUR-COUNTRY-CODE" add your own country code from this list. This will block all other countries than the one you choose. You can also add more than one country if you want.

US yes; CA yes; GB yes;

The geo $lan-ip is for allowing you to access the domain on your LAN.

Check out https://www.aelius.com/njh/subnet_sheet.html if you are wondering what your CIDR notation is. Most often it will be /24 (netmask 255.255.255.0)

To find your netmask run ipconfig /all on windows or ifconfig | grep netmask on linux.

Note: The geo $lan-ip part is only needed if you set default to no

For it to actually block you need to add this in your server block :

# LOCAL IP ALLOW GEO BLOCK if ($lan-ip = yes) { set $allowed_country yes; } # COUNTRY GEO BLOCK if ($allowed_country = no) { return 444; }

So if you created a guacamole.conf file in the nginx/site-confs folder you add it there. It will then look like this:

# GUACAMOLE CONTAINER server { listen 80; server_name guacamole.domain.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name guacamole.domain.com; ##GEOBLOCK # LOCAL IP ALLOW GEO BLOCK if ($lan-ip = yes) { set $allowed_country yes; } # COUNTRY GEO BLOCK if ($allowed_country = no) { return 444; } ##SSL SETTINGS ## READ THE COMMENT ON add_header X-Frame-Options AND add_header Content-Security-Policy IF YOU USE THIS ON A SUBDOMAIN YOU WANT TO IFRAME! ## Certificates from LE container placement ssl_certificate /config/keys/letsencrypt/fullchain.pem; ssl_certificate_key /config/keys/letsencrypt/privkey.pem; ## Strong Security recommended settings per cipherli.st ssl_dhparam /config/nginx/dhparams.pem; # Bit value: 4096 ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384; ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0 ssl_session_timeout 10m; ## NOTE: The add_header Content-Security-Policy won't work with duckdns since you don't own the root domain. Just buy a domain. It's cheap ## Settings to add strong security profile (A+ on securityheaders.io/ssllabs.com) add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header X-Robots-Tag none; #SET THIS TO index IF YOU WANT GOOGLE TO INDEX YOU SITE! add_header Content-Security-Policy "frame-ancestors https://*.$server_name https://$server_name"; ## Use *.domain.com, not *.sub.domain.com (*.$server_name) when using this on a sub-domain that you want to iframe! add_header X-Frame-Options "ALLOW-FROM https://*.$server_name" always; ## Use *.domain.com, not *.sub.domain.com (*.$server_name) when using this on a sub-domain that you want to iframe! add_header Referrer-Policy "strict-origin-when-cross-origin"; proxy_cookie_path / "/; HTTPOnly; Secure"; ##NOTE: This may cause issues with unifi. Remove HTTPOnly; or create another ssl config for unifi. more_set_headers "Server: Classified"; more_clear_headers 'X-Powered-By'; ##END SSL SETTINGS location / { proxy_pass http://192.168.1.34:8089/; proxy_buffering off; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $http_connection; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_no_cache $cookie_session; } }

Blocked

You can test if it worked with a VPN or do a performance test from a location that is blocked here https://www.webpagetest.org/

Optional: Organizr

Another security layer is using Organizr to block access to your guacamole.domain.com by having you to log into Organizr first! And you can even add a fail2ban filter on the Organizr login form! By using server authentication you will be shown a 401 Unauthorized page unless you log in first.

Optional: Basic http auth

Another security layer is using basic http auth. Linuxservers letsencrypt container is already be pre configured to ban failed http auths with fail2ban!

Sources: https://github.com/fail2ban/fail2ban/issues/1574

For any questions you can find me here: