1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
|
#!/bin/bash
# This script uses openssl, gnutls, or stunnel to secure an rsync daemon connection.
# By default this script takes rsync args and hands them off to the actual
# rsync command with an --rsh option that makes it open an SSL connection to an
# rsync daemon. See the rsync-ssl manpage for usage details and env variables.
# When the first arg is --HELPER, we are being used by rsync as an --rsh helper
# script, and the args are (note the trailing dot):
#
# rsync-ssl --HELPER HOSTNAME rsync --server --daemon .
#
# --HELPER is not a user-facing option, so it is not documented in the manpage.
# The first SSL setup was based on: http://dozzie.jarowit.net/trac/wiki/RsyncSSL
# Note that an stunnel connection requires at least version 4.x of stunnel.
function rsync_ssl_run {
case "$*" in
*rsync://*) ;;
*::*) ;;
*)
echo "You must use rsync-ssl with a daemon-style hostname." 1>&2
exit 1
;;
esac
exec rsync --rsh="$0 --HELPER" "${@}"
}
function rsync_ssl_helper {
if [[ -z "$RSYNC_SSL_TYPE" ]]; then
found=`path_search openssl stunnel4 stunnel` || exit 1
if [[ "$found" == */openssl ]]; then
RSYNC_SSL_TYPE=openssl
RSYNC_SSL_OPENSSL="$found"
elif [[ "$found" == */gnutls-cli ]]; then
RSYNC_SSL_TYPE=gnutls
RSYNC_SSL_GNUTLS="$found"
else
RSYNC_SSL_TYPE=stunnel
RSYNC_SSL_STUNNEL="$found"
fi
fi
case "$RSYNC_SSL_TYPE" in
openssl)
if [[ -z "$RSYNC_SSL_OPENSSL" ]]; then
RSYNC_SSL_OPENSSL=`path_search openssl` || exit 1
fi
optsep=' '
;;
gnutls)
if [[ -z "$RSYNC_SSL_GNUTLS" ]]; then
RSYNC_SSL_GNUTLS=`path_search gnutls-cli` || exit 1
fi
optsep=' '
;;
stunnel)
if [[ -z "$RSYNC_SSL_STUNNEL" ]]; then
RSYNC_SSL_STUNNEL=`path_search stunnel4 stunnel` || exit 1
fi
optsep=' = '
;;
*)
echo "The RSYNC_SSL_TYPE specifies an unknown type: $RSYNC_SSL_TYPE" 1>&2
exit 1
;;
esac
if [[ -z "$RSYNC_SSL_CERT" ]]; then
certopt=""
gnutls_cert_opt=""
else
certopt="-cert$optsep$RSYNC_SSL_CERT"
gnutls_cert_opt="--x509certfile=$RSYNC_SSL_CERT"
fi
if [[ -z "$RSYNC_SSL_KEY" ]]; then
keyopt=""
gnutls_key_opt=""
else
keyopt="-key$optsep$RSYNC_SSL_KEY"
gnutls_key_opt="--x509keyfile=$RSYNC_SSL_KEY"
fi
if [[ -z ${RSYNC_SSL_CA_CERT+x} ]]; then
# RSYNC_SSL_CA_CERT unset - default CA set AND verify:
# openssl:
caopt="-verify_return_error -verify 4"
# gnutls:
gnutls_opts=""
# stunnel:
# Since there is no way of using the default CA certificate collection,
# we cannot do any verification. Thus, stunnel should really only be
# used if nothing else is available.
cafile=""
verify=""
elif [[ "$RSYNC_SSL_CA_CERT" == "" ]]; then
# RSYNC_SSL_CA_CERT set but empty -do NO verifications:
# openssl:
caopt="-verify 1"
# gnutls:
gnutls_opts="--insecure"
# stunnel:
cafile=""
verify="verifyChain = no"
else
# RSYNC_SSL_CA_CERT set - use CA AND verify:
# openssl:
caopt="-CAfile $RSYNC_SSL_CA_CERT -verify_return_error -verify 4"
# gnutls:
gnutls_opts="--x509cafile=$RSYNC_SSL_CA_CERT"
# stunnel:
cafile="CAfile = $RSYNC_SSL_CA_CERT"
verify="verifyChain = yes"
fi
port="${RSYNC_PORT:-0}"
if [[ "$port" == 0 ]]; then
port="${RSYNC_SSL_PORT:-874}"
fi
# If the user specified USER@HOSTNAME::module, then rsync passes us
# the -l USER option too, so we must be prepared to ignore it.
if [[ "$1" == "-l" ]]; then
shift 2
fi
hostname="$1"
shift
if [[ -z "$hostname" || "$1" != rsync || "$2" != --server || "$3" != --daemon ]]; then
echo "Usage: rsync-ssl --HELPER HOSTNAME rsync --server --daemon ." 1>&2
exit 1
fi
if [[ $RSYNC_SSL_TYPE == openssl ]]; then
exec $RSYNC_SSL_OPENSSL s_client $caopt $certopt $keyopt -quiet -verify_quiet -servername $hostname -verify_hostname $hostname -connect $hostname:$port
elif [[ $RSYNC_SSL_TYPE == gnutls ]]; then
exec $RSYNC_SSL_GNUTLS --logfile=/dev/null $gnutls_cert_opt $gnutls_key_opt $gnutls_opts $hostname:$port
else
# devzero@web.de came up with this no-tmpfile calling syntax:
exec $RSYNC_SSL_STUNNEL -fd 10 11<&0 <<EOF 10<&0 0<&11 11<&-
foreground = yes
debug = crit
connect = $hostname:$port
client = yes
TIMEOUTclose = 0
$verify
$certopt
$cafile
EOF
fi
}
function path_search {
IFS_SAVE="$IFS"
IFS=:
for prog in "${@}"; do
for dir in $PATH; do
[[ -z "$dir" ]] && dir=.
if [[ -f "$dir/$prog" && -x "$dir/$prog" ]]; then
echo "$dir/$prog"
IFS="$IFS_SAVE"
return 0
fi
done
done
IFS="$IFS_SAVE"
echo "Failed to find on your path: $*" 1>&2
echo "See the rsync-ssl manpage for configuration assistance." 1>&2
return 1
}
if [[ "$#" == 0 ]]; then
echo "Usage: rsync-ssl [--type=SSL_TYPE] RSYNC_ARG [...]" 1>&2
echo "The SSL_TYPE can be openssl or stunnel"
exit 1
fi
if [[ "$1" = --help || "$1" = -h ]]; then
exec rsync --help
fi
if [[ "$1" == --HELPER ]]; then
shift
rsync_ssl_helper "${@}"
fi
if [[ "$1" == --type=* ]]; then
export RSYNC_SSL_TYPE="${1/--type=/}"
shift
fi
rsync_ssl_run "${@}"
|