← All posts
Hardening Your Validator Server: SSH, Firewalls & Fail2Ban
validatorsecuritydevopslinuxcosmos

Hardening Your Validator Server: SSH, Firewalls & Fail2Ban

Your validator key is only as safe as the server it runs on — Ubuntu 24.04 edition

Why Server Hardening Is Part of Validator Security

Most validator security conversations jump straight to key management — HSMs, remote signers, Horcrux. Important, yes. But none of it matters if someone can SSH into your box because you left port 22 open to the world with password auth enabled.

Your validator server is a high-value target. It’s publicly known to be running infrastructure that controls staked funds and signs blocks. Bots scan the entire IPv4 range constantly looking for exactly this kind of exposure. Server hardening isn’t a “nice to have” layer — it’s the perimeter that everything else depends on.

This guide covers three pillars on Ubuntu 24.04: SSH hardening, firewall configuration (UFW), and Fail2Ban for automated intrusion prevention.

Part 1: SSH Hardening

Step 1 — Create a Non-Root User

If you’re still logging in as root, stop. Create a dedicated user with sudo access first.

adduser validator-ops
usermod -aG sudo validator-ops

Test that the new user can sudo before continuing — don’t lock yourself out.

su - validator-ops
sudo whoami # should print "root"

Step 2 — Set Up SSH Key Authentication

Password authentication is the single biggest SSH attack surface. Replace it with key-based auth.

On your local machine, generate a key pair if you don’t have one:

ssh-keygen -t ed25519 -C "validator-ops-key"

Copy the public key to your server:

ssh-copy-id validator-ops@your-server-ip

Or manually, if ssh-copy-id isn't available:

cat ~/.ssh/id_ed25519.pub | ssh validator-ops@your-server-ip "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

Verify key-based login works before disabling password auth:

ssh validator-ops@your-server-ip

Step 3 — Harden sshd_config

Edit the SSH daemon config:

sudo nano /etc/ssh/sshd_config

Apply these settings:

# Disable root login entirely
PermitRootLogin no
# Disable password authentication — keys only
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
# Disable empty passwords
PermitEmptyPasswords no
# Limit login attempts per connection
MaxAuthTries 3
# Limit concurrent unauthenticated connections
MaxStartups 3:50:10
# Disable X11 forwarding (not needed on a validator server)
X11Forwarding no
# Restrict to specific user(s)
AllowUsers validator-ops
# Optional but recommended: change default port
Port 2222

On changing the SSH port: This isn’t real security — it’s “security through obscurity” — but it does dramatically cut down on automated scanner noise hitting port 22. Combine it with the other settings above, not as a replacement for them.

Restart SSH to apply:

sudo systemctl restart ssh

Critical: Keep your current SSH session open while you test the new connection in a separate terminal. If something is misconfigured, you don’t want to be locked out.

# In a NEW terminal window — test before closing your existing session
ssh -p 2222 validator-ops@your-server-ip

Step 4 — Disable Unused Authentication Methods

In the same sshd_config, also confirm these are disabled:

GSSAPIAuthentication no
HostbasedAuthentication no
IgnoreRhosts yes

Step 5 — Set an Idle Timeout

Prevent abandoned sessions from staying open indefinitely:

ClientAliveInterval 300
ClientAliveCountMax 2

This disconnects idle sessions after 10 minutes (300s × 2 checks) of inactivity.

Part 2: Firewall Configuration with UFW

UFW (Uncomplicated Firewall) ships with Ubuntu and is the simplest way to enforce strict inbound rules.

Step 1 — Set Default Policies

sudo ufw default deny incoming
sudo ufw default allow outgoing

This is the foundation: deny everything inbound by default, then explicitly allow only what’s needed.

Step 2 — Allow SSH (Your New Port)

sudo ufw allow 2222/tcp comment 'SSH'

Do this before enabling UFW — if you forget this step and enable the firewall, you’ll lock yourself out instantly.

Step 3 — Allow Validator-Specific Ports

For a typical Cosmos SDK validator, you need:

# P2P — must be open for peer connections
sudo ufw allow 26656/tcp comment 'CometBFT P2P'
# RPC — only if you need external access; otherwise keep closed or restrict by IP
# sudo ufw allow 26657/tcp comment 'CometBFT RPC'
# Metrics — restrict to your monitoring server's IP only
sudo ufw allow from YOUR_MONITORING_SERVER_IP to any port 26660 comment 'Prometheus metrics'

Never expose RPC (26657) or metrics (26660) publicly on a validator. These should either stay closed or be restricted to specific trusted IPs — your monitoring server, your own admin IP, nothing else.

If you do need RPC for a sentry node architecture, that’s a different node — not your signing validator.

Step 4 — Restrict Access by IP Where Possible

If you have a static IP or VPN, lock down SSH further:

