Moving data files securely from one environment to another is a frequent business application requirement, so I was disappointed to learn scp doesn’t support a ‘least privilege’ approach ‘out-of-the-box’. The authors of O’Reilly’s book give an incomplete solution and note various issues, but that’s about it. Other solutions involve jailing SSH, a custom SSH shell like rssh or switching to WebDAV or ftps and using certificates. I thought these were overkill so I came up with this alternative to scp-wrapper
#!/bin/ksh
#
# scp-restricted
# 1.0 Piers C Oct-07 Original
# Inspired by http://www.snailbook.com/faq/restricted-scp.auto.html
# Tested with OpenSSH 3.x server and Putty client
#
integer argc=0
typeset command="exec /usr/bin/scp"
typeset filename
readonly SCRIPTNAME=$(basename $0)
function fail {
print "$SCRIPTNAME: $2" >&2
print "$SCRIPTNAME: SSH original command should be 'scp [-v] [-t|-f] filename'" >&2
exit $1
}
if [[ "$1" == "-T" ]]; then # see test-scp-retricted
command="print "${command}
fi
if [[ -z $SSH_ORIGINAL_COMMAND ]]; then
fail 1 "environment variable SSH_ORIGINAL_COMMAND not set"
fi
for arg in $SSH_ORIGINAL_COMMAND; do
argv[$argc]=$arg
argc=argc+1
done
if (( $argc == 4 )); then
if [[ ${argv[1]} != "-v" ]]; then
fail 6 "arg 2 of 4 not '-v'"
fi
command=${command}" -v"
elif (( $argc != 3 )); then
fail 2 "wrong number of args"
fi
if [[ ${argv[0]} != "scp" ]]; then
fail 3 "arg[0] must be 'scp'"
fi
filename=${argv[argc-1]}
# be very conservative with filenames that we'll accept
if print ${filename} | egrep -vs '^[a-zA-Z0-9][.a-zA-Z0-9]*$'; then
fail 5 "bad filename: $filename
(must be alphanum, may include but not start with period)"
fi
if [[ ${argv[1]} == "-t" || ${argv[2]} == "-t" ]]; then
cd $HOME/inbound || fail 7 "unable to cd ~/inbound"
${command} -t ${filename}
elif [[ ${argv[1]} == "-f" || ${argv[2]} == "-f" ]]; then
cd $HOME/outbound || fail 8 "unable to cd ~/outbound"
${command} -f ${filename}
else
fail 4 "args must include -t or -f"
fi
#end#
#!/bin/ksh
#
# test-scp-restricted
#
integer succeeded=0
integer failed=0
function dotest {
export SSH_ORIGINAL_COMMAND=$1
print "======================================"
print 'SSH_ORIGINAL_COMMAND="'$SSH_ORIGINAL_COMMAND'"'
print "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=="
./scp-restricted -T; rc=$?
print "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=="
if (( $rc == $2 )); then
print 'exited ('$rc') - test **SUCCESS**'
succeeded=$succeeded+1
else
print 'exited ('$rc') - test **FAILED**'
failed=$failed+1
fi
}
dotest "" 1
dotest "x" 2
dotest "x y z" 3
dotest "scp -p z" 4
dotest 'scp -t foo.dat' 0
dotest 'scp -t 7' 0
dotest "scp -t .." 5
dotest "scp -t a;ls" 5
dotest 'scp -t a*ls' 5
dotest 'scp -t a/ls' 5
dotest 'scp -t a�73ls' 5
dotest 'scp -f bar.dat' 0
print "Succeeded: $succeeded"
print "Failed: $failed"