Part 4 - Proxy HTTP(S) into the internal network

Proxmox VE
Remote access
Network
DNS
Proxy
Nginx
Make your internal network accessible from the outside
Author

ProtossGP32

Published

January 17, 2023

What’s the idea here?

We want to use Nginx as reverse proxy to make HTTP(S) requests to internal machines without noticing that they are actually on a private network.

Expected requests workflow

  1. Someone requests test-a.pve-internal.protossnet.local
    • We need to make sure the domain resolves to our Proxmox server (HOW???)
  2. If HTTPS is being used, Nginx provides a valid TLS certificate
    • We need a wildcard certificate for *.pve-internal.protoss.local (HOW???)
  3. Nginx proxies the request to the internal machine
    • It uses the same protocol that is being requested

Reverse proxy for Specific Machines

If only a single machine shall be available from outside the internal network, a machine-specific configuration can be added to nginx in the following path:

/etc/nginx/sites-available/test-a
server {
  listen 80;
  listen [::]:80;
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name test-a.pve-internal.protossnet.local;

  # Certificate should be valid for the domain
  ssl_certificate_key /etc/pve/local/pve-ssl.key;
  ssl_certificate     /etc/pve/local/pve-ssl.pem;

  # Proxy configuration
  location / {
    # Use internal DNS server for name resolution
    resolver 10.0.0.2;
    set $backend $scheme://test-a.pve-internal.protossnet.local;
    proxy_pass $backend;
    proxy_buffering off;
    client_max_body_size 0;
    proxy_set_header Host $host;
    proxy_ssl_name $host;
  }
}
Use the internal DNS!

Make sure to use the internal DNS server to resolve the internal hostname. This will return the internal IP address, and Nginx can use that to reverse proxy to the internal server.

As always, we must create a softlink from the sites-enabled directory to make it available:

cd /etc/nginx/sites-enabled
ln -s /etc/nginx/sites-available/test-a

We check if Nginx can spot any errors:

nginx -t

Finally, restart Nginx

# Restart the Nginx service
systemctl restart nginx.service

Reverse proxy for all machines

ATTENTION!!

This will expose all internal network machines to the external network via HTTP(S). Your internal network thus has only limited protection against attacks via HTTP.

For automated proxying to any internal machine, use this file:

/etc/nginx/sites-available/internal-host-proxy
server {
  listen 80;
  listen [::]:80;
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name *.pve-internal.protossnet.local;

  ssl_certificate_key /etc/pve/local/pve-ssl.key;
  ssl_certificate     /etc/pve/local/pve-ssl.pem;

  # Proxy configuration
  location / {
    # Use internal DNS server for name resolution
    resolver 10.0.0.2;
    set $backend $scheme://$host;
    proxy_pass $backend;
    proxy_buffering off;
    client_max_body_size 0;
    proxy_set_header Host $host;
    proxy_ssl_name $host;
  }
}

This is a more generic version of the single-host configuration that uses wildcard to match any host and also now sets the backend dynamically

Remember to create the softlink, test and restart the service as always:

cd /etc/nginx/sites-enabled
ln -s /etc/nginx/sites-available/internal-host-proxy
nginx -t
systemctl restart nginx

⚠ Don’t Proxy to External Servers!

As we have deleted the default configuration, now our server proxies any unknown server name. We have to explicitly define a default server which just returns a static response instead. Create the following file:

/etc/nginx/sites-available/default
server {
  listen 80 default_server;
  listen [::]:80 default_server;
  listen 443 ssl http2 default_server;
  listen [::]:443 ssl http2 default_server;
  server_name _;

  ssl_certificate_key /etc/pve/local/pve-ssl.key;
  ssl_certificate     /etc/pve/local/pve-ssl.pem;

  location / {
    return 404 'Not found\n';
  }
}

Remember to create the softlink, test and restart the service as always. Now any impersonation attempt will be answered with a 404 Not found:

curl -i http://test-a.pve-internal.protossnet.local -H 'Host: google.com'
HTTP/1.1 404 Not Found