Metadata-Version: 2.4
Name: authkeys
Version: 0.4.0
Summary: Pluggable AuthorizedKeysCommand provider for OpenSSH (file/HTTP/LDAP sources, TTL cache, optional key server)
Project-URL: Homepage, https://github.com/jose-pr/authkeys/
Project-URL: Documentation, https://jose-pr.github.io/authkeys/
Project-URL: Issues, https://github.com/jose-pr/authkeys/issues
Author: Jose A.
License-Expression: MIT
License-File: LICENSE
Keywords: authorized_keys,cli,ldap,openssh,ssh
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: System Administrators
Classifier: Operating System :: POSIX
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: System :: Systems Administration :: Authentication/Directory
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: duho
Provides-Extra: all
Requires-Dist: cryptography; extra == 'all'
Requires-Dist: ldap3; extra == 'all'
Requires-Dist: requests; extra == 'all'
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: hatchling; extra == 'dev'
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: pytest-cov; extra == 'dev'
Requires-Dist: pytest-timeout; extra == 'dev'
Requires-Dist: requests; extra == 'dev'
Requires-Dist: twine; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs; extra == 'docs'
Requires-Dist: mkdocs-material; extra == 'docs'
Requires-Dist: mkdocstrings[python]; extra == 'docs'
Provides-Extra: http
Requires-Dist: requests; extra == 'http'
Provides-Extra: ldap
Requires-Dist: cryptography; extra == 'ldap'
Requires-Dist: ldap3; extra == 'ldap'
Description-Content-Type: text/markdown

# authkeys

[![PyPI version](https://img.shields.io/pypi/v/authkeys.svg)](https://pypi.org/project/authkeys/)
[![Python versions](https://img.shields.io/pypi/pyversions/authkeys.svg)](https://pypi.org/project/authkeys/)
[![Documentation](https://img.shields.io/badge/docs-jose--pr.github.io%2Fauthkeys-blue.svg)](https://jose-pr.github.io/authkeys/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/jose-pr/authkeys/blob/main/LICENSE)

A pluggable OpenSSH [`AuthorizedKeysCommand`](https://man.openbsd.org/sshd_config#AuthorizedKeysCommand)
provider. It resolves a user's authorized SSH keys from one or more configured
**sources** — local key files, an HTTP endpoint, or LDAP-stored X.509
certificates — with optional TTL caching, user/group aliasing, and a small
unattended HTTP key server for hosts that fetch keys centrally.

Built on the [duho](https://github.com/jose-pr/duho) declarative CLI framework.

## Install

```bash
pip install authkeys            # core (file source only)
pip install authkeys[http]      # + HTTP source (requests)
pip install authkeys[ldap]      # + LDAP cert source (ldap3, cryptography)
pip install authkeys[all]       # everything
```

> authkeys targets POSIX systems (it reads the system user/group databases via
> `pwd`/`grp`). The package imports on any platform for testing, but resolution
> requires a POSIX host.

## Usage

```bash
authkeys resolve alice          # print alice's authorized keys
authkeys alice                  # 'resolve' is the default command
authkeys serve --bind 0.0.0.0 --port 8090
```

Wire it into `sshd_config`:

```
AuthorizedKeysCommand /usr/bin/authkeys resolve %u
AuthorizedKeysCommandUser nobody
```

## Configuration

authkeys reads (in order) `/etc/authkeys.conf`,
`/etc/authkeys/authkeys.conf`, `/etc/ssh/authkeys.conf`, or a colon-separated
list passed with `--config`. A per-user `~/.ssh/authkeys.conf` may authorize
additional users/groups. See [`examples/authkeys.conf`](examples/authkeys.conf)
for a fully commented example.

```ini
[cache]
backend = authkeys.cache.AuthKeysCacheMemBackend
expire = 3600
expired_on_error = 1

[source:files]
backend = authkeys.sources.authorizedkeys
paths =
    authorized_keys
    authorized_keys2

[source:ldap]
enabled = 1
backend = authkeys.sources.ldap
server = ldaps://ldap.example.com:636
basedn = o=Example,c=US
```

### Sources

| Alias (`backend =`)                | Reads keys from                              | Extra dep |
| ---------------------------------- | -------------------------------------------- | --------- |
| `authkeys.sources.authorizedkeys`  | `~/.ssh/authorized_keys*` files              | —         |
| `authkeys.sources.http`            | an HTTP URL (`{username}` templated)         | `requests` |
| `authkeys.sources.ldap`            | X.509 certs in an LDAP directory             | `ldap3`, `cryptography` |

Each `[source:<name>]` section supports `enabled` and `cached` (both default
`true`) and an optional `sanitize` callable to rewrite/drop keys.

### Caching

- `AuthKeysCacheMemBackend` — per-process, in-memory (default).
- `AuthKeysCacheFileBackend` — on-disk under `path`, shared across invocations.

With `expired_on_error = 1`, a source failure falls back to the last-known
(expired) cached keys instead of returning nothing — useful when the SSH login
must not be blocked by a transient LDAP/HTTP outage.

### User / group aliasing

A per-user `~/.ssh/authkeys.conf` may grant other principals' keys:

```ini
[authorized]
users =
    alice
    bob
groups =
    admins
```

## HTTP key server (`authkeys serve`)

For hosts that can't run the command locally, `authkeys serve` exposes the same
resolution over HTTP:

```
GET /keys?username=alice&apikey=<key>
```

Bind address, port, path, and API key come from the `[serve]` config section
(or `--bind`/`--port`). The API key is compared in constant time; pull it from
the environment with `api_key = ${env:AUTHKEYS_APIKEY}` to keep it off disk. If
no API key is configured, authentication is disabled — only bind to a trusted
interface in that case.

## License

MIT — see [LICENSE](LICENSE).
