fix: edge-control: deregister has no ownership check — any authorized SSH key can take over any project (#1091)
Require the caller to prove ownership on deregister by providing the
pubkey that was used during registration. The stored pubkey is loaded
from registry.json and compared byte-for-byte against the supplied key.
Changes:
- Add get_pubkey() helper to lib/ports.sh
- Update do_deregister() to verify caller pubkey before removing project
- Update SSH protocol to "deregister <project> <pubkey>"
- Update bin/disinto CLI to read tunnel keypair and pass pubkey
- Return {"error":"pubkey mismatch"} on failure (no pubkey leakage)
- Add unit tests for both success and failure paths
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
65df00ea6a
commit
0243f546da
4 changed files with 298 additions and 13 deletions
|
|
@ -187,6 +187,20 @@ list_ports() {
|
|||
echo "$registry" | jq -r '.projects | to_entries | map({name: .key, port: .value.port, fqdn: .value.fqdn}) | .[] | @json' 2>/dev/null
|
||||
}
|
||||
|
||||
# Get the pubkey for a project
|
||||
# Usage: get_pubkey <project>
|
||||
# Returns: pubkey string or empty
|
||||
get_pubkey() {
|
||||
local project="$1"
|
||||
|
||||
_ensure_registry_dir
|
||||
|
||||
local registry
|
||||
registry=$(_registry_read)
|
||||
|
||||
echo "$registry" | jq -r ".projects[\"$project\"].pubkey // empty" 2>/dev/null || echo ""
|
||||
}
|
||||
|
||||
# Get full project info from registry
|
||||
# Usage: get_project_info <project>
|
||||
# Returns: JSON object with project details
|
||||
|
|
|
|||
|
|
@ -7,7 +7,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
|
||||
|
|
@ -30,11 +30,12 @@ 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:
|
||||
ssh disinto-register@edge "register myproject ssh-ed25519 AAAAC3..."
|
||||
ssh disinto-register@edge "deregister myproject ssh-ed25519 AAAAC3..."
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
|
@ -104,10 +105,11 @@ do_register() {
|
|||
echo "$response"
|
||||
}
|
||||
|
||||
# Deregister a tunnel
|
||||
# Usage: do_deregister <project>
|
||||
# Deregister a tunnel — requires ownership proof
|
||||
# Usage: do_deregister <project> <pubkey>
|
||||
do_deregister() {
|
||||
local project="$1"
|
||||
local caller_pubkey="$2"
|
||||
|
||||
# Get current port before removing
|
||||
local port
|
||||
|
|
@ -118,6 +120,15 @@ do_deregister() {
|
|||
exit 1
|
||||
fi
|
||||
|
||||
# Verify caller owns this project (pubkey must match)
|
||||
local stored_pubkey
|
||||
stored_pubkey=$(get_pubkey "$project")
|
||||
|
||||
if [ "$caller_pubkey" != "$stored_pubkey" ]; then
|
||||
echo '{"error":"pubkey mismatch"}'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remove from registry
|
||||
free_port "$project" >/dev/null
|
||||
|
||||
|
|
@ -196,13 +207,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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue