On this page
A dedicated VPN address makes SSH allowlists manageable: your laptop can move between networks while cloud servers see one predictable source. The useful security property is not secrecy of that address, but a smaller, explicitly controlled path to your SSH daemon.
This walkthrough covers address selection, layered firewall rules, key authentication, and lockout testing. It assumes an Ubuntu cloud server, sudo access, a working VPN subscription or gateway, and access to the provider's recovery console.
Identify the address your server actually sees
A dedicated exit IP is a public source address assigned to your VPN service. It is not the laptop's private tunnel address. For example, your laptop might use 10.44.0.2, while the cloud server sees connections arriving from 198.51.100.27 after the VPN gateway applies network address translation.
Use the Subnet & CIDR Calculator below to enter 198.51.100.27/32. Confirm the range contains exactly the intended address. The tool's membership check helps catch mistakes before an allowlist accidentally admits an entire provider network. Documentation addresses here are placeholders; substitute your verified exit address in actual rules.
Distinguish a dedicated address from a shared static exit. A shared exit stays predictable but lets other customers approach the same SSH firewall rule. Neither arrangement replaces authentication. Check whether your provider preserves the address across reconnects, device changes, and subscription changes, including any separate IPv6 assignment.
Confirm the observed source from a successful SSH session using echo "$SSH_CONNECTION" on the server. The first field is the client address as seen by SSH. Do this after connecting the VPN, and compare it with the provider's assignment. If they differ, investigate routing before changing access controls.
Prepare authentication and an escape hatch
Keep your current session open and verify the out-of-band console works before editing rules. Know how to attach a recovery environment or revert a cloud firewall rule from the provider dashboard. A saved password that nobody has tested is not a recovery plan.
Generate an SSH key on the client if you do not already have a suitable one. Give the private key a passphrase, install only its public half in the server account's authorized keys, and verify a second login. These commands assume the admin account already exists and the initial SSH path is permitted:
ssh-keygen -t ed25519 -f ~/.ssh/cloud_admin_ed25519
ssh-copy-id -i ~/.ssh/cloud_admin_ed25519.pub admin@203.0.113.10
ssh -i ~/.ssh/cloud_admin_ed25519 admin@203.0.113.10Use separate keys per device, so losing a laptop does not require replacing every administrator's credentials. Record which authorized key belongs to which person and machine. Do not copy private keys into VPN dashboards, support tickets, or server setup scripts.
After testing key login and sudo, disable password authentication and direct root login in the effective OpenSSH server configuration. Account for distribution drop-ins and earlier settings: inspect sshd -T, run sudo sshd -t, and reload the appropriate service only when validation succeeds. Confirm keyboard-interactive authentication does not retain an unwanted password path. Keep your working session throughout.
Apply the same boundary at both firewall layers
Start with the cloud firewall. Add an inbound TCP rule for port 22 with source 198.51.100.27/32, leaving your temporary administrative exception in place until testing succeeds. Security groups and provider firewalls differ in statefulness and outbound defaults; inspect those settings rather than assuming they match UFW.
On Ubuntu, review UFW's documentation before enabling a firewall remotely. The following example preserves a temporary source, adds the VPN exit, and establishes a deny-by-default inbound policy. Replace both sources and confirm any required web or application ports are already allowed.
sudo ufw status numbered
sudo ufw allow from 192.0.2.44/32 to any port 22 proto tcp
sudo ufw allow from 198.51.100.27/32 to any port 22 proto tcp
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enable
sudo ufw status verboseThe temporary address represents your currently working administrative path, not a universal recovery address. Never copy these documentation ranges unchanged. Remove older broad SSH rules, including an OpenSSH profile allowed from anywhere, only after inspecting the resulting policy and testing the narrow rule.
Treat IPv6 separately. If SSH listens on IPv6 and the server has a public IPv6 address, an IPv4 allowlist alone does not close that path. Add the corresponding restricted IPv6 rule or deliberately deny inbound IPv6 SSH in both layers. Do not disable IPv6 casually on a server that depends on it.
Verify routing before removing temporary access
An allowlist only works if SSH actually leaves through the VPN. With split tunneling, a client might send ordinary browsing through the tunnel while excluding terminal traffic or specific cloud ranges. Consult the WireGuard quick start if you manage the tunnel yourself; routing and peer selection are distinct from SSH authorization.
On a Linux client, inspect the selected route and then establish a fresh connection:
ip route get 203.0.113.10
ssh -o IdentitiesOnly=yes -i ~/.ssh/cloud_admin_ed25519 admin@203.0.113.10
# Run inside the new server session:
echo "$SSH_CONNECTION"
sudo journalctl -u ssh --since "10 minutes ago"A full-tunnel client may use policy routing, so a simple route listing is not always the whole story. The server-observed source and a successful new TCP connection are stronger evidence. Existing connections can survive rule changes because of connection tracking; they cannot prove that new sessions will work.
Test a fresh connection with the VPN disconnected from a separate terminal. It should fail once only the VPN source remains permitted. Reconnect, verify success, and then remove the temporary exception in both firewalls. Recheck from an independent network when practical.
Maintain the boundary without overselling privacy
A dedicated exit creates a stable identifier that destinations can correlate. It improves operational predictability, not anonymity. The VPN operator can observe connection metadata, and a compromised authorized laptop can still attack SSH through the permitted address. Use security updates, least-privilege accounts, and prompt key revocation alongside network restrictions.
Revisit the subnet calculator whenever a provider changes your assignment. Avoid broadening a /32 to a provider subnet just to make intermittent access work. Diagnose the changing exit first; otherwise you trade a clear boundary for an undocumented trust expansion.
Keep a short recovery runbook containing the provider console procedure, firewall identifiers, authorized administrators, and the expected public exit. Store credentials separately. Test recovery after major network changes, and alert on failed SSH activity without treating every scan as a breach.
For operating-system hardening beyond the VPN boundary, follow the Ubuntu VPS security walkthrough. For provider-side network controls and console procedures, use the relevant DigitalOcean documentation. Follow your employer's acceptable-use rules when administering organizational infrastructure; a personal VPN is not permission to bypass managed access requirements.
Key takeaways
- Allowlist the observed public exit, not a private tunnel address, and use a single-host prefix when appropriate.
- Keep key authentication and least privilege even when only one source address reaches SSH.
- Test fresh connections, IPv6 behavior, and VPN-disconnected failure before removing temporary access.
- Maintain independent recovery access and update both cloud and host firewall rules when assignments change.
Reach designers & developers at work
Put your product next to the tools people use every day. One sponsor per placement, no tracking pixels.
Become a sponsor