Securing remote access to OpenBSD: configuring a modern sshd_config with ed25519 keys, disabling passwords and root login, restricting which users may log in, using pf tables to mitigate brute force, and leveraging SSH tunneling and port forwarding.

In episode 13 you made pf work hard as a gateway and firewall. But a firewall doesn't change the fact that there's one door that must always be open: SSH. Almost every OpenBSD server is accessed over SSH, and that's why SSH is the most attacked path.
The good news: OpenSSH was born from OpenBSD, so you're working with the most mature and most audited SSH in the world. This episode teaches modern sshd_config configuration, ed25519 keys, access restrictions, brute force mitigation with pf, and tunneling.
OpenSSH was developed inside the OpenBSD project and became the SSH standard on almost every modern operating system. Because it's developed together, its integration with OpenBSD is unmatched — and you get the latest version right at release. Everything you learn here applies wherever OpenSSH is used.
Key authentication is far stronger than passwords. ed25519 is a modern algorithm that's fast and secure. Create a key pair on your client machine:
ssh-keygen -t ed25519 -a 200
ssh-keygen -t ed25519 -a 200 -f ~/.ssh/deploy_ed25519-a 200 adds KDF iterations to protect your key. The defaults ~/.ssh/id_ed25519 and id_ed25519.pub will be used by most commands. Copy the public key to the server:
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@obsd1ssh-copy-id places the key into ~/.ssh/authorized_keys on the server. Once the key is active, you can disable password logins.
The sshd configuration file is at /etc/ssh/sshd_config. The recommended version for production:
Port 22
AddressFamily inet
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
LoginGraceTime 30
MaxAuthTries 3
AllowUsers arman deploy
AllowGroups wheel
Protocol 2PermitRootLogin no — no direct root login.PasswordAuthentication no — public keys only.AuthenticationMethods publickey — explicit: publickey only.LoginGraceTime 30 — login time limited to 30 seconds.AllowUsers and AllowGroups — a whitelist of accounts allowed to log in.After changing, validate and reload:
sshd -t
rcctl reload sshdsshd -t checks the syntax. rcctl reload sshd reloads without dropping existing connections.
Danger
Before disabling password logins, make sure your key is tested: open a second terminal and log in with the key, then change the configuration. A single SSH connection accidentally dropped after PasswordAuthentication no could lock you out of the server.
An SSH exposed to the internet will receive constant brute force attempts. Instead of extra packages, OpenBSD has a built-in solution: combine sshd logging with a pf table. First, log failed login attempts:
tail -f /var/log/authlog
grep "Failed password" /var/log/authlogThen automate adding to a pf table. A simple scheme uses pfctl -t bruteforce -T add for addresses that failed login, triggered by the log. Since pf in episode 13 already opened table <bruteforce> persist and the rule block quick from <bruteforce>, you just need to populate it.
For convenience, the fail2ban package is available and integrates with pf:
pkg_add fail2ban
rcctl enable fail2ban
rcctl start fail2banFail2ban reads sshd logs and automatically adds offender addresses to a pf table. The choice: use a manual pf table mechanism for full control, or fail2ban for instant automation.
There's one important difference: rcctl restart sshd will drop your own SSH connection. That's why you should use reload for configuration, not restart:
sshd -t && rcctl reload sshdIf you really need to restart (for example debugging), do it on a safe schedule or through a persistent tmux session — one of the reasons tmux is always available in the base system.
SSH isn't just for logging in — it's also a secure tunnel. The two most useful patterns:
ssh -L 8080:localhost:80 user@obsd1
ssh -D 1080 user@obsd1-L 8080:localhost:80: maps local port 8080 to port 80 on the server — useful for accessing internal services securely.-D 1080: a SOCKS proxy — all browser traffic goes through the server.For long-lived connections, use additional options:
ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 user@obsd1ServerAliveInterval keeps the connection alive and detects failures faster.
Verify that your configuration works and see attack attempts:
rcctl status sshd
grep "Failed password" /var/log/authlog | tail
ssh -i ~/.ssh/id_ed25519 arman@obsd1The /var/log/authlog log is the source of truth: successful and failed logins are recorded there. Making a habit of checking it is good practice.
In episode 14 you secured OpenBSD's remote access: getting to know OpenSSH, creating ed25519 keys, hardening sshd_config by disabling root and passwords, restricting users, mitigating brute force with pf tables and fail2ban, and leveraging tunneling and port forwarding.
Key takeaways:
PermitRootLogin no and PasswordAuthentication no.sshd -t then rcctl reload sshd — reload, not restart, to avoid dropping connections.-L for port forwarding, -D for SOCKS.In the next episode, episode 15, we enter the most distinctive part of OpenBSD: security architecture with pledge and unveil — the userland syscall sandboxing system, application examples in base tools, and supporting features like W^X, KARL, and security sysctls.