SSH & Access Configuration
Architecture and access rules for secure shell and Git operations.
ap-northeast-1
I run a customized, dual-port SSH setup, but instead of just opening up a general-purpose shell daemon to the world, I gave each port a very specific, narrowly-scoped job. I also route traffic through subdomains (like git.jelius.dev) rather than raw IP addresses. Why? Mostly so I don't have to memorize IPs, but it also means the underlying host IP can change without breaking everyone's Git config. Plus, it makes the access points naturally self-documenting.
Architecture Overview
Rather than tangling up a single configuration file with a spaghetti of complex Match blocks, the two ports are handled by two distinct sshd processes running completely separate configurations. This completely isolates the toy environment from the real one.
Port 22: Secure Monitoring
I left port 22 open on purpose. Closing it entirely just pushes automated scanner noise to logs elsewhere, since internet-wide bots are going to relentlessly hammer port 22 regardless of whether anything real is behind it. So, instead of hiding it, I gave them a toy to play with.
- Access Level: Zero interactive shell access. Period.
- Functionality: If you actually manage to authenticate with a key on port 22, you do not get a shell. I configured
sshdwithForceCommand btop(andPermitTTY yesso the TUI actually renders). The only thing that can ever run in that session is thebtopresource monitor. There is no bash shell underneath it to escape into—the SSH session's program isbtop. - Exit behavior: The moment
btopexits (viaqor a disconnect), the forced command's process ends, andsshdimmediately tears down the session. There is no shell left behind to fall through to, because one never existed in the first place.
The practical effect? I get a genuinely useful, at-a-glance system monitor from anywhere I have my key. Meanwhile, a scanner or opportunistic attacker gets absolutely nothing except read-only visibility into how much RAM they are currently wasting.
Port 69: Administration
The real functional SSH daemon—the one I use for actual work—listens on a non-standard, unprivileged port: 69. Moving the real daemon off port 22 mostly serves to cut down on log noise and automated brute-force attempts targeting standard SSH setups.
- Endpoint:
<ADMIN_USER>@<INTERNAL_SUBDOMAIN> - Access Level: Full interactive shell (zsh). This is the only identity on this entire setup with this privilege.
- Mechanism: My admin user gets a normal login shell exactly as you would on any ordinary server.
- Why a separate internal subdomain? Keeping the admin entrypoint on its own hostname ensures it doesn't show up in public-facing DNS records. It keeps the actual maintenance door completely off the radar.
Implementation Guide: The Dual-Daemon Setup
Here is exactly how I built this from a standard, fresh Linux install where sshd is still sitting on port 22. Instead of tangling up a single configuration file with a spaghetti of complex user-match blocks for the honeypot, I run two distinct sshd processes. It completely isolates the toy environment from the real one.
1. Evicting the Real Daemon First, I moved the actual functional SSH daemon out of the line of fire.
- Action: Open the primary
/etc/ssh/sshd_configand changePort 22toPort 69. - Safety check: Restart the main
sshservice, but do not close your current terminal. Open a new tab and verify you can successfully get back in on port 69 before proceeding, so you don't accidentally lock yourself out.
2. Creating the Toy Configuration Next, I duplicated the main configuration to create a dedicated file for the port 22 trap:
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config_btopThen, I modified sshd_config_btop to strip out all utility and force the monitor session. Here is the exact diff of what I changed:
21c21
< Port 69
---
> Port 22
113c113
< #PidFile /var/run/sshd.pid
---
> PidFile /var/run/sshd_btop.pid
127,130c127,129
< # X11Forwarding no
< # AllowTcpForwarding no
< # PermitTTY no
< # ForceCommand cvs server
---
> X11Forwarding no
> AllowTcpForwarding no
> ForceCommand /usr/local/bin/btop-force-utf8- The PID File: Changing
PidFileto/var/run/sshd_btop.pidis critical. Without this, the twosshdprocesses will fight over the same PID tracking file, andsystemdwill invariably kill one while trying to manage the other. - The Lockdown: Explicitly disabling X11 and TCP forwarding ensures that even if someone miraculously bypasses the forced command, they cannot use port 22 as a pivot proxy into my internal network.
3. The UTF-8 Wrapper
You'll notice the ForceCommand points to /usr/local/bin/btop-force-utf8 instead of the raw btop binary. Why? Because when sshd executes a forced command directly, it bypasses standard shell initialization. That means locale environment variables (like LANG=C.UTF-8) get dropped entirely.
If btop launches without UTF-8 awareness, its TUI turns into a garbled mess of broken ASCII artifacts. The wrapper script is just a dead-simple way to inject the locale before execution:
#!/bin/sh
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
exec sudo /usr/local/bin/btop --force-utf4. Spinning it Up
To manage this second daemon natively, I created a drop-in systemd service (e.g., /etc/systemd/system/ssh-btop.service) that mirrors the standard SSH service, but points the executable to my alternate config file: ExecStart=/usr/sbin/sshd -D -f /etc/ssh/sshd_config_btop:
[Unit]
Description=OpenSSH server daemon
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target sshd-keygen.target
Wants=sshd-keygen.target
[Service]
Type=notify
EnvironmentFile=-/etc/sysconfig/sshd
ExecStart=/usr/sbin/sshd -D -f /etc/ssh/sshd_config_btop
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s
[Install]
WantedBy=multi-user.targetEnable and start that unit, and the port 22 trap is officially live:
sudo systemctl daemon-reload
sudo systemctl enable --now ssh-btop.serviceap-south-1
I recently migrated my primary Git server over to this new instance. Because this box is single-purpose and entirely dedicated to version control, the SSH architecture here skips the dual-daemon gymnastics I use in ap-northeast-1. It is a standard SSH configuration focused on doing exactly one thing securely.
(For details on the application layer and how the Git service itself is deployed, see the Forgejo Git Server.)
Git Operations
- Endpoint:
git@git.jelius.dev - Access Level: Git wire-protocol commands only. No arbitrary command execution.
- Behavior: If you try to SSH in normally (
ssh git@git.jelius.dev), the server prints a success banner—Forgejo's polite way of saying "Hi! You successfully authenticated, but we don't provide shells"—and instantly kicks you out. It confirms your key works without actually giving you the keys to the kingdom.