keep authelia's keys in bao, seeded from the existing key files
This commit is contained in:
parent
a70b25847e
commit
99fab06348
2 changed files with 557 additions and 89 deletions
|
|
@ -409,13 +409,14 @@ let
|
|||
# The whole point of a second principal. The two prefixes and one leaf it
|
||||
# publishes to and not `swarm/`, so it cannot touch an agent's
|
||||
# credentials or the appservice token beside the controller's client;
|
||||
# and `read` on one leaf alone, the client secret of the journal forwarder
|
||||
# that shares its identity in authelia's container, so a unit whose job
|
||||
# is copying a file cannot recover what is already there. Pinned as the
|
||||
# full capability list per path, because an added capability is exactly
|
||||
# what a presence check misses. The one `read` is found at that leaf and
|
||||
# counted once, so a read on any other path fails the count.
|
||||
name = "the publisher's grant writes the hive and service prefixes and the controller's client, and reads its forwarder's client alone";
|
||||
# and `read` on six exact leaves alone: the client secret of the journal forwarder
|
||||
# that shares its identity in authelia's container, and authelia's five keys
|
||||
# (the next case), so a unit whose job is copying a file cannot recover
|
||||
# what is already there. Pinned as the full capability list per path,
|
||||
# because an added capability is exactly what a presence check misses.
|
||||
# The forwarder's `read` is found at its leaf, and the six together are
|
||||
# counted, so a read on any other path fails the count.
|
||||
name = "the publisher's grant writes the hive and service prefixes and the controller's client, and reads its forwarder's client and authelia's keys alone";
|
||||
ok =
|
||||
let
|
||||
s = baoGrantHere.systemd.services.swarm-bao-secret-publisher-policy.script;
|
||||
|
|
@ -429,9 +430,48 @@ let
|
|||
&& !(lib.hasInfix "secret/data/swarm/*" s)
|
||||
&& !(lib.hasInfix "sys/policies/acl" s)
|
||||
&& lib.hasInfix "path \"secret/data/swarm/services/swarm-authelia-collector/oidc/client\" {\n capabilities = [\"create\", \"update\", \"read\"]\n}" s
|
||||
&& lib.length (lib.splitString "\"read\"" s) == 2
|
||||
&& lib.length (lib.splitString "\"read\"" s) == 7
|
||||
&& !(lib.hasInfix "\"list\"" s);
|
||||
}
|
||||
{
|
||||
# authelia's keys are created once and read at every start, never
|
||||
# replaced: the storage-encryption key encrypts authelia's database and
|
||||
# the issuer key signs every id token. Each is an exact leaf, which
|
||||
# outranks the publisher's `services/*` write and drops its `update`.
|
||||
# The controls are the OIDC leaf beside them, still the prefix's, and a
|
||||
# sixth name under the same directory, which is the prefix's too.
|
||||
name = "the publisher creates and reads authelia's five keys and may not replace one";
|
||||
ok =
|
||||
let
|
||||
grants = grantsIn baoGrantHere.systemd.services.swarm-bao-secret-publisher-policy.script;
|
||||
key = name: grantFor grants "secret/data/swarm/services/swarm-authelia/keys/${name}";
|
||||
in
|
||||
lib.all
|
||||
(
|
||||
name:
|
||||
(key name).caps == [
|
||||
"create"
|
||||
"read"
|
||||
]
|
||||
)
|
||||
[
|
||||
"jwt"
|
||||
"session"
|
||||
"storage-encryption"
|
||||
"oidc-hmac"
|
||||
"oidc-issuer"
|
||||
]
|
||||
&&
|
||||
(key "other").caps == [
|
||||
"create"
|
||||
"update"
|
||||
]
|
||||
&&
|
||||
(grantFor grants "secret/data/swarm/services/swarm-nats/oidc/client").caps == [
|
||||
"create"
|
||||
"update"
|
||||
];
|
||||
}
|
||||
{
|
||||
# The publisher's `services/*` write covers the callout xkey's seed, and
|
||||
# only the exact-path denial, which outranks the prefix, keeps it from
|
||||
|
|
|
|||
|
|
@ -422,6 +422,36 @@ let
|
|||
&& ((inAuthelia secretPublisherHere).systemd.services ? swarm-secret-publish)
|
||||
&& lib.any (lib.hasPrefix "--load-credential=") secretPublisherHere.containers.swarm-authelia.extraFlags;
|
||||
}
|
||||
{
|
||||
# Every key authelia signs or encrypts with comes from the store, seeded
|
||||
# from the file it was generated into, and lands at the path the config
|
||||
# already names. The storage-encryption key encrypts authelia's database,
|
||||
# so a value that moved would leave it unreadable. With no identity the
|
||||
# unit still generates the files, which is the control for each absence:
|
||||
# the generator and the store sequence are found by the same lookups.
|
||||
name = "authelia's keys are read from the store with the publisher's identity, and generated on disk without one";
|
||||
ok =
|
||||
let
|
||||
unit = m: (inAuthelia m).systemd.services.authelia-swarm-secrets;
|
||||
viaStore = unit secretPublisherHere;
|
||||
onDisk = unit secretPublisherNoIdentity;
|
||||
in
|
||||
lib.hasInfix "store_seeded secret/swarm/services/swarm-authelia/keys/\"$name\" \"$p\" \"$@\"" viaStore.script
|
||||
&& lib.hasInfix "p=/var/lib/authelia-swarm/\"$name\".key" viaStore.script
|
||||
&& lib.hasInfix "keep_key oidc-issuer mint_issuer" viaStore.script
|
||||
&& lib.hasInfix "for f in jwt session storage-encryption oidc-hmac; do\n keep_key \"$f\" mint_hex" viaStore.script
|
||||
&& !(lib.hasInfix "genrsa -out" viaStore.script)
|
||||
&&
|
||||
viaStore.environment.BAO_CLIENT_CERT
|
||||
== "/run/credentials/authelia-swarm-secrets.service/publisher-bao-cert"
|
||||
&& lib.elem "publisher-bao-cert" viaStore.serviceConfig.LoadCredential
|
||||
&& viaStore.serviceConfig.Restart == "on-failure"
|
||||
&& !(lib.hasInfix "store_seeded" onDisk.script)
|
||||
&& lib.hasInfix "genrsa -out" onDisk.script
|
||||
&& lib.hasInfix "if [ ! -s \"$p\" ]; then" onDisk.script
|
||||
&& !(onDisk.environment ? BAO_ADDR)
|
||||
&& !(onDisk.serviceConfig ? LoadCredential);
|
||||
}
|
||||
{
|
||||
# The path unit's start is a no-op while its target is `active (exited)`,
|
||||
# so a secret minted after the first run would never be published.
|
||||
|
|
|
|||
Loading…
Reference in a new issue