Skip to content

Instantly share code, notes, and snippets.

@sivel
Last active April 8, 2024 07:53
Show Gist options
  • Save sivel/c68f601137ef9063efd7 to your computer and use it in GitHub Desktop.
Save sivel/c68f601137ef9063efd7 to your computer and use it in GitHub Desktop.
Better SSH Authorized Keys Management

Better SSH Authorized Keys Management

A seemingly common problem that people encounter is how to handle all of your users authorized_keys file.

People struggle over management, ensuring that users only have specific keys in the authorized_keys file or even a method for expiring keys. A centralized key management system could help provide all of this functionality with a little scripting.

One piece of functionality overlooked in OpenSSH is the AuthorizedKeysCommand configuration keyword. This configuration allows you to specify a command that will run during login to retrieve a users public key file from a remote source and perform validation just as if the authorized_keys file was local.

Here is an example directory structure for a set of users with SSH public keys that can be shared out via a web server:

users/
├── dave
│   └── keys
├── matt
│   └── keys
├── nathen
│   └── keys
└── paul
    └── keys

Each of these keys files might look like:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCt8lGmfZ0fxPz/66JlNg9CmZNaLsJ/TDrYnpBpiWWeuoLxP1tEbDiutApVOkjjQszBQV6CgvG3PeBYYAcJxUTRKhY8dUUbsAvVK3SRVwpr8jhtcohYgRE4V9/xPnwilDAfd9TymCMvM/mBpauQCyL40SImFQMJl5aBAhBiy6zyWx6WeDTzJ4+ZGUTmwFFyaWzzIqIZXWe1QiM98rfzle0mYM8KSKdTuGEf0EmY63MbMl3PQ61ms/qkR3fnKWpGF+EsigS0NgT6nBYoOZm5nFtrB2WM8nixyD5v82Z6yA6+O2SfLxtzJ6OcowtwtitrcZrAZdcNIwOAX1T7G4qcFEFn matt@sivel.net
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCZ0N1dcto3td7j5/7UPCE2XlhDaCOZTlYtCgNifJygM5GNAG97JcChnnoYbdmiEM+dFMs7Jk6fS/WzG0Q0Ypu3rQ9AzzeUEMbhrFB90f28JsfUtgnkYuUF+1dNDGZn1fhYMlNwwyIt5s0KSS18iJNU6ZrSTudk9v1gyBM+Sxz97YMg2RiiGpCajPHzZbj2AwMl52MjT8ZDCGLt2qFo+w4u4BNQdtAA+zs/GiwgFbdGHM2HR1VxmII61LpvyyeuRkRwxN1ak3R7FcPMmYNhC9cvzbnvpmVcXxwXChI/9ceOm6DODCgHl9YeOgngoe5gEtZHnqtOWZWao8cFfd4wcEEN matt@sivel.net

If I were to serve out the users/ directory using a webserver such as nginx, I might be able to request the keys for the user matt such as:

curl -sf https://keyserver.example.org/users/matt/keys

The AuthorizedKeysCommand expects an executable that takes a single argument, which is the username to retrieve the keys for. An example executable may look like:

#!/bin/bash
curl -sf https://keyserver.example.org/users/$1/keys

Name this file something like /usr/local/bin/userkeys.sh and make it executable: chmod a+x /usr/local/bin/userkeys.sh

Now add the following to your /etc/sshd/sshd_config file:

AuthorizedKeysCommand      /usr/local/bin/userkeys.sh
AuthorizedKeysCommandUser  nobody

Most operating systems have a nobody user, but you can replace that user with any non-root user that is different from the user running OpenSSH on the server. This should preferably be a user not already in use by another daemon.

Now, when a user logs in, userkeys.sh will be executed and if there are keys for that user they will be returned by our simple script.

Now all you need to do is manage that users/ directory via something like git and you are good to go.

GitHub

If all your users are on GitHub, you can even have them use GitHub for the storage location of their SSH public keys, and you can replace the URL with https://github.com/$1.keys.

Although, if you usernames don't match GitHub, then you would have to maintain a lookup table that may get complicated.

This also works for GitHub Enterprise too, which if your company uses it could solve the username issues.

@dlo
Copy link

dlo commented Feb 5, 2018

If it's helpful for anyone else, I had to change the AuthorizedKeysCommand to be AuthorizedKeysCommand /usr/local/bin/userkeys.sh %u for the username argument to be passed through to the script. This was on Ubuntu 16.04.

@ierror
Copy link

ierror commented Aug 18, 2018

@pspaulding
Copy link

Thanks for the article. I was only able to get this to work with pre-existing users. Is there a way to dynamically create users on the fly? I know that is possible with LDAP authentication, but I could not figure out how to make this work with the above approach.

@rchench
Copy link

rchench commented Feb 19, 2019

Thanks for the guide. It works perfectly. Just found one typo and need to restart sshd service after update.
/etc/sshd/sshd_config should be /etc/ssh/sshd_config

@murph-cat
Copy link

Per the sshd_config man page, this changed terminology from AuthorizedKeysCommandUser to AuthorizedKeysCommandRunAs. Looks like it happened in 2018.

@netlore
Copy link

netlore commented Sep 7, 2020

I've had a tool using AuthorizedKeysCommand up and running at work for some time, but after failing to get them to agree to open source it, I've been working on a ground up rewrite... It implements a central management server which wraps this up into a "product", that manages both static trust, and has a gateway that lets users register their own keys, and allows you to refer to them based on whatever directory is used on the authentication server.... AuthorizedKeysCommand requires openssh 7.x to work. It's not 100% done, still a few things to do before I release a 1.0 version, but I'd appreciate comments :- https://netlore.github.io/OpenAKC/ - It wraps in some clever stuff with session recording, and linux capabilities too ;)

@netlore
Copy link

netlore commented Sep 9, 2020

@murph-cat - I think you have that backwards, in the OpenSSH man page as of 8.3, it's AuthorizedKeysCommandUser... and I've tested the original AKC, and subsequently OpenAKC with every version of SSH from 7.2 up. There was a slightly incompatible version using, AuthorizedKeysCommandRunAs instead, which was in one of the last versions of OpenSSH 6, but I elected to simply support 7.x+ with my tool as I don't think there were any extra distro's that I was going to be able to support if I went to the trouble of checking the SSH version. I did ultimately build OpenSSH 7.6 packages for RHEL5 RHEL6 and SLES11, so that I could support the last few of those machines at that company...

@xenago
Copy link

xenago commented Apr 26, 2022

@pspaulding Did you ever figure out how to create users on the fly?

@pspaulding
Copy link

@xenago No, never figured it out and moved on. Still curious though.

@xenago
Copy link

xenago commented Apr 26, 2022

@pspaulding I found this, which I think is the right type of solution. I haven't tested it but the idea seems sound. NSS is used before PAM/key auth, so adapting an NSS module should do the trick. https://github.com/osallou/nss-external

@xenago
Copy link

xenago commented Jun 20, 2022

@pspaulding I created this project libnss_shim which should enable this. I think one would just need to create simple scripts which read the username/group in as a command line parameter and then perform prep (such as creating a homedir if one doesn't exist for that username). The script could either add the user to the system db with a command like adduser or just print out a passwd/group entry. Then when the scripts are added to the libnss_shim config, any username can be made 'valid' at login time. It might be fun to have an ssh container which allows any username/password to log in this way.

@pspaulding
Copy link

@xenago very cool!

@nfultz
Copy link

nfultz commented Jan 8, 2024

Great writeup. I went ahead and packaged this for debian / ubuntu to be more reusable - https://github.com/njnmco/sshd-gh-key-helper

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment