Hardening the SSH transport for rsync: ed25519 key-based auth, strict -e ssh options without passwords, restrict (no-port-forwarding) keys for rsync-only accounts, and why an rsyncd daemon without TLS should be wrapped in an SSH tunnel.

Episode 13 set one rule: use rsync ≥3.4.4 and SSH. Episode 14 completes the SSH side — because "just use SSH" isn't enough; you need to know how to harden it. A poorly configured SSH can still be a doorway: brute-forced passwords, abused port forwarding, or a backup account with full shell access.
The goal of this episode: build an rsync transport that is passwordless, interactive-less, and with the fewest privileges possible — a secure architecture ready for cron.
Step one: eliminate password authentication. An ed25519 key is created once and used forever:
ssh-keygen -t ed25519 -f ~/.ssh/backup_key -N ""
ssh-copy-id -i ~/.ssh/backup_key.pub user@backup-host-t ed25519 — a modern, secure algorithm (not slow/weak RSA 2048).-f — a specific filename for the backup key, so it doesn't mix with personal keys.-N "" — no passphrase, because cron must be able to use it non-interactively.To lock down SSH on the server side so passwords are never accepted:
PasswordAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
KbdInteractiveAuthentication noWarning
Turn off PasswordAuthentication only after you're sure the key can log in. Otherwise you could lock yourself out. Test from a second terminal before restarting sshd (sudo systemctl restart ssh).
When invoking rsync, define the SSH transport explicitly with strict options — this also ensures cron doesn't depend on ~/.ssh/config files or an agent:
rsync -avh -e "ssh -i /root/.ssh/backup_key -o BatchMode=yes -o ConnectTimeout=10" \
/home/data/ backup-user@backup-host:/backup/| Option | Meaning |
|---|---|
-i | Specific key file |
BatchMode=yes | Fail if a password/prompt is needed — never hangs |
ConnectTimeout=10 | Cut off after 10 seconds if the host is unreachable |
BatchMode=yes is crucial for cron: without it, SSH that can't authenticate waits for a password that will never be provided — hanging the backup forever.
The key you put on the backup host should be limited. When setting up an rsync-only account, add the restrict key option in authorized_keys:
restrict,no-port-forwarding,no-agent-forwarding,no-pty \
ssh-ed25519 AAAA...backup-key user@backuprestrict rejects all SSH facilities (forwarding, agent, PTY) by default; no-port-forwarding reaffirms the ban on port forwarding. The result: this key can only run rsync, it can't be used as a doorway into other internal server services.
A stricter step: lock the key to one rsync command only, using command=:
restrict,command="rsync --server --sender -logDtpre.iLsfxCIvu . /backup/" \
ssh-ed25519 AAAA...backup-key backup-user@backupWith this, anyone logging in with that key can only execute the predetermined rsync command — not a shell. If command= is present, SSH ignores any command from the client. This is the secure "deployment key" pattern for automation accounts.
Tip
Figuring out the exact --server string can eat time. The trick: run rsync -av -e ssh -v ... once and read the command line sent to the remote in the verbose output — then copy it into command=. Start from exactly that command.
The rsyncd daemon on port 873 is not encrypted (touched on in episodes 2 and 13). If you still want daemon mode for sensitive data, don't open port 873 to the network — wrap it in an SSH tunnel:
ssh -N -L 873:localhost:873 tunnel-user@server &
rsync -avh rsync://localhost/backup/ ./restored/SSH forwards local port 873 to port 873 on the server over an encrypted connection. The client only talks to localhost:873; the actual network traffic goes through SSH. The result: you get the convenience of daemon mode and SSH encryption at once — without exposing the daemon port to the world.
Important
A short rule: SSH for almost every case, daemon only for public mirrors, and if the daemon touches sensitive data, wrap it in an SSH tunnel. Never choose a bare daemon "because it's faster" — that trade is never worth the plaintext risk.
Summary of the safe pattern on the backup destination host:
backup-user (not root) with stable UID/GID (episode 11).authorized_keys with restrict.sshd_config: PasswordAuthentication no, PermitRootLogin no.backup-user, modules/filesystems mounted with noexec where possible.ssh -i key backup-user@host "echo ok" then run rsync.In this episode you've hardened the SSH transport for rsync.
Key takeaways:
PasswordAuthentication.-e "ssh -i key -o BatchMode=yes -o ConnectTimeout=10" for a transport that never hangs.restrict + no-port-forwarding in authorized_keys for rsync-only accounts.command="rsync --server ..." locks a key to a single command — not a shell.In episode 15 we ensure full fidelity during migration: ACL, XATTR & SELinux context — -A for ACLs, -X for extended attributes and SELinux security contexts, -H for hardlinks, plus caution about policy mismatches at the destination. See you in episode 15!