summaryrefslogtreecommitdiff
path: root/cryptonomic-vpn
diff options
context:
space:
mode:
authorAndrew Cady <d@jerkface.net>2021-10-09 05:16:40 -0400
committerAndrew Cady <d@jerkface.net>2021-10-09 05:19:13 -0400
commit001803289d137b61b84778a518744be47bf3e70b (patch)
tree8f64a5fb68d8febc1c7350f847b5f3bcfec9600f /cryptonomic-vpn
parent00dfe043a4f6a58a81e3e00738f30a97819b2176 (diff)
rename executable to cryptonomic-vpn
Diffstat (limited to 'cryptonomic-vpn')
-rwxr-xr-xcryptonomic-vpn375
1 files changed, 375 insertions, 0 deletions
diff --git a/cryptonomic-vpn b/cryptonomic-vpn
new file mode 100755
index 0000000..5e95558
--- /dev/null
+++ b/cryptonomic-vpn
@@ -0,0 +1,375 @@
1#!/bin/bash
2set -e
3[ "$UID" = 0 ] || exec sudo -- "$0" "$@"
4
5help()
6{
7 prefixU="Usage: $0 "
8 prefix0=" $0 "
9 prefix1=${prefix0//?/ }
10 cat <<EOF
11
12Synopsis:
13
14$prefix0 <hostname>
15
16$prefix0 --remote-ip <IP address> \\
17$prefix1 --remote-name <hostname>
18
19
20Usage:
21
22$prefix0 --remote-ip <IP address> \\
23$prefix1 --remote-name <hostname> \\
24$prefix1 [--remote-key-type rsa] \\
25$prefix1 [--local-key <filename>]
26
27
28Options:
29
30 --remote-name
31
32 Specify the name that we give to the remote side.
33
34 Remote name is mandatory, except that if a single non-option argument is
35 given, then it is treated as if it were the argument to --remote-name.
36
37 --remote-ip
38
39 The IP specifies an IP to connect to.
40
41 The name specifies a name we give to the remote side.
42
43 If not specified, the hostname specified with the --remote-name option
44 will be resolved to obtain the IP.
45
46 --remote-key-type
47
48 The remote key will be fetched via the SSH protocol on port 22 on the
49 remote IP. This option specifies which server key type to request from
50 the server.
51
52 If specified, it must be 'rsa'.
53
54 --local-key
55
56 This specifies the location of the private key file.
57
58 The filename is considered relative to /etc/ssh if it has no slashes.
59
60 The default is 'ssh_host_rsa_key'.
61
62 The key must be an RSA key.
63
64EOF
65}
66
67
68# Default command line argument values.
69NO_ACT=y
70REMOTE_KEY_TYPE=rsa
71LOCAL_KEY=ssh_host_rsa_key
72
73# Hard-coded private key source and destinations.
74LOCAL_KEY_DEST_BASENAME=ssh_host_rsa_key
75LOCAL_PRIVATE_KEY_DEST=/etc/swanctl/private/$LOCAL_KEY_DEST_BASENAME
76LOCAL_PUBLIC_KEY_DEST=/etc/swanctl/pubkey/$LOCAL_KEY_DEST_BASENAME.pub
77
78die() { printf 'Error: %s\n' "$*" >&2; exit 1; }
79warn() { printf 'Warning: %s\n' "$*" >&2; }
80
81parse_options()
82{
83 OPTS=$(getopt \
84 --options 'hynf' \
85 --longoptions 'help,yes-act,no-act,remote-ip:,remote-name:,remote-key-type:,local-key:' \
86 -n connect-vpn \
87 -- "$@")
88 eval set -- "$OPTS"
89 unset OPTS
90
91 while true
92 do
93 case "$1" in
94 -h | --help ) help; exit;;
95 -y | --yes-act ) NO_ACT=;;
96 -n | --no-act ) NO_ACT=y;;
97 --remote-ip ) shift; REMOTE_IP=$1;;
98 --remote-name ) shift; REMOTE_NAME=$1;;
99 --remote-key-type ) shift; REMOTE_KEY_TYPE=$1;;
100 --local-key ) shift; LOCAL_KEY=$1;;
101 -- ) shift; break;;
102 esac
103 shift
104 done
105
106 case "$# $1" in
107 1\ *.*.*.cryptonomic.net) CRYPTONOMIC_DOMAIN=$1; shift ;;
108 1\ *) REMOTE_NAME=$1; shift ;;
109 esac
110
111 if [ "$CRYPTONOMIC_DOMAIN" ]
112 then
113 REMOTE_NAME=${CRYPTONOMIC_DOMAIN%%.*}
114 [ "$REMOTE_IP" ] || REMOTE_IP=$(resolve_domain_name "$REMOTE_NAME")
115
116 elif [ $# != 0 ]
117 then
118 help
119 exit 1
120 fi
121
122 # The validation functions modify the values to normalize them.
123 validate_remote_ip || die 'invalid remote ip'
124 validate_remote_name || die "invalid remote name '$REMOTE_NAME'"
125 validate_remote_key_type || die 'invalid remote key type'
126 validate_local_key || die 'invalid local key'
127}
128
129resolve_domain_name()
130{
131 normalize_address "$@"
132}
133
134normalize_address()
135{
136 local IP
137 [ "$1" ] || return
138 IP=$(getent ahosts "$1")
139 IP=${IP%% *}
140 [ "$IP" ] || return
141 echo "$IP"
142}
143
144validate_remote_ip()
145{
146 REMOTE_IP=$(normalize_address "${REMOTE_IP:-$REMOTE_NAME}")
147}
148
149validate_remote_name()
150{
151 domain_name_regexp='^[[:alpha:]][-.[:alnum:]]*[[:alnum:]]*$'
152 [[ $REMOTE_NAME =~ $domain_name_regexp ]]
153}
154
155validate_remote_key_type()
156{
157 [ "$REMOTE_KEY_TYPE" = rsa ]
158}
159
160validate_local_key()
161{
162 # TODO: check that it is RSA
163 case "$LOCAL_KEY" in
164 */*) ;;
165 *) LOCAL_KEY=/etc/ssh/$LOCAL_KEY ;;
166 esac
167 [ -f "$LOCAL_KEY" -a -r "$LOCAL_KEY" ] || die "local key ($LOCAL_KEY)"
168}
169
170main()
171{
172 parse_options "$@"
173 if [ "$NO_ACT" ]
174 then
175 exec 2>&1
176 keycopy
177 test_new_config
178 else
179 die unimplemented
180 fi
181 exit
182}
183
184match_and_drop_first_word()
185{
186 expect=$1
187 while read word rest
188 do
189 if [ "$word" = "$expect" ]
190 then
191 printf '%s\n' "$rest"
192 return
193 fi
194 done
195 false
196}
197
198keyscan()
199{
200 if [ -e keyscan.cache ]
201 then
202 cat keyscan.cache
203 else
204 semi_quietly ssh-keyscan -t "${REMOTE_KEY_TYPE}" "$1"
205 fi
206}
207
208write_successfully()
209{
210 local f=$(mktemp) || return
211 local out="$1"
212 [ "$2" = -- ] || return
213 shift 2
214 if "$@" > "$f"
215 then
216 if [ "$NO_ACT" ]
217 then
218 (
219 exec >&2
220 echo "Write $out:"
221 case "$(file --mime-encoding "$f")" in
222 *': binary') xxd "$f" ;;
223 *) cat "$f" ;;
224 esac | sed 's/^/ /'
225 echo
226 )
227 rm -f "$f"
228 else
229 mv "$f" "$out"
230 fi
231 else
232 rm -f "$f"
233 return 1
234 fi
235}
236
237semi_quietly()
238{
239 local t=$(mktemp)
240 if "$@" 2>"$t"
241 then
242 rm -f "$t"
243 else
244 cat "$t" >&2
245 fi
246}
247
248openssl()
249{
250 semi_quietly command openssl "$@"
251}
252
253write_public_key()
254{
255 openssl rsa -in "$1" -outform DER
256}
257write_private_key()
258{
259 openssl rsa -in "$1" -outform DER -pubout
260}
261
262write_remote_key()
263{
264 case "$REMOTE_KEY_TYPE" in
265 rsa) ssh-keygen -e -f "$1" -m PEM | openssl rsa -RSAPublicKey_in -outform DER ;;
266 *) echo "Unsupported key type." >&2; exit 1 ;;
267 esac
268}
269
270keycopy()
271{
272 private_key_tmp=$(mktemp) || return
273 cp "$LOCAL_KEY" "$private_key_tmp"
274 ssh-keygen -N '' -p -m PEM -f "$private_key_tmp" >/dev/null 2>&1
275 trap 'rm -f "$private_key_tmp"' EXIT
276
277 write_successfully "$LOCAL_PRIVATE_KEY_DEST" -- write_private_key "$private_key_tmp"
278 write_successfully "$LOCAL_PUBLIC_KEY_DEST" -- write_public_key "$private_key_tmp"
279
280 trap - EXIT
281 rm -f "$private_key_tmp"
282
283 trap 'rm -f "$t"' EXIT
284 t=$(mktemp)
285 keyscan "$REMOTE_IP" | match_and_drop_first_word "$REMOTE_IP" > "$t"
286 write_successfully /etc/swanctl/pubkey/"$REMOTE_NAME".pub -- write_remote_key "$t"
287 trap - EXIT
288 rm -f "$t"
289}
290
291nocomments()
292{
293 sed 's/#.*//; /^ *$/d'
294}
295
296config()
297{
298 local conn="$1" remote_addrs="$2" local_key="$3"
299 local public_key_file="$4" private_key_file="$5"
300 local remote_ts=0::0/0 vips=::
301 id=$(key_to_suffix "$local_key") || return
302 sed -e 's/^ //' <<END
303 connections {
304 ${conn} {
305 remote_addrs = ${remote_addrs}
306 vips = ${vips}
307 local {
308 pubkeys = ${public_key_file}
309 id = ${id}
310 }
311 remote {
312 id = "${remote_addrs}"
313 pubkeys = ${conn}.pub
314 }
315 children {
316 child {
317 remote_ts = ${remote_ts}
318 dpd_action = restart
319 }
320 }
321 }
322 }
323 secrets {
324 private {
325 file = ${private_key_file}
326 }
327 }
328END
329}
330
331get_my_mac()
332{
333 iface=$(ip -oneline route get "$1" | sed -ne 's/.* dev \([^ ]*\) .*/\1/p')
334 [ "$iface" ] || return
335 my_mac=$(ip -oneline -6 addr show dev "$iface" | sed -ne 's/.* inet6 fe80::\([^/]*\)\/.*/\1/p')
336 [ "$my_mac" ]
337}
338
339key_to_suffix()
340{
341 local keytype=1 hashtype=2
342 ssh-keygen -r . -f "$1" | sed -E -ne 's/^. IN SSHFP '"$keytype $hashtype"' .{48}(.{4})(.{4})(.{4})(.{4})$/\1:\2:\3:\4/p'
343}
344
345NO_ACT()
346{
347 [ "$NO_ACT" ] || "$@"
348}
349
350write_config()
351{
352 write_successfully /etc/swanctl/conf.d/"$REMOTE_NAME".conf -- \
353 config \
354 "$REMOTE_NAME" \
355 "$REMOTE_IP" \
356 "$LOCAL_KEY" \
357 "$LOCAL_PUBLIC_KEY_DEST" \
358 "$LOCAL_PRIVATE_KEY_DEST"
359}
360
361test_new_config()
362{
363 NO_ACT ipsec stop
364
365 write_config
366
367 NO_ACT ipsec start
368 NO_ACT sleep 2
369 NO_ACT swanctl -c
370 NO_ACT ipsec listpubkeys
371 NO_ACT ipsec up "${REMOTE_NAME}"
372}
373
374main "$@"
375