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"