diff options
Diffstat (limited to '')
-rw-r--r-- | contrib/ssh-copy-id | 62 | ||||
-rw-r--r-- | contrib/ssh-copy-id.1 | 21 |
2 files changed, 57 insertions, 26 deletions
diff --git a/contrib/ssh-copy-id b/contrib/ssh-copy-id index da6bd18..dcf5798 100644 --- a/contrib/ssh-copy-id +++ b/contrib/ssh-copy-id @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright (c) 1999-2023 Philip Hands <phil@hands.com> +# Copyright (c) 1999-2024 Philip Hands <phil@hands.com> # 2021 Carlos Rodríguez Gili <carlos.rodriguez-gili@upc.edu> # 2020 Matthias Blümel <blaimi@blaimi.de> # 2017 Sebastien Boyron <seb@boyron.eu> @@ -56,19 +56,19 @@ then a bug describing your setup, and the shell you used to make it work. EOF - printf '%s: ERROR: Less dimwitted shell required.\n' "$0" + printf '%s: ERROR: Less dimwitted shell required.\n' "$0" >&2 exit 1 fi fi # shellcheck disable=SC2010 -DEFAULT_PUB_ID_FILE=$(ls -t "${HOME}"/.ssh/id*.pub 2>/dev/null | grep -v -- '-cert.pub$' | head -n 1) +DEFAULT_PUB_ID_FILE=$(ls -dt "${HOME}"/.ssh/id*.pub 2>/dev/null | grep -v -- '-cert.pub$' | head -n 1) SSH="ssh -a -x" TARGET_PATH=".ssh/authorized_keys" umask 0177 usage () { - printf 'Usage: %s [-h|-?|-f|-n|-s|-x] [-i [identity_file]] [-p port] [-F alternative ssh_config file] [-t target_path] [[-o <ssh -o options>] ...] [user@]hostname\n' "$0" >&2 + printf 'Usage: %s [-h|-?|-f|-n|-s|-x] [-i [identity_file]] [-t target_path] [-F ssh_config] [[-o ssh_option] ...] [-p port] [user@]hostname\n' "$0" >&2 printf '\t-f: force mode -- copy keys without trying to check if they are already installed\n' >&2 printf '\t-n: dry run -- no keys are actually copied\n' >&2 printf '\t-s: use sftp -- use sftp instead of executing remote-commands. Can be useful if the remote only allows sftp\n' >&2 @@ -86,7 +86,7 @@ use_id_file() { L_ID_FILE="$1" if [ -z "$L_ID_FILE" ] ; then - printf '%s: ERROR: no ID file found\n' "$0" + printf '%s: ERROR: no ID file found\n' "$0" >&2 exit 1 fi @@ -103,11 +103,10 @@ use_id_file() { ErrMSG=$( { : < "$f" ; } 2>&1 ) || { L_PRIVMSG="" [ "$f" = "$PRIV_ID_FILE" ] && L_PRIVMSG=" (to install the contents of '$PUB_ID_FILE' anyway, look at the -f option)" - printf "\\n%s: ERROR: failed to open ID file '%s': %s\\n" "$0" "$f" "$(printf '%s\n%s\n' "$ErrMSG" "$L_PRIVMSG" | sed -e 's/.*: *//')" + printf "\\n%s: ERROR: failed to open ID file '%s': %s\\n" "$0" "$f" "$(printf '%s\n%s\n' "$ErrMSG" "$L_PRIVMSG" | sed -e 's/.*: *//')" >&2 exit 1 } done - printf '%s: INFO: Source of key(s) to be installed: "%s"\n' "$0" "$PUB_ID_FILE" >&2 GET_ID="cat \"$PUB_ID_FILE\"" } @@ -115,15 +114,31 @@ if [ -n "$SSH_AUTH_SOCK" ] && ssh-add -L >/dev/null 2>&1 ; then GET_ID="ssh-add -L" fi -while getopts "i:o:p:F:t:fnsxh?" OPT +OPTS="io:p:F:t:fnsxh?" + +while getopts "$OPTS" OPT do case "$OPT" in i) [ "${SEEN_OPT_I}" ] && { - printf '\n%s: ERROR: -i option must not be specified more than once\n\n' "$0" + printf '\n%s: ERROR: -i option must not be specified more than once\n\n' "$0" >&2 usage } SEEN_OPT_I="yes" + + # Check for -i's optional parameter + eval "nextarg=\${$OPTIND}" + # shellcheck disable=SC2154 + if [ $OPTIND = $# ]; then + if [ -r "$nextarg" ] && grep -iq ssh "$nextarg"; then + printf '\n%s: ERROR: Missing hostname. Use "-i -- %s" if you really mean to use this as the hostname\n\n' "$0" "$nextarg" >&2 + usage + fi + elif ! expr -- "$nextarg" : "-[$(echo "$OPTS" | tr -d :)-]" >/dev/null ; then + # when not at the last arg, and not followed by an option, -i has an argument + OPTARG="$nextarg" + OPTIND=$((OPTIND + 1)) + fi use_id_file "${OPTARG:-$DEFAULT_PUB_ID_FILE}" ;; o|F) @@ -176,16 +191,28 @@ if [ -z "$(eval $GET_ID)" ] && [ -r "${PUB_ID_FILE:=$DEFAULT_PUB_ID_FILE}" ] ; t use_id_file "$PUB_ID_FILE" fi +printf '%s: INFO: Source of key(s) to be installed: %s\n' "$0" "${GET_ID#cat }" >&2 + # shellcheck disable=SC2086 if [ -z "$(eval $GET_ID)" ] ; then printf '%s: ERROR: No identities found\n' "$0" >&2 exit 1 fi +# assert_scratch_ok() +# ensures that $SCRATCH_DIR is setup. +assert_scratch_ok() { + [ "$SCRATCH_DIR" ] && [ -d "$SCRATCH_DIR" ] && [ -w "$SCRATCH_DIR" ] && return 0 + + printf 'ERROR: Assertion failure: in %s(): scratch_dir was not correctly set up (SCRATCH_DIR = "%s")\n' "$1" "$SCRATCH_DIR" >&2 + return 1 +} + # filter_ids() # tries to log in using the keys piped to it, and filters out any that work filter_ids() { L_SUCCESS="$1" + assert_scratch_ok filter_ids || return L_TMP_ID_FILE="$SCRATCH_DIR"/popids_tmp_id L_OUTPUT_FILE="$SCRATCH_DIR"/popids_output @@ -288,23 +315,24 @@ installkeys_via_sftp() { # repopulate "$@" inside this function eval set -- "$SSH_OPTS" - L_KEYS=$SCRATCH_DIR/authorized_keys - L_SHARED_CON=$SCRATCH_DIR/master-conn + assert_scratch_ok installkeys_via_sftp || return 1 + L_KEYS="$SCRATCH_DIR"/authorized_keys + L_SHARED_CON="$SCRATCH_DIR"/master-conn $SSH -f -N -M -S "$L_SHARED_CON" "$@" - L_CLEANUP="$SSH -S $L_SHARED_CON -O exit 'ignored' >/dev/null 2>&1 ; $SCRATCH_CLEANUP" + L_CLEANUP="$SSH -S '$L_SHARED_CON' -O exit 'ignored' >/dev/null 2>&1 ; $SCRATCH_CLEANUP" #shellcheck disable=SC2064 trap "$L_CLEANUP" EXIT TERM INT QUIT - sftp -b - -o "ControlPath=$L_SHARED_CON" "ignored" <<-EOF || return 1 + sftp -b - -o "ControlPath='$L_SHARED_CON'" "ignored" <<-EOF || return 1 -get "$AUTH_KEY_FILE" "$L_KEYS" EOF # add a newline or create file if it's missing, same like above [ -z "$(tail -1c "$L_KEYS" 2>/dev/null)" ] || echo >> "$L_KEYS" # append the keys being piped in here cat >> "$L_KEYS" - sftp -b - -o "ControlPath=$L_SHARED_CON" "ignored" <<-EOF || return 1 + sftp -b - -o "ControlPath='$L_SHARED_CON'" "ignored" <<-EOF || return 1 -mkdir "$AUTH_KEY_DIR" chmod 700 "$AUTH_KEY_DIR" - put $L_KEYS "$AUTH_KEY_FILE" + put "$L_KEYS" "$AUTH_KEY_FILE" chmod 600 "$AUTH_KEY_FILE" EOF #shellcheck disable=SC2064 @@ -321,7 +349,7 @@ then #shellcheck disable=SC2064 trap "$SCRATCH_CLEANUP" EXIT TERM INT QUIT else - printf '%s: ERROR: failed to create required temporary directory under ~/.ssh\n' "$0" >&2 + printf '%s: ERROR: failed to create required temporary directory under ~/.ssh (HOME="%s")\n' "$0" "$HOME" >&2 exit 1 fi @@ -379,7 +407,7 @@ else Number of key(s) added: $ADDED - Now try logging into the machine, with: "${SFTP:-ssh}${SSH_PORT:+ -${PORT_OPT:-p} $SSH_PORT} ${OPTS_USER_HOST}" + Now try logging into the machine, with: "${SFTP:-ssh} ${SEEN_OPT_I:+-i${PRIV_ID_FILE:+ $PRIV_ID_FILE} }${SSH_PORT:+-${PORT_OPT:-p} $SSH_PORT }${OPTS_USER_HOST}" and check to make sure that only the key(s) you wanted were added. EOF diff --git a/contrib/ssh-copy-id.1 b/contrib/ssh-copy-id.1 index 74eec2f..dbdb45a 100644 --- a/contrib/ssh-copy-id.1 +++ b/contrib/ssh-copy-id.1 @@ -1,5 +1,5 @@ .ig \" -*- nroff -*- -Copyright (c) 1999-2023 hands.com Ltd. <http://hands.com/> +Copyright (c) 1999-2024 Philip Hands <phil@hands.com> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -34,9 +34,10 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .Op Fl s .Op Fl x .Op Fl i Op Ar identity_file -.Op Fl p Ar port -.Op Fl o Ar ssh_option .Op Fl t Ar target_path +.Op Fl F Ar ssh_config +.Op Bo Fl o Ar ssh_option Bc ... +.Op Fl p Ar port .Op Ar user Ns @ Ns .Ar hostname .Nm @@ -67,7 +68,7 @@ command instead. .Pp The options are as follows: .Bl -tag -width Ds -.It Fl i Ar identity_file +.It Fl i Op Ar identity_file Use only the key(s) contained in .Ar identity_file (rather than looking for identities via @@ -104,11 +105,13 @@ on commands which can be used on the remote side. .It Fl t Ar target_path the path on the target system where the keys should be added (defaults to ".ssh/authorized_keys") -.It Fl p Ar port , Fl o Ar ssh_option -These two options are simply passed through untouched, along with their -argument, to allow one to set the port or other -.Xr ssh 1 -options, respectively. +.It Fl p Ar port +Specifies the port to connect to on the remote host. +.It Fl F Ar ssh_config , Fl o Ar ssh_option +These options are simply passed through untouched (with their argument) +to ssh/sftp, +allowing one to set an alternative config file, +or other options, respectively. .Pp Rather than specifying these as command line options, it is often better to use (per-host) settings in |