#linux #network #homelab #security

This guide explains how to set up a VPN tunnel using Wireguard and NGINX to securely connect your homelab to the internet without opening ports on your router. Youโ€™ll need a domain and a VPS with a static public IP. The VPS will forward traffic to your home network via TLS passthrough, while your homelab server connects to the VPS without requiring any port forwarding (see Figure). This setup provides secure, remote access while keeping your internal network shielded.

Firewall Setup

The following ports must be open on the Server (VPS):

PortProtocolService
80TCPHTTP
443TCPHTTPS
22TCPSSH (best to restrict access to specific IPs)
51820UDPWireGuard

The following ports must be open on the Client (Home Proxy):

PortProtocolService
80TCPHTTP
443TCPHTTPS
22TCPSSH (best to restrict access to specific IPs)

Example commands using ufw:
sudo ufw allow in on wg0 from 172.31.0.1 to any port 80 proto tcp
sudo ufw allow in on wg0 from 172.31.0.1 to any port 433 proto tcp





Generate Keys (CLIENT, SERVER)

To start, generate the keys for both the client and server. Store them securely (the location is not critical).

Run the following commands:
sudo su 
mkdir /etc/wireguard/keys && cd /etc/wireguard/keys/
umask 077; wg genkey | tee private | wg pubkey > public
chmod 600 /etc/wireguard/keys/private # Only root can read/write



Create WireGuard Config (CLIENT, SERVER)


Server Configuration: /etc/wireguard/wg0.conf
[Interface]
Address = 172.31.0.1/32
MTU = 1420 # Adjust message size (important)
SaveConfig = true
ListenPort = 51820 # Ensure port 51820 is open on your VPS
PrivateKey = <server priv-key> # The server's private key

[Peer]
PublicKey = <client1 pub-key> # The client's public key
AllowedIPs = 172.31.0.2/32

# To add more clients (peers), use the following format:
# [Peer]
# PublicKey = <client2 pub-key>
# AllowedIPs = 172.31.0.3/32


Client Config: /etc/wireguard/wg0.conf
[Interface]
PrivateKey = <client priv-key>
Address = 172.31.0.2/32
MTU = 1360 # Adjust message size (important)

[Peer]
PublicKey = <server pub-key>
Endpoint = <server pub ip>:51820
AllowedIPs = 172.31.0.1/32
PersistentKeepalive = 25


On both:
sudo wg-quick up wg0 # Starting the interfaces
sudo systemctl enable wg-quick@wg0 # Enable WireGuard at boot


To verify the VPN connection, you can ping the server's internal IP from the client or vice versa.
~ ยป ping 172.31.0.1
PING 172.31.0.1 (172.31.0.1) 56(84) bytes of data.
64 bytes from 172.31.0.1: icmp_seq=1 ttl=64 time=31.6 ms
64 bytes from 172.31.0.1: icmp_seq=2 ttl=64 time=31.0 ms


This concludes the VPN Tunnel Setup.




Setup TSL Pass-Through with NGINX

Before configuring TLS pass-through, you must enable IPv4 forwarding.

Enable IPv4 Forwarding
To enable IPv4 forwarding, edit the /etc/sysctl.conf file and either add the following line:

net.ipv4.ip\_forward\=1

Or, if the line already exists, simply uncomment it by removing the # at the beginning. After saving your changes, apply the new configuration by running:
sudo sysctl-p


Install NGINX with the ngx_stream_core_module
On Debian-based distributions, the recommended way to install this is by running:

sudo apt install nginx-full


Edit Config
Base config that includes the passthrough.conf: /etc/nginx/nginx.conf

user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log warn;

include /etc/nginx/modules-enabled/*.conf;

events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;
    sendfile on;
    #tcp_nopush on;
    keepalive_timeout  75;
    #gzip on;

    include /etc/nginx/conf.d/*.conf;
}

include /etc/nginx/passthrough.conf;


Streaming module config: /etc/nginx/passthrough.conf
stream {
        # Define upstream backend servers
        upstream syslaf-http {
                server 172.31.0.2:80 max_fails=3 fail_timeout=10s;
        }
        upstream syslaf-https {
                server 172.31.0.2:443 max_fails=3 fail_timeout=10s;
        }

        # Configure logging format and log file locations
        log_format basic '$remote_addr [$time_local] '
                         '$protocol $status $bytes_sent $bytes_received '
                         '$session_time "$upstream_addr" '
                         '"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time" '
                         '"$request"';

        access_log /var/log/nginx/syslaf_access.log basic;
        error_log /var/log/nginx/syslaf_error.log;

        # Configure server blocks for listening on specified ports
        server {
                listen 80;
                proxy_pass syslaf-http; # Forward HTTP traffic to syslaf-http
        }

        server {
                listen 443;
                proxy_pass syslaf-https; # Forward HTTPS traffic to syslaf-https
                # Uncommented since only one backend server is there currently
                # proxy_next_upstream on;
        }
}


Tighten access for the log files
  • Allow root full access
  • Allow only users in the adm group to read logs (e.g., admins, log rotation tools)
  • Block regular users

sudo chmod 640 /var/log/nginx/syslaf_*.log
sudo chown root:adm /var/log/nginx/syslaf_*.log