Site icon Zit Seng's Blog

Nginx Reverse Proxy with QNAP NAS

This post is about setting up an Nginx reverse proxy to provide public Internet access to a QNAP NAS inside a private home network. This can easily be expanded to proxy other services inside the QNAP NAS, as well as on other servers in the home network.

If you’ve run any services inside your home network that you wanted to access from the public Internet, you know there are some extra things you need to setup. The simplest of which is to do port forwarding in your router. Port forwarding could work as well for multiple services, as long as you pick a unique port number for each of them. However, running HTTP or HTTPS on non-standard port numbers might not look very nice.

There’s a solution. If you can assign a unique hostname to each of those internal services, then, run a reverse proxy service, have all those hostnames point to this reverse proxy, and then configure the reverse proxy to distribute corresponding traffic to the internal services.

Of course, I skipped some details, though I’m sure most experienced network people will know what to do. Those unique hostnames need to actually point to your router, and then your router forwards the required ports (e.g. 80, 443) to the internal reverse proxy. You probably also want a hostname static override in your internal network so that all lookups to those names will point to the reverse proxy’s private IP address. (In MikroTik Router OS, you will find local static entries configured under IP, DNS, Static.)

I recently started using a QNAP TS-464 NAS, and I’ve decided that I want it properly configured so that I can use it securely both at home and from anywhere in the world. I need a couple of things on the QNAP NAS: access to its administration interface, access to webdav service, and access to the Virtualisation Station UI.

I’m using Nginx to run the reverse proxy. It actually runs inside a Ubuntu 22.04 VM inside the QNAP NAS, but you can just as well have this proxy run on a different box. As you’ll see, this is easily expanded to provide access to other internal services. You just need a different hostname so that Nginx knows how to deal with each one of them.

I’m going to assume you have some basic competency with Linux so I’m going to jump straight into the Nginx configuration. These are the changes required from default installed configuration. Create a new file in /etc/nginx/sites-enabled, say, /etc/nginx/sites-enabled/nas.somewhere.com (or just directly edit the default file if you so prefer), and put this:

# Needed to handle WebSockets, used by Virtualisation Station
map $http_upgrade $connection_upgrade {
    default update;
    '' close;
}

server {
    server_name nas.somewhere.com;

    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 $host;
    proxy_set_header X-Forwarded-Port $server_port;
    proxy_connect_timeout 300s;
    proxy_send_timeout 300s;
    proxy_read_timeout 300s;
    proxy_buffering off;
    proxy_http_version 1.1;

    # Only needed if you want to use SSL with the NAS
    proxy_ssl_session_reuse on;
    proxy_ssl_verify on;
    proxy_ssl_verify_depth 3;

    # Point this to the TLS certificate used by your NAS
    proxy_ssl_trusted_certificate /etc/ssl/cert-for-nas.pem;

    location / {
        # Replace with your NAS IP, and http/8080 if using plain HTTP
        proxy_pass https://192.168.1.100:443;
        proxy_set_header Host $host;
        proxy_http_version 1.1;

        # Needed for WebSockets used by Virtualisation Station
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }

    location /webdav {
        set $dest $http_destination;

        # Uncomment below if using HTTP to the NAS
        # if ($http_destination ~ "^https://(?<myvar>(.+))") {
        #         set $dest http://$myvar;
        # }

        proxy_set_header Destination $dest;
        proxy_pass https://192.168.1.100:5001;

        proxy_buffering off;
        client_max_body_size 0m;

        proxy_http_version 1.1;
        proxy_set_header Connect "";
        proxy_set_header Host $host;
    }
}

In my example above, the NAS IP is at 192.168.1.100. The NAS’ domain name is nas.somewhere.com. Pease read the inline comments for some changes that you need to make.

Notice that I’m using SSL from the reverse proxy to the NAS for extra security. Many people don’t need this, and if you are satisfied that plain HTTP is fine inside your home network, you can go ahead and just use HTTP.

When using HTTP, you’ll need to muck around with the Destination header for the WebDAV service, otherwise MOVE and COPY methods will fail. (Thanks to this blog post.)

WebDAV requests may involve very large content sizes, so it is necessary to tell Nginx to not buffer and not check client request body size. Take note that my QNAP TSS-464 configuration has WebDAV running on a dedicated port (the default IIRC). You can check this in Control Panel, Network & File Services, Win/Mac/NFS/WebDAV, WebDAV.

If you use SSL to the NAS, then note that you’ll need to generate an SSL self-signed certificate in the QNAP NAS with its actual private IP address filled into the CN field. You can generate the self-signed certificate from Control Panel, System, Security, SSL Certificate & Private Key. Or, if you just want HTTPS but don’t care about verifying the certificate, you can just remove the proxy_ssl_verify* and proxy_ssl_trusted_certificate lines.

In order for Nginx to verify self-signed certificates, you need to give it the certificate. You can get the cert from the upstream server by running:

openssl s_client -showcerts -connect 192.168.1.100:443

Grab the block between the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- lines, including those markers, and put it in a file specified by the proxy_ssl_trusted_certificate config line. If, for some reason you have more than one block (e.g. your certificate is signed by your own private CA), in principle, using any of them would be fine, though if you did setup a private CA, it might be more convenient to give it the CA’s certificate (the last block in the output).

The reverse proxy config to the QNAP NAS is a bit non-trivial due to the need to handle WebSockets (for Virtualisation Station) and WebDAV (which uses a different upstream port).

Suppose you have more services. If they are simple, like the admin interface for my MikroTik router, I would set them up in another file, e.g. /etc/nginx/sites-enabled/router.somewhere.com, with contents like such:

server {
    server_name router.somewhere.com;

    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 $host;
    proxy_set_header X-Forwarded-Port $server_port;
    proxy_connect_timeout 300s;
    proxy_send_timeout 300s;
    proxy_read_timeout 300s;

    # Only needed if you use HTTPS to the upstream server
    proxy_ssl_session_reuse on;
    proxy_ssl_verify on;
    proxy_ssl_verify_depth 3;
    proxy_ssl_trusted_certificate /etc/nginx/snippets/cert-ca-lzs.pem;

    location / {
        proxy_pass https://192.168.1.1;
        proxy_set_header Host $host;

        proxy_set_header Destination $dest;

        proxy_http_version 1.1;
        proxy_ssl_name 192.168.1.1;
    }
}

The upstream server (i.e. router) in this case is at 192.168.1.1.

You may have noticed I seemed to have left out steps about configuring the reverse proxy itself to serve HTTPS. That’s because I wanted to leave it to Certbot to do the magic. Just install certbot via snap “snap install certbot –classic”, and then run certbot. Follow the prompts and let it do its magic.

Nginx can serve all the requests via HTTPS port 443 (and/or HTTP port 80), and differentiate between the actual upstream service required based on the requested hostname. This would be indicated in the HTTPS/HTTP request headers.

In other words, in the examples above, the two nas.somewhere.com and router.somewhere.com are on the same Nginx reverse proxy, which will then sort out whether the request should go to 192.168.1.100 or 192.168.1.1. You can add more reverse proxy configurations. The Nginx server itself can also serve static files locally at the same time.

I hope the information here is useful for others trying to configure a reverse proxy in front of the QNAP NAS.

Exit mobile version