
Vultr's NetBird deployment article provides a useful starting point for identity-controlled private administration. For an existing SSH server, define success in testable terms: authorized administrators can connect, revoked identities cannot, and public port 22 no longer reaches the origin.
1. Decide where access is enforced
| Option | Useful property | Operational question |
|---|---|---|
| VPN | Private access for multiple protocols | Does one login expose too much network? |
| Bastion | Central SSH entry point | Who maintains and monitors that host? |
| Outbound tunnel | No origin-side inbound port needed | What happens when the connector fails? |
| Zero Trust access | Identity-based authorization | How are MFA, scope and revocation enforced? |
VPNs and bastions can be parts of a Zero Trust design; neither establishes that design by itself. Our worked path is SSH client → Cloudflare Access/identity provider → outbound tunnel → local cloudflared → sshd. Access authentication does not replace the server's Unix account and SSH credentials.
You need a Cloudflare-managed domain, Access/Tunnel administration rights, cloudflared on the client and server, individual SSH keys, sudo and a tested out-of-band console. The firewall example assumes UFW is already active. Adapt it if your host uses another firewall manager.
sudo ss -lntpsudo sshd -tsudo ufw status verbosesudo ufw status numberedsudo systemctl status ssh --no-pagerFind every path exposing 22: explicit rules, OpenSSH profiles, broad network rules, cloud security groups, NAT and IPv6. Record the actual public addresses and emergency access route. Keep your current session open, but do not mistake that surviving session for a successful reconnect test.
2. Establish the new route before closing the old one
- Create an Access application for your chosen hostname, such as
ssh.example.com. Allow only the administrator group. Configure SSO and require MFA through the IdP or available Access policy controls. Avoid broad Bypass rules. - Create the tunnel in the dashboard and install its connector as a service on the server. Treat the installation token as a credential; do not commit it or include it in screenshots.
- Add a published SSH application route pointing to
localhost:22. This assumes cloudflared runs on the SSH server itself. Preserve the connector's required outbound connectivity. - Confirm Access protects that exact hostname. Add this client configuration:
Host shop-admin HostName ssh.example.com User deploy IdentityFile ~/.ssh/id_ed25519 IdentitiesOnly yes ProxyCommand cloudflared access ssh --hostname %hEnsure cloudflared is on the client's PATH, replace the hostname and account, then make a fresh connection:
ssh shop-adminComplete the browser-based identity check, verify the SSH host key through a separate trusted channel and test the intended sudo permissions. A user outside the allowed group must be denied before reaching sshd.
Only after testing both the tunnel and emergency console should you remove public ingress. If the existing public allowance is exactly UFW's allow 22/tcp and unmatched inbound traffic is already denied:
sudo ufw delete allow 22/tcpsudo ufw status numberedThis deletes that specific rule; it does not remove other broad allowances. Remove equivalent rules you identified, including the cloud firewall opening and IPv6 exposure. Do not reset the firewall or enable UFW blindly on a remotely administered host.
3. Test the closed port and the recovery route
From an external machine authorized to test your server, substitute its real address:
nc -vz -w 5 203.0.113.10 22ssh shop-adminsudo journalctl -u cloudflared --since '-15 min'sudo journalctl -u ssh --since '-15 min'Direct TCP access should fail while a new Access-mediated SSH session succeeds. Repeat for the public IPv6 address if one exists.
Send Access identity events, Unix authentication records and sudo activity to a central collector. Connection logs are not complete command recordings; use a dedicated mechanism if that is required. Monitor failed MFA, group membership changes, connector health and login latency.
Test revocation explicitly. Removing a person from an identity group does not necessarily terminate an SSH session already running. Invalidate the appropriate Access sessions and terminate Unix sessions where your incident procedure requires it.
This design adds dependencies on DNS, the identity provider and the tunnel service. Measure reconnect behavior and transfer performance, and design connector redundancy for your actual topology. Exercise a provider outage before declaring the path production-ready.
For emergency access, maintain an independently reachable console, named operator credentials and MFA where available. Store its recovery credentials outside the same IdP dependency. Through that console, an authorized operator can temporarily restore port 22 access from a single administrative IP, repair the tunnel and remove the exception. Record the reason, owner and expiry. A permanent worldwide SSH opening is not the rollback plan.
Sources: Vultr's NetBird private-network deployment, Cloudflare client-side cloudflared SSH.