View | Details | Raw Unified | Return to bug 1980
Collapse All | Expand All

(-)contrib/ssh-copy-id (-34 / +174 lines)
Lines 1-54 Link Here
1
#!/bin/sh
1
#!/bin/sh
2
2
3
# Shell script to install your public key on a remote machine
3
# Copyright (c) 1999-2010 Philip Hands <phil@hands.com>
4
# Takes the remote machine name as an argument.
4
#               2010 Adeodato =?iso-8859-1?Q?Sim=F3?= <asp16@alu.ua.es>
5
# Obviously, the remote machine must accept password authentication,
5
#               2010 Eric Moret <eric.moret@gmail.com>
6
# or one of the other keys in your ssh-agent, for this to work.
6
#               2009 Xr <xr@i-jeuxvideo.com>
7
#               2007 Justin Pryzby <justinpryzby@users.sourceforge.net>
8
#               2004 Reini Urban <rurban@x-ray.at>
9
#               2003 Colin Watson <cjwatson@debian.org>
10
# Modification and redistribution is permitted provided that due credit
11
# is given to the authors by leaving this copyright notice intact.
7
12
8
ID_FILE="${HOME}/.ssh/id_rsa.pub"
13
# Shell script to install your public key(s) on a remote machine
14
# See the ssh-copy-id(1) man page for details
9
15
10
if [ "-i" = "$1" ]; then
16
DEFAULT_PUB_ID_FILE=$(ls -t ${HOME}/.ssh/id*.pub | head -n 1)
11
  shift
17
12
  # check if we have 2 parameters left, if so the first is the new ID file
18
usage () {
13
  if [ -n "$2" ]; then
19
  echo "Usage: $0 [-h|-?|-n] [-i [identity_file]] [-p port] [user@]hostname" >&2
14
    if expr "$1" : ".*\.pub" > /dev/null ; then
20
  exit 1
15
      ID_FILE="$1"
21
}
16
    else
22
17
      ID_FILE="$1.pub"
23
use_id_file() {
18
    fi
24
  local L_ID_FILE=$1
19
    shift         # and this should leave $1 as the target name
25
20
  fi
26
  if expr "$L_ID_FILE" : ".*\.pub$" >/dev/null ; then
21
else
27
    PUB_ID_FILE="$L_ID_FILE"
22
  if [ x$SSH_AUTH_SOCK != x ] && ssh-add -L >/dev/null 2>&1; then
28
  else
23
    GET_ID="$GET_ID ssh-add -L"
29
    PUB_ID_FILE="$L_ID_FILE.pub"
24
  fi
30
  fi
31
32
  PRIV_ID_FILE=$(dirname "$PUB_ID_FILE")/$(basename "$PUB_ID_FILE" .pub)
33
34
  # check that the files are readable
35
  for f in $PUB_ID_FILE $PRIV_ID_FILE ; do
36
    ErrMSG=$( { : < $f ; } 2>&1 ) || {
37
      printf "\n%s: ERROR: failed to open ID file '%s': %s\n\n" "$0" "$f" "$(echo $ErrMSG | sed -e 's/.*: *//')"
38
      exit 1
39
    }
40
  done
41
  GET_ID="cat \"$PUB_ID_FILE\""
42
}
43
44
if [ -n "$SSH_AUTH_SOCK" ] && ssh-add -L >/dev/null 2>&1 ; then
45
  GET_ID="ssh-add -L"
25
fi
46
fi
26
47
27
if [ -z "`eval $GET_ID`" ] && [ -r "${ID_FILE}" ] ; then
48
GETOPT_PARSED=$(getopt --options 'i::p:nh?' --name "$0" --quiet -- "$@")
28
  GET_ID="cat \"${ID_FILE}\""
49
50
eval set -- "$GETOPT_PARSED"
51
while true ; do
52
  case "$1" in
53
    -i)
54
      case "$2" in
55
        '')
56
          CHECK_EXTRA_PARAM=1
57
          use_id_file $DEFAULT_PUB_ID_FILE
58
          ;;
59
        *)
60
          use_id_file $2
61
          ;;
62
      esac
63
      shift 2
64
      ;;
65
    -p)
66
      if 2>/dev/null [ "$2" -gt 0 ] && [ "$2" -lt 65536 ] ; then
67
        PORTOPTION="-p $2 "
68
      else
69
        echo "Bad port '$2'" >&2
70
        exit 1
71
      fi
72
      shift 2
73
      ;;
74
    -n)
75
      DRY_RUN=1
76
      shift
77
      ;;
78
    -h|-\?)
79
      usage
80
      shift
81
      ;;
82
    --)
83
      shift
84
      break
85
      ;;
86
  esac
