diff options
Diffstat (limited to 'connect-vpn.sh')
-rwxr-xr-x | connect-vpn.sh | 244 |
1 files changed, 193 insertions, 51 deletions
diff --git a/connect-vpn.sh b/connect-vpn.sh index f4f302c..6292979 100755 --- a/connect-vpn.sh +++ b/connect-vpn.sh | |||
@@ -1,38 +1,124 @@ | |||
1 | #!/bin/sh | 1 | #!/bin/bash |
2 | ROUTER_IP=68.48.18.140 | 2 | [ "$UID" = 0 ] || exec sudo -- "$0" "$@" || exit |
3 | ROUTER_NAME=andy | ||
4 | 3 | ||
5 | CLIENT_KEY_BASENAME=ssh_host_rsa_key | 4 | help() |
6 | CLIENT_KEY_DIRNAME=/etc/ssh | ||
7 | CLIENT_KEY=${CLIENT_KEY_DIRNAME}/${CLIENT_KEY_BASENAME} | ||
8 | |||
9 | ssh2der() | ||
10 | { | 5 | { |
11 | ssh-keygen -e -f "$1" -m PEM | openssl rsa -RSAPublicKey_in -outform DER | 6 | prefixU="Usage: $0 " |
7 | prefix0=" $0 " | ||
8 | prefix1=${prefix0//?/ } | ||
9 | cat <<EOF | ||
10 | |||
11 | Synopsis: | ||
12 | |||
13 | $prefix0 <hostname>.<keystring>.<keytype>.cryptonomic.net | ||
14 | |||
15 | Synopsis: | ||
16 | |||
17 | $prefix0 --remote-ip <IP address> \\ | ||
18 | $prefix1 --remote-name <hostname> | ||
19 | |||
20 | |||
21 | Usage: | ||
22 | |||
23 | $prefix0 --remote-ip <IP address> \\ | ||
24 | $prefix1 --remote-name <hostname> \\ | ||
25 | $prefix1 [--remote-key-type rsa] \\ | ||
26 | $prefix1 [--local-key <filename>] | ||
27 | |||
28 | |||
29 | Options: | ||
30 | |||
31 | --remote-ip | ||
32 | --remote-name | ||
33 | |||
34 | Remote IP and NAME are MANDATORY. | ||
35 | |||
36 | --remote-key | ||
37 | |||
38 | Remote key, if specified, must be 'rsa'. | ||
39 | |||
40 | --local-key | ||
41 | |||
42 | The specified key must be an RSA key. | ||
43 | |||
44 | The filename is considered relative to /etc/ssh if it has no slashes. | ||
45 | |||
46 | The default is 'ssh_host_rsa_key'. | ||
47 | |||
48 | EOF | ||
12 | } | 49 | } |
50 | die() { printf 'Error: %s\n' "$*"; exit 1; } | ||
51 | |||
52 | REMOTE_IP=68.48.18.140 | ||
53 | REMOTE_NAME=andy | ||
54 | REMOTE_KEY_TYPE=rsa | ||
55 | LOCAL_KEY=ssh_host_rsa_key | ||
56 | |||
57 | LOCAL_KEY_DEST_BASENAME=ssh_host_rsa_key | ||
58 | LOCAL_PRIVATE_KEY_DEST=/etc/swanctl/private/$LOCAL_KEY_DEST_BASENAME | ||
59 | LOCAL_PUBLIC_KEY_DEST=/etc/swanctl/pubkey/$LOCAL_KEY_DEST_BASENAME.pub | ||
60 | |||
61 | OPTS=$(getopt \ | ||
62 | --options 'h' \ | ||
63 | --longoptions 'help,remote-ip:,remote-name:,remote-key-type:,local-key:' \ | ||
64 | -n connect-vpn \ | ||
65 | -- "$@") | ||
66 | eval set -- "$OPTS" | ||
67 | unset OPTS | ||
68 | |||
69 | while true | ||
70 | do | ||
71 | case "$1" in | ||
72 | -h|--help) help; exit;; | ||
73 | --remote-ip) REMOTE_IP=$2; shift 2;; | ||
74 | --remote-name) REMOTE_NAME=$2; shift 2;; | ||
75 | --remote-key-type) REMOTE_KEY_TYPE=$2; shift 2;; | ||
76 | --local-key) LOCAL_KEY=$2; shift 2;; | ||
77 | --) shift; break;; | ||
78 | esac | ||
79 | shift | ||
80 | done | ||
81 | |||
82 | if [ $# != 0 ] | ||
83 | then | ||
84 | help | ||
85 | exit 1 | ||
86 | fi | ||
87 | |||
88 | [ "$REMOTE_IP" ] || die 'remote ip' # todo parse IP | ||
89 | [ "$REMOTE_NAME" ] || die 'remote name' # todo parse valid hostname | ||
90 | [ "$REMOTE_KEY_TYPE" = rsa ] | ||
91 | |||
92 | case "$LOCAL_KEY" in | ||
93 | */*) ;; | ||
94 | *) LOCAL_KEY=/etc/ssh/$LOCAL_KEY ;; | ||
95 | esac | ||
96 | |||
97 | [ -f "$LOCAL_KEY" -a -r "$LOCAL_KEY" ] || die "local key ($LOCAL_KEY)" | ||
98 | |||
13 | 99 | ||
14 | match_and_drop_first_word() | 100 | match_and_drop_first_word() |
15 | { | 101 | { |
16 | expect=$1 | 102 | expect=$1 |
17 | while read word rest | 103 | while read word rest |
18 | do | 104 | do |
19 | if [ "$word" = "$expect" ] | 105 | if [ "$word" = "$expect" ] |
20 | then | 106 | then |
21 | printf '%s\n' "$rest" | 107 | printf '%s\n' "$rest" |
22 | return | 108 | return |
23 | fi | 109 | fi |
24 | done | 110 | done |
25 | false | 111 | false |
26 | } | 112 | } |
27 | 113 | ||
28 | keyscan() | 114 | keyscan() |
29 | { | 115 | { |
30 | if [ -e keyscan.cache ] | 116 | if [ -e keyscan.cache ] |
31 | then | 117 | then |
32 | cat keyscan.cache | 118 | cat keyscan.cache |
33 | else | 119 | else |
34 | ssh-keyscan -t rsa "$1" | 120 | semi_quietly ssh-keyscan -t "${REMOTE_KEY_TYPE}" "$1" |
35 | fi | 121 | fi |
36 | } | 122 | } |
37 | 123 | ||
38 | write_successfully() | 124 | write_successfully() |
@@ -45,7 +131,16 @@ write_successfully() | |||
45 | then | 131 | then |
46 | if [ "$NO_ACT" ] | 132 | if [ "$NO_ACT" ] |
47 | then | 133 | then |
48 | echo "mv $f $out" >&2 | 134 | ( |
135 | exec >&2 | ||
136 | echo "Write $out:" | ||
137 | case "$(file --mime-encoding "$f")" in | ||
138 | *': binary') xxd "$f" ;; | ||
139 | *) cat "$f" ;; | ||
140 | esac | sed 's/^/ /' | ||
141 | echo | ||
142 | ) | ||
143 | rm -f "$f" | ||
49 | else | 144 | else |
50 | mv "$f" "$out" | 145 | mv "$f" "$out" |
51 | fi | 146 | fi |
@@ -55,36 +150,70 @@ write_successfully() | |||
55 | fi | 150 | fi |
56 | } | 151 | } |
57 | 152 | ||
153 | semi_quietly() | ||
154 | { | ||
155 | local t=$(mktemp) | ||
156 | if "$@" 2>"$t" | ||
157 | then | ||
158 | rm -f "$t" | ||
159 | else | ||
160 | cat "$t" >&2 | ||
161 | fi | ||
162 | } | ||
163 | |||
164 | openssl() | ||
165 | { | ||
166 | semi_quietly command openssl "$@" | ||
167 | } | ||
168 | |||
169 | write_public_key() | ||
170 | { | ||
171 | openssl rsa -in "$1" -outform DER | ||
172 | } | ||
173 | write_private_key() | ||
174 | { | ||
175 | openssl rsa -in "$1" -outform DER -pubout | ||
176 | } | ||
177 | |||
178 | write_remote_key() | ||
179 | { | ||
180 | case "$REMOTE_KEY_TYPE" in | ||
181 | rsa) ssh-keygen -e -f "$1" -m PEM | openssl rsa -RSAPublicKey_in -outform DER ;; | ||
182 | *) echo "Unsupported key type." >&2; exit 1 ;; | ||
183 | esac | ||
184 | } | ||
185 | |||
58 | keycopy() | 186 | keycopy() |
59 | { | 187 | { |
60 | private_key_tmp="$(mktemp)" || return | 188 | private_key_tmp=$(mktemp) || return |
61 | cp "$CLIENT_KEY" "$private_key_tmp" | 189 | cp "$LOCAL_KEY" "$private_key_tmp" |
62 | ssh-keygen -N '' -P '' -p -m PEM -f "$private_key_tmp" | 190 | ssh-keygen -N '' -p -m PEM -f "$private_key_tmp" >/dev/null 2>&1 |
63 | trap 'rm -f "$private_key_tmp"' EXIT | 191 | trap 'rm -f "$private_key_tmp"' EXIT |
64 | 192 | ||
65 | write_successfully /etc/swanctl/private/"$CLIENT_KEY_BASENAME" -- openssl rsa -in "$private_key_tmp" -outform DER | 193 | write_successfully "$LOCAL_PRIVATE_KEY_DEST" -- write_private_key "$private_key_tmp" |
66 | write_successfully /etc/swanctl/pubkey/"$CLIENT_KEY_BASENAME".pub -- openssl rsa -in "$private_key_tmp" -outform DER -pubout | 194 | write_successfully "$LOCAL_PUBLIC_KEY_DEST" -- write_public_key "$private_key_tmp" |
67 | 195 | ||
68 | trap - EXIT | 196 | trap - EXIT |
69 | rm -f "$private_key_tmp" | 197 | rm -f "$private_key_tmp" |
70 | 198 | ||
71 | t=$(mktemp) | 199 | trap 'rm -f "$t"' EXIT |
72 | keyscan "$ROUTER_IP" | match_and_drop_first_word "$ROUTER_IP" > "$t" | 200 | t=$(mktemp) |
73 | write_successfully /etc/swanctl/pubkey/"$ROUTER_NAME".pub -- ssh2der "$t" | 201 | keyscan "$REMOTE_IP" | match_and_drop_first_word "$REMOTE_IP" > "$t" |
74 | rm -f "$t" | 202 | write_successfully /etc/swanctl/pubkey/"$REMOTE_NAME".pub -- write_remote_key "$t" |
203 | trap - EXIT | ||
204 | rm -f "$t" | ||
75 | } | 205 | } |
76 | 206 | ||
77 | nocomments() | 207 | nocomments() |
78 | { | 208 | { |
79 | sed 's/#.*//; /^ *$/d' | 209 | sed 's/#.*//; /^ *$/d' |
80 | } | 210 | } |
81 | 211 | ||
82 | |||
83 | config() | 212 | config() |
84 | { | 213 | { |
85 | local conn="$1" remote_addrs="$2" id="$3" | 214 | local conn="$1" remote_addrs="$2" id="$3" |
215 | local public_key_file="$4" private_key_file="$5" | ||
86 | local remote_ts=0::0/0 vips=:: | 216 | local remote_ts=0::0/0 vips=:: |
87 | local public_key_file="${CLIENT_KEY_BASENAME}.pub" private_key_file="${CLIENT_KEY_BASENAME}" | ||
88 | sed -e 's/^ //' <<END | 217 | sed -e 's/^ //' <<END |
89 | connections { | 218 | connections { |
90 | ${conn} { | 219 | ${conn} { |
@@ -116,10 +245,17 @@ END | |||
116 | 245 | ||
117 | get_my_mac() | 246 | get_my_mac() |
118 | { | 247 | { |
119 | iface=$(ip -oneline route get "$1" | sed -ne 's/.* dev \([^ ]*\) .*/\1/p') | 248 | iface=$(ip -oneline route get "$1" | sed -ne 's/.* dev \([^ ]*\) .*/\1/p') |
120 | [ "$iface" ] || return | 249 | [ "$iface" ] || return |
121 | my_mac=$(ip -oneline -6 addr show dev "$iface" | sed -ne 's/.* inet6 fe80::\([^/]*\)\/.*/\1/p') | 250 | my_mac=$(ip -oneline -6 addr show dev "$iface" | sed -ne 's/.* inet6 fe80::\([^/]*\)\/.*/\1/p') |
122 | [ "$my_mac" ] | 251 | [ "$my_mac" ] |
252 | } | ||
253 | |||
254 | crypto_get_my_mac() | ||
255 | { | ||
256 | my_mac=$(ssh-keygen -r . -f "$LOCAL_KEY" | | ||
257 | sed -E -ne 's/^[^ ]+ IN SSHFP [^ ]+ 2 .{48}(.{4})(.{4})(.{4})(.{4})$/\1:\2:\3:\4/p') | ||
258 | [ "$my_mac" ] | ||
123 | } | 259 | } |
124 | 260 | ||
125 | NO_ACT() | 261 | NO_ACT() |
@@ -129,25 +265,31 @@ NO_ACT() | |||
129 | 265 | ||
130 | write_config() | 266 | write_config() |
131 | { | 267 | { |
132 | get_my_mac "$ROUTER_IP" || return | 268 | crypto_get_my_mac "$REMOTE_IP" || return |
133 | write_successfully /etc/swanctl/conf.d/"$ROUTER_NAME".conf -- config "$ROUTER_NAME" "$ROUTER_IP" "$my_mac" | 269 | write_successfully /etc/swanctl/conf.d/"$REMOTE_NAME".conf -- \ |
270 | config \ | ||
271 | "$REMOTE_NAME" \ | ||
272 | "$REMOTE_IP" \ | ||
273 | "$my_mac" \ | ||
274 | "$LOCAL_PUBLIC_KEY_DEST" \ | ||
275 | "$LOCAL_PRIVATE_KEY_DEST" | ||
134 | } | 276 | } |
135 | 277 | ||
136 | test_new_config() | 278 | test_new_config() |
137 | { | 279 | { |
138 | NO_ACT ipsec stop | 280 | NO_ACT ipsec stop |
139 | 281 | ||
140 | write_config | 282 | write_config |
141 | 283 | ||
142 | NO_ACT ipsec start | 284 | NO_ACT ipsec start |
143 | NO_ACT sleep 2 | 285 | NO_ACT sleep 2 |
144 | NO_ACT swanctl -c | 286 | NO_ACT swanctl -c |
145 | NO_ACT ipsec listpubkeys | 287 | NO_ACT ipsec listpubkeys |
146 | NO_ACT ipsec up ${ROUTER_NAME} | 288 | NO_ACT ipsec up "${REMOTE_NAME}" |
147 | } | 289 | } |
148 | 290 | ||
149 | NO_ACT=y | 291 | NO_ACT=y |
292 | exec 2>&1 | ||
150 | set -e | 293 | set -e |
151 | keycopy | 294 | keycopy |
152 | test_new_config | 295 | test_new_config |
153 | |||