#!/bin/bash set -e [ "$UID" = 0 ] || exec sudo -- "$0" "$@" help() { prefixU="Usage: $0 " prefix0=" $0 " prefix1=${prefix0//?/ } cat < $prefix0 --remote-ip \\ $prefix1 --remote-name Usage: $prefix0 --remote-ip \\ $prefix1 --remote-name \\ $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 IP specifies an IP to connect to. The name specifies a name we give to the remote side. If not specified, the hostname specified with the --remote-name option will be resolved to obtain the IP. --remote-key-type The remote key will be fetched via the SSH protocol on port 22 on the remote IP. This option specifies which server key type to request from the server. If specified, it must be 'rsa'. --local-key This specifies 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'. The key must be an RSA key. EOF } # Default command line argument values. NO_ACT=y REMOTE_KEY_TYPE=rsa LOCAL_KEY=ssh_host_rsa_key # Hard-coded private key source and destinations. LOCAL_KEY_DEST_BASENAME=ssh_host_rsa_key LOCAL_PRIVATE_KEY_DEST=/etc/swanctl/private/$LOCAL_KEY_DEST_BASENAME LOCAL_PUBLIC_KEY_DEST=/etc/swanctl/pubkey/$LOCAL_KEY_DEST_BASENAME.pub die() { printf 'Error: %s\n' "$*" >&2; exit 1; } warn() { printf 'Warning: %s\n' "$*" >&2; } parse_options() { OPTS=$(getopt \ --options 'hynf' \ --longoptions 'help,yes-act,no-act,remote-ip:,remote-name:,remote-key-type:,local-key:' \ -n connect-vpn \ -- "$@") eval set -- "$OPTS" unset OPTS while true do case "$1" in -h | --help ) help; exit;; -y | --yes-act ) NO_ACT=;; -n | --no-act ) NO_ACT=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 case "$# $1" in 1\ *.*.*.cryptonomic.net) CRYPTONOMIC_DOMAIN=$1; shift ;; 1\ *) REMOTE_NAME=$1; shift ;; esac if [ "$CRYPTONOMIC_DOMAIN" ] then REMOTE_NAME=${CRYPTONOMIC_DOMAIN%%.*} [ "$REMOTE_IP" ] || REMOTE_IP=$(resolve_domain_name "$REMOTE_NAME") elif [ $# != 0 ] then help exit 1 fi # The validation functions modify the values to normalize them. validate_remote_ip || die 'invalid remote ip' validate_remote_name || die "invalid remote name '$REMOTE_NAME'" validate_remote_key_type || die 'invalid remote key type' validate_local_key || die 'invalid local key' } 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() { # TODO: check that it is RSA case "$LOCAL_KEY" in */*) ;; *) LOCAL_KEY=/etc/ssh/$LOCAL_KEY ;; esac [ -f "$LOCAL_KEY" -a -r "$LOCAL_KEY" ] || die "local key ($LOCAL_KEY)" } main() { parse_options "$@" if [ "$NO_ACT" ] then exec 2>&1 keycopy test_new_config else die unimplemented fi exit } match_and_drop_first_word() { expect=$1 while read word rest do if [ "$word" = "$expect" ] then printf '%s\n' "$rest" return fi done false } keyscan() { if [ -e keyscan.cache ] then cat keyscan.cache else semi_quietly ssh-keyscan -t "${REMOTE_KEY_TYPE}" "$1" fi } write_successfully() { local f=$(mktemp) || return local out="$1" [ "$2" = -- ] || return shift 2 if "$@" > "$f" then if [ "$NO_ACT" ] then ( exec >&2 echo "Write $out:" case "$(file --mime-encoding "$f")" in *': binary') xxd "$f" ;; *) cat "$f" ;; esac | sed 's/^/ /' echo ) rm -f "$f" else mv "$f" "$out" fi else rm -f "$f" return 1 fi } semi_quietly() { local t=$(mktemp) if "$@" 2>"$t" then rm -f "$t" else cat "$t" >&2 fi } openssl() { semi_quietly command openssl "$@" } write_public_key() { openssl rsa -in "$1" -outform DER } write_private_key() { openssl rsa -in "$1" -outform DER -pubout } write_remote_key() { case "$REMOTE_KEY_TYPE" in rsa) ssh-keygen -e -f "$1" -m PEM | openssl rsa -RSAPublicKey_in -outform DER ;; *) echo "Unsupported key type." >&2; exit 1 ;; esac } keycopy() { 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_successfully "$LOCAL_PRIVATE_KEY_DEST" -- write_private_key "$private_key_tmp" write_successfully "$LOCAL_PUBLIC_KEY_DEST" -- write_public_key "$private_key_tmp" trap - EXIT rm -f "$private_key_tmp" trap 'rm -f "$t"' EXIT t=$(mktemp) keyscan "$REMOTE_IP" | match_and_drop_first_word "$REMOTE_IP" > "$t" write_successfully /etc/swanctl/pubkey/"$REMOTE_NAME".pub -- write_remote_key "$t" trap - EXIT rm -f "$t" } nocomments() { sed 's/#.*//; /^ *$/d' } config() { local conn="$1" remote_addrs="$2" local_key="$3" local public_key_file="$4" private_key_file="$5" local remote_ts=0::0/0 vips=:: id=$(key_to_suffix "$local_key") || return sed -e 's/^ //' <