Skip to content

Remote Access

Remote access to the homelab is built around three seats — three independent places to work from, so losing any one of them never means losing access. The OpenVPN endpoint described on the rest of this page belongs to the first seat.

Three Seats

Seat Where Role
mbptillo Workstation Build/fix seat — full toolchain for development and repair work; hosts the sslh endpoint (OpenVPN / SSH / mosh sharing vpn.home.tillo.ch:443, external VIP 31.3.128.58)
Jump pod In-cluster, jump namespace, LoadBalancer 192.168.1.62 Recurrent/mobile seatssh -p443 plus mosh + byobu for day-to-day and on-the-go sessions; automation state lives on its PVC, independent of the workstation
bpi-r4 jumpfail Edge router Emergency seat — a self-contained failover work environment baked into the router firmware, restorable from the NAS; see BPI-R4 → Jumpfail Seat

The seats degrade gracefully: everyday sessions land on the jump pod, heavier work happens on mbptillo, and if both the workstation and the cluster are down, the router still provides a working seat.

OpenVPN

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

Traffic Flow

flowchart TD
    client["OpenVPN client\nvpn.home.tillo.ch:443\n(external VIP 31.3.128.58)"]

    subgraph bpir4["BPI-R4"]
        dnat[":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 for vpn.home.tillo.ch:443; routes OpenVPN to :9443 and SSH (plus mosh sessions bootstrapped over it) to :22
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

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).

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 lets OpenVPN, SSH, and mosh coexist on 443, so the endpoint is reachable regardless of network restrictions. The jump pod applies the same trick on its own address — ssh -p443 against 192.168.1.62.