-
Notifications
You must be signed in to change notification settings - Fork 24
/
Copy pathshare-zscaler.v2.sh
executable file
·166 lines (146 loc) · 5.27 KB
/
share-zscaler.v2.sh
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
#!/bin/bash
#
# A script to share a Zscaler VPN tunnel.
#
# Usage:
# ./share-zscaler.v2.sh --probe foo.bar.internal --domain internal
#
# The script sets up network address translation (NAT) so that
# all requests coming from the network of interface en0 have access
# to the network the probe host routes to
# (e.g. 10.100.0.0/16 using the tunnel interface utun3).
#
# The script also outputs a script that
# can be executed locally to configure the host
# to make use of the tunnel for the specified domains.
#
# Without a local copy, this script can also be called using curl:
# bash -c "$(curl -so- https://raw.githubusercontent.com/bkahlert/kill-zscaler/main/share-zscaler.v2.sh)" -- --probe foo.bar.internal --domain internal
#
# bashsupport disable=BP5001
# The IP of the (virtual) host with Zscaler installed (default: IP of en0 interface)
declare -r VPN_CLIENT_ADDRESS=${SHARE_ZSCALER_VPN_CLIENT_ADDRESS:-$(ipconfig getifaddr en0)}
# The network/IP you want to share (default: IP of en0 interface)
declare -r SOURCE_ADDRESS=${SHARE_ZSCALER_SOURCE_ADDRESS:-$(ipconfig getifaddr en0)/24}
# Executes the passed command line while prefixing the output with #
comment_run() {
"$@" | sed 's/^/# /'
}
# Prints error and exits with 1
die() {
{
tput setaf 1
printf '%s' "$*"
tput sgr0
printf '\n'
} >&2
exit 1
}
# Resolves the given hostname
print_resolved_host() {
host "${1?host missing}" | awk '/has address/ { print $4 ; exit }'
}
# Sets up NAT on this VPN client and
# prints the script to configure the host
# to use the VPN tunnel.
# Globals:
# SOURCE_ADDRESS
# VPN_CLIENT_ADDRESS
# Arguments:
# --probe - name of the host to resolve to find the VPN tunnel details
# --domain[@] - domain(s) to include in the host DNS resolve configuration script
# --debug - if specified, outputs diagnostic commands to the host configuration script
main() {
local debug probe
local -a domains=() args=()
while (($#)); do
case $1 in
--debug)
debug=1 && shift
;;
--probe)
[ "${2-}" ] || die "$1 value missing"
probe=$2 && shift 2
;;
--domain)
[ "${2-}" ] || die "$1 value missing"
domains+=("$2") && shift 2
;;
*)
args+=("$1") && shift
;;
esac
done
set -- "${args[@]}"
[ "$probe" ] || die "--probe value missing"
declare probe_ip route_info vpn_address vpn_interface vpn_gateway
probe_ip=$(print_resolved_host "$probe") || {
die "Failed to resolve probe: $probe"
}
route_info=$(route get "$probe_ip") || {
die "Failed to get route information for $probe_ip"
}
vpn_address=$(echo "$route_info" | grep 'destination:' | sed -E 's,.*:[[:space:]]*,,')
vpn_interface=$(echo "$route_info" | grep 'interface:' | sed -E 's,.*:[[:space:]]*,,')
vpn_gateway=$(echo "$route_info" | grep 'gateway:' | sed -E 's,.*:[[:space:]]*,,')
# enables kernel to route packages
comment_run sudo sysctl -w net.inet.ip.forwarding=1 || {
die "Failed to enable routing packages"
}
# disable network address translation
comment_run sudo pfctl -d || true
# flush all rules
comment_run sudo pfctl -F all || {
die "Failed to flush all rules"
}
# enable network address translation
declare nat_file && nat_file=$(mktemp)
echo "nat on $vpn_interface from $SOURCE_ADDRESS to any -> ($vpn_interface)" >"$nat_file"
comment_run sudo pfctl -f "$nat_file" -e || {
die ' 💡 Hint: type `sudo pfctl -s nat` to see applied NAT rules'
}
comment_run rm "$nat_file"
declare -a prolog_cmds=()
prolog_cmds+=('bash -c "source <(curl -LfsS https://git.io/logr.sh) && banr \"ShareZScaler Host Configuration\""')
declare -a route_cmds=()
route_cmds+=("printf 'Configuring route to %s\n' '$vpn_address' >&2")
route_cmds+=("$(printf "sudo bash -c 'route delete -net %s; route add -net %s -host %s'" "$vpn_address" "$vpn_address" "$VPN_CLIENT_ADDRESS")")
declare -a dns_cmds=("sudo mkdir -p '/etc/resolver'")
for domain in "${domains[@]}"; do
dns_cmds+=("printf 'Configuring resolver for %s\n' '$domain' >&2")
dns_cmds+=("sudo bash -c 'echo \"domain $domain
search $domain
nameserver $vpn_gateway
\" > /etc/resolver/$domain'")
done
dns_cmds+=("printf 'Flushing DNS cache\n' >&2")
dns_cmds+=('sudo killall -HUP mDNSResponder')
dns_cmds+=('dscacheutil -flushcache')
declare unshare_sh='$HOME/unshare-zscaler.v2.sh'
dns_cmds=('if [ -x "'"$unshare_sh"'" ]; then "'"$unshare_sh"'"; fi' "${dns_cmds[@]}")
dns_cmds+=('echo "cd /etc/resolver && sudo rm '"${domains[*]}"' 2>/dev/null" > "'"$unshare_sh"'" && chmod +x "'"$unshare_sh"'"')
if [ "${debug-}" ]; then
dns_cmds+=("scutil --dns")
dns_cmds+=("dns-sd -G v4v6 $probe")
fi
declare -a epilog_cmds=()
epilog_cmds+=("{
tput bold
printf 'Host configuration completed'
tput setaf 2
printf ' ✔'
tput sgr0
printf '\n'
} >&2")
comment_run printf "———\n"
comment_run printf "Successfully set up this VPN client.\n"
comment_run printf "To use the VPN tunnel on your host:\n"
comment_run printf "— either run the following script on it, or\n"
comment_run printf "— pipe this output to its Bash.\n"
comment_run printf "———\n"
printf "%s\n" "${prolog_cmds[@]}"
printf "%s\n" "${route_cmds[@]}"
printf "%s\n" "${dns_cmds[@]}"
printf "%s\n" "${epilog_cmds[@]}"
}
main "$@"