The long-stalled FaceKom “VirtualOffice” OpenVPN client is working on the exit-vpn Incus container on telep-mainframe, running as a full tunnel and re-exported to the tailnet as an on-demand Tailscale exit node. This note records the three real root causes (none of which was the CA), the routing discovery behind “VPN-only” FaceKom hosts, and the persistence/Tailscale wiring. Supersedes the STALLED section in SESSION-HANDOVER.
Three root causes (all previously misdiagnosed)
1. Wrong server IP — the fake “bad CA” symptom
farm.conf had remote 92.119.122.185 1194. The real gateway is 92.119.122.32.
Both IPs run OpenVPN on tcp/1194. The client therefore completed a TCP connection and got a TLS handshake — but from a different server with a different CA. That is exactly what produced the earlier conclusion “bundle CA ≠ server live CA”. The certificates were valid the entire time.
Verify the gateway before blaming certs
Two OpenVPN servers live on 92.119.122.0/24. Connecting to the wrong one presents a foreign CA and looks identical to a corrupt/stale bundle. Confirm the peer IP first.
2. Wrong crypto
Config negotiated data-ciphers AES-256-GCM + auth SHA256. The server actually uses AES-256-CBC + auth SHA1.
3. Corrupted password → the actual AUTH_FAILED
The vendor bundle’s auth.txt is labelled and TAB-delimited:
(jelszó = Hungarian for “password”; 7 chars / 8 bytes in UTF-8.)
A previously-created /etc/openvpn/tt/auth-clean.txt had only line 1 de-labelled (17 chars, correct) — line 2 was left as the raw 25-byte jelszó:<TAB><password>. OpenVPN dutifully sent the Hungarian label as part of the password:
AUTH_FAILED, user tt_lederer_andras authentication failed
Fix: the auth file must be exactly two lines — bare username, bare password. No labels, no tabs, no trailing whitespace.
Key insight: the “new” bundle wasn’t new
The freshly downloaded lederera_virtual_office.tar was byte-identical in credentials to what was already deployed:
CA fingerprint 75:1E:25:A8:98:15:27:46:35:71:BE:14:86:7F:00:62:B9:B6:F7:5E:FC:9C:D1:BB:41:49:37:E0:E0:01:A4:CA — unchanged
Client cert AF:85:37:DC:… — unchanged
The genuinely new information was in balazs’s screenshots of his working NetworkManager config, which revealed the correct gateway IP and the cipher/HMAC settings.
Lesson
When a vendor re-sends a bundle, diff the cert fingerprints before assuming new material — and actually read the screenshots. The config metadata was the payload, not the keys.
”VPN-only hosts” are a routing problem, not DNS
Hosts like https://npm.facekom.net/ were assumed to need VPN-side DNS wiring. They don’t:
npm.facekom.net resolves to 92.119.122.189 identically from the office DNS (192.168.105.1) and from public DNS (1.1.1.1).
That is a public IP behind an nginx source-IP ACL: 403 Forbidden from the home WAN, 200 OK when sourced from the VPN.
So name resolution was never involved — only the source address of the request.
WAN-vs-VPN diagnostic (reusable)
Curl the host over the normal WAN, then again with a temporary host route through the tunnel, and compare http_code + body:
curl -s -o /dev/null -w '%{http_code}\n' https://npm.facekom.net/sudo ip route add 92.119.122.189/32 dev tun0curl -s -o /dev/null -w '%{http_code}\n' https://npm.facekom.net/sudo ip route del 92.119.122.189/32 dev tun0
403 → 200 means source-IP ACL (routing). Identical results with different resolvers means DNS is not the variable.
Split tunnel → full tunnel
The server pushes no default route. PUSH_REPLY contains only:
So by default only 192.168.105.0/24 traversed tun0.
Tested whether the office NATs general egress: pinned an IP-echo service with curl --resolve plus a /32 route via tun0 → reported public IP came back as 92.119.122.32. The office does allow full internet egress.
Added redirect-gateway def1. OpenVPN then installs 0.0.0.0/1 + 128.0.0.0/1 via tun0and a host route 92.119.122.32/32 via the original gateway — preserving tunnel transport with no routing loop.
Testing gotcha — pin the IP
When checking whether a route change took effect, curl may resolve a different IP than the one you routed (CDN/Cloudflare anycast pools). An early test wrongly reported the home IP for exactly this reason. Always pin: curl --resolve host:443:<IP>.
Final working config
/etc/openvpn/tt/farm.conf on the exit-vpn container:
client
dev tun
proto tcp-client
remote 92.119.122.32 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca /etc/openvpn/tt/ca.crt
cert /etc/openvpn/tt/client.crt
key /etc/openvpn/tt/client.key
auth-user-pass /etc/openvpn/tt/auth-clean.txt
remote-cert-tls server
data-ciphers AES-256-CBC:AES-256-GCM
data-ciphers-fallback AES-256-CBC
auth SHA1
verb 3
redirect-gateway def1
openvpn.service on Debian is only a /bin/true meta-unit, and the config lives in the /etc/openvpn/tt/ subdirectory, which neither openvpn@.service nor openvpn-client@.service scans. Fix — symlink into the scanned dir and enable the templated unit:
Tailscale 1.98.9 installed in the exit-vpn container:
tailscale up --advertise-routes=192.168.105.0/24 --advertise-exit-node \ --accept-dns=false --hostname=exit-vpn
Node: exit-vpn / 100.98.208.98
net.ipv4.ip_forward + net.ipv6.conf.all.forwarding enabled via /etc/sysctl.d/99-tailscale.conf
Admin console approval required — the exit node and the subnet route are separate checkboxes. The exit node was approved; 192.168.105.0/24 may still need ticking.
Result: with the exit node selected on the Mac → public IP becomes 92.119.122.32 and npm.facekom.net returns 200. With it off → home IP, 403.
Preference is off by default, toggled on demand:
tailscale set --exit-node=exit-vpn # ontailscale set --exit-node= # off
Exit nodes do not carry RFC1918
A Tailscale exit node’s 0.0.0.0/0excludes private ranges. Reaching the office LAN by private IP (192.168.105.x) requires the advertised 192.168.105.0/24 subnet route to be approved — the exit node alone is not enough. Public FaceKom hosts work via the exit node alone.
Gotcha: tailscaled goes offline after VPN route changes
Enabling the full tunnel rewrites the default route out from under tailscaled, whose control connection was already established. The node then shows offline on the tailnet (health: “Unable to connect to the Tailscale coordination server”) even though curl to controlplane.tailscale.com succeeds and netcheck is healthy.
Fix = restart tailscaled after the tunnel comes up.
Failed approach — do NOT repeat
Adding script-security 2 + route-up /etc/openvpn/tt/ts-restart.sh to farm.confkills the VPN. openvpn-client@.service is hardened with a process limit, so OpenVPN cannot fork the script:
openvpn_execve: unable to fork: Resource temporarily unavailable (errno=11)
Exiting due to fatal error
The service enters failed and the tunnel never comes up. Any up/route-up script hits this.
Working approach — a separate systemd unit, /etc/systemd/system/tailscale-vpn-refresh.service:
Enabled and verified: restarting openvpn-client@farm brings the tunnel up and Tailscale returns Online automatically. (A completed oneshot correctly reports inactive — that is not a failure.)
Secrets hygiene
Bundle extracted to ~/telep/facekom-vpn/ on the Mac (dir 700, files 600), including balazs’s setup screenshots (2026-07-09 and 2026-07-14 sets).
Both credential tars deleted from ~/Downloads.
No certificates or passwords are stored in this vault — location reference only.