#!/bin/bash set -e [ "$UID" = 0 ] || exec sudo -- "$0" "$@" help() { prefixU="Usage: $0 " prefix0=" $0 " prefix1=${prefix0//?/ } cat < $prefix0 --remote-name \\ $prefix1 --remote-ip \\ $prefix1 --remote-key-type rsa \\ $prefix1 --local-key Options: --remote-name Specify the name that we give to the remote side. Remote name is mandatory, except that if a single non-option argument is given, then it is treated as if it were the argument to --remote-name. --remote-ip The address of the VPN gateway. If not specified, the hostname specified with the --remote-name option will be resolved to obtain an IP. --remote-key-type The type of key to request from the remote IP via the SSH protocol. If specified, it must be 'rsa'. --local-key The location of the private key file. The filename is considered relative to /etc/ssh if it has no slashes. The default is 'ssh_host_rsa_key'. It must be an RSA key. EOF } # Default command line argument values. NO_ACT=y REMOTE_KEY_TYPE=rsa LOCAL_KEY=ssh_host_rsa_key die() { printf 'Error: %s\n' "$*" >&2; exit 1; } warn() { [ "$QUIET" ] || printf 'Warning: %s\n' "$*" >&2; } notice() { [ "$QUIET" ] || printf 'Notice: %s\n' "$*" >&2; } parse_options() { OPTS=$(getopt \ --options 'hynfq' \ --longoptions 'help,yes-act,no-act,quiet,remote-ip:,remote-name:,remote-key-type:,local-key:' \ -n connect-vpn \ -- "$@") eval set -- "$OPTS" unset OPTS if [ $# = 0 ] then help exit fi while true do case "$1" in -h | --help ) help; exit;; -y | --yes-act ) NO_ACT=;; -n | --no-act ) NO_ACT=y;; -q | --quiet ) QUIET=y;; --remote-ip ) shift; REMOTE_IP=$1;; --remote-name ) shift; REMOTE_NAME=$1;; --remote-key-type ) shift; REMOTE_KEY_TYPE=$1;; --local-key ) shift; LOCAL_KEY=$1;; -- ) shift; break;; esac shift done if [ $# = 1 -a -z "$REMOTE_NAME" ] then REMOTE_NAME=$1 elif [ $# != 0 -o -z "$REMOTE_NAME" ] then help exit 1 fi } resolve_domain_name() { normalize_address "$@" } normalize_address() { local IP [ "$1" ] || return IP=$(getent ahosts "$1") IP=${IP%% *} [ "$IP" ] || return echo "$IP" } validate_remote_ip() { REMOTE_IP=$(normalize_address "${REMOTE_IP:-$REMOTE_NAME}") } validate_remote_name() { domain_name_regexp='^[[:alpha:]][-.[:alnum:]]*[[:alnum:]]*$' [[ $REMOTE_NAME =~ $domain_name_regexp ]] } validate_remote_key_type() { [ "$REMOTE_KEY_TYPE" = rsa ] } validate_local_key() { case "$LOCAL_KEY" in */*) ;; *) LOCAL_KEY=/etc/ssh/$LOCAL_KEY ;; esac [ -f "$LOCAL_KEY" -a -r "$LOCAL_KEY" ] || die "could not read local key (filename=$LOCAL_KEY)" LOCAL_KEY_DEST_BASENAME=$(sshfp_rsa_filename_string "$LOCAL_KEY") || die "parsing local key (filename=$LOCAL_KEY)" LOCAL_PRIVATE_KEY_DEST=/etc/swanctl/private/$LOCAL_KEY_DEST_BASENAME LOCAL_PUBLIC_KEY_DEST=/etc/swanctl/pubkey/$LOCAL_KEY_DEST_BASENAME.pub } main() { parse_options "$@" # The validation functions modify the values to normalize them. validate_remote_name || die 'invalid remote name' validate_remote_ip || die 'invalid remote ip' validate_remote_key_type || die 'invalid remote key type' validate_local_key || die 'invalid local key' if [ "$NO_ACT" ] then exec 2>&1 # Start with the remote public key, to fail early if the server is # unavailable. install_remote_public_rsa_key install_local_private_rsa_key test_new_config else die unimplemented fi exit } test_new_config() { NO_ACT ipsec stop install_stronswan_config NO_ACT ipsec start NO_ACT sleep 2 NO_ACT swanctl -c NO_ACT ipsec listpubkeys NO_ACT ipsec up "${REMOTE_NAME}" } write_if_successful() { local out="$1" f [ "$2" = -- ] || return shift 2 f=$(mktemp) || return if "$@" > "$f" then if [ "$NO_ACT" ] then simulate_write "$f" "$out" rm -f "$f" else mv "$f" "$out" fi else rm -f "$f" return 1 fi } simulate_write() { ( exec >&2 echo "Write $2:" case "$(file --mime-encoding "$1")" in *': binary') xxd "$1" ;; *) cat "$1" ;; esac | sed 's/^/ /' echo ) } quiet_if_successful() { local t=$(mktemp) if "$@" 2>"$t" then rm -f "$t" else cat "$t" >&2 fi } write_public_rsa_key() { quiet_if_successful openssl rsa -in "$1" -outform DER -pubout } write_private_rsa_key() { quiet_if_successful openssl rsa -in "$1" -outform DER } write_remote_rsa_key() { case "$REMOTE_KEY_TYPE" in rsa) ssh-keygen -e -f "$1" -m PEM | quiet_if_successful openssl rsa -RSAPublicKey_in -outform DER ;; *) echo "Unsupported key type." >&2; exit 1 ;; esac } sshfp_rsa_filename_string() { local keytype=1 hashtype=2 ssh-keygen -r. -f "$1" | sed -ne "/^. IN SSHFP $keytype $hashtype / { s/. IN //; y/ /_/; p; q; }" } install_local_private_rsa_key() { private_key_tmp=$(mktemp) || return cp "$LOCAL_KEY" "$private_key_tmp" ssh-keygen -N '' -p -m PEM -f "$private_key_tmp" >/dev/null 2>&1 trap 'rm -f "$private_key_tmp"' EXIT write_if_successful "$LOCAL_PRIVATE_KEY_DEST" -- write_private_rsa_key "$private_key_tmp" write_if_successful "$LOCAL_PUBLIC_KEY_DEST" -- write_public_rsa_key "$private_key_tmp" trap - EXIT rm -f "$private_key_tmp" } scan_knownhosts_files() { local host="$1" f files [ "$host" ] || return files=$(ssh -G "$host" | sed -E -ne 's/(global|user)knownhostsfile //p') for f in $files do [ -e "$f" ] || continue egrep -v '^(#|$)' "$f" | while read _hosts keytype key comment do echo "$keytype $key" done done } find_known_ssh_host_rsa_key_by_name() { local target="$1" name="$2" keytype_wanted='ssh-rsa' scan_knownhosts_files "$name" | ( while read keytype key do [ "$keytype" = "$keytype_wanted" ] || continue notice "found host key: $name $keytype $key" echo "$keytype $key" > "$target" exit done false ) } install_remote_public_rsa_key() { trap 'rm -f "$t"' EXIT t=$(mktemp) if find_known_ssh_host_rsa_key_by_name "$t" "$REMOTE_NAME" then notice "using host key from OpenSSH KnownHostsFiles for $REMOTE_NAME" else notice "scanning the network for host keys for $REMOTE_NAME" ./ssh-update-host-keys "$REMOTE_NAME" || die ssh-update-host-keys find_known_ssh_host_rsa_key_by_name "$t" "$REMOTE_NAME" || die "could not find host rsa key for $REMOTE_NAME" fi REMOTE_PUBLIC_KEY_DEST=/etc/swanctl/pubkey/$(sshfp_rsa_filename_string "$t").pub write_if_successful "$REMOTE_PUBLIC_KEY_DEST" -- write_remote_rsa_key "$t" trap - EXIT rm -f "$t" } strongswan_config() { local conn="$1" remote_addrs="$2" local_key="$3" local public_key_file="$4" private_key_file="$5" remote_public_key_file="$6" local remote_ts=0::0/0 vips=:: id=$(rsa_key_to_ip_suffix "$local_key") || return sed -e 's/^ //' <