72

Is it possible to find out which ssh key was used to access an account? I have an account on a server that I let several (trusted!) people have access to via ssh. I'd find it useful to be able to know who logged in and when. I have root access so I can look at the logs, but there doesn't seem to be anything there. Is there some configuration switch that will put some way of identifying the key in the logs?

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
Andrew Stacey
  • 2,355
  • 3
  • 19
  • 21
  • It would be amazingly useful to be able to find out which key was used to authorize the current session - in my case, for access control on a Mercurial repository accessed through a shared login. All the existing techniques involve threading the identity through a command option, which is a bit clunky. – Tom Anderson Jun 28 '11 at 16:28
  • 5
    There's an OpenSSH feature request about this: [Please add pubkey fingerprint to authentication log message](https://bugzilla.mindrot.org/show_bug.cgi?id=2082) – Steffen Apr 29 '13 at 07:59
  • Centos mechanism: https://unix.stackexchange.com/questions/147295/how-can-i-determine-which-ssh-keyfile-was-used-to-authenticate-a-login – jhfrontz Nov 27 '18 at 18:07
  • In case someone wants to detect the _currently used ssh key_ from _within the current ssh session_ you may have a look at [my answer to a similar question on serverfault](https://serverfault.com/a/1092836/953792). I post this as a comment here because it’s not the same question, just highly related. – Daniel Faber Feb 06 '22 at 11:47

7 Answers7

43

If you go into the sshd config file (usually /etc/ssh/sshd_config) and change the LogLevel directive to VERBOSE:

LogLevel VERBOSE

...you can see something like this in the logs:

Jun 24 22:43:42 localhost sshd[29779]: Found matching RSA key: d8:d5:f3:5a:7e:27:42:91:e6:a5:e6:9e:f9:fd:d3:ce
Jun 24 22:43:42 localhost sshd[29779]: Accepted publickey for caleb from 127.0.0.1 port 59630 ssh2

From man sshd_config:

   LogLevel
          Gives  the  verbosity  level that is used when logging messages from
          sshd(8).  The possible values are: QUIET, FATAL, ERROR,  INFO,  VER-
          BOSE,  DEBUG,  DEBUG1,  DEBUG2,  and  DEBUG3.   The default is INFO.
          DEBUG and DEBUG1 are equivalent.  DEBUG2  and  DEBUG3  each  specify
          higher  levels of debugging output.  Logging with a DEBUG level vio-
          lates the privacy of users and is not recommended.
Caleb
  • 69,278
  • 18
  • 196
  • 226
  • That looks promising. The fingerprint then tells me which key is used. Great, thanks. – Andrew Stacey Jun 24 '11 at 20:12
  • For printing fingerprint of current session: `sed -ne "/sshd.$PPID.:.*matching DSA key/{s/^.* //g;p;q}" /var/log/auth.log` – F. Hauri - Give Up GitHub Dec 31 '12 at 13:00
  • 3
    @F.Hauri, Unless I'm missing something, wouldn't that return the wrong thing if a PID is reused for a second SSH session? It looks like it will always return the earliest fingerprint for the given PID in auth.log rather than the latest. – godlygeek Jul 29 '14 at 21:48
  • @godlygeek Oh yes! I have better to whipe `q` directive, store line until end of file... sed line become: `sed -ne "/sshd.$PPID.:.*matching DSA key/{s/^.* //g;h};\${x;p}" /var/log/auth.log` . Definitively: I Love sed! – F. Hauri - Give Up GitHub Jul 29 '14 at 22:09
  • @F.Hauri Yep, that version looks better to me. – godlygeek Jul 30 '14 at 13:53
  • @godlygeek I really thank you: your comment was upvoted and my answer edited. This was a real bug I never seen because I don't make a lot of connections and have not a lot of users connecting through ssh... I use this in prod for storing separate bash history for each sessions (have a look on comments on my answer;). – F. Hauri - Give Up GitHub Jul 30 '14 at 16:45
  • 2
    This will only list the fingerprint. If you want to get fingerprint, you can run `ssh-keygen -E md5 -lf /root/.ssh/authorized_keys`. – Zhang Buzz Apr 01 '18 at 02:52
23

Somewhat similar to @user37161's answer. If the shared account is running a custom shell and the shell needs to know what user is there, then running the "wrapper" script might not be sufficient, since information there isn't passed into the custom shell except through methods that could cause race conditions.

Instead you can use the environment= option in authorized_keys file to set an environment variable, which the custom shell can then read.

Inside your .ssh/authorized_keys file, prepend each line with an environment variable set, like the following:

environment="REMOTEUSER=jrhacker" ssh-rsa ....
environment="REMOTEUSER=jbloggs" ssh-rsa ....

Then the custom shell, or any of the various rc scripts, can read the $REMOTEUSER variable and take the appropriate action.

However, note that if you're using a standard shell, then the logged-in user is capable of modifying the file to thwart various things. Also, there is some risks in allowing users to set environment variables such as LDPRELOAD. See the sshd_config documentation about PermitUserEnvironment.

Chris Cogdon
  • 330
  • 2
  • 5
20

Some scripts for proper installation

There is a full useable method to track/log ssh connections by key with expention to username.

Introduction

In addition to @Caleb's answer, I would like to share some little tricks there:

Note: I'm working on Debian 6.0.

Server installation

SSHD Log level

First ensuring that server config has sufficient logging level:

as root, this will set and active verbose logging:

sed '/^[^#]*LogLevel.*\(QUIET\|FATAL\|ERROR\|INFO\)/{s/^/# /;h;s/$/\nLogLevel VERBOSE/};${p;g;/./!{iLogLevel VERBOSE'$'\n;};D}'  -i /etc/ssh/sshd_config

Could be written:

sed '
     /^[^#]*LogLevel.*\(QUIET\|FATAL\|ERROR\|INFO\)/{
        s/^/# /;
        h;
        s/$/\nLogLevel VERBOSE/
    };
    ${
        p;
        g;
        /./!{
            iLogLevel VERBOSE
        };
        D
    }'  -i /etc/ssh/sshd_config

or in a sed script:

#!/bin/sed -f
/^[^#]*LogLevel.*\(QUIET\|FATAL\|ERROR\|INFO\)/{
    s/^/# /;
    h;
    s/$/\nLogLevel VERBOSE/
};
${
    p;
    g;
    /./!{
        iLogLevel VERBOSE
    };
    D
}

Which could be run as:

patchSshdConfigLogLevel.sed -i /etc/ssh/sshd_config

Then for activating this:

service ssh restart

Syslog: making fingerprints user readable

Now take fingerprints in user readable file:

echo ':msg, regex, "Found matching .* key:" -/var/log/sshdusers.log' \
    > /etc/rsyslog.d/ssh_key_user.conf 
echo ':msg, regex, "Accepted publickey for" -/var/log/sshdusers.log' \
    >> /etc/rsyslog.d/ssh_key_user.conf 

service rsyslog restart

Try to (re-)login from ssh to ensure new file sshdusers.log is created (and contain something), then

chmod 644 /var/log/sshdusers.log

Last step: making them rotate.

Add in /etc/logrotate.d/:

cat >/etc/logrotate.d/sshdusers <<eosshdusers
/var/log/sshdusers.log {
  rotate 3
  daily
  compress
  missingok
  postrotate
        touch /var/log/sshdusers.log
        chmod 644 /var/log/sshdusers.log
        /usr/lib/rsyslog/rsyslog-rotate
  endscript
  notifempty
}
eosshdusers

Usage

This will print current sessions's fingerprint:

sed -ne "/sshd.$PPID.:.*matching .SA key/{s/^.* //g;h};\${x;p}" /var/log/sshdusers.log
sed -ne "/sshd.\($(($(ps ho ppid $PPID)))\|$PPID\).:.*\(Accepted publickey\|matching .SA key\)/{s/^.* //g;h};\${x;p}" /var/log/sshdusers.log

Plug-in for .bashrc

And finally, there is a little add-on to put at the end of your /etc/bash.bashrc or user's .bashrc :

ssh_oPwd=$OLDPWD
ssh_oUmask=$(umask)
umask 077
ssh_tempdir=$(mktemp -d /tmp/ssh-id-XXXXXXX)
cd $ssh_tempdir || exit 1

ssh_crtFp=$(
    sed -ne "/sshd.\($(($(ps ho ppid $PPID)))\|$PPID\).:.*\(Accepted publickey\|matching .SA key\)/{s/^.* //g;h};\${x;p}" /var/log/sshdusers.log
)
for ((ssh_i=1;ssh_i<=$(wc -l <$HOME/.ssh/authorized_keys);ssh_i++));do
    export ssh_line="$(sed -ne ${ssh_i}p <$HOME/.ssh/authorized_keys)"
    echo "$ssh_line" >tempKey
    export ssh_lFp=($(ssh-keygen -l -f tempKey))
    if [ "${ssh_lFp[1]}" == "$ssh_crtFp" ] ;then
        export SSH_KEY_USER=${ssh_line##* }
        break
      fi
  done

cd $OLDPWD
OLDPWD=$ssh_oPwd
rm -fR $ssh_tempdir
umask $ssh_oUmask
unset ssh_lFp ssh_line ssh_i ssh_crtFp ssh_tempdir ssh_oUmask ssh_oPwd

so after re-login from SSH, you will see:

set | grep ^SSH
SSH_CLIENT='192.168.1.31 43734 22'
SSH_CONNECTION='192.168.1.31 43734 192.168.1.2 22'
SSH_KEY_USER=user@mydesk
SSH_TTY=/dev/pts/2

Note On some installation, the authorized key file maybe something differently named, like $HOME/.ssh/authorized_keys2...

Jeff Schaller
  • 66,199
  • 35
  • 114
  • 250
  • When this was published I was under ***GNU/Linux Debian 6***, but this work quite same under ***Debian 7***... – F. Hauri - Give Up GitHub Jan 29 '14 at 16:55
  • This is linked to [Record bash_history to private database for all users](http://superuser.com/a/520638/178656) – F. Hauri - Give Up GitHub May 09 '14 at 12:03
  • Upgraded due to change in log format – F. Hauri - Give Up GitHub Oct 31 '16 at 16:18
  • Nice. Your patchSshdConfigLogLevel.sed shouldn't have the ".sed" on the end, since it would expose an implementation detail unnecessarily. The #! line is entirely sufficient. – Alex North-Keys Nov 02 '17 at 17:05
  • 1
    @AlexNorth-Keys *extensions* under *UN\*X* are generaly technicaly unseless, as we prefer use *mime* and `file` for knowing file types. But as for human who browse filesystems, having extensions like `.pl`, `.py`, `.sh`, `.awk`, `.sed`, `.tar.gz`, or even `.png.b64.gz` is usefull! – F. Hauri - Give Up GitHub Nov 03 '17 at 08:33
  • While the idea that having language extension on commands is handy in the way you describe, it is a fairly recent, misplaced trend in Unix, and using them exposes an implementation detail in the API (the command line) that later can become a lie (due to reimplementing the command in a different language or a compiled one) that then *must* be kept due to other command relying on it. Simply put **commands should never have filename extensions**. Sure, the system doesn't care, but adding some .foo you may never be able to rid of when it becomes a lie is a bad pattern. – Alex North-Keys Nov 03 '17 at 13:39
  • Can someone please explain what the `{s/^.* //g;h};\${x;p}` part of the sed expression means? – kos Mar 25 '22 at 00:53
  • 1
    @kos command `h` will **store** current *pattern space* into *hold* space. `g` command *`get`* *hold space* into *current pattern space*, and `x` command will exchange content of *hold space* with content of *current pattern space*. See `info sed`. In this use case, I wait upto last line to ensure job done, or adding one more line. – F. Hauri - Give Up GitHub Mar 25 '22 at 05:35
9

Suppose that users "joe" and "deb" have access to account "x". Then in account x's .ssh_authorized_keys you add the lines:

command='wrapper joe' joe public key
command='wrapper deb' deb public key

Also in the wrapper script you can do anything you want, logging that joe's private key has been using ssh at a particular date & time with command $ORIGINAL_COMMAND.

jasonwryan
  • 71,734
  • 34
  • 193
  • 226
user37161
  • 91
  • 1
  • 2
4

On fedora 20+ the login attempts and successes are saved in /var/log/audit/audit.log . This log saves the login attempts (failures and successes), and the key fingerprint used for login attempt is saved in the field named fp.

You can compare the logged in key fingerprint with the fingerprints in the authorized_keys by running it line by line through ssh-keygen -l

A detailed explanation with respect to ssh logins and their security and intrusion detection is here: http://vpathak.tumblr.com/post/121343814158/fedora-audit-log-with-love-from-russia

vpathak
  • 101
  • 2
3

You can try this:

ssh-add -L | awk '{ print $2 }' | xargs -i grep '{}' ~/.ssh/authorized_keys  | head -n 1

This will:

  • ssh-add -L: List public keys
  • awk '{ print $2 }': Get just the fingerprint
  • xargs -i grep '{}' ~/.ssh/authorized_keys: With each key, check which one is on authorized_keys
  • head -n 1: Get only the first one
pci
  • 39
  • 1
  • Arguably more precise and less cpu intensive: `ssh-add -L | awk 'NR==FNR { k=$2;next } /^#/{next} $2==k { print $3;exit} $3==k {print $4;exit} ' - ~/.ssh/authorized_keys` – Otheus Jun 30 '19 at 21:40
2

In addition to @F. Hauri answer, I prepare useful "LoggedIn prompt".

One additional file is optional ($HOME/.ssh/users):

kszumny@laptop kszumny
kszumny@comp2 kszumny
tom@laptop tom
pati@home
chris@workstation1 chris
chris@workstation2 chris

This part should be pasted to /etc/profile (for all users) or to ~/.bashrc

other_users_prompt()
{
    pids=`ps fx | grep "sshd:\s" | awk '{print $1}'`
    users=""
    for uid in $pids
    do
        ssh_crtFp=`sed -ne "/sshd.$uid.:.*matching .SA key/{s/^.* //g;p;q}" /var/log/sshdusers.log`
        for ((ssh_i=1;ssh_i<=$(wc -l <$HOME/.ssh/authorized_keys);ssh_i++));do
            export ssh_line="$(sed -ne ${ssh_i}p <$HOME/.ssh/authorized_keys)"
            echo "$ssh_line" >tempKey
            export ssh_lFp=($(ssh-keygen -l -f tempKey))
            if [ "${ssh_lFp[1]}" == "$ssh_crtFp" ] ;then
                export SSH_KEY_USER=${ssh_line##* }
                ST_USER=`cat $HOME/.ssh/users | grep "${SSH_KEY_USER}" | awk '{print $2}'`
                if [ -z "$ST_USER" ]; then
                    ST_USER=$SSH_KEY_USER
                fi
                if [ -z "$users" ]; then
                    users="$ST_USER"
                else
                    users="$users\n$ST_USER"
                fi
                break
            fi
        done
    done

    if [ `echo -e "$users" | sort | uniq -c | wc -l` == 1  ]; then
       exit
    fi

    users=`echo -e "$users" | sort | uniq -c | awk '{print $2"("$1")"}' | xargs echo -e`
    echo -e "[LoggedIn:$users] "

}

PS1='$(other_users_prompt)\u@\h:\w\$ '

Result

enter image description here

noisy
  • 131
  • 6