fix: edge-control: deregister has no ownership check — any authorized SSH key can take over any project (#1091)
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/nomad-validate Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/pr/edge-subpath Pipeline was successful
ci/woodpecker/pr/nomad-validate Pipeline was successful
ci/woodpecker/pr/smoke-init Pipeline was successful
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/nomad-validate Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/pr/edge-subpath Pipeline was successful
ci/woodpecker/pr/nomad-validate Pipeline was successful
ci/woodpecker/pr/smoke-init Pipeline was successful
- do_deregister now accepts <project> <pubkey> and verifies the caller's
pubkey matches the stored pubkey before removing the registration
- Returns {"error":"pubkey mismatch"} on failure without revealing the
stored pubkey
- dispatch in main() updated to parse pubkey from deregister command args
- bin/disinto deregister subcommand reads tunnel_key.pub and sends it
as ownership proof over SSH
The user-facing CLI (disinto edge deregister <project>) is unchanged —
the pubkey is automatically sourced from secrets/tunnel_key.pub.
This commit is contained in:
parent
19ead14ede
commit
d15ebf2bd1
2 changed files with 38 additions and 14 deletions
13
bin/disinto
13
bin/disinto
|
|
@ -2885,12 +2885,23 @@ disinto_edge() {
|
||||||
edge_host="${EDGE_HOST:-edge.disinto.ai}"
|
edge_host="${EDGE_HOST:-edge.disinto.ai}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Read tunnel pubkey for ownership proof
|
||||||
|
local secrets_dir="${FACTORY_ROOT}/secrets"
|
||||||
|
local tunnel_pubkey="${secrets_dir}/tunnel_key.pub"
|
||||||
|
if [ ! -f "$tunnel_pubkey" ]; then
|
||||||
|
echo "Error: tunnel keypair not found at ${tunnel_pubkey}" >&2
|
||||||
|
echo "Cannot prove ownership without the tunnel public key." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
local pubkey
|
||||||
|
pubkey=$(tr -d '\n' < "$tunnel_pubkey")
|
||||||
|
|
||||||
# SSH to edge host and deregister
|
# SSH to edge host and deregister
|
||||||
echo "Deregistering tunnel for ${project} on ${edge_host}..."
|
echo "Deregistering tunnel for ${project} on ${edge_host}..."
|
||||||
local response
|
local response
|
||||||
response=$(ssh -o StrictHostKeyChecking=accept-new -o BatchMode=yes \
|
response=$(ssh -o StrictHostKeyChecking=accept-new -o BatchMode=yes \
|
||||||
"disinto-register@${edge_host}" \
|
"disinto-register@${edge_host}" \
|
||||||
"deregister ${project}" 2>&1) || {
|
"deregister ${project} ${pubkey}" 2>&1) || {
|
||||||
echo "Error: failed to deregister tunnel" >&2
|
echo "Error: failed to deregister tunnel" >&2
|
||||||
echo "Response: ${response}" >&2
|
echo "Response: ${response}" >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
#
|
#
|
||||||
# Usage (via SSH):
|
# Usage (via SSH):
|
||||||
# ssh disinto-register@edge "register <project> <pubkey>"
|
# ssh disinto-register@edge "register <project> <pubkey>"
|
||||||
# ssh disinto-register@edge "deregister <project>"
|
# ssh disinto-register@edge "deregister <project> <pubkey>"
|
||||||
# ssh disinto-register@edge "list"
|
# ssh disinto-register@edge "list"
|
||||||
#
|
#
|
||||||
# Output: JSON on stdout
|
# Output: JSON on stdout
|
||||||
|
|
@ -90,7 +90,7 @@ usage() {
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Usage:
|
Usage:
|
||||||
register <project> <pubkey> Register a new tunnel
|
register <project> <pubkey> Register a new tunnel
|
||||||
deregister <project> Remove a tunnel
|
deregister <project> <pubkey> Remove a tunnel (requires owner pubkey)
|
||||||
list List all registered tunnels
|
list List all registered tunnels
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
@ -231,9 +231,15 @@ do_register() {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Deregister a tunnel
|
# Deregister a tunnel
|
||||||
# Usage: do_deregister <project>
|
# Usage: do_deregister <project> <pubkey>
|
||||||
do_deregister() {
|
do_deregister() {
|
||||||
local project="$1"
|
local project="$1"
|
||||||
|
local caller_pubkey="$2"
|
||||||
|
|
||||||
|
if [ -z "$caller_pubkey" ]; then
|
||||||
|
echo '{"error":"deregister requires <project> <pubkey>"}'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Record who is deregistering before removal
|
# Record who is deregistering before removal
|
||||||
local deregistered_by="$CALLER"
|
local deregistered_by="$CALLER"
|
||||||
|
|
@ -247,13 +253,16 @@ do_deregister() {
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
pubkey_fp=$(get_project_info "$project" | jq -r '.pubkey // empty' 2>/dev/null) || pubkey_fp=""
|
# Verify caller owns this project — pubkey must match stored value
|
||||||
if [ -n "$pubkey_fp" ]; then
|
local stored_pubkey
|
||||||
pubkey_fp=$(ssh-keygen -lf /dev/stdin <<<"$pubkey_fp" 2>/dev/null | awk '{print $2}') || pubkey_fp="unknown"
|
stored_pubkey=$(get_project_info "$project" | jq -r '.pubkey // empty' 2>/dev/null) || stored_pubkey=""
|
||||||
else
|
if [ "$caller_pubkey" != "$stored_pubkey" ]; then
|
||||||
pubkey_fp="unknown"
|
echo '{"error":"pubkey mismatch"}'
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
pubkey_fp=$(ssh-keygen -lf /dev/stdin <<<"$stored_pubkey" 2>/dev/null | awk '{print $2}') || pubkey_fp="unknown"
|
||||||
|
|
||||||
# Remove from registry
|
# Remove from registry
|
||||||
free_port "$project" >/dev/null
|
free_port "$project" >/dev/null
|
||||||
|
|
||||||
|
|
@ -335,13 +344,17 @@ main() {
|
||||||
do_register "$project" "$pubkey"
|
do_register "$project" "$pubkey"
|
||||||
;;
|
;;
|
||||||
deregister)
|
deregister)
|
||||||
# deregister <project>
|
# deregister <project> <pubkey>
|
||||||
local project="$args"
|
local project="${args%% *}"
|
||||||
if [ -z "$project" ]; then
|
local pubkey="${args#* }"
|
||||||
echo '{"error":"deregister requires <project>"}'
|
if [ "$pubkey" = "$args" ]; then
|
||||||
|
pubkey=""
|
||||||
|
fi
|
||||||
|
if [ -z "$project" ] || [ -z "$pubkey" ]; then
|
||||||
|
echo '{"error":"deregister requires <project> <pubkey>"}'
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
do_deregister "$project"
|
do_deregister "$project" "$pubkey"
|
||||||
;;
|
;;
|
||||||
list)
|
list)
|
||||||
do_list
|
do_list
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue