An open API service indexing awesome lists of open source software.

https://github.com/dolph/ussher

Remotely source SSH authorized_keys
https://github.com/dolph/ussher

ssh sshd sshd-config

Last synced: about 2 months ago
JSON representation

Remotely source SSH authorized_keys

Awesome Lists containing this project

README

          

# `ussher`

`ussher` aims to provide a backend for `sshd`'s [`AuthorizedKeysCommand` option](https://man.openbsd.org/sshd_config.5#AuthorizedKeysCommand), by remotely sourcing SSH `authorized_keys`. In short:

> `AuthorizedKeysCommand`: Specifies a program to be used to look up the user's public keys. The program must be owned by root, not writable by group or others and specified by an absolute path.
>
> The program should produce on standard output zero or more lines of authorized_keys output. `AuthorizedKeysCommand` is tried after the usual `AuthorizedKeysFile` files and will not be executed if a matching key is found there. By default, no `AuthorizedKeysCommand` is run.

When `~/.ssh/authorized_keys` does not contain the keys required to authenticate a user, `sshd` invokes `ussher` to provide additional, remotely-sourced keys, such as from Github or another identity and access management provider.

## How it works

`ussher` provides a fallback mechanism for statically-defined `authorized_keys` files, such as when `authorized_keys` needs to be frequently updated, composed from a large number of sources, or simply defined at the moment of authorization.

1. When you `ssh $USER@$HOSTNAME`, `sshd` first reads something like `/home/$USER/.ssh/authorized_keys` to authenticate incoming SSH connections. If `authorized_keys` contains a public key that matches the incoming connection, then the connection attempt proceeds normally.

1. If that file does not exist or does not contain a public key for the incoming connection, `sshd` will invoke `AuthorizedKeysCommand` as `AuthorizedKeysCommandUser`. The `AuthorizedKeysCommand` (`ussher`, in this case), is responsible for returning a list of authorized public keys, which `sshd` then uses to validate the incoming connection.

1. When invoked, `ussher` sources authorized keys from any number of remote sources, such as a static text file or more commonly something like `https://github.com/{username}.keys`. `ussher` then returns a single set of authorized keys to `sshd` which are used to validate the incoming connection.

## Recommended installation & usage

