mirror of
git://anongit.mindrot.org/openssh.git
synced 2025-01-03 00:02:05 +00:00
add -s flag: to install keys via SFTP
This is prompted by: https://bugzilla.mindrot.org/show_bug.cgi?id=3201 Thanks go to Matthias Blümel for the idea, and the helpful patch, from which this patch grew. SSH-Copy-ID-Upstream: f7c76dc64427cd20287a6868f672423b62057614
This commit is contained in:
parent
f92424970c
commit
a9c9e91a82
@ -1,6 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (c) 1999-2020 Philip Hands <phil@hands.com>
|
||||
# 2020 Matthias Blümel <blaimi@blaimi.de>
|
||||
# 2017 Sebastien Boyron <seb@boyron.eu>
|
||||
# 2013 Martin Kletzander <mkletzan@redhat.com>
|
||||
# 2010 Adeodato =?iso-8859-1?Q?Sim=F3?= <asp16@alu.ua.es>
|
||||
@ -61,11 +62,14 @@ fi
|
||||
|
||||
# shellcheck disable=SC2010
|
||||
DEFAULT_PUB_ID_FILE=$(ls -t "${HOME}"/.ssh/id*.pub 2>/dev/null | grep -v -- '-cert.pub$' | head -n 1)
|
||||
SSH="ssh -a -x"
|
||||
umask 0177
|
||||
|
||||
usage () {
|
||||
printf 'Usage: %s [-h|-?|-f|-n] [-i [identity_file]] [-p port] [-F alternative ssh_config file] [[-o <ssh -o options>] ...] [user@]hostname\n' "$0" >&2
|
||||
printf 'Usage: %s [-h|-?|-f|-n|-s] [-i [identity_file]] [-p port] [-F alternative ssh_config file] [[-o <ssh -o options>] ...] [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
|
||||
printf '\t-h|-?: print this help\n' >&2
|
||||
exit 1
|
||||
}
|
||||
@ -108,9 +112,8 @@ 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:fnh?" OPT
|
||||
while getopts "i:o:p:F:fnsh?" OPT
|
||||
do
|
||||
|
||||
case "$OPT" in
|
||||
i)
|
||||
[ "${SEEN_OPT_I}" ] && {
|
||||
@ -129,6 +132,9 @@ do
|
||||
n)
|
||||
DRY_RUN=1
|
||||
;;
|
||||
s)
|
||||
SFTP=sftp
|
||||
;;
|
||||
h|\?)
|
||||
usage
|
||||
;;
|
||||
@ -137,9 +143,6 @@ done
|
||||
#shift all args to keep only USER_HOST
|
||||
shift $((OPTIND-1))
|
||||
|
||||
|
||||
|
||||
|
||||
if [ $# = 0 ] ; then
|
||||
usage
|
||||
fi
|
||||
@ -170,6 +173,8 @@ fi
|
||||
# and has the side effect of setting $NEW_IDS
|
||||
populate_new_ids() {
|
||||
L_SUCCESS="$1"
|
||||
L_TMP_ID_FILE="$SCRATCH_DIR"/popids_tmp_id
|
||||
L_OUTPUT_FILE="$SCRATCH_DIR"/popids_output
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
if [ "$FORCED" ] ; then
|
||||
@ -180,15 +185,6 @@ populate_new_ids() {
|
||||
# repopulate "$@" inside this function
|
||||
eval set -- "$SSH_OPTS"
|
||||
|
||||
umask 0177
|
||||
L_TMP_ID_FILE=$(mktemp ~/.ssh/ssh-copy-id_id.XXXXXXXXXX)
|
||||
if test $? -ne 0 || test "x$L_TMP_ID_FILE" = "x" ; then
|
||||
printf '%s: ERROR: mktemp failed\n' "$0" >&2
|
||||
exit 1
|
||||
fi
|
||||
L_CLEANUP="rm -f \"$L_TMP_ID_FILE\" \"${L_TMP_ID_FILE}.stderr\""
|
||||
# shellcheck disable=SC2064
|
||||
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
|
||||
# shellcheck disable=SC2086
|
||||
NEW_IDS=$(
|
||||
@ -200,16 +196,19 @@ populate_new_ids() {
|
||||
# assumption will break if we implement the possibility of multiple -i options.
|
||||
# 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}" \
|
||||
$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" </dev/null
|
||||
-o IdentitiesOnly=yes "$@" exit >"$L_OUTPUT_FILE" 2>&1 </dev/null
|
||||
if [ "$?" = "$L_SUCCESS" ] ; then
|
||||
: > "$L_TMP_ID_FILE"
|
||||
elif [ "$SFTP" ] && grep 'allows sftp connections only' "$L_OUTPUT_FILE" >/dev/null ; then
|
||||
# this error counts as a success when we're setting up an sftp connection
|
||||
: > "$L_TMP_ID_FILE"
|
||||
else
|
||||
grep 'Permission denied' "$L_TMP_ID_FILE.stderr" >/dev/null || {
|
||||
sed -e 's/^/ERROR: /' <"$L_TMP_ID_FILE.stderr" >"$L_TMP_ID_FILE"
|
||||
grep 'Permission denied' "$L_OUTPUT_FILE" >/dev/null || {
|
||||
sed -e 's/^/ERROR: /' <"$L_OUTPUT_FILE" >"$L_TMP_ID_FILE"
|
||||
cat >/dev/null #consume the other keys, causing loop to end
|
||||
}
|
||||
fi
|
||||
@ -218,7 +217,6 @@ populate_new_ids() {
|
||||
done
|
||||
}
|
||||
)
|
||||
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
|
||||
@ -264,7 +262,49 @@ EOF
|
||||
printf "exec sh -c '%s'" "${INSTALLKEYS_SH}"
|
||||
}
|
||||
|
||||
REMOTE_VERSION=$(ssh -v -o PreferredAuthentications=',' -o ControlPath=none "$@" 2>&1 |
|
||||
installkeys_via_sftp() {
|
||||
|
||||
# repopulate "$@" inside this function
|
||||
eval set -- "$SSH_OPTS"
|
||||
|
||||
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"
|
||||
trap "$L_CLEANUP" EXIT TERM INT QUIT
|
||||
sftp -b - -o "ControlPath=$L_SHARED_CON" "ignored" <<-EOF || return 1
|
||||
-get .ssh/authorized_keys $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
|
||||
-mkdir .ssh
|
||||
chmod 700 .ssh
|
||||
put $L_KEYS .ssh/authorized_keys
|
||||
chmod 600 .ssh/authorized_keys
|
||||
EOF
|
||||
eval "$L_CLEANUP" && trap "$SCRATCH_CLEANUP" EXIT TERM INT QUIT
|
||||
}
|
||||
|
||||
|
||||
# create a scratch dir for any temporary files needed
|
||||
SCRATCH_DIR=$(mktemp -d ~/.ssh/ssh-copy-id.XXXXXXXXXX)
|
||||
if test $? -ne 0 || test "x$SCRATCH_DIR" = "x" ; then
|
||||
printf '%s: ERROR: mktemp failed\n' "$0" >&2
|
||||
exit 1
|
||||
fi
|
||||
chmod 0700 $SCRATCH_DIR
|
||||
if [ -d "$SCRATCH_DIR" ] ; then
|
||||
SCRATCH_CLEANUP="rm -rf \"$SCRATCH_DIR\""
|
||||
trap "$SCRATCH_CLEANUP" EXIT TERM INT QUIT
|
||||
else
|
||||
printf '%s: ERROR: Required scratch directory (%s) was not created\n' "$0" "$SCRATCH_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
REMOTE_VERSION=$($SSH -v -o PreferredAuthentications=',' -o ControlPath=none "$@" 2>&1 |
|
||||
sed -ne 's/.*remote software version //p')
|
||||
|
||||
# shellcheck disable=SC2029
|
||||
@ -277,7 +317,7 @@ case "$REMOTE_VERSION" in
|
||||
printf '%s: WARNING: Non-dsa key (#%d) skipped (NetScreen only supports DSA keys)\n' "$0" "$KEY_NO" >&2
|
||||
continue
|
||||
}
|
||||
[ "$DRY_RUN" ] || printf 'set ssh pka-dsa key %s\nsave\nexit\n' "$KEY" | ssh -T "$@" >/dev/null 2>&1
|
||||
[ "$DRY_RUN" ] || printf 'set ssh pka-dsa key %s\nsave\nexit\n' "$KEY" | $SSH -T "$@" >/dev/null 2>&1
|
||||
if [ $? = 255 ] ; then
|
||||
printf '%s: ERROR: installation of key #%d failed (please report a bug describing what caused this, so that we can make this message useful)\n' "$0" "$KEY_NO" >&2
|
||||
else
|
||||
@ -291,16 +331,21 @@ case "$REMOTE_VERSION" in
|
||||
dropbear*)
|
||||
populate_new_ids 0
|
||||
[ "$DRY_RUN" ] || printf '%s\n' "$NEW_IDS" | \
|
||||
ssh "$@" "$(installkeys_sh /etc/dropbear/authorized_keys)" \
|
||||
$SSH "$@" "$(installkeys_sh /etc/dropbear/authorized_keys)" \
|
||||
|| exit 1
|
||||
ADDED=$(printf '%s\n' "$NEW_IDS" | wc -l)
|
||||
;;
|
||||
*)
|
||||
# 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 "$@" "$(installkeys_sh)" \
|
||||
|| exit 1
|
||||
if ! [ "$DRY_RUN" ] ; then
|
||||
printf '%s\n' "$NEW_IDS" | \
|
||||
if [ "$SFTP" ] ; then
|
||||
installkeys_via_sftp
|
||||
else
|
||||
$SSH "$@" "$(installkeys_sh)"
|
||||
fi || exit 1
|
||||
fi
|
||||
ADDED=$(printf '%s\n' "$NEW_IDS" | wc -l)
|
||||
;;
|
||||
esac
|
||||
@ -318,7 +363,7 @@ else
|
||||
|
||||
Number of key(s) added: $ADDED
|
||||
|
||||
Now try logging into the machine, with: "ssh $SSH_OPTS"
|
||||
Now try logging into the machine, with: "${SFTP:-ssh} $SSH_OPTS"
|
||||
and check to make sure that only the key(s) you wanted were added.
|
||||
|
||||
EOF
|
||||
|
@ -1,5 +1,5 @@
|
||||
.ig \" -*- nroff -*-
|
||||
Copyright (c) 1999-2016 hands.com Ltd. <http://hands.com/>
|
||||
Copyright (c) 1999-2020 hands.com Ltd. <http://hands.com/>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
@ -31,6 +31,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
.Nm
|
||||
.Op Fl f
|
||||
.Op Fl n
|
||||
.Op Fl s
|
||||
.Op Fl i Op Ar identity_file
|
||||
.Op Fl p Ar port
|
||||
.Op Fl o Ar ssh_option
|
||||
@ -84,6 +85,12 @@ 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.
|
||||
.It Fl s
|
||||
SFTP mode: usually the public keys are installed by executing commands on the remote side.
|
||||
With this option the user's
|
||||
.Pa ~/.ssh/authorized_keys
|
||||
file will be downloaded, modified locally and uploaded with sftp.
|
||||
This option is useful if the server has restrictions on commands which can be used on the remote side.
|
||||
.It Fl h , Fl ?
|
||||
Print Usage summary
|
||||
.It Fl p Ar port , Fl o Ar ssh_option
|
||||
|
Loading…
Reference in New Issue
Block a user