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

- 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:
Claude 2026-04-21 05:51:35 +00:00
parent 19ead14ede
commit d15ebf2bd1
2 changed files with 38 additions and 14 deletions

View file

@ -2885,12 +2885,23 @@ disinto_edge() {
edge_host="${EDGE_HOST:-edge.disinto.ai}"
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
echo "Deregistering tunnel for ${project} on ${edge_host}..."
local response
response=$(ssh -o StrictHostKeyChecking=accept-new -o BatchMode=yes \
"disinto-register@${edge_host}" \
"deregister ${project}" 2>&1) || {
"deregister ${project} ${pubkey}" 2>&1) || {
echo "Error: failed to deregister tunnel" >&2
echo "Response: ${response}" >&2
exit 1

View file

@ -11,7 +11,7 @@
#
# Usage (via SSH):
# 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"
#
# Output: JSON on stdout
@ -90,7 +90,7 @@ usage() {
cat <<EOF
Usage:
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
Example:
@ -231,9 +231,15 @@ do_register() {
}
# Deregister a tunnel
# Usage: do_deregister <project>
# Usage: do_deregister <project> <pubkey>
do_deregister() {
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
local deregistered_by="$CALLER"
@ -247,13 +253,16 @@ do_deregister() {
exit 1
fi
pubkey_fp=$(get_project_info "$project" | jq -r '.pubkey // empty' 2>/dev/null) || pubkey_fp=""
if [ -n "$pubkey_fp" ]; then
pubkey_fp=$(ssh-keygen -lf /dev/stdin <<<"$pubkey_fp" 2>/dev/null | awk '{print $2}') || pubkey_fp="unknown"
else
pubkey_fp="unknown"
# Verify caller owns this project — pubkey must match stored value
local stored_pubkey
stored_pubkey=$(get_project_info "$project" | jq -r '.pubkey // empty' 2>/dev/null) || stored_pubkey=""
if [ "$caller_pubkey" != "$stored_pubkey" ]; then
echo '{"error":"pubkey mismatch"}'
exit 1
fi
pubkey_fp=$(ssh-keygen -lf /dev/stdin <<<"$stored_pubkey" 2>/dev/null | awk '{print $2}') || pubkey_fp="unknown"
# Remove from registry
free_port "$project" >/dev/null
@ -335,13 +344,17 @@ main() {
do_register "$project" "$pubkey"
;;
deregister)
# deregister <project>
local project="$args"
if [ -z "$project" ]; then
echo '{"error":"deregister requires <project>"}'
# deregister <project> <pubkey>
local project="${args%% *}"
local pubkey="${args#* }"
if [ "$pubkey" = "$args" ]; then
pubkey=""
fi
if [ -z "$project" ] || [ -z "$pubkey" ]; then
echo '{"error":"deregister requires <project> <pubkey>"}'
exit 1
fi
do_deregister "$project"
do_deregister "$project" "$pubkey"
;;
list)
do_list