Notes and templates from running a single small VPS as a family VPN endpoint, and from the security audit I did on it afterwards.
The point of this repository is one design decision, applied consistently:
Nothing that manages the server is reachable from the internet. The admin panel, the file sync service and everything else with a login page listen only on an internal WireGuard subnet. The only things the public internet can reach are the VPN entry points and a rate-limited SSH port.
Everything here uses placeholders. No addresses, ports, hostnames or keys from a real
deployment appear in this repository — pick your own values as described in
hardening-checklist.md.
internet
|
+---------------+----------------+
| | |
VPN inbounds WireGuard SSH (rate-limited,
(xray/VLESS) <WG_PORT>/udp keys only, <SSH_PORT>)
| |
| v
| admin tunnel <ADMIN_SUBNET>
| |
| +---------+----------+
| | |
| admin panel WebDAV / file sync
| <PANEL_PORT> <HTTP_PORT>
| (tunnel only) (tunnel only)
v
family devices
Two separate planes:
- Data plane — the VPN itself (VLESS + Reality over xray). Public by necessity: family devices connect to it from arbitrary networks.
- Management plane — the xray admin panel and the WebDAV endpoint used to sync notes between devices. Both bind to the host but are firewalled to the WireGuard subnet, so from the internet they do not exist.
WireGuard here is not a privacy tunnel. It is the administrative access path, and all
clients run split tunnel (AllowedIPs = <ADMIN_SUBNET>) so only traffic to the server
goes through it.
| Component | Choice | Why |
|---|---|---|
| OS | Ubuntu 22.04 LTS | boring, long support window |
| VPN | xray, VLESS + Reality | survives protocol filtering better than plain TLS |
| Panel | 3x-ui | web management for xray — never exposed publicly |
| Admin access | WireGuard | small, fast, and trivial to firewall by subnet |
| File sync | Apache WebDAV | notes sync, content encrypted client-side before upload |
| Firewall | ufw | enough for a single host, easy to audit |
| Certificates | acme.sh, Let's Encrypt, DNS-01 | see below |
| Intrusion response | fail2ban | sshd and the WebDAV auth jail |
A management interface on a high port is still on the internet. ufw allow from <ADMIN_SUBNET> to any port <PANEL_PORT> is a different statement from
ufw allow <PANEL_PORT>, and only the first one is a security control.
After this change an external scan sees the VPN ports and nothing else. The panel is not "hard to find" — it is unreachable.
The obvious way to renew a certificate is HTTP-01 validation, which needs port 80 open to the world. The common workaround is worse: a cron job that opens the port, runs the renewal, and closes it again.
That job is a scheduled hole. If the renewal hangs, the port stays open, and nothing alerts you.
DNS-01 validation removes the problem instead of scheduling around it: acme.sh proves
control of the domain through the DNS provider's API, and port 80 is never opened at all.
Any DDNS or DNS provider with an acme.sh DNS plugin works.
The most useful part of the audit was deleting rules, not writing them. On my host the firewall carried allow rules for:
- a DNS port, where the resolver was actually listening on loopback only
- an internal xray port, also loopback only
- a TLS port where nothing was listening at all
None of these were exploited. All of them were lies about the system's shape, and they
made the real attack surface harder to see. ss -tulpn next to ufw status numbered,
line by line, is a twenty-minute job that pays for itself.
| File | What it is |
|---|---|
hardening-checklist.md |
The audit, as a checklist you can run against your own host |
ufw-example.sh |
Firewall policy with placeholders |
wireguard-example.conf |
Server and client templates for the admin tunnel |
This is a single host serving one family, not a production fleet. There is no configuration management, no monitoring stack, and no high availability. Backups of the server config are manual.
Automated hardening scanners will not give a host like this a high score, and chasing that number is not the goal — most of the remaining findings are about audit daemons and package tooling that make sense on a fleet and not on one VPS. The controls that matter here are the ones above: the management plane is not routable from the internet, SSH takes keys only, and certificate renewal does not open a port.
MIT — see LICENSE.