sudo ufw delete allow 2222/tcp
sudo ufw allow from YOUR_STATIC_IP to any port 2222 proto tcp comment 'SSH - admin only'

Step 5 — Enable UFW

sudo ufw enable

Confirm your rules:

sudo ufw status verbose

Expected output should look like:

Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
To                         Action      From
-- ------ ----
2222/tcp ALLOW IN YOUR_STATIC_IP
26656/tcp ALLOW IN Anywhere
26660/tcp ALLOW IN YOUR_MONITORING_SERVER_IP

Step 6 — Enable Logging

sudo ufw logging on

Logs go to /var/log/ufw.log — useful for spotting reconnaissance attempts later.

Part 3: Fail2Ban — Automated Intrusion Prevention

UFW blocks based on static rules. Fail2Ban adds a dynamic layer — it watches logs in real time and automatically bans IPs showing malicious behavior, like repeated failed SSH logins.

Step 1 — Install Fail2Ban

sudo apt update
sudo apt install fail2ban -y

Step 2 — Create a Local Config Override

Never edit jail.conf directly — it gets overwritten on updates. Create a local override instead:

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local

Step 3 — Configure the SSH Jail

Find the [sshd] section (or add it) and configure:

[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
findtime = 600
bantime = 3600

What this means:

  • maxretry = 3 — ban after 3 failed attempts
  • findtime = 600 — within a 10-minute window
  • bantime = 3600 — ban for 1 hour

For a validator server, consider being more aggressive:

[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
findtime = 600
bantime = 86400

Bumping bantime to 86400 (24 hours) discourages persistent scanners without permanently blacklisting legitimate users who fat-finger a password once.

Step 4 — Set Global Defaults

In the [DEFAULT] section of jail.local:

[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
backend = systemd
destemail = your-email@example.com
sendername = Fail2Ban-Validator
action = %(action_mwl)s

action_mwl sends an email with logs when a ban occurs — useful if you want notifications, though most validator operators route this through their existing Telegram/Prometheus alerting instead.

Step 5 — Enable and Start Fail2Ban

sudo systemctl enable fail2ban
sudo systemctl start fail2ban

Step 6 — Verify It’s Working

sudo fail2ban-client status
sudo fail2ban-client status sshd

Expected output:

Status for the jail: sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: 12
| `- Journal matches: _SYSTEMD_UNIT=ssh.service + _COMM=sshd
`- Actions
|- Currently banned: 1
|- Total banned: 4
`- Banned IP list: 203.0.113.45

Step 7 — Manage Bans Manually if Needed

# Unban an IP (e.g., if you accidentally banned yourself)
sudo fail2ban-client set sshd unbanip YOUR_IP
# Manually ban an IP
sudo fail2ban-client set sshd banip SUSPICIOUS_IP
# Check ban list
sudo fail2ban-client get sshd banip

Bonus: Extend Fail2Ban to UFW

By default, Fail2Ban uses iptables directly. You can configure it to work alongside UFW more cleanly:

[DEFAULT]
banaction = ufw

This makes Fail2Ban add its bans as UFW rules, keeping your firewall state consistent and easier to audit in one place (sudo ufw status).

Putting It All Together: Verification Checklist

After completing all three sections, verify your setup end-to-end.

SSH:

# Confirm root login is disabled
ssh root@your-server-ip # should be refused
# Confirm password auth is disabled
ssh -o PubkeyAuthentication=no validator-ops@your-server-ip -p 2222 # should fail
# Confirm key-based login works
ssh -p 2222 validator-ops@your-server-ip # should succeed

Firewall:

sudo ufw status verbose
# Confirm only intended ports are open

Fail2Ban:

sudo fail2ban-client status sshd
# Confirm jail is active and monitoring

Common Mistakes to Avoid

Locking yourself out Always test new SSH settings in a second terminal before closing your original session. If you’re on a cloud provider, also check if they offer a console/VNC access option as a fallback.

Forgetting to allow SSH before enabling UFW sudo ufw enable without an SSH allow rule in place first means an instant lockout if you're connected remotely.

Exposing RPC or metrics ports publicly These are commonly left open by default during initial node setup and forgotten. Audit your UFW rules periodically — sudo ufw status numbered.

Using the same SSH key across multiple critical servers If one server’s key is compromised, you don’t want that to compromise your validator too. Use dedicated keys per environment.

Not testing Fail2Ban bans Run a few deliberate failed login attempts (from a non-critical IP, like a VPN) to confirm the jail actually triggers before you rely on it in production.

None of this replaces proper key management for your validator’s signing key — that’s a separate, equally critical concern. But if your server itself isn’t hardened, none of that matters. An attacker with SSH access has access to everything else on the box too.

Treat your validator server like the high-value target it is, because it already looks like one to everyone scanning the internet.


Hardening Your Validator Server: SSH, Firewalls & Fail2Ban was originally published in Vitwit on Medium, where people are continuing the conversation by highlighting and responding to this story.