RFC: ssh-copy-id tweaks

Jim Knoble jmknoble at pobox.com
Fri Feb 1 10:28:37 EST 2008


Circa 2008-01-31 13:00 dixit Nick Dokos:

: I'd like to propose a couple of tweaks to ssh-copy-id:
: 
: o Change the default ID_FILE from identity.pub to id_dsa.pub or perhaps
:   {id_dsa,id_rsa,identity}.pub to cover all the bases, although the
:   patch below deals only with id_dsa.pub - it would need some more
:   tweaking to deal with more than one (possibly non-existent) file.

If this is to be done, i would propose the default be id_rsa.pub, since
the RSA patent has expired, but that it be changeable via the
environment.  See below.

: o If the destination authorized_keys file already contains the keys,
:   they should not be duplicated. I use ssh-copy-id in a regression harness
:   and I end up adding the same key tens or hundreds of times. I have not
:   seen any problem but it is somewhat distasteful.
: 
:   The method proposed is frankly a hack, but it is simple and I think it
:   is foolproof and portable. At least initially, it will mess up the
:   order of the keys, but given that the file is mostly write-only by
:   humans, that should not make any difference.

See below for comments.

: --- ssh-copy-id.orig	2008-01-31 12:01:03.000000000 -0500
: +++ ssh-copy-id	2008-01-31 12:05:16.000000000 -0500
: @@ -1,11 +1,11 @@
:  #!/bin/sh
:  
: -# Shell script to install your identity.pub on a remote machine
: +# Shell script to install your id_dsa.pub on a remote machine
:  # Takes the remote machine name as an argument.
:  # Obviously, the remote machine must accept password authentication,
:  # or one of the other keys in your ssh-agent, for this to work.
:  
: -ID_FILE="${HOME}/.ssh/identity.pub"
: +ID_FILE="${HOME}/.ssh/id_dsa.pub"

I'd propose changing this to:

    SSH_IDENTITY_FILE="${SSH_IDENTITY_FILE:-${HOME}/.ssh/id_rsa.pub}"

or, alternatively, if there's concern about ${...:-...} being
non-portable:

    [ x"${SSH_IDENTITY_FILE}" = x"" ] && \
    SSH_IDENTITY_FILE="${HOME}/.ssh/id_rsa.pub"

This allows the default identity to be set in the environment.

Even more interesting would be to make ssh-copy-id use the same
approach as ssh-add, i.e., copying all of the available identities
that exist (~/.ssh/id_rsa.pub, ~/.ssh/id_dsa.pub, and ~/.ssh/identity).
(Alternatively, for more paranoai, ssh-copy-id could copy only the first
found of the three).

This would be more complex, but might do the right thing for more folks.

:  
:  if [ "-i" = "$1" ]; then
:    shift
: @@ -38,7 +38,7 @@
:    exit 1
:  fi
:  
: -{ eval "$GET_ID" ; } | ssh $1 "umask 077; test -d .ssh || mkdir .ssh ; cat >> .ssh/authorized_keys" || exit 1
: +{ eval "$GET_ID" ; } | ssh $1 "umask 077; test -d .ssh || mkdir .ssh ; cat >> .ssh/authorized_keys && sort -u -o .ssh/authorized_keys .ssh/authorized_keys" || exit 1

I would propose the following pipeline instead (on multiple lines for
legibility):

    { eval "$GET_ID" ; } \
    |ssh "$1" '
        umask 077
        test -d .ssh || mkdir .ssh
        cd .ssh \
        && ( test -f authorized_keys || touch authorized_keys ) \
        && ( cat authorized_keys; cat ) |sort |uniq >authorized_keys.$$ \
        && mv -f authorized_keys.$$ authorized_keys
    ' || exit 1

This does the following:

    * ensures that the .ssh directory exists and is searchable after the
      test -d and mkdir

    * avoids writing to the authorized_keys file until it has been
      written completely and successfully

    * avoids depending on 'sort -u', which may not be available on all
      systems

    * remains (i believe) compatible with Bourne/ksh, and csh
      shells and their descendants on the remote host

Alternatively, the following would keep the authorized_keys file from
being reordered, using 'grep -F' to check whether the identity is
already present:

    { eval "$GET_ID" ; } \
    |ssh "$1" '
        umask 077
        test -d .ssh || mkdir .ssh
        cd .ssh || exit 1
        SSH_IDENTITY="`cat`"
        test x"${SSH_IDENTITY}" = x"" && exit 1
        ( test -f authorized_keys || touch authorized_keys ) \
        grep -F "${SSH_IDENTITY}" authorized_keys >/dev/null 2>&1 \
        || {
            ( cat authorized_keys; echo "${SSH_IDENTITY}" )
            >authorized_keys.$$ \
            && mv -f authorized_keys.$$ authorized_keys
        }
    ' || exit 1

Unfortunately, the use of the SSH_IDENTITY variable makes this only work
With Bourne/ksh shells and their descendants.  A shell-independent
version of this may be able to be written with nawk or gawk, but that
poses its own portability problems....

-- 
jim knoble  |  jmknoble at pobox.com  |  http://www.pobox.com/~jmknoble/
(GnuPG key ID: 6F39C2CC  >>>>>>  http://www.pobox.com/~jmknoble/keys/ )
(GnuPG fingerprint: 5024:D578:7CF4:5660:7269::F6F3:B919:9307:6F39:C2CC)
+----------------------------------------------------------------------+
|[L]iberty, as we all know, cannot flourish in a country that is perma-|
| nently on a war footing, or even a near-war footing.  --Aldous Huxley|
+----------------------------------------------------------------------+


More information about the openssh-unix-dev mailing list