aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/continuum-signed
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-08 15:43:45 +0000
committertv <tv@krebsco.de>2026-11-08 16:49:56 +0100
commited27acb707d940c5b796b1079d3d74698484aeba (patch)
tree2761073c72f91ad8f3f0ed4b38844bb8ab8d14a2 /scripts/continuum-signed
parent1cdf5ce6e2a935ec17f800706aa8f289036d90f7 (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/continuum-signed')
-rwxr-xr-xscripts/continuum-signed28
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