From 1a52b2d612b1d0c2a15dfcdc8da560704909ec72 Mon Sep 17 00:00:00 2001 From: Philip Hands Date: Sat, 27 Jul 2013 14:16:52 +0100 Subject: [PATCH] echo --> printf "%s: ERROR... (for consistency) --- ssh-copy-id | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id index ae88e99..516b87f 100755 --- $1/contrib/ssh-copy-id +++ $1/contrib/ssh-copy-id @@ -200,7 +200,7 @@ populate_new_ids() { umask 0177 local L_TMP_ID_FILE=$(mktemp ~/.ssh/ssh-copy-id_id.XXXXXXXXXX) if test $? -ne 0 || test "x$L_TMP_ID_FILE" = "x" ; then - echo "mktemp failed" 1>&2 + printf '%s: ERROR: mktemp failed\n' "$0" >&2 exit 1 fi trap "rm -f $L_TMP_ID_FILE ${L_TMP_ID_FILE}.pub" EXIT TERM INT QUIT -- 1.9.1 From baebbb9e18e4a1af7554d939710eacb665a24b68 Mon Sep 17 00:00:00 2001 From: Philip Hands Date: Wed, 25 Nov 2015 17:05:39 +0100 Subject: [PATCH] Deal with remote user shell being e.g. tcsh (fixes: 2206) as suggested by Jakub Jelen --- ssh-copy-id | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id index 516b87f..6a0447a 100755 --- $1/contrib/ssh-copy-id +++ $1/contrib/ssh-copy-id @@ -269,10 +269,8 @@ case "$REMOTE_VERSION" in *) # Assuming that the remote host treats ~/.ssh/authorized_keys as one might expect populate_new_ids 0 - [ "$DRY_RUN" ] || printf '%s\n' "$NEW_IDS" | ssh "$@" " - umask 077 ; - mkdir -p .ssh && cat >> .ssh/authorized_keys || exit 1 ; - if type restorecon >/dev/null 2>&1 ; then restorecon -F .ssh .ssh/authorized_keys ; fi" \ + [ "$DRY_RUN" ] || printf '%s\n' "$NEW_IDS" | \ + ssh "$@" "exec sh -c 'umask 077 ; mkdir -p .ssh && cat >> .ssh/authorized_keys || exit 1 ; if type restorecon >/dev/null 2>&1 ; then restorecon -F .ssh .ssh/authorized_keys ; fi'" \ || exit 1 ADDED=$(printf '%s\n' "$NEW_IDS" | wc -l) ;; -- 1.9.1 From 35f05e39cda8670b3f6797330a3e521fda509a4c Mon Sep 17 00:00:00 2001 From: Philip Hands Date: Wed, 25 Nov 2015 21:14:00 +0100 Subject: [PATCH] set LogLevel to ensure that it's not set to 'None' (closes: 2214) As pointed out by Sami Haahtinen , the LogLevel is set to 'None' we'll not get the Permission Denied we're looking for. --- ssh-copy-id | 1 + 1 file changed, 1 insertion(+) diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id index 6a0447a..70d3866 100755 --- $1/contrib/ssh-copy-id +++ $1/contrib/ssh-copy-id @@ -215,6 +215,7 @@ populate_new_ids() { # The point being that if file based, ssh needs the private key, which it cannot # find if only given the contents of the .pub file in an unrelated tmpfile ssh -i "${PRIV_ID_FILE:-$L_TMP_ID_FILE}" \ + -o LogLevel=INFO \ -o PreferredAuthentications=publickey \ -o IdentitiesOnly=yes "$@" exit 2>$L_TMP_ID_FILE.stderr Date: Wed, 25 Nov 2015 22:30:43 +0100 Subject: [PATCH] set ControlPath=none (closes: 2488) MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Thanks to Salvador Fandiño for the patch This seems to be the same problem as described in 2195 --- ssh-copy-id | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id index 70d3866..7df7fad 100755 --- $1/contrib/ssh-copy-id +++ $1/contrib/ssh-copy-id @@ -215,6 +215,7 @@ populate_new_ids() { # The point being that if file based, ssh needs the private key, which it cannot # find if only given the contents of the .pub file in an unrelated tmpfile ssh -i "${PRIV_ID_FILE:-$L_TMP_ID_FILE}" \ + -o ControlPath=none \ -o LogLevel=INFO \ -o PreferredAuthentications=publickey \ -o IdentitiesOnly=yes "$@" exit 2>$L_TMP_ID_FILE.stderr &2 } -REMOTE_VERSION=$(ssh -v -o PreferredAuthentications=',' "$@" 2>&1 | +REMOTE_VERSION=$(ssh -v -o PreferredAuthentications=',' -o ControlPath=none "$@" 2>&1 | sed -ne 's/.*remote software version //p') case "$REMOTE_VERSION" in -- 1.9.1 From 6fa6f1e3dbec32636e77d01228ceecfa3851c7e8 Mon Sep 17 00:00:00 2001 From: Philip Hands Date: Wed, 25 Nov 2015 23:24:13 +0100 Subject: [PATCH] add -f (forced) option to install keys unconditionally (closes: 2110) Thanks for the patch from Petr Lautrbach which inspired this. --- ssh-copy-id | 15 +++++++++++++-- ssh-copy-id.1 | 5 +++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id index 7df7fad..3121171 100755 --- $1/contrib/ssh-copy-id +++ $1/contrib/ssh-copy-id @@ -59,7 +59,10 @@ fi DEFAULT_PUB_ID_FILE=$(ls -t ${HOME}/.ssh/id*.pub 2>/dev/null | grep -v -- '-cert.pub$' | head -n 1) usage () { - printf 'Usage: %s [-h|-?|-n] [-i [identity_file]] [-p port] [[-o ] ...] [user@]hostname\n' "$0" >&2 + printf 'Usage: %s [-h|-?|-f|-n] [-i [identity_file]] [-p port] [[-o ] ...] [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-h|-?: print this help\n' >&2 exit 1 } @@ -121,7 +124,7 @@ do } shift ;; - -n|-h|-\?) + -f|-n|-h|-\?) OPT="$1" OPTARG= shift @@ -154,6 +157,9 @@ do -o|-p) SSH_OPTS="${SSH_OPTS:+$SSH_OPTS }$OPT '$(quote "$OPTARG")'" ;; + -f) + FORCED=1 + ;; -n) DRY_RUN=1 ;; @@ -194,6 +200,11 @@ fi populate_new_ids() { local L_SUCCESS="$1" + if [ "$FORCED" ] ; then + NEW_IDS=$(eval $GET_ID) + return + fi + # repopulate "$@" inside this function eval set -- "$SSH_OPTS" diff --git $1/contrib/ssh-copy-id.1 $1/contrib/ssh-copy-id.1 index 67a59e4..8850cce 100644 --- $1/contrib/ssh-copy-id.1 +++ $1/contrib/ssh-copy-id.1 @@ -29,6 +29,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .Nd use locally available keys to authorise logins on a remote machine .Sh SYNOPSIS .Nm +.Op Fl f .Op Fl n .Op Fl i Op Ar identity_file .Op Fl p Ar port @@ -76,6 +77,10 @@ is used. Note that this can be used to ensure that the keys copied have the comment one prefers and/or extra options applied, by ensuring that the key file has these set as preferred before the copy is attempted. +.It Fl f +Forced mode: doesn't check if the keys are present on the remote server. +This means that it does not need the private key. Of course, this can result +in more than one copy of the key being installed on the remote system. .It Fl n do a dry-run. Instead of installing keys on the remote system simply prints the key(s) that would have been installed. -- 1.9.1 From ab185eea5a03cdd846c909d83e5dd0a07a44fb54 Mon Sep 17 00:00:00 2001 From: Philip Hands Date: Wed, 25 Nov 2015 23:47:06 +0100 Subject: [PATCH] deal with #2331 by suggesting the use of the -f option --- ssh-copy-id | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id index 3121171..8666cea 100755 --- $1/contrib/ssh-copy-id +++ $1/contrib/ssh-copy-id @@ -250,7 +250,8 @@ populate_new_ids() { exit 1 fi if [ -z "$NEW_IDS" ] ; then - printf '\n%s: WARNING: All keys were skipped because they already exist on the remote system.\n\n' "$0" >&2 + printf '\n%s: WARNING: All keys were skipped because they already exist on the remote system.\n' "$0" >&2 + printf '\t\t(if you think this is a mistake, you may want to use -f option)\n\n' "$0" >&2 exit 0 fi printf '%s: INFO: %d key(s) remain to be installed -- if you are prompted now it is to install the new keys\n' "$0" "$(printf '%s\n' "$NEW_IDS" | wc -l)" >&2 -- 1.9.1 From de78897ada50ed12f4b0c9faa6e935ce82ee49a6 Mon Sep 17 00:00:00 2001 From: Philip Hands Date: Thu, 26 Nov 2015 00:25:56 +0100 Subject: [PATCH] handle keys with missing trailing newline (closes: 2350) --- ssh-copy-id | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id index 8666cea..362b49b 100755 --- $1/contrib/ssh-copy-id +++ $1/contrib/ssh-copy-id @@ -218,7 +218,7 @@ populate_new_ids() { printf '%s: INFO: attempting to log in with the new key(s), to filter out any that are already installed\n' "$0" >&2 NEW_IDS=$( eval $GET_ID | { - while read ID ; do + while read ID || [ "$ID" ] ; do printf '%s\n' "$ID" > $L_TMP_ID_FILE # the next line assumes $PRIV_ID_FILE only set if using a single id file - this -- 1.9.1 From 6b903ab99a3f0107bb0dbde748a4372033bab00c Mon Sep 17 00:00:00 2001 From: Philip Hands Date: Thu, 26 Nov 2015 00:36:09 +0100 Subject: [PATCH] add a cd to ensure we're in the remote's home directory (closes: 2349) --- ssh-copy-id | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id index 362b49b..2932936 100755 --- $1/contrib/ssh-copy-id +++ $1/contrib/ssh-copy-id @@ -284,7 +284,7 @@ case "$REMOTE_VERSION" in # Assuming that the remote host treats ~/.ssh/authorized_keys as one might expect populate_new_ids 0 [ "$DRY_RUN" ] || printf '%s\n' "$NEW_IDS" | \ - ssh "$@" "exec sh -c 'umask 077 ; mkdir -p .ssh && cat >> .ssh/authorized_keys || exit 1 ; if type restorecon >/dev/null 2>&1 ; then restorecon -F .ssh .ssh/authorized_keys ; fi'" \ + ssh "$@" "exec sh -c 'cd ; umask 077 ; mkdir -p .ssh && cat >> .ssh/authorized_keys || exit 1 ; if type restorecon >/dev/null 2>&1 ; then restorecon -F .ssh .ssh/authorized_keys ; fi'" \ || exit 1 ADDED=$(printf '%s\n' "$NEW_IDS" | wc -l) ;; -- 1.9.1 From 441892cbf4ff96fd96908582b8170f51890b5deb Mon Sep 17 00:00:00 2001 From: Philip Hands Date: Sat, 28 Nov 2015 14:42:36 +0100 Subject: [PATCH] add comment about why the ugly one-line remote command is as it is In case anyone looks here for the details: * tcsh doesn't support multi-line strings, which is why it's a one-liner. * tcsh doesn't do 2>&1, and fish doesn't do 'command || command' which is why we're runnig this under sh (which is very likely to be a POSIX shell on any vaguely Unix-like system) * The 'cd' is there to make sure we're in the home dir, because there was a bug report about having a cd in ~/.bashrc that resulted in a .ssh being created elsewhere. * the 'exec' ensures that we're not relying on anything beyond the (hopefully POSIX) shell that's available as 'sh' on the remote system --- ssh-copy-id | 1 + 1 file changed, 1 insertion(+) diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id index 2932936..04c03eb 100755 --- $1/contrib/ssh-copy-id +++ $1/contrib/ssh-copy-id @@ -283,6 +283,7 @@ case "$REMOTE_VERSION" in *) # Assuming that the remote host treats ~/.ssh/authorized_keys as one might expect populate_new_ids 0 + # in ssh below - to defend against quirky remote shells: use 'exec sh -c' to get POSIX; 'cd' to be at $HOME; and all on one line, because tcsh. [ "$DRY_RUN" ] || printf '%s\n' "$NEW_IDS" | \ ssh "$@" "exec sh -c 'cd ; umask 077 ; mkdir -p .ssh && cat >> .ssh/authorized_keys || exit 1 ; if type restorecon >/dev/null 2>&1 ; then restorecon -F .ssh .ssh/authorized_keys ; fi'" \ || exit 1 -- 1.9.1 From 8b59b122d321b97badd15c41e1a22863aa922a02 Mon Sep 17 00:00:00 2001 From: Philip Hands Date: Sat, 28 Nov 2015 14:46:47 +0100 Subject: [PATCH] with '-f' there's no need to have access to the private key --- ssh-copy-id | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id index 04c03eb..d3ff83b 100755 --- $1/contrib/ssh-copy-id +++ $1/contrib/ssh-copy-id @@ -80,7 +80,7 @@ use_id_file() { PUB_ID_FILE="$L_ID_FILE.pub" fi - PRIV_ID_FILE=$(dirname "$PUB_ID_FILE")/$(basename "$PUB_ID_FILE" .pub) + [ "$FORCED" ] || PRIV_ID_FILE=$(dirname "$PUB_ID_FILE")/$(basename "$PUB_ID_FILE" .pub) # check that the files are readable for f in $PUB_ID_FILE $PRIV_ID_FILE ; do -- 1.9.1 From 1b931894de0614099255244be789ad097fd0948a Mon Sep 17 00:00:00 2001 From: Philip Hands Date: Sat, 28 Nov 2015 14:47:35 +0100 Subject: [PATCH] if the private key is missing, point out that '-f' might be what's needed --- ssh-copy-id | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id index d3ff83b..f0b01aa 100755 --- $1/contrib/ssh-copy-id +++ $1/contrib/ssh-copy-id @@ -85,7 +85,9 @@ use_id_file() { # check that the files are readable for f in $PUB_ID_FILE $PRIV_ID_FILE ; do ErrMSG=$( { : < $f ; } 2>&1 ) || { - printf "\n%s: ERROR: failed to open ID file '%s': %s\n\n" "$0" "$f" "$(printf "%s\n" "$ErrMSG" | sed -e 's/.*: *//')" + local 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/.*: *//')" exit 1 } done -- 1.9.1 From fd3e8b115e160a1332773cd8e06a3305d0d680ab Mon Sep 17 00:00:00 2001 From: Philip Hands Date: Sat, 28 Nov 2015 21:10:39 +0100 Subject: [PATCH] +INFO message to mitigate the surprise described in #2196 --- ssh-copy-id | 1 + 1 file changed, 1 insertion(+) diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id index f0b01aa..994194e 100755 --- $1/contrib/ssh-copy-id +++ $1/contrib/ssh-copy-id @@ -91,6 +91,7 @@ use_id_file() { 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\"" } -- 1.9.1 From 783ef08b0a757402aba67313f08f8dbfa9bf85f3 Mon Sep 17 00:00:00 2001 From: Philip Hands Date: Mon, 30 Nov 2015 20:46:19 +0100 Subject: [PATCH] deal with $HOME and id filenames that include a space --- ssh-copy-id | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git $1/contrib/ssh-copy-id $1/contrib/ssh-copy-id index 994194e..afde8b1 100755 --- $1/contrib/ssh-copy-id +++ $1/contrib/ssh-copy-id @@ -56,7 +56,7 @@ then fi fi -DEFAULT_PUB_ID_FILE=$(ls -t ${HOME}/.ssh/id*.pub 2>/dev/null | grep -v -- '-cert.pub$' | head -n 1) +DEFAULT_PUB_ID_FILE="$HOME/$(cd "$HOME" ; ls -t .ssh/id*.pub 2>/dev/null | grep -v -- '-cert.pub$' | head -n 1)" usage () { printf 'Usage: %s [-h|-?|-f|-n] [-i [identity_file]] [-p port] [[-o ] ...] [user@]hostname\n' "$0" >&2 @@ -83,15 +83,15 @@ use_id_file() { [ "$FORCED" ] || PRIV_ID_FILE=$(dirname "$PUB_ID_FILE")/$(basename "$PUB_ID_FILE" .pub) # check that the files are readable - for f in $PUB_ID_FILE $PRIV_ID_FILE ; do - ErrMSG=$( { : < $f ; } 2>&1 ) || { + for f in "$PUB_ID_FILE" ${PRIV_ID_FILE:+"$PRIV_ID_FILE"} ; do + ErrMSG=$( { : < "$f" ; } 2>&1 ) || { local 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/.*: *//')" exit 1 } done - printf '%s: INFO: Source of key(s) to be installed: "%s"\n' "$0" $PUB_ID_FILE >&2 + printf '%s: INFO: Source of key(s) to be installed: "%s"\n' "$0" "$PUB_ID_FILE" >&2 GET_ID="cat \"$PUB_ID_FILE\"" } @@ -217,12 +217,13 @@ populate_new_ids() { printf '%s: ERROR: mktemp failed\n' "$0" >&2 exit 1 fi - trap "rm -f $L_TMP_ID_FILE ${L_TMP_ID_FILE}.pub" EXIT TERM INT QUIT + local L_CLEANUP="rm -f \"$L_TMP_ID_FILE\" \"${L_TMP_ID_FILE}.stderr\"" + trap "$L_CLEANUP" EXIT TERM INT QUIT printf '%s: INFO: attempting to log in with the new key(s), to filter out any that are already installed\n' "$0" >&2 NEW_IDS=$( eval $GET_ID | { while read ID || [ "$ID" ] ; do - printf '%s\n' "$ID" > $L_TMP_ID_FILE + printf '%s\n' "$ID" > "$L_TMP_ID_FILE" # the next line assumes $PRIV_ID_FILE only set if using a single id file - this # assumption will break if we implement the possibility of multiple -i options. @@ -246,7 +247,7 @@ populate_new_ids() { done } ) - rm -f $L_TMP_ID_FILE* && trap - EXIT TERM INT QUIT + eval "$L_CLEANUP" && trap - EXIT TERM INT QUIT if expr "$NEW_IDS" : "^ERROR: " >/dev/null ; then printf '\n%s: %s\n\n' "$0" "$NEW_IDS" >&2 -- 1.9.1 diff --git a/contrib/ssh-copy-id b/contrib/ssh-copy-id index afde8b1..cd52764 100644 --- a/contrib/ssh-copy-id +++ b/contrib/ssh-copy-id @@ -99,6 +99,8 @@ if [ -n "$SSH_AUTH_SOCK" ] && ssh-add -L >/dev/null 2>&1 ; then GET_ID="ssh-add -L" fi +[ "x$SSH_COPY_ID_LEGACY" != "x" ] && FORCED=1 + while test "$#" -gt 0 do [ "${SEEN_OPT_I}" ] && expr "$1" : "[-]i" >/dev/null && { diff --git a/contrib/ssh-copy-id.1 b/contrib/ssh-copy-id.1 index 8850cce..62f112d 100644 --- a/contrib/ssh-copy-id.1 +++ b/contrib/ssh-copy-id.1 @@ -185,6 +185,22 @@ should prove enlightening (N.B. the modern approach is to use the .Fl W option, rather than .Xr nc 1 ) . +.Sh ENVIRONMENT +.Bl -tag -width Ds +.Pp +.It Pa SSH_COPY_ID_LEGACY +If the +.Cm SSH_COPY_ID_LEGACY +environment variable is set, the +.Nm +is run in a legacy mode. In this mode, the +.Nm +doesn't check an existence of a private key and doesn't do remote checks +of the remote server versions or if public keys are already installed +(equivalent to +.Fl f +switch). +.El .Sh "SEE ALSO" .Xr ssh 1 , .Xr ssh-agent 1 ,