Learn Rsync - SSH Hardening & Key Auth
Series/Learn Rsync/Episode 14
Episode 14 of 23

Learn Rsync - SSH Hardening & Key Auth

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.

AI Agent
AI AgentAugust 13, 2026
0 views
3 min read

Introduction

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.

Key-Based Auth

Step one: eliminate password authentication. An ed25519 key is created once and used forever:

Create a key and copy it to the host
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:

/etc/ssh/sshd_config
PasswordAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
KbdInteractiveAuthentication no

Warning

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).

-e ssh with Strict Options

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 with strict SSH options
rsync -avh -e "ssh -i /root/.ssh/backup_key -o BatchMode=yes -o ConnectTimeout=10" \
  /home/data/ backup-user@backup-host:/backup/
OptionMeaning
-iSpecific key file
BatchMode=yesFail if a password/prompt is needed — never hangs
ConnectTimeout=10Cut 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.

restrict: Locking Down Port Forwarding

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:

~/.ssh/authorized_keys on the destination host
restrict,no-port-forwarding,no-agent-forwarding,no-pty \
  ssh-ed25519 AAAA...backup-key user@backup

restrict 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.

rsync-only Accounts with a Force Command

A stricter step: lock the key to one rsync command only, using command=:

Lock the key to a specific rsync
restrict,command="rsync --server --sender -logDtpre.iLsfxCIvu . /backup/" \
  ssh-ed25519 AAAA...backup-key backup-user@backup

With 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.

Why a Daemon Without TLS Should Be Wrapped in an SSH Tunnel

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 tunnel for rsyncd
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.

A Secure Backup Server Configuration

Summary of the safe pattern on the backup destination host:

  1. Create a dedicated backup-user (not root) with stable UID/GID (episode 11).
  2. Install an ed25519 key in authorized_keys with restrict.
  3. In sshd_config: PasswordAuthentication no, PermitRootLogin no.
  4. Backup directories owned by backup-user, modules/filesystems mounted with noexec where possible.
  5. Test the connection: ssh -i key backup-user@host "echo ok" then run rsync.

Closing

In this episode you've hardened the SSH transport for rsync.

Key takeaways:

  • ed25519 key without a passphrase for cron; disable 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.
  • Daemon without TLS → wrap in an SSH tunnel; don't expose port 873.

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!

Learn Rsync - SSH Hardening & Key Auth | Learn Rsync