let the operator's oidc login write the cache's tokens and facts
This commit is contained in:
parent
e0d2350902
commit
f22f4ee6ac
5 changed files with 87 additions and 15 deletions
|
|
@ -355,8 +355,8 @@ principal and create what's missing.
|
|||
|
||||
A unit creates each secret only while the store holds none, so a second start
|
||||
mints nothing. Both tokens last ten years and nothing renews them. To replace one,
|
||||
write the new token to its path in the store: nothing mints over a value that
|
||||
exists. A new signing key invalidates both tokens.
|
||||
write the new token to its path in the store, in its UI under the OIDC login:
|
||||
nothing mints over a value that exists. A new signing key invalidates both tokens.
|
||||
|
||||
The unit creates the address, name and key once and never updates them. After
|
||||
you change `swarm.attic.domain`, or after the server's database goes and the
|
||||
|
|
@ -387,8 +387,8 @@ the swarm at an existing tenant:
|
|||
`services.hyperhive.swarm.attic.cache` to the cache's name on it. Set
|
||||
both options on every host that reads them.
|
||||
2. Seed the push token in the swarm secret store at
|
||||
`secret/swarm/services/attic/push-token`, field `value`. Use the store's UI,
|
||||
or `bao kv put` with `value=-` and the token on stdin, so the token is in no
|
||||
`secret/swarm/services/attic/push-token`, field `value`. Use the store's UI under
|
||||
the OIDC login, or `bao kv put` with `value=-` and the token on stdin, so the token is in no
|
||||
command line. The token needs push rights on that cache. Until it
|
||||
exists, every push fails and builds stay green with `cached` false.
|
||||
Seed the hives' pull token the same way at
|
||||
|
|
|
|||
|
|
@ -556,11 +556,17 @@ The OIDC tab logs in through authelia as the client `swarm-bao-ui`
|
|||
(`swarm.bao.ui.oidc.clientId`). An `admins` member gets a token under
|
||||
`swarm-operator-viewer`: `list` and `read` on `secret/metadata/*`, and
|
||||
`create` and `update` on
|
||||
`secret/data/swarm/services/review-classifier/anthropic-api-key` and
|
||||
`secret/data/swarm/services/review-classifier/acp-provider`. That shows the key
|
||||
tree and each key's versions and timestamps, and no value: the store refuses
|
||||
every `secret/data/` read. The two writes are the review classifier's keys,
|
||||
which the operator fills in by hand. Anything more needs a token.
|
||||
`secret/data/swarm/services/review-classifier/anthropic-api-key`,
|
||||
`secret/data/swarm/services/review-classifier/acp-provider` and the cache's five
|
||||
leaves under `secret/data/swarm/services/attic/` (`push-token`, `pull-token`,
|
||||
`url`, `cache` and `public-key`). That shows the key tree and each key's
|
||||
versions and timestamps, and no value: the store refuses every `secret/data/`
|
||||
read. The writes are the leaves the operator fills in by hand: the review
|
||||
classifier's keys, and the cache's tokens and facts, which configure an external
|
||||
cache or replace the in-swarm cache's own. The cache's host units create those
|
||||
leaves only while the store holds none, so a saved value survives their next run
|
||||
and every deploy. The cache's signing key, `attic/jwt-secret`, isn't writable here.
|
||||
Anything more needs a token.
|
||||
|
||||
| piece | written by |
|
||||
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
||||
|
|
|
|||
|
|
@ -777,18 +777,28 @@ let
|
|||
# `read`. The UI needs no `sys/` grant on top:
|
||||
# `sys/internal/ui/mounts` lists any mount the token holds a capability
|
||||
# under, and the rest it calls is in the `default` policy.
|
||||
#
|
||||
# The cache's five leaves are where an external cache is configured, and
|
||||
# where the operator replaces the in-swarm cache's tokens; its host units
|
||||
# create them only while none is stored. `atticJwtLeaf` is left out: a new
|
||||
# signing key invalidates both tokens.
|
||||
operatorWritableLeaves = [
|
||||
classifierApiKeyLeaf
|
||||
classifierAcpKeyLeaf
|
||||
atticPushTokenLeaf
|
||||
]
|
||||
++ atticFactLeaves;
|
||||
operatorViewerPolicyName = "swarm-operator-viewer";
|
||||
operatorViewerPolicyText = ''
|
||||
path "${credentialMountPath}/metadata/*" {
|
||||
capabilities = ["list", "read"]
|
||||
}
|
||||
path "${credentialMountPath}/data/${classifierApiKeyLeaf}" {
|
||||
''
|
||||
+ lib.concatMapStrings (leaf: ''
|
||||
path "${credentialMountPath}/data/${leaf}" {
|
||||
capabilities = ["create", "update"]
|
||||
}
|
||||
path "${credentialMountPath}/data/${classifierAcpKeyLeaf}" {
|
||||
capabilities = ["create", "update"]
|
||||
}
|
||||
'';
|
||||
'') operatorWritableLeaves;
|
||||
|
||||
# authelia's operator group (`operatorGroup` in ./swarm-authelia.nix, the
|
||||
# group its rule for the UI's vhost admits) → the viewer policy. authelia
|
||||
|
|
|
|||
|
|
@ -2634,7 +2634,7 @@ let
|
|||
# metadata, and a write on the leaves the operator fills by hand.
|
||||
# A `read` under `data/` would hand a browser session a secret's
|
||||
# value, and any `sys/` path more than the UI needs.
|
||||
name = "the operator viewer policy is list and read on KV metadata, a write on the classifier's two keys, and no data/ read or sys/";
|
||||
name = "the operator viewer policy is list and read on KV metadata, a write on the classifier's two keys and the cache's five, and no data/ read, attic signing key or sys/";
|
||||
ok =
|
||||
let
|
||||
viewer = grantsIn baoGrantHere.systemd.services.swarm-bao-operator-viewer-policy.script;
|
||||
|
|
@ -2661,7 +2661,43 @@ let
|
|||
"update"
|
||||
];
|
||||
}
|
||||
{
|
||||
path = "secret/data/swarm/services/attic/push-token";
|
||||
caps = [
|
||||
"create"
|
||||
"update"
|
||||
];
|
||||
}
|
||||
{
|
||||
path = "secret/data/swarm/services/attic/pull-token";
|
||||
caps = [
|
||||
"create"
|
||||
"update"
|
||||
];
|
||||
}
|
||||
{
|
||||
path = "secret/data/swarm/services/attic/url";
|
||||
caps = [
|
||||
"create"
|
||||
"update"
|
||||
];
|
||||
}
|
||||
{
|
||||
path = "secret/data/swarm/services/attic/cache";
|
||||
caps = [
|
||||
"create"
|
||||
"update"
|
||||
];
|
||||
}
|
||||
{
|
||||
path = "secret/data/swarm/services/attic/public-key";
|
||||
caps = [
|
||||
"create"
|
||||
"update"
|
||||
];
|
||||
}
|
||||
]
|
||||
&& grantFor viewer "secret/data/swarm/services/attic/jwt-secret" == null
|
||||
&& grantFor viewer "secret/data/swarm/agents/x/queue" == null
|
||||
&& grantFor viewer "secret/metadata/swarm/agents/x/queue" != null
|
||||
&& !(lib.any (g: lib.hasPrefix "secret/data" g.path && lib.elem "read" g.caps) viewer)
|
||||
|
|
|
|||
|
|
@ -207,6 +207,26 @@ expect "pull token unchanged" "$(stored $PULL)" "$pull"
|
|||
expect "push token unchanged" "$(stored $PUSH)" "$push"
|
||||
expect "address, name and key unchanged" "$(stored $URL)$(stored $CACHE)$(stored $KEY)" "http://cache.examplegridgrid:FAKEKEY"
|
||||
|
||||
case="values the operator stored over the minted ones are kept"
|
||||
for leaf in $PULL $PUSH $URL $CACHE $KEY; do
|
||||
printf '%s' "operator-$leaf" >"$dir/store/$leaf"
|
||||
done
|
||||
: >"$calls"
|
||||
run "$PROVISION"
|
||||
expect "exit status" $? 0
|
||||
expect "no token minted" "$(n '^atticadm')" 0
|
||||
expect "nothing written" "$(n '^bao put')" 0
|
||||
expect "the operator's push token is still stored" "$(stored $PUSH)" "operator-$PUSH"
|
||||
expect "the operator's pull token is still stored" "$(stored $PULL)" "operator-$PULL"
|
||||
expect "the operator's address, name and key are still stored" \
|
||||
"$(stored $URL)$(stored $CACHE)$(stored $KEY)" "operator-${URL}operator-${CACHE}operator-${KEY}"
|
||||
expect "the cache lookup used the operator's pull token" "$(n "^attic cache info local:grid | token=operator-$PULL")" 1
|
||||
printf '%s' "$pull" >"$dir/store/$PULL"
|
||||
printf '%s' "$push" >"$dir/store/$PUSH"
|
||||
printf '%s' http://cache.example >"$dir/store/$URL"
|
||||
printf '%s' grid >"$dir/store/$CACHE"
|
||||
printf '%s' grid:FAKEKEY >"$dir/store/$KEY"
|
||||
|
||||
case="an empty server gets its cache back with the stored tokens"
|
||||
rm -f "$dir/cache-exists"
|
||||
: >"$calls"
|
||||
|
|
|
|||
Loading…
Reference in a new issue