OpenVPN upstreams
Use type: openvpn, source.kind: config, and a .ovpn file. The parser requires at least one remote. It reads protocol, device, connection blocks, and redirect-gateway, then hands the validated original text to OpenVPN inside the tunnel namespace.
External file references and command references are rejected, including certificate/key paths, auth-user-pass paths, scripts, plugins, and prefixed forms. Put certificates, keys, and a complete username/password pair inline, then SOPS-encrypt the one file:
clientdev tunproto udpremote openvpn.example.com 1194auth-user-pass<auth-user-pass>synthetic-usersynthetic-password-replace-before-use</auth-user-pass><ca>-----BEGIN CERTIFICATE-----SYNTHETIC-DOCUMENTATION-CERTIFICATE-----END CERTIFICATE-----</ca><cert>-----BEGIN CERTIFICATE-----SYNTHETIC-DOCUMENTATION-CERTIFICATE-----END CERTIFICATE-----</cert><key>-----BEGIN PRIVATE KEY-----SYNTHETIC-DOCUMENTATION-KEY-----END PRIVATE KEY-----</key>On a fresh hub, open a root SSH session and bootstrap the age identity consumed by the systemd units:
ssh root@"$HUB_HOST"At the remote root prompt, run:
apt-get updateapt-get install -y ageinstall -d -m 0700 /etc/vpn-hub/age /etc/vpn-hub/secretstest ! -e /etc/vpn-hub/age/keys.txtage-keygen -o /etc/vpn-hub/age/keys.txtchmod 0600 /etc/vpn-hub/age/keys.txtage-keygen -y /etc/vpn-hub/age/keys.txt > /etc/vpn-hub/age/recipient.txtchmod 0644 /etc/vpn-hub/age/recipient.txtCreate plaintext only on tmpfs and encrypt it explicitly as binary data. Point source.value in hub.yaml at secrets/office.ovpn.sops.binary, then prove the actual runtime decrypt and OpenVPN parser paths:
umask 077plain=$(mktemp /run/office-ovpn.XXXXXX)encrypted=$(mktemp /run/office-ovpn-sops.XXXXXX)trap 'rm -f -- "$plain" "$encrypted"' EXIT${EDITOR:-vi} "$plain"recipient=$(cat /etc/vpn-hub/age/recipient.txt)sops encrypt --age "$recipient" --input-type binary --output-type binary "$plain" \ > "$encrypted"install -o root -g root -m 0600 "$encrypted" \ /etc/vpn-hub/secrets/office.ovpn.sops.binaryrm -f -- "$plain" "$encrypted"trap - EXITexport SOPS_AGE_KEY_FILE=/etc/vpn-hub/age/keys.txtsops --decrypt /etc/vpn-hub/secrets/office.ovpn.sops.binary >/dev/nullhubctl --config /etc/vpn-hub/hub.yaml validateverify_state=$(mktemp -d /run/vpn-hub-verify.XXXXXX)trap 'rm -rf -- "$verify_state"' EXIThubctl --config /etc/vpn-hub/hub.yaml deploy --state-dir "$verify_state"vpn-hub-agent reconcile --dry-run --state-dir "$verify_state" \ --config-dir /etc/vpn-hub --server-key /etc/vpn-hub/server.keyrm -rf -- "$verify_state"trap - EXITExpected result: decryption exits zero and dry-run prints planned operations rather than a SOPS or OpenVPN parser error. Back up /etc/vpn-hub/age/keys.txt separately; rotate a disclosed identity by re-encrypting every provider file to a new recipient before removing the old key.
The placeholders are not valid credentials. A private-network OpenVPN profile must not contain redirect-gateway; VPN Hub rejects it because it would make that namespace a default route.
Next: Private networks and Secrets.