Sunday March 24 2019
Automatically populate SSH keys from Github or Gogs API
There’s quite often a need for your servers to have up to date SSH keys for
their users. Traditional methods of editing ~username/.ssh/authorized_keys
is not only tedius, but error prone and does not update automatically.
Configuration Management
Some solve this problem by pulling in configuration management frameworks such as Puppet, Ansible, Chef, Salt, etc. While those work they have the problem of still allowing the user to edit the key that they have on disk ( even if it is being overwritten at some intervals ) and it requires a change to the configuration management solution to update the keys. Unless they’re a system’s engineer it’s not likely that they’re going to be touching the configuration management software all that often.
User Directories
Another solution is to use something like FreeIPA, or LDAP with SSH keys, while this works, it’s a complete pain to configure and if you’re not already using some sort of centralized directory getting people to switch is going to be a complete pain.
Using Existing Infrastructure
For my purposes engineers who need access to servers are going to have either a public Github account, or something internal to the company, since those services are often heavily used by engineers and will usually have up to date SSH keys we can have a small program pull them out of there.
For instance with Github we can see the key for Linus Torvalds with a simple curl command:
$ curl -s -X GET https://api.github.com/users/torvalds/keys | jq '.[].key'
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCoQ9S7V+CufAgwoehnf2TqsJ9LTsu8pUA3FgpS2mdVwcMcTs++8P5sQcXHLtDmNLpWN4k7NQgxaY1oXy5e25x/4VhXaJXWEt3luSw+Phv/PB2+aGLvqCUirsLTAD2r7ieMhd/pcVf/HlhNUQgnO1mupdbDyqZoGD/uCcJiYav8i/V7nJWJouHA8yq31XS2yqXp9m3VC7UZZHzUsVJA9Us5YqF0hKYeaGruIHR2bwoDF9ZFMss5t6/pzxMljU/ccYwvvRDdI7WX4o4+zLuZ6RWvsU6LGbbb0pQdB72tlV41fSefwFsk4JRdKbyV3Xjf25pV4IXOTcqhy+4JTB/jXxrF"
Well that’s great, but how are we going to make use of this? It’s pretty simple
The OpenSSH server offers us an
AuthorizedKeysCommand
Which will allow us to write a small program to take advantage of the API and
supply the key to the server.
From here you could probably bash together a short shell script to complete
this task. Although I often use shell scripts, something like this that
requires using curl and something along the lines of jq
or manually
trying to parse the JSON I’m going to reach for something a little bit more
cohesive.
To accomplish this I’ve written a simple Go program that takes a few arguments for the API endpoint as well as the username to search for:
package main
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
)
var (
endpoint = flag.String("e", "https://gogs.example.com/api/v1",
"Gogs server endpoint, possibly github e.g. https://api.github.com")
user = flag.String("u", "", "Username to look for")
)
type Key struct {
Key string `json: key`
}
func (k Key) String() string {
return k.Key
}
func errDie(err error) {
if err != nil {
log.Fatal(err)
}
}
func main() {
flag.Parse()
str := fmt.Sprintf("%s/users/%s/keys", *endpoint, *user)
resp, err := http.Get(str)
errDie(err)
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
errDie(err)
keyList := []Key{}
err = json.Unmarshal(b, &keyList)
errDie(err)
for _, k := range keyList {
fmt.Println(k)
}
}
( You could also easily map Github usernames to different local usernames with a few minimal modifications by using a hash table or so. Not included here for simplicity )
Configuring OpenSSH
Pretty straightforward, simply add AuthorizedKeysCommand
pointed at our
new sshauth
command:
AuthorizedKeysCommand /usr/bin/sshauth -e https://api.github.com -u "%u"
Now all you have to do is useradd -M $github_username
and they’ll be able
to login to your server if they have SSH keys associated with their Github
account!
Preventing users from managing their own keys
If you wish to turn off the ability for users to leave behind keys it’s as simple as setting:
AuthorizedKeys none
Leaving AuthorizedKeys
on for specific users
It’s as simple as adding a Match
directive towards the end of your sshd_config
Match User mitch
AuthorizedKeys .ssh/authorized_keys
You can also do this on a per group, LocalAddress, Host, or Address, you can read more about it in the documentation.
Additional Thoughts
This obviously doesn’t implement any sort of caching, although it would be
possible to write out the keys to a directory in /tmp/
or /etc/ssh
,
and cache them permanently, for only a few minutes, an hour, etc I choose
not to specifically so it would always grab the newest keys for the user,
and in the event that an account is disabled and the keys are removed on
a private Gogs server there’s no chance that they can login. A central choke
point if you will.
Reading through and understanding a little bit of the documentation for the system software you’re using as well as looking at the incentives, and use cases and a little bit of programming ability to make things happen goes a long way.
For instance, most are going to see managing SSH keys in some sort of directory as a hassle, but when integrated with a tool they’re using all the time such as Github it becomes quite painless.
I always encourage more engineers to read the documentation for the tools that they’re using, half the battle of solving a problem is knowing what tools that you have at your disposal.
Find out that you have more than a hammer available and then everything stops looking like a nail.