87
done
88
89
if [ -n "$CHECK_EXTRA_PARAM" ] && [ $# = 2 ] ; then
90
  use_id_file $1
91
  shift
29
fi
92
fi
30
93
31
if [ -z "`eval $GET_ID`" ]; then
94
if [ $# != 1 ] ; then
32
  echo "$0: ERROR: No identities found" >&2
95
  usage
33
  exit 1
96
fi
97
98
# drop a trailing colon
99
USER_HOST=${1%:}
100
101
if [ -z "$(eval $GET_ID)" ] && [ -r "$PUB_ID_FILE" ] ; then
102
  use_id_file $PUB_ID_FILE
34
fi
103
fi
35
104
36
if [ "$#" -lt 1 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
105
if [ -z "$(eval $GET_ID)" ] ; then
37
  echo "Usage: $0 [-i [identity_file]] [user@]machine" >&2
106
  echo "$0: ERROR: No identities found" >&2
38
  exit 1
107
  exit 1
39
fi
108
fi
40
109
41
# strip any trailing colon
110
# populate_new_ids() uses several global variables ($USER_HOST, $PORTOPTION ...)
42
host=`echo $1 | sed 's/:$//'`
111
# and has the side effect of setting $NEW_IDS
112
populate_new_ids() {
113
  local L_SUCCESS="$1"
114
115
  local L_TMP_ID_FILE=$(mktemp ~/.ssh/ssh-copy-id_id.XXXXXXXXXX)
116
  trap "rm -f $L_TMP_ID_FILE*" EXIT TERM INT QUIT
117
  NEW_IDS=$(
118
    eval $GET_ID | {
119
      while read ID ; do
120
        printf "$ID\n" > $L_TMP_ID_FILE
121
122
        # the next line assumes $PRIV_ID_FILE only set if using a single id file - this
123
        # assumption will break if we implement the possibility of multiple -i options.
124
        # The point being that if file based, ssh needs the private key, which it cannot
125
        # find if only given the contents of the .pub file in an unreleated tmpfile
126
        ssh -i "${PRIV_ID_FILE:-$L_TMP_ID_FILE}" \
127
            -o PreferredAuthentications=publickey \
128
            -o IdentitiesOnly=yes $PORTOPTION $USER_HOST exit 2>$L_TMP_ID_FILE.stderr </dev/null
129
        if [ "$?" = "$L_SUCCESS" ] ; then
130
          : > $L_TMP_ID_FILE 
131
        else
132
          if ! grep -q 'Permission denied' $L_TMP_ID_FILE.stderr ; then
133
            sed -e 's/^/ERROR: /' <$L_TMP_ID_FILE.stderr >$L_TMP_ID_FILE
134
            cat >/dev/null #consume the other keys, causing loop to end
135
          fi
136
        fi
137
138
        cat $L_TMP_ID_FILE
139
      done
140
    }
141
  )
142
  rm -f $L_TMP_ID_FILE* && trap - EXIT TERM INT QUIT
143
144
  if expr "$NEW_IDS" : "^ERROR: " >/dev/null ; then
145
    printf "\n$0: $NEW_IDS\n\n" >&2
146
    exit 1
147
  fi
148
  if [ -z "$NEW_IDS" ] ; then
149
    printf "\n$0: WARNING: All keys were skipped because they already exist on the remote system.\n\n" >&2
150
    exit 0
151
  fi
152
}
153
154
REMOTE_VERSION=$(ssh -v -o PreferredAuthentications=',' $PORTOPTION $USER_HOST 2>&1 |
155
                 sed -ne 's/.*remote software version //p')
43
156
44
{ eval "$GET_ID" ; } | ssh $host "umask 077; test -d ~/.ssh || mkdir ~/.ssh ; cat >> ~/.ssh/authorized_keys" || exit 1
157
case "$REMOTE_VERSION" in
158
  NetScreen*)
159
    populate_new_ids 1
160
    for KEY in $(echo "$NEW_IDS"| cut -d' ' -f2) ; do
161
      [ "$DRY_RUN" ] || printf 'set ssh pka-dsa key %s\nsave\nexit\n' "$KEY" | ssh -T $PORTOPTION $USER_HOST >/dev/null 2>&1
162
      if [ $? = 255 ] ; then
163
        echo "$0: WARNING: NetScreen only supports dsa keys" >&2
164
      else
165
        ADDED=$(($ADDED + 1))
166
      fi
167
    done
168
    if [ -z "$ADDED" ] ; then
169
      exit 1
170
    fi
171
    ;;
172
  *)
173
    # Assuming default being OpenSSH
174
    populate_new_ids 0
175
    [ "$DRY_RUN" ] || printf "\n$NEW_IDS\n" | ssh $PORTOPTION $USER_HOST "umask 077 ; mkdir -p .ssh ; cat >> .ssh/authorized_keys" || exit 1
176
    ADDED=$(printf "$NEW_IDS\n" | wc -l)
177
    ;;
178
esac
179
180
if [ "$DRY_RUN" ] ; then
181
  echo =-=-=-=-=-=-=-=
182
  echo "Would have added the following key(s):"
183
  printf "\n$NEW_IDS\n"
184
  echo =-=-=-=-=-=-=-=
185
fi
45
186
46
cat <<EOF
187
cat <<-EOF
47
Now try logging into the machine, with "ssh '$host'", and check in:
48
188
49
  ~/.ssh/authorized_keys
189
	Number of key(s) added: $ADDED
50
190
51
to make sure we haven't added extra keys that you weren't expecting.
191
	Now try logging into the machine, with "ssh $PORTOPTION'$USER_HOST'", and check
192
	to make sure we haven't added extra keys that you weren't expecting.
52
193
53
EOF
194
EOF
54
(-)contrib/ssh-copy-id.1 (-57 / +147 lines)
Lines 1-5 Link Here
1
.ig \"  -*- nroff -*-
1
.ig \"  -*- nroff -*-
2
Copyright (c) 1999 Philip Hands Computing <http://www.hands.com/>
2
Copyright (c) 1999-2010 hands.com Ltd. <http://hands.com/>
3
3
4
Permission is granted to make and distribute verbatim copies of
4
Permission is granted to make and distribute verbatim copies of
5
this manual provided the copyright notice and this permission notice
5
this manual provided the copyright notice and this permission notice
Lines 16-75 versions, except that this permission no Link Here
16
translations approved by the Free Software Foundation instead of in
16
translations approved by the Free Software Foundation instead of in
17
the original English.
17
the original English.
18
..
18
..
19
.TH SSH-COPY-ID 1 "14 November 1999" "OpenSSH"
19
.Dd $Mdocdate: June 17 2010 $
20
.SH NAME
20
.Dt SSH-COPY-ID 1
21
ssh-copy-id \- install your public key in a remote machine's authorized_keys
21
.Os
22
.SH SYNOPSIS
22
.Sh NAME
23
.B ssh-copy-id [-i [identity_file]]
23
.Nm ssh-copy-id
24
.I "[user@]machine"
24
.Nd use locally available keys to authorise logins on a remote machine
25
.Sh SYNOPSIS
26
.Nm ssh-copy-id
27
.Op Fl n
28
.Op Fl i Op Ar identity_file
29
.Op Fl p Ar port
30
.Op Ar user Ns @ Ns
31
.Ar hostname
32
.Nm ssh-copy-id
33
.Fl h | Fl ?
25
.br
34
.br
26
.SH DESCRIPTION
35
.Sh DESCRIPTION
27
.BR ssh-copy-id
36
.Nm
28
is a script that uses ssh to log into a remote machine and
37
is a script that uses
29
append the indicated identity file to that machine's
38
.Xr ssh 1
30
.B ~/.ssh/authorized_keys
39
to log into a remote machine (presumably using a login password,
31
file.
40
so password authentication should be enabled, unless you've done some
32
.PP
41
clever use of multiple identities).  It assembles a list of one or more
33
If the
42
fingerprints (as described below) and tries to log in with each key, to
34
.B -i
43
see if any of them are already installed (of course, if you are not using
35
option is given then the identity file (defaults to
44
.Xr ssh-agent 1
36
.BR ~/.ssh/id_rsa.pub )
45
this may result in you being repeatedly prompted for pass-phrases).
37
is used, regardless of whether there are any keys in your
46
It then assembles a list of those that failed to log in, and using ssh,
38
.BR ssh-agent .
47
enables logins with those keys on the remote server.  By default it adds
39
Otherwise, if this:
48
the keys by appending them to the remote user's
40
.PP
49
.Pa ~/.ssh/authorized_keys
41
.B "      ssh-add -L"
50
(creating the file, and directory, if necessary).  It is also capable
42
.PP
51
of detecting if the remote system is a NetScreen, and using its
43
provides any output, it uses that in preference to the identity file.
52
.Ql set ssh pka-dsa key ...
44
.PP
53
command instead.
45
If the
54
.Pp
46
.B -i
55
The options are as follows:
47
option is used, or the
56
.Bl -tag -width Ds
48
.B ssh-add
57
.It Fl i Ar identity_file
49
produced no output, then it uses the contents of the identity
58
Use only the key(s) contained in
50
file.  Once it has one or more fingerprints (by whatever means) it
59
.Ar identity_file
51
uses ssh to append them to
60
(rather than looking for identities via
52
.B ~/.ssh/authorized_keys
61
.Xr ssh-add 1
53
on the remote machine (creating the file, and directory, if necessary.)
62
or in the default_ID_file).
54
63
If the filename does not end in
55
.SH NOTES
64
.Pa .pub
56
This program does not modify the permissions of any
65
this is added.  If the filename is omitted, the default_ID_file is used.
57
pre-existing files or directories. Therefore, if the remote
66
.Pp
58
.B sshd
67
Note that this can be used to ensure that the keys copied have the
59
has
68
comment one prefers and/or extra options applied, by ensuring that the
60
.B StrictModes
69
key file has these set as preferred before the copy is attempted.
61
set in its
70
.Nm .
62
configuration, then the user's home,
71
.It Fl p Ar port
63
.B ~/.ssh
72
Port to connect to on the remote host.
64
folder, and
73
This can be specified on a
65
.B ~/.ssh/authorized_keys
74
per-host basis in
66
file may need to have group writability disabled manually, e.g. via
75
.Xr ssh 1 Ns 's
67
76
configuration file.
68
.B "      chmod go-w ~ ~/.ssh ~/.ssh/authorized_keys"
77
.It Fl n
69
78
do a dry-run.  Instead of installing keys on the remote system simply
70
on the remote machine.
79
prints the key(s) that would have been installed.
71
80
.It Fl h , Fl ?
72
.SH "SEE ALSO"
81
Print Usage summary
73
.BR ssh (1),
82
.El
74
.BR ssh-agent (1),
83
.Pp
75
.BR sshd (8)
84
Default behaviour without
85
.Fl i ,
86
is to check if
87
.Ql ssh-add -L
88
provides any output, and if so uses that.  Note that this results in
89
the comment on the key being the filename that was given to
90
.Nm ssh-add
91
when the key was loaded into your
92
.Nm ssh-agent
93
rather than the comment contained in that file, which is a bit of a shame.
94
Otherwise, if
95
.Xr ssh-add 1
96
provides no keys it uses the contents of the default_ID_file.
97
.Pp
98
The
99
.Em default_ID_file
100
is the most recent file that matches:
101
.Pa ~/.ssh/id*.pub ,
102
so if you want to create a key that is not the one you want ssh-copy-id
103
to use, either call it something that does not start with
104
.Ql id
105
or after creating the new key, use
106
.Xr touch 1
107
on your preferred key to reinstate it as the most recent.
108
.Pp
109
.Sh EXAMPLES
110
If you have already installed keys from one system on a lot of remote
111
hosts, and you then create a new key, on a new client machine, say,
112
it can be difficult to keep track of which systems on which you've
113
installed the new key.  One way of dealing with this is to load both
114
the new key and old key(s) into your
115
.Xr ssh-agent 1 .
116
Load the new key first, without the
117
.Fl c
118
option, then load one or more old keys into the agent, possibly by
119
ssh-ing to the client machine that has that old key, using the
120
.Fl A
121
option to allow agent forwarding:
122
.Pp
123
.D1 user@newclient$ ssh-add
124
.D1 user@newclient$ ssh -A old.client
125
.D1 user@oldl$ ssh-add -c
126
.D1 No   ... prompt for pass-phrase ...
127
.D1 user@old$ logoff
128
.D1 user@newclient$ ssh someserver
129
.Pp
130
now, if the new key is installed on the server, you'll be allowed in
131
unprompted, whereas if you only have the old key(s) enabled, you'll be
132
asked for confirmation, which is your cue to log back out and run
133
.Pp
134
.D1 user@newclient$ ssh-copy-id -i someserver
135
.Pp
136
The reason you might want to specify the -i option in this case is to
137
ensure that the comment on the installed key is the one from the
138
.Pa .pub
139
file, rather than just the filename that was loaded into you agent.
140
It also ensures that only the id you intended is installed, rather than
141
all the keys that you have in your
142
.Xr ssh-agent 1 .
143
Of course, you can specify another id, or use the contents of the
144
.Xr ssh-agent 1
145
as you prefer.
146
.Pp
147
Having mentioned
148
.Xr ssh-add 1 Ns 's
149
.Fl c
150
option, you might consider using this whenever using agent forwarding
151
to avoid your key being hijacked, but it is much better to instead use
152
.Xr ssh 1 Ns 's
153
.Ar ProxyCommand
154
option with
155
.Xr netcat 1
156
to bounce through remote servers while always doing direct end-to-end
157
authentication. This way the middle hop(s) don't get access to your
158
.Xr ssh-agent 1 .
159
A web search for
160
.Ql ssh proxycommand nc
161
should prove enlightening.
162
.Sh "SEE ALSO"
163
.Xr ssh 1 ,
164
.Xr ssh-agent 1 ,
165
.Xr sshd 8

Return to bug 1980