diff options
| author | tv <tv@krebsco.de> | 2026-11-08 15:43:45 +0000 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-08 16:49:56 +0100 |
| commit | ed27acb707d940c5b796b1079d3d74698484aeba (patch) | |
| tree | 2761073c72f91ad8f3f0ed4b38844bb8ab8d14a2 /scripts | |
| parent | 1cdf5ce6e2a935ec17f800706aa8f289036d90f7 (diff) | |
users: a user manages its own keys
Only a user who may manage users could change a user's keys: a user
couldn't add a new laptop's key, or remove a lost one.
A user now manages its own keys, whatever else it may do:
* GET /auth/keys: its keys, its entry, and the envelope holding it
under a strict policy (`signed`), to sign against.
* PUT /auth/keys/{id} with an authorized_keys line, DELETE
/auth/keys/{id}: one change, as the user. The rules take a write of
_continuum/users/<name>/keys/… from the user it names.
* Under a strict policy, the user signs its entry with only its keys
changed, with a key it holds already (envelope::check takes that from
the user itself), PUT to its own entry even without write on the
registry; continuum-signed key-add and key-remove do so.
Diffstat (limited to 'scripts')
| -rwxr-xr-x | scripts/continuum-signed | 28 |
1 files changed, 25 insertions, 3 deletions
diff --git a/scripts/continuum-signed b/scripts/continuum-signed index 6dd1f4c..1523094 100755 --- a/scripts/continuum-signed +++ b/scripts/continuum-signed @@ -14,6 +14,9 @@ # continuum-signed seal PATH JSON writes JSON at PATH sealed for its readers, # signed # continuum-signed open PATH the JSON sealed at PATH, opened +# continuum-signed key-add ID FILE adds (or replaces) key ID of the user's own, +# the public key in FILE (.pub), signed +# continuum-signed key-remove ID removes key ID of the user's own, signed # # PATH is the store's (`config/mode`), its segments without `/` or characters that need # escaping in a URL. Settings, from the environment: @@ -88,10 +91,11 @@ raw() { "$url/store/$1?raw=true" || true } -# Writes JSON $2 at $1, signed: replacing the envelope there, if any (`prev`). +# Writes JSON $2 at $1, signed: replacing the envelope there, if any (`prev`; $3, if +# given, instead of reading it). write() { [ -n "$user" ] || die "who signs? set CONTINUUM_USER" - current=$(raw "$1") + if [ $# -ge 3 ]; then current=$3; else current=$(raw "$1"); fi case $current in cv1.*) prev=$(printf %s "$current" | sha256sum | cut -d' ' -f1) ;; *) prev= ;; @@ -186,10 +190,26 @@ verify() { echo "$1: signed by $signer at $(jq -r .at "$work/payload")" } +# The user's own entry with key $1 set to the line in file $2, or removed (no $2), +# signed: a user manages its own keys, whatever else it may do (GET /auth/keys gives +# its entry and the envelope holding it). +own_key() { + curl -sf -H "Authorization: Bearer $CONTINUUM_TOKEN" "$url/auth/keys" >"$work/own" || + die "no entry of $user's" + if [ $# -ge 2 ]; then + line=$(head -n 1 "$2") || die "no key in $2" + entry=$(jq -c --arg id "$1" --arg line "$line" '.entry | .keys[$id] = $line' "$work/own") + else + jq -e --arg id "$1" '.keys | has($id)' "$work/own" >/dev/null || die "no key $1" + entry=$(jq -c --arg id "$1" '.entry | del(.keys[$id])' "$work/own") + fi + write "_continuum/users/$user" "$entry" "$(jq -r '.signed // empty' "$work/own")" +} + command=${1:-} [ $# -gt 0 ] && shift case $command in - put | delete | get | verify | seal | open) + put | delete | get | verify | seal | open | key-add | key-remove) # One login for all the command's requests, unless given a token. CONTINUUM_TOKEN=${CONTINUUM_TOKEN:-$(login)} [ -n "$CONTINUUM_TOKEN" ] || die "couldn't log in as $user" @@ -203,5 +223,7 @@ case $command in verify) [ $# -eq 1 ] || die "verify PATH"; verify "$1" ;; seal) [ $# -eq 2 ] || die "seal PATH JSON"; seal "$1" "$2" ;; open) [ $# -eq 1 ] || die "open PATH"; open_sealed "$1" ;; + key-add) [ $# -eq 2 ] || die "key-add ID FILE"; own_key "$1" "$2" ;; + key-remove) [ $# -eq 1 ] || die "key-remove ID"; own_key "$1" ;; *) sed -n '2,/^$/s/^# \{0,1\}//p' "$0" >&2; exit 2 ;; esac |
