├── .gitignore ├── crontab.sh.crontab.sample ├── list.sh ├── detect-tunnel-sites.sh ├── confirmed-tunnel-sites.sh ├── defaults.sh ├── stop.sh ├── local.sh.sample ├── common.sh ├── ssh-keygen.sh ├── autossh.sh ├── crontab.sh ├── ssh-copy-id.sh ├── setup.sh └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | crontab.sh.crontab 2 | confirmed-tunnel-sites 3 | local.sh 4 | -------------------------------------------------------------------------------- /crontab.sh.crontab.sample: -------------------------------------------------------------------------------- 1 | $cron_unique_label 2 | 0 * * * * $PWD/autossh.sh 3 | 4 | -------------------------------------------------------------------------------- /list.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # 3 | # File: list.sh 4 | # Purpose: show all running autossh screen sessions 5 | # 6 | 7 | screen -ls | grep -F .autossh- 8 | -------------------------------------------------------------------------------- /detect-tunnel-sites.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # 3 | # File: detect-tunnel-sites.sh 4 | # Purpose: detect tunnel sites configured in ~/.ssh/config 5 | # 6 | 7 | grep '^Host autossh-.*' ~/.ssh/config | sed -e 's/.* //' 8 | 9 | # eof 10 | -------------------------------------------------------------------------------- /confirmed-tunnel-sites.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # 3 | # File: confirmed-tunnel-sites.sh 4 | # Purpose: print detected tunnel sites that have been confirmed to work 5 | # 6 | 7 | cd $(dirname "$0") 8 | . ./common.sh 9 | 10 | for tunnelsite in $(./detect-tunnel-sites.sh); do 11 | test -f $confirmed_dir/$tunnelsite || continue 12 | echo $tunnelsite 13 | done 14 | 15 | # eof 16 | -------------------------------------------------------------------------------- /defaults.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # 3 | # File: defaults.sh 4 | # Purpose: default variable settings 5 | # Note: feel free to override any of these in ./local.sh 6 | # 7 | 8 | # path to the ssh private key used when connecting to tunnel sites 9 | ssh_key_file=~/.ssh/autossh-id_rsa 10 | 11 | # comment used in the ssh public key when generating it with ./ssh-keygen.sh 12 | ssh_key_comment=autossh-$(hostname) 13 | 14 | # eof 15 | -------------------------------------------------------------------------------- /stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # 3 | # File: stop.sh 4 | # Purpose: stop autossh for tunnel sites 5 | # 6 | 7 | cd $(dirname "$0") 8 | . ./common.sh 9 | 10 | test "$1" && tunnelsites="$@" || tunnelsites=$(./confirmed-tunnel-sites.sh) 11 | 12 | for tunnelsite in $tunnelsites; do 13 | info stopping $tunnelsite ... 14 | screen -S $tunnelsite -wipe >/dev/null || : 15 | screen -S $tunnelsite -p 0 -X stuff  16 | done 17 | -------------------------------------------------------------------------------- /local.sh.sample: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # File: local.sh 4 | # Purpose: override variables and functions defined in ./defaults.sh 5 | # Note: DO NOT COMMIT THIS TO VCS ! 6 | # 7 | 8 | # path to the ssh private key used when connecting to tunnel sites 9 | ssh_key_file=~/.ssh/autossh-id_rsa 10 | 11 | # comment used in the ssh public key when generating it with ./ssh-keygen.sh 12 | ssh_key_comment=autossh-$(hostname) 13 | 14 | # eof 15 | -------------------------------------------------------------------------------- /common.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # 3 | # File: common.sh 4 | # Purpose: common functions 5 | # 6 | 7 | . ./defaults.sh 8 | test -f ./local.sh && . ./local.sh 9 | 10 | confirmed_dir=./confirmed-tunnel-sites 11 | mkdir -p $confirmed_dir 12 | 13 | is_confirmed_tunnelsite() { 14 | file=$confirmed_dir/$1 15 | test -f $file && return 0 || return 1 16 | } 17 | 18 | info() { 19 | echo '[info]' $@ 20 | } 21 | result() { 22 | echo '[result]' $@ 23 | } 24 | warn() { 25 | echo '[WARN]' $@ 26 | } 27 | 28 | require() { 29 | if ! type "$1" >/dev/null 2>/dev/null; then 30 | echo 'Fatal: program "'$1'" is either not in PATH or not installed. Exit.' 31 | exit 1 32 | fi 33 | } 34 | 35 | # eof 36 | -------------------------------------------------------------------------------- /ssh-keygen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # 3 | # File: ssh-keygen.sh 4 | # Purpose: create an SSH key without passphrase to use with autossh 5 | # in screen, with restrictive options in the public key 6 | # 7 | 8 | cd $(dirname "$0") 9 | . ./common.sh 10 | 11 | info "checking ssh key file: $ssh_key_file" 12 | if test -f $ssh_key_file; then 13 | result ssh key file exists 14 | else 15 | result ssh key file does NOT exist, creating now 16 | ssh-keygen -t rsa -C $ssh_key_comment -N '' -f $ssh_key_file 17 | info "adding restrictive options to public key" 18 | sed -e "s/^/command=\"__PATH_TO_FALSE__\",no-agent-forwarding,no-X11-forwarding,no-pty /" $ssh_key_file.pub >$ssh_key_file.pub.bak && mv $ssh_key_file.pub.bak $ssh_key_file.pub 19 | fi 20 | 21 | info contents of the public key: $ssh_key_file.pub 22 | cat $ssh_key_file.pub 23 | 24 | # eof 25 | -------------------------------------------------------------------------------- /autossh.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # 3 | # File: autossh.sh 4 | # Purpose: run autossh for each tunnel site, if not already running 5 | # 6 | 7 | cd $(dirname "$0") 8 | . ./common.sh 9 | 10 | test "$1" && tunnelsites="$@" || tunnelsites=$(./confirmed-tunnel-sites.sh) 11 | 12 | # for Mac OS X, otherwise it just doesn't work... 13 | export AUTOSSH_PORT=0 14 | 15 | match_session() { 16 | screen -ls | grep -F .$1 17 | } 18 | 19 | for tunnelsite in $tunnelsites; do 20 | if ! is_confirmed_tunnelsite $tunnelsite; then 21 | warn tunnelsite: $tunnelsite is not confirmed skipping. 22 | continue 23 | fi 24 | 25 | # stop running session, unless it matches "Attached" or "Detached" 26 | if ! match_session $tunnelsite | awk '$0 !~ /tached/ { exit 1 }'; then 27 | ./stop.sh $tunnelsite 28 | fi 29 | 30 | if ! match_session $tunnelsite >/dev/null; then 31 | echo \* starting autossh ... 32 | screen -d -m -S $tunnelsite autossh -N $tunnelsite 33 | echo \* done. 34 | fi 35 | done 36 | -------------------------------------------------------------------------------- /crontab.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | cd $(dirname "$0") 4 | 5 | test "$1" = --remove && mode=remove || mode=add 6 | 7 | cron_unique_label="# $PWD" 8 | 9 | crontab="$0".crontab 10 | test -f $crontab || cp $crontab.sample $crontab 11 | 12 | workfile="$0".tmp 13 | cleanup() { rm -f "$workfile"; } 14 | trap 'cleanup; exit 1;' 1 2 3 15 15 | 16 | # if crontab is executable 17 | if type crontab >/dev/null 2>/dev/null; then 18 | if test $mode = add; then 19 | # if this crontab is not added yet 20 | if ! crontab -l 2>/dev/null | grep -x "$cron_unique_label" >/dev/null 2>/dev/null; then 21 | export cron_unique_label 22 | cat $crontab | perl -ne 's/\$(\w+)/$ENV{$+}/g; print' >$workfile 23 | echo 'Appending to crontab:' 24 | cat $workfile 25 | crontab -l 2>/dev/null | cat - $workfile | crontab - 26 | else 27 | echo 'Crontab entry already exists, skipping ...' 28 | echo 29 | fi 30 | echo "To remove previously added crontab entry, run: $0 --remove" 31 | echo 32 | elif test $mode = remove; then 33 | # if this crontab entry exists 34 | if crontab -l 2>/dev/null | grep -x "$cron_unique_label" >/dev/null 2>/dev/null; then 35 | echo Removing crontab entry ... 36 | crontab -l 2>/dev/null | sed -e "\?^$cron_unique_label\$?,/^\$/ d" | crontab - 37 | else 38 | echo Crontab entry does not exist, nothing to do. 39 | fi 40 | fi 41 | fi 42 | 43 | cleanup 44 | 45 | # eof 46 | -------------------------------------------------------------------------------- /ssh-copy-id.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # 3 | # File: ssh-copy-id.sh 4 | # Purpose: install SSH key in `~/.ssh/authorized_keys` detected tunnel sites 5 | # 6 | 7 | cd $(dirname "$0") 8 | . ./common.sh 9 | 10 | test "$1" && tunnelsites="$@" || tunnelsites=$(./detect-tunnel-sites.sh) 11 | 12 | check_access_and_set_exitcode() { 13 | info testing access to tunnel site $tunnelsite with ssh key 14 | set +e 15 | SSH_AGENT_PID= SSH_AUTH_SOCK= ssh -i $ssh_key_file -o BatchMode=yes $tunnelsite date 16 | exitcode=$? 17 | set -e 18 | } 19 | 20 | ssh_copy_id() { 21 | info trying to add ssh key to remote authorized_keys file 22 | info 'you may be prompted for your remote password' 23 | ssh $tunnelsite "mkdir -p .ssh; sed -e \"s|__PATH_TO_FALSE__|\`which false\`|\" >> .ssh/authorized_keys; chmod -R go-rwx .ssh" < $ssh_key_file.pub 24 | } 25 | 26 | for tunnelsite in $tunnelsites; do 27 | recheck= 28 | confirmed_access= 29 | check_access_and_set_exitcode 30 | if test $exitcode = 0; then 31 | warn 'It seems there is an active ControlMaster.' 32 | warn 'In this case it is impossible to validate the custom ssh key' 33 | warn 'perfectly, but trying best effort.' 34 | if ssh $tunnelsite grep "^command.*$ssh_key_comment" .ssh/authorized_keys >/dev/null; then 35 | info 'Custom ssh key is in authorized_keys, so probably it works.' 36 | confirmed_access=1 37 | else 38 | info 'Custom ssh is not in authorized_keys.' 39 | ssh_copy_id && recheck=1 || continue 40 | fi 41 | elif test $exitcode = 1; then 42 | info 'Exit code was 1, most probably because the key is authorized' 43 | info 'with the forced command /bin/false or /usr/bin/false as expected.' 44 | confirmed_access=1 45 | else 46 | ssh_copy_id && recheck=1 || continue 47 | fi 48 | if test "$recheck"; then 49 | info 'checking access again' 50 | check_access_and_set_exitcode 51 | test $exitcode = 0 -o $exitcode = 1 && confirmed_access=1 52 | fi 53 | if test "$confirmed_access"; then 54 | info marking tunnel site confirmed 55 | > $confirmed_dir/$tunnelsite 56 | fi 57 | done 58 | -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # 3 | # File: setup.sh 4 | # Purpose: configure autossh-tunnel project 5 | # 6 | 7 | cd $(dirname "$0") 8 | . ./common.sh 9 | 10 | require autossh 11 | require screen 12 | 13 | heading() { 14 | echo '###' $* 15 | } 16 | 17 | heading variables 18 | echo ssh_key_file=$ssh_key_file 19 | if ! test -f $ssh_key_file; then 20 | warn The dedicated ssh key file "($ssh_key_file)" does not exist 21 | warn Create it manually or run ./ssh-keygen.sh helper script 22 | fi 23 | echo 24 | 25 | heading detected tunnel sites 26 | detected_tunnel_sites=$(./detect-tunnel-sites.sh) 27 | if test "$detected_tunnel_sites"; then 28 | for tunnelsite in $detected_tunnel_sites; do 29 | echo $tunnelsite 30 | done 31 | else 32 | warn 'Could not detect tunnel site configurations in ~/.ssh/config' 33 | warn You can configure a tunnel site like this: 34 | cat<