Skip to content

OpenVPN Jump Host

The jump host (mbptillo) runs an OpenVPN server that authenticates users against OpenLDAP, providing secure remote access to the homelab network. It shares port 443 with HTTPS and SSH via sslh, so no extra firewall rules are needed.

Traffic Flow

flowchart TD
    client["OpenVPN client\nvpn.home.tillo.ch:443"]

    subgraph bpir4["BPI-R4"]
        dnat["TCP :443 DNAT\n→ mbptillo:4443"]
    end

    subgraph mbptillo["mbptillo"]
        sslh["sslh :4443\nprotocol demux"]
        openvpn["OpenVPN\n127.0.0.1:9443\nTCP mode"]
        sssd["sssd + PAM\nLDAP auth"]
        tun["tun10\n10.8.10.0/24"]
    end

    subgraph k8s["mdapi-prod"]
        ldap["OpenLDAP\n192.168.1.52"]
        services["K8s services\n192.168.1.x"]
    end

    client --> dnat --> sslh
    sslh -->|"OpenVPN detected"| openvpn
    openvpn --> sssd --> ldap
    openvpn --> tun --> services

Components

Component Location Notes
sslh mbptillo :4443 Protocol demux; routes OpenVPN to :9443, SSH to :22, TLS to ingress-nginx
OpenVPN server mbptillo :9443 (TCP) Subnet 10.8.10.0/24 via tun10
sssd mbptillo PAM auth → OpenLDAP at 192.168.1.52; cache_credentials=true
EasyRSA PKI /etc/openvpn/easyrsa/pki-root EC/prime256v1 CA; ta.key for tls-auth
vpn-nat.service mbptillo systemd iptables MASQUERADE for 10.8.10.0/24
ovpn-admin K8s openvpn namespace Web UI at vpnadmin.home.tillo.ch; reads PKI via hostPath

Authentication

OpenVPN uses PAM for user authentication, which chains through sssd → OpenLDAP. This means VPN access is controlled by the same LDAP directory as mail and other services — no separate user database to maintain. Certificates act as a second factor (tls-auth with ta.key); username/password provides the identity.

cache_credentials=true in sssd ensures users can authenticate even if the cluster is temporarily unreachable (e.g. during a maintenance window).

ovpn-admin

ovpn-admin provides a web UI for generating client .ovpn files. It connects to the OpenVPN management socket at 127.0.0.1:7505 and reads the EasyRSA PKI from the host filesystem via a Kubernetes hostPath volume.

The pod runs with hostNetwork: true and is pinned to mbptillo via nodeSelector. It is deployed via kubectl apply against the mdapi-rancher management cluster (not through Fleet) since it has tight coupling to the mbptillo host.

Why sslh instead of a dedicated port?

Exposing port 443 externally is reliably unblocked on all networks — hotels, corporate firewalls, mobile carriers. A dedicated VPN port (1194, 1194/UDP, etc.) is frequently blocked. sslh allows SSH, HTTPS, and OpenVPN to coexist on 443, so the jump host is reachable regardless of network restrictions.