-
Notifications
You must be signed in to change notification settings - Fork 34
/
ssh-tunnels-cleanup
executable file
·74 lines (64 loc) · 2.52 KB
/
ssh-tunnels-cleanup
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
#!/bin/bash
prefix=tun- kill= sessions=
usage() {
bin=$(basename $0)
echo >&2 "Usage: $bin [ -l | --list | --ls ] [ prefix ]"
echo >&2 "Usage: $bin { -k | --kill } [ -s | --cleanup-sessions ] [ prefix ]"
echo >&2 "Usage: $bin { -k | --kill } [ -s | --cleanup-sessions ] [ prefix ]"
echo >&2
echo >&2 "Detects/kills all user sshd pids created for 'ssh -R'"
echo >&2 " tunnels, that have no listening socket associated with them,"
echo >&2 " or socket that doesn't give openssh prompt to 'ncat' connection."
echo >&2 "Default prefix for user of these pids is"
echo >&2 " '${prefix}', so e.g. '${prefix}user' pids will be detected/killed."
echo >&2 "'kill' is used without any extra args, i.e. just SIGTERM."
exit ${1:-0}
}
[[ "$1" = -h || "$1" = --help ]] && usage
[[ "$1" = -l || "$1" = --list || "$1" = --ls ]] && shift
[[ "$1" = -k || "$1" = --kill ]] && { kill=t; shift; }
[[ "$1" = -s || "$1" = --cleanup-sessions ]] && { sessions=t; shift; }
[[ -z "$1" ]] || prefix=$1
[[ -z "$1" ]] || usage 1
set -e -o pipefail
diff() {
# Usage: diff a1[@] a2[@]
# Prints elemements in a1 that are not in a2, assuming no duplicate values in each
for v1 in "${!1}"; do
local m=
for v2 in "${!2}"; do [[ "$v1" != "$v2" ]] || { m=t; break; }; done
[[ -n "$m" ]] || echo "$v1"
done
}
user_pids=( $(ps ax -o pid=,cmd= | gawk '/sshd: '"$prefix"'/&&!/( gawk |\[priv\]$)/ {print $1}') )
sshd_socket_pids=()
declare -A sshd_ports
while read pid port; do
sshd_socket_pids+=( $pid )
[[ "$port" -eq 22 ]] || sshd_ports["$port"]+=" $pid"
done < <( ss -tnlp |
gawk 'match($0,/users:\(\("sshd",pid=([0-9]+),/,a) {match($4,/:([0-9]+)$/,b); print a[1], b[1]}' |
sort -u )
pids_to_kill=( $(diff user_pids[@] sshd_socket_pids[@]) )
for port in "${!sshd_ports[@]}"; do
ssh_hello=
coproc ncat --recv-only -i5 127.0.0.1 "$port" 2>/dev/null
read -r ssh_hello </dev/fd/"${COPROC[0]}" ||:
eval "exec ${COPROC[0]}>&- ${COPROC[1]}>&-"
if [[ -n "$ssh_hello" ]]
then kill "$COPROC_PID" 2>/dev/null ||: # not timed out yet
else pids_to_kill+=( ${sshd_ports["$port"]} )
fi
[[ -z "$COPROC_PID" ]] || wait "$COPROC_PID" 2>/dev/null ||:
done
if [[ -z "$kill" ]]
then
[[ ${#pids_to_kill[@]} -eq 0 ]] || echo "${pids_to_kill[@]}"
else
[[ ${#pids_to_kill[@]} -eq 0 ]] || kill "${pids_to_kill[@]}" 2>/dev/null ||:
[[ -z "$sessions" ]]\
|| while read s; do
[[ "$(loginctl show-session -p State --value "$s" 2>/dev/null ||:)" = closing ]] || continue
loginctl terminate-session "$s" ||:
done < <(loginctl list-sessions --no-legend | awk '$3~/'"$prefix"'/ {print $1}')
fi