1. Download the latest release of `ussher` from [github.com/dolph/ussher](https://github.com/dolph/ussher/releases/latest), along with its `sha256` checksum, and verify the binary before installing:

```bash
curl -fLO https://github.com/dolph/ussher/releases/latest/download/ussher
curl -fLO https://github.com/dolph/ussher/releases/latest/download/ussher.sha256
sha256sum -c ussher.sha256
```

`sha256sum -c` exits non-zero if the binary doesn't match the published checksum. Don't proceed past this step on a mismatch.

2. Create a dedicated user and group to run `ussher`, named `ussher`:

```bash
sudo adduser --system --user-group ussher
```

3. Install the `ussher` binary: to `/usr/local/bin`.

```bash
sudo install -o root -g ussher -m 0750 ussher /usr/local/bin/ussher
```

4. Create a configuration directory.

```bash
sudo mkdir --parents /etc/ussher
```

5. Create a directory for caching remotely-sourced data.

```bash
sudo mkdir --parents /var/cache
sudo mkdir --parents --mode=0700 /var/cache/ussher
sudo chown ussher:ussher /var/cache/ussher
```

6. Create a directory for logging.

```bash
sudo mkdir --parents --mode=0700 /var/log/ussher
sudo chown ussher:ussher /var/log/ussher
```

7. Configure `sshd` to invoke `ussher`. Add the following lines to
`/etc/ssh/sshd_config`:

```
AuthorizedKeysCommand /usr/local/bin/ussher
AuthorizedKeysCommandUser ussher
```

You can script this with:

```bash
sudo sed -i -E "s~^#?AuthorizedKeysCommand .*~AuthorizedKeysCommand /usr/local/bin/ussher~" /etc/ssh/sshd_config
sudo sed -i -E "s~^#?AuthorizedKeysCommandUser .*~AuthorizedKeysCommandUser ussher~" /etc/ssh/sshd_config
```

Validate sshd's new configuration and restart `sshd`, for example:

```bash
sudo sshd -t
sudo systemctl restart sshd
```

## Configuration

`/etc/ussher` contains configuration files for each user it supports. For example, to allow @dolph to SSH to your host as root (but, you know, _don't_), you would configure `/etc/ussher/root.yml` using:

```yaml
sources:
- url: https://github.com/dolph.keys
```

### `cache_ttl`

Responses from each source URL are cached on disk under `/var/cache/ussher` so that frequent SSH attempts don't hammer the upstream. Cached entries expire after `cache_ttl`; once expired, the next login refetches the source. After `cache_ttl` elapses, key revocations on the upstream propagate to this host. Smaller values mean revocations take effect sooner and add upstream load; larger values reduce upstream load and lengthen the window during which a revoked key could still authenticate.

`cache_ttl` accepts any duration string understood by Go's [`time.ParseDuration`](https://pkg.go.dev/time#ParseDuration) — for example `30s`, `5m`, `1h`. The default when unset is `5m`.

```yaml
cache_ttl: 5m
sources:
- url: https://github.com/dolph.keys
```

### `http_timeout`

`http_timeout` caps how long a single upstream fetch (connect + headers + body read) can take before `ussher` gives up on it. A hung or stalled source — DNS that resolves but never returns, a TLS handshake that hangs, an HTTP server that accepts the connection but never responds — would otherwise block `sshd`'s authentication path until the OS-default timeout fires (often 2+ minutes per attempt). With `http_timeout`, `ussher` abandons the source quickly, treats the failure exactly like any other (logs and contributes zero keys from that source), and the rest of the configured sources continue to be served.

`http_timeout` accepts any duration string understood by [`time.ParseDuration`](https://pkg.go.dev/time#ParseDuration) — for example `500ms`, `10s`, `30s`. The default when unset is `10s`. Tighter values trade a higher rate of "slow but healthy upstream gave up" denials for tighter login latency under partial outages; looser values do the opposite.

```yaml
http_timeout: 10s
sources:
- url: https://github.com/dolph.keys
```

## Troubleshooting

### `Refusing to run unnecessarily writable binary`

Per the `sshd` man page:

> The program must be owned by root, not writable by group or others and specified by an absolute path.

`ussher` checks to ensure it's own binary is not unnecessarily writable at startup. If it is, a malicious user could remove this check or return any set of keys to sshd, which may be difficult to detect.

Ensure the file mode is similar to:

```
$ ls -l /usr/local/bin/ussher
-rwxr-x---. 1 root ussher 7823184 Apr 27 11:10 /usr/local/bin/ussher*
```

For example:

```bash
sudo chmod g-w /usr/local/bin/ussher
sudo chmod o-w /usr/local/bin/ussher
```

... but the binary may have been tainted. Verify it's checksum.

### `Refusing to run as root`

`ussher` checks to ensure it's not unnecessarily _running_ as root.

Ensure `sshd_config` specifies a non-root user:

```
AuthorizedKeysCommandUser ussher
```

### `Failed to write to /var/log/ussher`

`ussher` tries to ensure it can produce file-based logging output for auditing purposes. It's first preference is for `/var/log/ussher` which requires:

```bash
sudo mkdir --parents --mode=0700 /var/log/ussher
sudo chown ussher:ussher /var/log/ussher
```

### `Refusing to run without being able to log to /var/log/ussher/ or current working directory`

`ussher` tries to ensure it can produce file-based logging output for auditing purposes, and will fallback to the current working directory if `/var/log/ussher` is not writable. The best solution is to ensure `/var/log/ussher` is writable (see previous troubleshooting issue).

### `Refusing to run due to permissions issue on the ussher executable`

`ussher` tries to ensure that its own binary is not susceptible to manipulation by unauthorized users. This error may be accompanied by a more specific error in stdout.

Possible solutions include:

```bash
sudo chmod g-w /usr/local/bin/ussher
sudo chmod o-w /usr/local/bin/ussher
```

### `usage: ussher `

Per the `sshd_config` man page:

> Arguments to AuthorizedKeysCommand accept the tokens described in the TOKENS section. If no arguments are specified then the username of the target user is used.

`ussher` only expects the default configuration from sshd (the username of the target user).

Ensure `sshd_config` only specifies the absolute path to `ussher`, without any additional arguments:

```
AuthorizedKeysCommand /usr/local/bin/ussher
```

### `User not found`

The user specified to `ussher` is either not a valid Linux username or not an existing user on the host. Double check the username specified to `ussher` as well as the `AuthorizedKeysCommand` value in `/etc/ssh/sshd_config`.

## Changelog

See [`CHANGELOG.md`](./CHANGELOG.md) for the per-release history. The project follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and adheres to [Semantic Versioning](https://semver.org/).

## License

Apache 2.0