diff options
author | Andrew Cady <d@jerkface.net> | 2021-10-09 05:05:23 -0400 |
---|---|---|
committer | Andrew Cady <d@jerkface.net> | 2021-10-09 05:05:23 -0400 |
commit | 00dfe043a4f6a58a81e3e00738f30a97819b2176 (patch) | |
tree | cb64fbc39263e123f714ffcc535387373d9096c2 | |
parent | 1e20926763855b24f0474bdf65bf9b4a593eb731 (diff) |
improve options considerably
-rwxr-xr-x | connect-vpn.sh | 192 |
1 files changed, 136 insertions, 56 deletions
diff --git a/connect-vpn.sh b/connect-vpn.sh index 6292979..5e95558 100755 --- a/connect-vpn.sh +++ b/connect-vpn.sh | |||
@@ -1,5 +1,6 @@ | |||
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | [ "$UID" = 0 ] || exec sudo -- "$0" "$@" || exit | 2 | set -e |
3 | [ "$UID" = 0 ] || exec sudo -- "$0" "$@" | ||
3 | 4 | ||
4 | help() | 5 | help() |
5 | { | 6 | { |
@@ -10,9 +11,7 @@ help() | |||
10 | 11 | ||
11 | Synopsis: | 12 | Synopsis: |
12 | 13 | ||
13 | $prefix0 <hostname>.<keystring>.<keytype>.cryptonomic.net | 14 | $prefix0 <hostname> |
14 | |||
15 | Synopsis: | ||
16 | 15 | ||
17 | $prefix0 --remote-ip <IP address> \\ | 16 | $prefix0 --remote-ip <IP address> \\ |
18 | $prefix1 --remote-name <hostname> | 17 | $prefix1 --remote-name <hostname> |
@@ -28,74 +27,159 @@ $prefix1 [--local-key <filename>] | |||
28 | 27 | ||
29 | Options: | 28 | Options: |
30 | 29 | ||
31 | --remote-ip | ||
32 | --remote-name | 30 | --remote-name |
33 | 31 | ||
34 | Remote IP and NAME are MANDATORY. | 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 | ||
35 | 38 | ||
36 | --remote-key | 39 | The IP specifies an IP to connect to. |
37 | 40 | ||
38 | Remote key, if specified, must be 'rsa'. | 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'. | ||
39 | 53 | ||
40 | --local-key | 54 | --local-key |
41 | 55 | ||
42 | The specified key must be an RSA key. | 56 | This specifies the location of the private key file. |
43 | 57 | ||
44 | The filename is considered relative to /etc/ssh if it has no slashes. | 58 | The filename is considered relative to /etc/ssh if it has no slashes. |
45 | 59 | ||
46 | The default is 'ssh_host_rsa_key'. | 60 | The default is 'ssh_host_rsa_key'. |
47 | 61 | ||
62 | The key must be an RSA key. | ||
63 | |||
48 | EOF | 64 | EOF |
49 | } | 65 | } |
50 | die() { printf 'Error: %s\n' "$*"; exit 1; } | ||
51 | 66 | ||
52 | REMOTE_IP=68.48.18.140 | 67 | |
53 | REMOTE_NAME=andy | 68 | # Default command line argument values. |
69 | NO_ACT=y | ||
54 | REMOTE_KEY_TYPE=rsa | 70 | REMOTE_KEY_TYPE=rsa |
55 | LOCAL_KEY=ssh_host_rsa_key | 71 | LOCAL_KEY=ssh_host_rsa_key |
56 | 72 | ||
73 | # Hard-coded private key source and destinations. | ||
57 | LOCAL_KEY_DEST_BASENAME=ssh_host_rsa_key | 74 | LOCAL_KEY_DEST_BASENAME=ssh_host_rsa_key |
58 | LOCAL_PRIVATE_KEY_DEST=/etc/swanctl/private/$LOCAL_KEY_DEST_BASENAME | 75 | LOCAL_PRIVATE_KEY_DEST=/etc/swanctl/private/$LOCAL_KEY_DEST_BASENAME |
59 | LOCAL_PUBLIC_KEY_DEST=/etc/swanctl/pubkey/$LOCAL_KEY_DEST_BASENAME.pub | 76 | LOCAL_PUBLIC_KEY_DEST=/etc/swanctl/pubkey/$LOCAL_KEY_DEST_BASENAME.pub |
60 | 77 | ||
61 | OPTS=$(getopt \ | 78 | die() { printf 'Error: %s\n' "$*" >&2; exit 1; } |
62 | --options 'h' \ | 79 | warn() { printf 'Warning: %s\n' "$*" >&2; } |
63 | --longoptions 'help,remote-ip:,remote-name:,remote-key-type:,local-key:' \ | 80 | |
64 | -n connect-vpn \ | 81 | parse_options() |
65 | -- "$@") | 82 | { |
66 | eval set -- "$OPTS" | 83 | OPTS=$(getopt \ |
67 | unset OPTS | 84 | --options 'hynf' \ |
68 | 85 | --longoptions 'help,yes-act,no-act,remote-ip:,remote-name:,remote-key-type:,local-key:' \ | |
69 | while true | 86 | -n connect-vpn \ |
70 | do | 87 | -- "$@") |
71 | case "$1" in | 88 | eval set -- "$OPTS" |
72 | -h|--help) help; exit;; | 89 | unset OPTS |
73 | --remote-ip) REMOTE_IP=$2; shift 2;; | 90 | |
74 | --remote-name) REMOTE_NAME=$2; shift 2;; | 91 | while true |
75 | --remote-key-type) REMOTE_KEY_TYPE=$2; shift 2;; | 92 | do |
76 | --local-key) LOCAL_KEY=$2; shift 2;; | 93 | case "$1" in |
77 | --) shift; break;; | 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 ;; | ||
78 | esac | 109 | esac |
79 | shift | ||
80 | done | ||
81 | 110 | ||
82 | if [ $# != 0 ] | 111 | if [ "$CRYPTONOMIC_DOMAIN" ] |
83 | then | 112 | then |
84 | help | 113 | REMOTE_NAME=${CRYPTONOMIC_DOMAIN%%.*} |
85 | exit 1 | 114 | [ "$REMOTE_IP" ] || REMOTE_IP=$(resolve_domain_name "$REMOTE_NAME") |
86 | fi | 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 | |||
129 | resolve_domain_name() | ||
130 | { | ||
131 | normalize_address "$@" | ||
132 | } | ||
87 | 133 | ||
88 | [ "$REMOTE_IP" ] || die 'remote ip' # todo parse IP | 134 | normalize_address() |
89 | [ "$REMOTE_NAME" ] || die 'remote name' # todo parse valid hostname | 135 | { |
90 | [ "$REMOTE_KEY_TYPE" = rsa ] | 136 | local IP |
137 | [ "$1" ] || return | ||
138 | IP=$(getent ahosts "$1") | ||
139 | IP=${IP%% *} | ||
140 | [ "$IP" ] || return | ||
141 | echo "$IP" | ||
142 | } | ||
91 | 143 | ||
92 | case "$LOCAL_KEY" in | 144 | validate_remote_ip() |
93 | */*) ;; | 145 | { |
94 | *) LOCAL_KEY=/etc/ssh/$LOCAL_KEY ;; | 146 | REMOTE_IP=$(normalize_address "${REMOTE_IP:-$REMOTE_NAME}") |
95 | esac | 147 | } |
96 | 148 | ||
97 | [ -f "$LOCAL_KEY" -a -r "$LOCAL_KEY" ] || die "local key ($LOCAL_KEY)" | 149 | validate_remote_name() |
150 | { | ||
151 | domain_name_regexp='^[[:alpha:]][-.[:alnum:]]*[[:alnum:]]*$' | ||
152 | [[ $REMOTE_NAME =~ $domain_name_regexp ]] | ||
153 | } | ||
98 | 154 | ||
155 | validate_remote_key_type() | ||
156 | { | ||
157 | [ "$REMOTE_KEY_TYPE" = rsa ] | ||
158 | } | ||
159 | |||
160 | validate_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 | |||
170 | main() | ||
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 | } | ||
99 | 183 | ||
100 | match_and_drop_first_word() | 184 | match_and_drop_first_word() |
101 | { | 185 | { |
@@ -211,9 +295,10 @@ nocomments() | |||
211 | 295 | ||
212 | config() | 296 | config() |
213 | { | 297 | { |
214 | local conn="$1" remote_addrs="$2" id="$3" | 298 | local conn="$1" remote_addrs="$2" local_key="$3" |
215 | local public_key_file="$4" private_key_file="$5" | 299 | local public_key_file="$4" private_key_file="$5" |
216 | local remote_ts=0::0/0 vips=:: | 300 | local remote_ts=0::0/0 vips=:: |
301 | id=$(key_to_suffix "$local_key") || return | ||
217 | sed -e 's/^ //' <<END | 302 | sed -e 's/^ //' <<END |
218 | connections { | 303 | connections { |
219 | ${conn} { | 304 | ${conn} { |
@@ -251,11 +336,10 @@ get_my_mac() | |||
251 | [ "$my_mac" ] | 336 | [ "$my_mac" ] |
252 | } | 337 | } |
253 | 338 | ||
254 | crypto_get_my_mac() | 339 | key_to_suffix() |
255 | { | 340 | { |
256 | my_mac=$(ssh-keygen -r . -f "$LOCAL_KEY" | | 341 | local keytype=1 hashtype=2 |
257 | sed -E -ne 's/^[^ ]+ IN SSHFP [^ ]+ 2 .{48}(.{4})(.{4})(.{4})(.{4})$/\1:\2:\3:\4/p') | 342 | ssh-keygen -r . -f "$1" | sed -E -ne 's/^. IN SSHFP '"$keytype $hashtype"' .{48}(.{4})(.{4})(.{4})(.{4})$/\1:\2:\3:\4/p' |
258 | [ "$my_mac" ] | ||
259 | } | 343 | } |
260 | 344 | ||
261 | NO_ACT() | 345 | NO_ACT() |
@@ -265,12 +349,11 @@ NO_ACT() | |||
265 | 349 | ||
266 | write_config() | 350 | write_config() |
267 | { | 351 | { |
268 | crypto_get_my_mac "$REMOTE_IP" || return | ||
269 | write_successfully /etc/swanctl/conf.d/"$REMOTE_NAME".conf -- \ | 352 | write_successfully /etc/swanctl/conf.d/"$REMOTE_NAME".conf -- \ |
270 | config \ | 353 | config \ |
271 | "$REMOTE_NAME" \ | 354 | "$REMOTE_NAME" \ |
272 | "$REMOTE_IP" \ | 355 | "$REMOTE_IP" \ |
273 | "$my_mac" \ | 356 | "$LOCAL_KEY" \ |
274 | "$LOCAL_PUBLIC_KEY_DEST" \ | 357 | "$LOCAL_PUBLIC_KEY_DEST" \ |
275 | "$LOCAL_PRIVATE_KEY_DEST" | 358 | "$LOCAL_PRIVATE_KEY_DEST" |
276 | } | 359 | } |
@@ -288,8 +371,5 @@ test_new_config() | |||
288 | NO_ACT ipsec up "${REMOTE_NAME}" | 371 | NO_ACT ipsec up "${REMOTE_NAME}" |
289 | } | 372 | } |
290 | 373 | ||
291 | NO_ACT=y | 374 | main "$@" |
292 | exec 2>&1 | 375 | |
293 | set -e | ||
294 | keycopy | ||
295 | test_new_config | ||