Configuration
authkeys reads one TOML file. Which one, highest first:
- the file named with
--config FILE(-c), which must exist; - the file named by
AUTHKEYS_CONFIG(set but empty means no file); /etc/authkeys.cfg.
When the chosen file is /etc/authkeys.cfg and it does not exist, authkeys logs
a warning and resolves with an empty configuration (exit status 0: "no keys" is
a valid answer for sshd). A file that does not parse is an error (status 3). See
examples/authkeys.cfg
for a fully commented example.
The file is read with anycfg: TOML, unless
its first line names another format (#!json, #!yaml). Two rules apply to
everything below:
${env.NAME}is replaced by the environment variableNAME(empty when unset), so a secret need not be written in the file:api_key = "${env.AUTHKEYS_APIKEY}".- Values are typed. Lists are TOML arrays, flags are
true/false, numbers are numbers. A key authkeys does not know, or a value of the wrong type, is logged as one WARNING naming the key (never the value) and ignored, so a typo does not silently look like it worked.
Sources
One [source.<name>] table per source; the name is yours, backend says what
kind of source it is:
[source.files]
backend = "file"
paths = ["authorized_keys", "authorized_keys2"]
Every source takes enabled and cached (both default true), expire (a
per-source TTL in seconds, 0 = do not cache it) and sanitize (the dotted path
of a callable that rewrites or drops keys). The rest are the source's own
options; see Key sources.
Cache
[cache]
backend = "memory" # or "file"
expire = 3600
negative_expire = 60
expired_on_error = true
memory— per-process, in-memory (default).file— on disk underpath(a directory, or a list of them: the first that exists is used), shared across invocations; entries are written atomically (temp file + rename) with0600permissions under a0700directory.
Behaviors:
expire— the positive TTL, seconds (default 300).negative_expire— a separate (usually shorter) TTL for a cached "this user has no keys" result, so a keyless principal isn't re-fetched every login but a newly-added key still appears quickly. Defaults toexpire.expired_on_error— on a source error (an exception, a non-2xx HTTP status, a refused LDAP search), fall back to the last-known (expired) cached keys instead of returning nothing, so a transient outage doesn't block logins. A merely empty result is not an error and never triggers the fallback (and an errored fetch is never cached as "empty").max_stale— seconds an entry may be past its TTL and still be served byexpired_on_error(unset: no limit). Bounds how long revoked keys keep working while a source is failing.
Inspecting and purging the cache
authkeys cache show # list entries with ages
authkeys cache purge --expired # drop expired entries
authkeys cache purge --user alice # drop one user (or --source, --all)
authkeys cache purge --all --dry-run # list what would go, remove nothing
authkeys cache warm alice bob # pre-resolve users (e.g. from cron)
show/purge need an on-disk backend (an in-memory cache is per-process).
The on-disk cache directory must be owned by, and writable only by, the account that runs authkeys: on POSIX an entry, source directory or root that another user owns or that group or others can write is ignored with a WARNING, since whoever can write there could plant keys for any user.
Per-user delegation
A user's ~/.ssh/authkeys.cfg may grant other principals' keys to that user's
account. Both authkeys resolve and authkeys serve honor it:
[authorized]
users = ["alice", "bob"]
groups = ["admins"]
Without users, the user's own keys are included, plus the members of groups.
The file is written by the user it delegates for, so authkeys treats it as
untrusted:
- it is TOML only: no
#!format line, no includes, and${env.NAME}is not expanded (only in the system configuration); - like
~/.ssh/authorized_keysit is read only when it is a regular file (not a symbolic link) of at most 1 MiB, and it and every directory up to the home are owned by the user or root and not writable by group or others (sshd's StrictModes rules). A file that fails is skipped with a WARNING; - a missing file is normal and logs nothing; a removed or broken one ends the
grant it made, also in a running
serve.
User and group mapping
[globals] can point usermap and groupmembers at custom callables (loaded by
dotted path) to translate a username into a source-specific id or to resolve
group membership from somewhere other than the local system group database.
[globals]
usermap = "mypkg.usermaps.some_map"
groupmembers = "mypkg.groupmembers.some_resolver"
Plugins
A distribution can add a source or a cache backend under a name of its own
through the authkeys.sources or authkeys.caches entry-point group:
# the plugin's pyproject.toml
[project.entry-points."authkeys.sources"]
vault = "mypkg.vault:VaultSource" # a subclass of authkeys.KeySource
backend = "vault" then selects it. A plugin cannot take a built-in name, and
one that fails to load is skipped with a WARNING.