Skip to content

VPN setup

  • Buy a domain name from a DNS registrar / provider (ie Namecheap)
    • In the future may need to use Cloudflare as the provider (while using the existing registrar)

Linode

sudo su
ufw allow ssh
ufw allow http
ufw allow https
ufw allow 3478
ufw allow 41641
ufw enable
  • Block brute force attempts
cd /root/homelab-rendered
apt install -y fail2ban python3-systemd
cp src/debian/jail.local /etc/fail2ban
systemctl enable fail2ban
systemctl restart fail2ban
  • Setup Headscale (src)
src/vpn/install_svcs.sh headscale
cp src/headscale/headscale_private.yaml /etc/headscale/config.yaml
systemctl restart headscale
  • Create an A record in Namecheap that maps your vpn subdomain to Linode's public IP (src)
  • Create pre-auth key
headscale completion bash > /etc/bash_completion.d/headcompletion
headscale users create admin@
headscale --user USER_ID preauthkeys create --expiration 100y

Clients

  • For home network, use Tailscale plugin on pfSense (src)
    • Setup as an Exit Node for the desired subnet
    • Restart pfSense (issue)
    • On the VPN server, enable pfSense's routes, ref
headscale nodes list-routes
# Repeat for all desired subnets
headscale nodes approve-routes --identifier NODE_ID --routes 0.0.0.0/0,::/0
headscale nodes approve-routes --identifier NODE_ID --routes 192.168.5.0/24
headscale nodes approve-routes --identifier NODE_ID --routes 192.168.7.0/24

# Create users

headscale users create jane@
headscale users create jayden@
headscale users create jasper@
# add_more_users
headscale users create guest1@

# for each user:
headscale --user USER_ID preauthkeys create --expiration 2h
  • For MacOS, use Tailscale app (src), or
brew install tailscale
sudo /opt/homebrew/opt/tailscale/bin/tailscaled
tailscale login --login-server https://vpn.janedoe.com:443 --accept-routes --auth-key AUTH_KEY

tailscale status
tailscale ping --tsmp other_node
# sudo tailscale set --exit-node=pfsense
  • For Debian bookworm (src)
src/vpn/install_svcs.sh tailscale
sudo tailscale up --login-server https://vpn.janedoe.com:443 --accept-routes --authkey AUTH_KEY
# --exit-node=pfsense
  • In cloud VM, check connected nodes: sudo headscale nodes list

Add public endpoint

Summary: Create a public user and a tailscale client on the vpn server. HAProxy forwards the vpn subdomain to Headscale on ports 8080, 8443. Vaultwarden, authelia, lldap and home assistant traffic is sent to the secsvcs VM via a local traefik instance (reverse proxy). All other traffic is sent to the websvcs VM also via traefik. Similar to Tailscale Funnel, and fulfills a similar role as a DMZ.

Notes: site-to-site, ACLs, troubleshooting

  • Route traffic via HAProxy
sudo su
cd /root/homelab-rendered
src/vpn/install_svcs.sh haproxy
  • Update Headscale
  • src/vpn/install_svcs.sh headscale
  • Update Namecheap A record

    • Change the host from vpn to all subdomains *
    • Add another A record for the bare domain, host = @
  • Add a Namecheap CAA record, ref

@ issue     letsencrypt.org
@ issuewild letsencrypt.org
@ iodep     mailto:jdoe@gmail.com
  • Create public user and connect, ref, snat
# Create user
headscale users create public
headscale --user USER_ID preauthkeys create --expiration 100y
# Setup client
src/vpn/install_svcs.sh tailscale
tailscale up --login-server https://vpn.janedoe.com:443 --accept-routes --snat-subnet-routes=false --authkey AUTH_KEY
# Clamp MTU
iptables -t mangle -A FORWARD -i tailscale0 -o eth0 -p tcp -m tcp \
  --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

# Hack ACLs via ufw, order of precedence = ASC 
ufw reset
ufw default deny incoming
ufw default deny outgoing
ufw allow out 53
ufw allow out on eth0
ufw allow in from any to any port 22,80,443 proto tcp
ufw allow in from any to any port 3478,41641 proto udp
ufw allow out on tailscale0 from any to 192.168.2.20 port 80,443 proto tcp
ufw allow out on tailscale0 from any to 192.168.4.20 port 80,443 proto tcp

# insert before the COMMIT
vim /etc/ufw/before.rules
# allow outbound icmp
-A ufw-before-output -p icmp -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
-A ufw-before-output -p icmp -m state --state ESTABLISHED,RELATED -j ACCEPT
ufw enable
ufw status verbose

Headscale UI (optional)

  • Create the OIDC credentials
exit
exit
ssh jdoe@secsvcs.janedoe.com
sudo podman run --rmi docker.io/authelia/authelia:latest authelia crypto rand --length 72 --charset rfc3986
sudo podman run docker.io/authelia/authelia:latest authelia crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986
# Store hashed and raw version of secret under hs_ui_oidc_secret(_hash). id under hs_ui_oidc_id
ssh -t jdoe@pve1.janedoe.com 'sudo /root/homelab-rendered/src/pve1/secret_update.sh secsvcs'
# Restart authelia to pick up hashed secret version
sudo systemctl restart authelia
exit
  • Generate and store the secrets
ssh jdoe@12.34.56.78
sudo su
mkdir -p /etc/opt/secrets
chmod 700 /etc/opt/secrets
headscale apikeys create | tail -n 1 > /etc/opt/secrets/headscale_api_key
openssl rand -base64 32 > /etc/opt/secrets/hs_ui_storage_key
# Grab the oidc client id and the raw version of the client secret from the steps above
vim /etc/opt/secrets/hs_ui_oidc_id
vim /etc/opt/secrets/hs_ui_oidc_secret
chmod 600 /etc/opt/secrets/*
  • Deploy on the VPN server
src/vpn/install_svcs.sh headscale-ui

Upgrade

upgrade doc