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?
- 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– Tom Anderson2011-06-28 16:28:58 +00:00Commented Jun 28, 2011 at 16:28
- 5There's an OpenSSH feature request about this: Please add pubkey fingerprint to authentication log messageSteffen– Steffen2013-04-29 07:59:29 +00:00Commented Apr 29, 2013 at 7:59
- Centos mechanism: unix.stackexchange.com/questions/147295/…jhfrontz– jhfrontz2018-11-27 18:07:34 +00:00Commented Nov 27, 2018 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. I post this as a comment here because it’s not the same question, just highly related.Daniel Faber– Daniel Faber2022-02-06 11:47:53 +00:00Commented Feb 6, 2022 at 11:47
7 Answers
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. - That looks promising. The fingerprint then tells me which key is used. Great, thanks.Andrew Stacey– Andrew Stacey2011-06-24 20:12:39 +00:00Commented Jun 24, 2011 at 20:12
- For printing fingerprint of current session:
sed -ne "/sshd.$PPID.:.*matching DSA key/{s/^.* //g;p;q}" /var/log/auth.logF. Hauri - Give Up GitHub– F. Hauri - Give Up GitHub2012-12-31 13:00:51 +00:00Commented Dec 31, 2012 at 13:00 - I like GNU
sed!F. Hauri - Give Up GitHub– F. Hauri - Give Up GitHub2012-12-31 13:01:35 +00:00Commented Dec 31, 2012 at 13:01 - 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– godlygeek2014-07-29 21:48:17 +00:00Commented Jul 29, 2014 at 21:48
- 2This 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– Zhang Buzz2018-04-01 02:52:12 +00:00Commented Apr 1, 2018 at 2:52
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.
New answer 2025
From time this was posted, ssh-keygen as systemd and lot of other thing did evolved.
Showing logs dynamically
Today, for showing logs, there are either:
tail -f /var/log/auth.log or
journalctl -axfu ssh (With colorized output.)
Showing fingerprints and name of keys.
Newer version of ssh-keygen -l support
- reading from STDIN
- processing multiple keys (one by line) in submited file
cat .ssh/authorized_keys /home/*/.ssh/authorized_keys | sort -u | ssh-keygen -l -f - Will show all authoritzed keys.
Combining both:
journalctl -axfu ssh | sed -ue "$( cat .ssh/authorized_keys /home/*/.ssh/authorized_keys | sort -u | ssh-keygen -l -f - | sed ' /^[0-9]\+ \([^ ]\+\) \([^ ]\+\) (\([^ ]\+\)) *$/{ s//s|\3 \1|\\o33[1m\2\\o33[0m|;/; }; ')" or with sudo:
sudo tail -f /var/log/daemon.log | sed -ue "$( sudo cat .ssh/authorized_keys /home/*/.ssh/authorized_keys | sort -u | ssh-keygen -l -f - | sed ' /^[0-9]\+ \([^ ]\+\) \([^ ]\+\) (\([^ ]\+\)) *$/{ s//s|\3 \1|\\o33[1m\2\\o33[0m|;/; }; ')" Notice: I use journalctl -axfu ssh, journalctl -axf, tail -f /var/log/auth.log or tail -f /var/log/deamon.log in same way.
Using journalctl ... |, while keeping colorization.
As journalctl don't have an option --colors=always like ls, I use /bin/script to show a fake tty:
sudo script -f /dev/null -c 'journalctl -axfu ssh' | sed -ue "$( sudo cat .ssh/authorized_keys /home/*/.ssh/authorized_keys | sort -u | ssh-keygen -l -f - | sed -e ' /^[0-9]\+ \([^ ]\+\) \([^ ]\+\) (\([^ ]\+\)) *$/{ s//s|\3 \1|\\o33[1;35m\2\\o33[0m|;/; }; ')" [OLD ANSWER] 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...
- When this was published I was under GNU/Linux Debian 6, but this work quite same under Debian 7...F. Hauri - Give Up GitHub– F. Hauri - Give Up GitHub2014-01-29 16:55:30 +00:00Commented Jan 29, 2014 at 16:55
- This is linked to Record bash_history to private database for all usersF. Hauri - Give Up GitHub– F. Hauri - Give Up GitHub2014-05-09 12:03:08 +00:00Commented May 9, 2014 at 12:03
- Upgraded due to change in log formatF. Hauri - Give Up GitHub– F. Hauri - Give Up GitHub2016-10-31 16:18:08 +00:00Commented Oct 31, 2016 at 16:18
- 1@AlexNorth-Keys extensions under UN*X are generaly technicaly unseless, as we prefer use mime and
filefor knowing file types. But as for human who browse filesystems, having extensions like.pl,.py,.sh,.awk,.sed,.tar.gz, or even.png.b64.gzis usefull!F. Hauri - Give Up GitHub– F. Hauri - Give Up GitHub2017-11-03 08:33:10 +00:00Commented Nov 3, 2017 at 8:33 - 1@kos command
hwill store current pattern space into hold space.gcommandgethold space into current pattern space, andxcommand will exchange content of hold space with content of current pattern space. Seeinfo sed. In this use case, I wait upto last line to ensure job done, or adding one more line.F. Hauri - Give Up GitHub– F. Hauri - Give Up GitHub2022-03-25 05:35:13 +00:00Commented Mar 25, 2022 at 5:35
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.
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
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

- Not tested but it seems amazingV. Bozz– V. Bozz2020-04-17 08:51:02 +00:00Commented Apr 17, 2020 at 8:51
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 keysawk '{ print $2 }': Get just the fingerprintxargs -i grep '{}' ~/.ssh/authorized_keys: With each key, check which one is onauthorized_keyshead -n 1: Get only the first one
- 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_keysOtheus– Otheus2019-06-30 21:40:46 +00:00Commented Jun 30, 2019 at 21:40