Skip to content

Instantly share code, notes, and snippets.

@michaelherger
Forked from lo48576/rrysnc.sh
Last active November 14, 2022 10:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save michaelherger/63f29db06a755e42a92f8f00b3a12d45 to your computer and use it in GitHub Desktop.
Save michaelherger/63f29db06a755e42a92f8f00b3a12d45 to your computer and use it in GitHub Desktop.
rrsync replacement without perl, useful for limited environment like CoreOS
#!/bin/sh
# Copyright (C) 2004 Joe Smith <js-cgi@inwap.com>
# Copyright (C) 2004-2015 Wayne Davison <wayned@samba.org>
# Copyright (C) 2016-2018 YOSHIOKA Takuma <tashioka.256@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# see https://www.samba.org/ftp/unpacked/rsync/support/rrsync for reference
# bash version: https://gist.github.com/lo48576/d120d139e091f2338d35e1de85f9315f
# This script is based on `support/rrsync` perl script in rsync-3.1.2.
USAGE="Use command=\"$0 [-ro|-wo] SUBDIR\"
in front of lines in ${HOME}/.ssh/authorized_keys"
LOGFILE="${HOME}/logs/rrsync.log"
CMDNAME="$0"
# Log the given message.
die() {
DATETIME="$(LANG=C date '+%F %T')"
echo "[$DATETIME] $CMDNAME: $1" >>"$LOGFILE"
echo >>"$LOGFILE"
exit 2
}
# Die with the given error message.
debug() {
DATETIME="$(LANG=C date '+%F %T')"
echo "[$DATETIME][DEBUG] $1" | tr '\n' '\0' | sed -e 's/\0/\n[DEBUG]'"$DATETIME"'/g' >>"$LOGFILE"
}
#debug ">>>>>>>>>>>>>>>>>>>>>>>>>"
#debug "$SSH_ORIGINAL_COMMAND"
# Get "only" option.
# It would be "w" for `-wo` (write only), "r" for `-ro` (read only), or empty string (neither `-ro` nor `-wo`).
ONLY=""
while [ "x$1" = "x-ro" -o "x$1" = "x-wo" ] ; do
if [ "x$1" = "x-ro" ] ; then
if [ "x$ONLY" = "xw" ] ; then
die "the -ro and -wo options conflict."
fi
ONLY="r"
shift
else
if [ "x$ONLY" = "xr" ] ; then
die "the -ro and -wo options conflict."
fi
ONLY="w"
shift
fi
done
# Now "$ONLY" has "r", "w" or "".
# Ensure the subdirectory is given.
if [ $# -lt 1 ] ; then
die "No subdirectory specified.\n${USAGE}"
fi
ALLOWED_DIR="$1"
cd "$ALLOWED_DIR" || die 'Failed to change current directory'
# Ensure the rrsync is invoked via sshd.
if [ -z "$SSH_ORIGINAL_COMMAND" ] ; then
die "Not invoked via sshd.\n${USAGE}"
fi
# Ensure the rrsync is invoked in server mode via remote rsync.
if ! echo "$SSH_ORIGINAL_COMMAND" | grep -sqE '^rsync\s+--server' ; then
die "SSH_ORIGINAL_COMMAND='$SSH_ORIGINAL_COMMAND' is not 'rsync --server' or '--server' is not specified first."
fi
# Check whether the rsync server should be sender or receiver.
AM_SENDER=0
if echo "$SSH_ORIGINAL_COMMAND" | grep -sqE '^rsync\s+--server\s+--sender\s' ; then
AM_SENDER=1
# The local rsync is sender mode, i.e. files should be sent from local to remote.
if [ "x$ONLY" = "xw" ] ; then
die "Sending to read-only server not allowed."
fi
else
# The local rsync is receiver mode, i.e. files should be sent from remote to local.
if [ "x$ONLY" = "xr" ] ; then
die "Reading to write-only server not allowed."
fi
fi
# Command line sanity check.
set -f
set -- $SSH_ORIGINAL_COMMAND
# For each argument:
CHECK_TYPE=0
while [ $# -ge 1 ] ; do
# Collapse continuous slashes in the argument.
ARG="$(echo "$1" | sed -e 's!///*!/!g')"
# Ensure the argument has no ".." as path component.
if echo "$ARG" | grep -sqE '(^(-[a-zA-Z0-9-]+=?)?|/)\.\.(/|$)' ; then
debug "command line: ${SSH_ORIGINAL_COMMAND}\ndangerous argument: \`$1\`"
die "Do not use .. in option argument, anchor the path at the root of your restricted dir."
fi
# Ensure the argument has no absolute path.
if echo "$ARG" | grep -sq '^/' ; then
debug "command line: ${SSH_ORIGINAL_COMMAND}\ndangerous argument: \`$1\`"
die "Do not use absolute path in argument, anchor the path at the root of your restricted dir."
fi
# values copied from https://www.samba.org/ftp/unpacked/rsync/support/rrsync -> $short_no_arg less $short_disabled
if echo "$ARG" | grep -sqE '^-([ACDEHIJKLORSWXbcdgklmnoprtuvxyz]*s[ACDEHIJKLORSWXbcdgklmnoprtuvxyz]*(e\d*\.\w*)?$)'; then
debug "command line: ${SSH_ORIGINAL_COMMAND}\ndangerous argument: --protect-args / -s"
die "--protect-args is not allowed as we need to inspect the target path"
fi
shift
done
#debug "$SSH_ORIGINAL_COMMAND"
# Arguments are safer!
# Execute rsync.
exec $SSH_ORIGINAL_COMMAND
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment