Rsync Without Login
You’d like to use rsync, but ensure users can only use rsync and can’t login with a shell, forward sessions, or other shenanigans. Do this with ssh keys and ForceCommand.
Limit Use With Keys and a Custom Script
# On your server, add a central location for keys
sudo mkdir /etc/ssh/authorized_keys
# Configure SSH to look for user public keys in that spot - the %u is the variable for user ID
echo "AuthorizedKeysFile /etc/ssh/authorized_keys/%u.pub" > /etc/ssh/sshd_config.d/authorized_users.conf
# Create a script that checks incoming ssh commands to make sure they are for rsync
sudo tee /etc/ssh/authorized_keys/checkssh.sh << "EOF"
#!/bin/bash
if [ -n "$SSH_ORIGINAL_COMMAND" ]; then
if [[ "$SSH_ORIGINAL_COMMAND" =~ ^rsync\ ]]; then
echo $SSH_ORIGINAL_COMMAND | systemd-cat -t rsync
exec $SSH_ORIGINAL_COMMAND
else
echo DENIED $SSH_ORIGINAL_COMMAND | systemd-cat -t rsync
fi
fi
EOF
chmod +x /etc/ssh/authorized_keys/checkssh.sh
systemctl restart ssh.service
Now that we have the SSH server configured, let’s create a system user (required, unfortunately) and a key. We’ll limit the account as much as possible, though you can’t use /usr/sbin/nologin shell, as rsync requires something to run in.
THE_USER="remote-account-1"
sudo adduser --no-create-home --home /nonexistent --disabled-password --gecos "" ${THE_USER}
# Its easiest to create the key yourself, but a .pub from them is also fine.
# Send out the private key from the folder (it's the one without the .pub on the end) to the remote system.
ssh-keygen -f /etc/ssh/authorized_keys/${THE_USER} -q -N "" -C "${THE_USER}"
Let’s add a ForcedCommand to the key so that it can only be used with the features we allow.
vi /etc/ssh/authorized_keys/${THE_USER}
# Paste this command="... string in front of the existing key
command="/etc/ssh/authorized_keys/checkssh.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC1...
This remote user can now use rsync, but can’t login or do other activities. Their command would look something like this (using the private key you created above)
rsync \
--rsh "ssh -i /where/your/private/key/is/remote-account-1" \
[email protected]:/some/folder /some/local/place/
Notes
Why not use rrsync?
The rrsync script is similar to the script we use, but is distributed and maintained as part of the rsync package. It’s arguably a better choice. I like the checkssh.sh approach as it’s more flexible, allows for things other than rsync, and doesn’t force relative paths. But if you’re only doing rsync, consider using rrsync like this;
# Paste this command="... string in front of the existing key
command="rrsync -ro /some/folder/share",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC1...
In your client’s rsync command, make the paths relative to path rrsync expects above.
rsync [email protected]:folder.1 /destination/folder/
If you see the client-side error message:
rrsync error: option -L has been disabled on this server
You discovered that following symlinks has been disabled by default in rrsync. You can enable with an edit to the script.
sudo sed -i 's/KLk//' /usr/bin/rrsync
# This changes
# short_disabled_subdir = 'KLk'
to
# short_disabled_subdir = ''
bash: line 1: /usr/bin/rrsync: cannot execute: required file not found
Python3 is needed for later versions of the script. 26M more in your container, but hey, everyone loves python.
apt install python3
Script It
If you need do it more than once, it might look something like this.
#!/bin/bash
HELP_MESSAGE="Usage: $0 <user> \n\nThis script requires a username to be specified.\n"
if [ "$#" -eq 0 ]; then
echo -e "$HELP_MESSAGE"
exit 1
fi
if [ id username &>/dev/null ]; then
echo "User already exists."
exit 1
fi
if [ "$EUID" -ne 0 ]; then
echo "This script must be run with sudo."
exit 1
fi
THE_USER=$1
THE_COMMAND="\
command=\
\"/etc/ssh/authorized_keys/checkssh.sh\",\
no-port-forwarding,\
no-X11-forwarding,\
no-agent-forwarding,\
no-pty "
useradd --home-dir /nonexistent ${THE_USER}
mkdir -p /etc/ssh/authorized_keys
ssh-keygen -f /etc/ssh/authorized_keys/${THE_USER} -q -N "" -C "${THE_USER}"
sed -i "1s|^|$THE_COMMAND|" /etc/ssh/authorized_keys/${THE_USER}.pub
Sources
https://peterbabic.dev/blog/transfer-files-between-servers-using-rrsync/ http://gergap.de/restrict-ssh-to-rsync.html https://superuser.com/questions/641275/make-linux-server-allow-rsync-scp-sftp-but-not-a-terminal-login
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.