User Tools

Chaining proxies

This approach consist in having a reverse proxy on your external server that collects and reidrect requests to your home server reverse proxy.

This is very effective, because it brings to the setup the following advantages:

  • You can use fail2ban
  • You can split public services on the external server and private services on the home server
  • You will reduce overhead and network bandwidth toward the home server

The mail disadvantage is mostly due to a more complex setup with the SSL certificates.

The link between the two proxies must be done trough the wireguard (or SSH) tunnel connecting the home and the external server.

Internal Proxy

The home proxy server is exactly the same you use on your home network. See this page for more details on how to set it up.

You should keep the two different ports:

  • 443 for internal access, from the home network
  • 8443 for external access, from the internet trough the external server

Since you will still be accessing your home server via HTTPS even then you are inside your home network, you must still provide HTTPS in the internal proxy.

External Proxy

I prefer to use a similar approach to the home server to keep things tidy.

Asusming i have mydomain.com that is hosted on the home server and www.mydomain.com that is hosted on the external server, the NGINX configuration would look something like:

nginx.conf
http {
        include /etc/nginx/mime.types;
        default_type application/octet-stream;
        log_format main
                '$remote_addr - $remote_user [$time_local] '
                '"$request" $status $bytes_sent '
                '"$http_referer" "$http_user_agent" '
                '"$gzip_ratio"';
        tcp_nopush on;
        tcp_nodelay on;
        sendfile on;
        types_hash_max_size 4096;
        index index.html;
        client_header_timeout 10m;
        client_body_timeout 10m;
        send_timeout 10m;
        connection_pool_size 256;
        client_header_buffer_size 1k;
        large_client_header_buffers 4 2k;
        request_pool_size 4k;
        output_buffers 1 32k;
        postpone_output 1460;
        keepalive_timeout 75 20;
        ignore_invalid_headers on;
        # General catch-all for HTTPS redirection, we don't like serving plain HTTP
        server {
                listen 80 default_server;
                return 301 https://$host$request_uri;
        }
        proxy_headers_hash_max_size 512;
        proxy_headers_hash_bucket_size 128;
        
        server {
                server_name *.mydomain.com;
                listen 443 ssl;
                access_log /var/log/nginx/mydomain.com_access_log main;
                error_log /var/log/nginx/mydomain.com_error_log info;
                location / {
                        proxy_pass https://10.100.0.1:8443/; # this is the home server address on the wireguard link
                }
                proxy_set_header   Host $host;
                proxy_set_header   ProxyHost $proxy_host;
                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_set_header X-Forwarded-Host $server_name;
                proxy_set_header X-Forwarded-Ssl on;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
                proxy_http_version 1.1;
        }

        server {
                server_name www.mydomain.com;
                listen 443 ssl;
                index index.php;        
                root /home/web/htdocs;
                access_log /var/log/nginx/www.mydomain.com_access_log main;
                error_log /var/log/nginx/www.mydomain.com_error_log info;
        }
        include /etc/letsencrypt/options-ssl-nginx.conf;
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
        ssl_certificate /etc/letsencrypt/live/mydomain.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/mydomain.com/privkey.pem;
}

If using SSH tunnel, rememeber to use 127.0.0.1 instead of 10.100.0.1 in the proxy_pass above.

Certificates

Both the home server and the external server need to access the SSL certificates, because both terminates the HTTPS connection. Unfortunately, requesting the certificates renewal requires the availability of port HTTP (80) and that is only avalable to either the external server, or the home server, but it cannot be available to both at the same time. No, you cannot use a different port as only port 80 will work with the ACME challenge.

So you only have the following options:

  • Generate / renew the certificates on the home server, then copy them to the external server
  • Generate / renew the certificates on the external server, then copy them to the home server

I prefer the second one because, conceptually, the external server should always be available. When the external server is not available, the home server will not be able to generate the certificates anyway.

Let me summarize what's needed now:

  • On the external server, generate the certificates and restart remote NGINX
  • On the external server, copy the certificates in a location where the home server can pick them up
  • On the home server, grab the copied certificates on the external server via sftp
  • On the home server, place the certificates and restart home NGINX

Additionally, since i need to have the certificates available to Stalwart, i also created a cert group for broader access.

So, follow the paragraph on certificates here on the external server, and create the following post-renewal hook. Create the following file as /etc/letsencrypt/renewal-hooks/post/copy-certs.sh:

copy-certs.sh
#!/bin/bash
# for stalwart access
chgrp certs -R /etc/letsencrypt
chmod g+r -R /etc/letsencrypt
chmod g+x /etc/letsencrypt/archive /etc/letsencrypt/live
test -e /home/backup/letsencrypt && rm -rf /home/backup/letsencrypt
cp -a /etc/letsencrypt /home/backup

i will use the backup user for sharing the certificates since the home server will already be able to access the external server automatically using this user.

On the home server, a simple crontab entry will do the rest:

15 6 * * * scp -pqr -P 522 backup@10.100.0.2:/home/backup/letsencrypt /etc/letsencrypt && /etc/init.d/nginx restart
59 16 * * * scp -pqr -P 522 backup@10.100.0.2:/home/backup/letsencrypt /etc/letsencrypt && /etc/init.d/nginx restart

Of course, add the external root SSH public key to user file /home/backup/.ssh/authorized_keys for passwordless access.

Fail2Ban

Fail2Ban is a nice daemon capable to block incoming connections based on reading logs to prevent abuse.

Since it uses the client IP address, it cannot be used on the home server directly, as all incoming connectiond would originate from the external server wireguard IP making fail2ban useless.

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also, you acknowledge that you have read and understand our Privacy Policy. If you do not agree, please leave the website.

More information