Back to Articles
Security2026-04-208 min read

How I Closed Port 22 on My Server and Still SSH Into It Every Day

Most developers leave SSH wide open on their servers. I locked it down completely using Zero Trust — here is exactly how I did it and why you should too.

Zero TrustNetbirdSSHLinuxSecurity

How I Closed Port 22 on My Server and Still SSH Into It Every Day

After deploying my project to a live server, my Wazuh SIEM was up and running too. What I noticed right away was a ton of IPs hitting my SSH port—Wazuh kept logging failed attempts, one after another. By the end of the first day, I’d already seen several thousand failed SSH attempts from bots all over the world. The traffic was coming from everywhere.

Even though I always log in securely from my own system and never set a password for SSH (which would take forever for anyone to crack), I still thought: why should this port be reachable at all?

So I closed port 22. Here’s exactly how I did it and how I still SSH into my server every day, securely, with zero stress and no extra steps.

The Problem with Leaving SSH Open

Every server with a public IP address gets scanned. It does not matter how obscure your IP is or how new your server is. Automated tools crawl the entire IPv4 address space continuously. If you have port 22 open, someone is trying to get in right anyhow.

The common advice is to use strong passwords or SSH keys and not worry about it. SSH keys are indeed much stronger than passwords, and with key-based authentication the brute force attempts will not get anywhere. But that assumes nothing ever goes wrong with your key management, your SSH configuration, or any other service running on that port.

The better question is: why should port 22 be reachable by anyone who is not you?

The answer is that it should not be. And with Zero Trust Network Access, it does not have to be.

What Zero Trust Actually Means Here

Zero Trust may sound like a buzzword. But the core idea is simple: nothing on your network is trusted by default. Every connection has to prove its identity before it gets through.

For a single developer with a VPS, this translates to one practical question: instead of having SSH open to the entire internet and hoping nobody gets in, what if SSH was only reachable by devices you explicitly enrolled?

That is exactly what Netbird does.

How Netbird Works

Netbird is a Zero Trust Network Access tool built on WireGuard. WireGuard is a modern VPN protocol that is fast, cryptographically solid, and much simpler than older protocols like OpenVPN or IPSec.

When you install Netbird on your server and on your laptop, it creates an encrypted private network between them. Your server gets a private IP address on that network — something like 100.117.7.3. Your laptop gets its own private address. They can talk to each other through an encrypted tunnel, but that tunnel is invisible to everyone else.

Here’s the important part: you set SSH to listen only on the Netbird network interface, not the public IP. Then you close port 22 on the public firewall completely.

From the outside, port 22 does not exist. From your enrolled laptop, SSH works exactly as it always did.

Step by Step — What I Actually Did

1. Install Netbird on the server

curl -fsSL https://pkgs.netbird.io/install.sh | sh
netbird up

After running this, your server appears in the Netbird dashboard at app.netbird.io. You will see it listed as a peer with its assigned private IP address.

2. Install Netbird on your local machine

Download and install the Netbird client for your OS from netbird.io. Log in with the same account. Your laptop now appears as a second peer in the dashboard. The two peers can reach each other through the encrypted tunnel.

3. Find your server's Netbird IP

In the Netbird dashboard, your server will have a private IP like 100.x.x.x. Copy this. You will use it for SSH going forward.

4. Restrict SSH to the Netbird interface

Edit your SSH configuration:

sudo nano /etc/ssh/sshd_config

Find or add the ListenAddress line:

ListenAddress 100.117.7.3

Replace 100.117.7.3 with your actual Netbird IP. Save and restart SSH:

sudo systemctl restart sshd

Important: Before closing your current terminal, open a second SSH session through the Netbird IP to confirm it works. Do not close your existing session until you have verified the new one connects. Otherwise you can lock yourself out.

5. Close port 22 on the public firewall

sudo ufw deny 22
sudo ufw reload

6. Verify from outside

Go to portchecker.co and test port 22 against your server's public IP. You should see it come back as closed or timed out. That means from the perspective of the entire internet, your SSH port does not exist.

![portchecker.co showing port 22 closed on the public IP]

What This Looks Like in Practice

My daily workflow has not changed at all. I open my terminal and SSH into the server the same way I always did — except now I use the Netbird private IP:

ssh user@100.117.7.3

Netbird runs in the background on my laptop and connects automatically. I do not think about it. The tunnel is just there.

The difference is that every automated scanner hitting my public IP hits a closed port and moves on. The attack surface is gone.

What About CI/CD Pipelines?

You might be wondering: if port 22 is closed, how does GitHub Actions (or any CI/CD tool) deploy to the server?

The answer is that you enroll your CI/CD runner in the Netbird network as well. In GitHub Actions, you can add a Netbird setup step at the start of your deploy workflow that authenticates using a service account token and brings up the tunnel before the SSH deployment step runs.

- name: Connect to Netbird
  run: |
    curl -fsSL https://pkgs.netbird.io/install.sh | sh
    netbird up --setup-key ${{ secrets.NETBIRD_SETUP_KEY }}
    sleep 5

- name: Deploy via SSH
  run: ssh user@100.117.7.3 './deploy.sh'

The setup key is a one-time enrollment token you generate in the Netbird dashboard. Store it as a GitHub Actions secret and your pipeline connects to the private network automatically before trying to deploy.

Is This Overkill for a Small Project?

No. And here is why.

The effort to set this up is about two hours the first time. After that, your workflow is identical to what it was before. You SSH the same way, deploy the same way, and manage the server the same way.

What you get in return is that port 22 on your public IP is permanently closed. There are no more brute force attempts to worry about. No need to watch fail2ban logs. No exposure from a misconfigured authorized_keys file. The attack surface at the network level is simply removed.

For a project that handles user data, authentication, or anything sensitive, taking this step is an ultimately secure approach—and it’s definitely worth the effort and time.

Related Articles

Continue reading

More writing on security, backend systems, architecture, and practical development.

Security
8 min read

How I Built an Automated Web Security Scanner — How It Works and How Your Website Benefits From It

Most websites have security loopholes their owners do not know about. I built a tool that finds them automatically — here is how it works and what it checks.

SecurityTypeScriptNode.jsExpressOWASPDMARCSSL
Security
9 min read

Developers Are the First Line of Defence — What Secure SDLC Actually Means

Security conversations tend to focus on the user. But many of the real problems start much earlier — in the decisions developers make during design, development, and deployment.

SSDLCSecuritySTRIDEOWASPSecure Development
Security
9 min read

Sessions vs JWT — They Are Not Alternatives, They Solve Different Problems

Every developer has seen the debate. Sessions or JWT? The truth is that framing the question that way misses the point entirely.

JWTSessionsAuthSecurityNode.js
Related Project

See the project behind the thinking

This article connects directly to practical backend architecture and secure system design work.

Node.jsTypeScriptPostgreSQLExpressJWTRBACSecurity

Reusable Secure Auth System

A production-deployed authentication and authorization backend built under Secure SDLC principles. Supports hybrid authentication for browser and API clients, role-based access control, and a full security documentation suite.