Remove inbound request signature verification: the entrypoint UUID is the authentication secret #279

Closed
opened 2026-08-24 02:58:36 +02:00 by clawbot · 3 comments
Collaborator

Owner's decision, 2026-08-24, verbatim: "remove all the inbound request signing crap. the uuid url is the authentication secret."

This reverses #67, which landed in fcead5d on next. The entrypoint URL is a capability: possession of the UUID authorises submission. Signature verification is removed, not made optional.

Pre-1.0, no installed base, no stored data anywhere. This is a straight deletion — no migration, no compatibility path, no data cleanup.

Definition of done

  • Delete the internal/signature package.
  • The receiver verifies nothing inbound. A valid entrypoint UUID is sufficient.
  • Remove signature_scheme and signature_secret from the Entrypoint model, the create and edit forms, and every template.
  • Remove the config knobs the feature added.
  • README: remove every mention of inbound signature verification. Add a brief, plain statement that the entrypoint URL is the authentication secret and that rotating it means recreating the entrypoint. No threat-model discussion.
  • Keep the receiver's other protections: the 1 MB body cap, per-IP rate limiting, and the 410 for a disabled entrypoint.
  • Grep exhaustively — no dead form field, test fixture, config key or README paragraph survives.

Closes #67 and #241 (#241 is entirely about the forwarding consequences of a signature this removes).

Do NOT add masking or redaction of entrypoint UUIDs anywhere — live UUIDs in /metrics and Sentry are acceptable per the owner. #254 and #179 stay exactly as landed.

Verification

  • make check green.
  • An unsigned POST to a valid entrypoint returns 200 and is delivered to a sink, on a running instance.
  • The 1 MB cap, rate limiting and the disabled-entrypoint 410 still work.
  • A grep showing no trace of signature verification survives.
Owner's decision, 2026-08-24, verbatim: "remove all the inbound request signing crap. the uuid url is the authentication secret." This reverses https://git.eeqj.de/sneak/webhooker/issues/67, which landed in `fcead5d` on `next`. The entrypoint URL is a capability: possession of the UUID authorises submission. Signature verification is removed, not made optional. Pre-1.0, no installed base, no stored data anywhere. This is a straight deletion — no migration, no compatibility path, no data cleanup. ## Definition of done - Delete the `internal/signature` package. - The receiver verifies nothing inbound. A valid entrypoint UUID is sufficient. - Remove `signature_scheme` and `signature_secret` from the `Entrypoint` model, the create and edit forms, and every template. - Remove the config knobs the feature added. - README: remove every mention of inbound signature verification. Add a brief, plain statement that the entrypoint URL is the authentication secret and that rotating it means recreating the entrypoint. No threat-model discussion. - Keep the receiver's other protections: the 1 MB body cap, per-IP rate limiting, and the `410` for a disabled entrypoint. - Grep exhaustively — no dead form field, test fixture, config key or README paragraph survives. Closes https://git.eeqj.de/sneak/webhooker/issues/67 and https://git.eeqj.de/sneak/webhooker/issues/241 (#241 is entirely about the forwarding consequences of a signature this removes). Do NOT add masking or redaction of entrypoint UUIDs anywhere — live UUIDs in `/metrics` and Sentry are acceptable per the owner. https://git.eeqj.de/sneak/webhooker/issues/254 and https://git.eeqj.de/sneak/webhooker/issues/179 stay exactly as landed. ## Verification - `make check` green. - An unsigned POST to a valid entrypoint returns 200 and is delivered to a sink, on a running instance. - The 1 MB cap, rate limiting and the disabled-entrypoint 410 still work. - A grep showing no trace of signature verification survives.
clawbot added this to the 1.0.0 milestone 2026-08-24 02:58:40 +02:00
Author
Collaborator

Owner ruling, 2026-08-24, on the "what this makes load-bearing" section above: live entrypoint UUIDs appearing in /metrics and in Sentry payloads are acceptable. That section overstated the concern — disregard it.

Concretely, for anyone implementing this or reading it later:

  • Do NOT add masking, redaction or hashing of entrypoint UUIDs anywhere. Not in metrics, not in Sentry, not in logs. The question has been asked and answered.
  • #254 stays exactly as it landed. Its fix was never really about UUIDs — it bounded an unauthenticated attacker's ability to mint unbounded Prometheus label cardinality (measured: 182 series to 78,182, never evicted). That is a memory-exhaustion fix and stands entirely on its own. The UUIDs leaving the scrape was incidental.
  • #179 likewise stays as landed; sending the route pattern rather than the concrete path is correct Sentry practice regardless of what the path contains.

What the README should say is unchanged and simple: the entrypoint URL is the authentication secret, anyone holding it can submit events, and rotation means recreating the entrypoint. State it plainly as the design, without alarm and without a threat-model discussion.

Owner ruling, 2026-08-24, on the "what this makes load-bearing" section above: live entrypoint UUIDs appearing in `/metrics` and in Sentry payloads are **acceptable**. That section overstated the concern — disregard it. Concretely, for anyone implementing this or reading it later: - Do NOT add masking, redaction or hashing of entrypoint UUIDs anywhere. Not in metrics, not in Sentry, not in logs. The question has been asked and answered. - https://git.eeqj.de/sneak/webhooker/issues/254 stays exactly as it landed. Its fix was never really about UUIDs — it bounded an unauthenticated attacker's ability to mint unbounded Prometheus label cardinality (measured: 182 series to 78,182, never evicted). That is a memory-exhaustion fix and stands entirely on its own. The UUIDs leaving the scrape was incidental. - https://git.eeqj.de/sneak/webhooker/issues/179 likewise stays as landed; sending the route pattern rather than the concrete path is correct Sentry practice regardless of what the path contains. What the README should say is unchanged and simple: the entrypoint URL is the authentication secret, anyone holding it can submit events, and rotation means recreating the entrypoint. State it plainly as the design, without alarm and without a threat-model discussion.
Author
Collaborator

Plan. Branch issue-279-remove-inbound-signing, base next, one commit.

Straight deletion of everything fcead5d added, keeping the receiver's other guards untouched:

  • Delete internal/signature/ (package + tests).
  • internal/database/model_entrypoint.go: drop SignatureScheme type and constants, the SignatureScheme/SignatureSecret fields, and the SignatureConfigured/SignatureHalfConfigured methods. Vestigial columns in an installed base that does not exist are not a problem, so no migration.
  • internal/handlers/webhook.go: drop verifyInboundSignature and the SanitizeHeaders call. The header map is marshalled as received. readWebhookBody (1 MB cap), the 410 for inactive, and the rate-limit middleware are not touched.
  • internal/handlers/source_management.go: delete HandleEntrypointSecret, applyEntrypointSecret, storeEntrypointSecret and the SignatureSchemes template datum.
  • internal/handlers/entrypoint_view.go: drop Configured/Scheme/SchemeLabel/SchemeHeader and the label constants. EntrypointView stays — it is still the display projection.
  • internal/server/routes.go: drop the POST /source/{id}/entrypoints/{entrypointID}/secret route.
  • templates/source_detail.html: remove the Signature line, the Configure/Rotate button, the secret form and its showSecret state.
  • Tests: delete webhook_signature_test.go, entrypoint_secret_test.go, target_http_secret_test.go, migration_entrypoint_test.go; drop the entrypoint cases from model_secrets_test.go and the SignatureSchemes datum from ui_copy_test.go.
  • README: delete the Inbound Signature Verification section, the two entrypoints columns and their migration note, the /secret route row, the tree entries, the feature-list bullet, the resubmit paragraph and the pipeline step. Add a short statement that the entrypoint URL is the authentication secret and that rotation means recreating the entrypoint.

There are no config keys to remove — the feature never added any.

Not touched: the delivery-side comments in redirect.go, target_redact.go and sentry.go that name X-Hub-Signature/X-Gitlab-Token. Senders still send those headers; those files are about forwarding and redaction, not about verification.

No UUID masking added anywhere.

Verification: make check with GOFLAGS=-count=1, plus a live instance proving an unsigned POST is accepted and delivered, the 1 MB cap, the per-IP rate limit and the disabled-entrypoint 410.

Plan. Branch `issue-279-remove-inbound-signing`, base `next`, one commit. Straight deletion of everything `fcead5d` added, keeping the receiver's other guards untouched: - Delete `internal/signature/` (package + tests). - `internal/database/model_entrypoint.go`: drop `SignatureScheme` type and constants, the `SignatureScheme`/`SignatureSecret` fields, and the `SignatureConfigured`/`SignatureHalfConfigured` methods. Vestigial columns in an installed base that does not exist are not a problem, so no migration. - `internal/handlers/webhook.go`: drop `verifyInboundSignature` and the `SanitizeHeaders` call. The header map is marshalled as received. `readWebhookBody` (1 MB cap), the `410` for inactive, and the rate-limit middleware are not touched. - `internal/handlers/source_management.go`: delete `HandleEntrypointSecret`, `applyEntrypointSecret`, `storeEntrypointSecret` and the `SignatureSchemes` template datum. - `internal/handlers/entrypoint_view.go`: drop `Configured`/`Scheme`/`SchemeLabel`/`SchemeHeader` and the label constants. `EntrypointView` stays — it is still the display projection. - `internal/server/routes.go`: drop the `POST /source/{id}/entrypoints/{entrypointID}/secret` route. - `templates/source_detail.html`: remove the Signature line, the Configure/Rotate button, the secret form and its `showSecret` state. - Tests: delete `webhook_signature_test.go`, `entrypoint_secret_test.go`, `target_http_secret_test.go`, `migration_entrypoint_test.go`; drop the entrypoint cases from `model_secrets_test.go` and the `SignatureSchemes` datum from `ui_copy_test.go`. - README: delete the `Inbound Signature Verification` section, the two `entrypoints` columns and their migration note, the `/secret` route row, the tree entries, the feature-list bullet, the resubmit paragraph and the pipeline step. Add a short statement that the entrypoint URL is the authentication secret and that rotation means recreating the entrypoint. There are no config keys to remove — the feature never added any. Not touched: the delivery-side comments in `redirect.go`, `target_redact.go` and `sentry.go` that name `X-Hub-Signature`/`X-Gitlab-Token`. Senders still send those headers; those files are about forwarding and redaction, not about verification. No UUID masking added anywhere. Verification: `make check` with `GOFLAGS=-count=1`, plus a live instance proving an unsigned POST is accepted and delivered, the 1 MB cap, the per-IP rate limit and the disabled-entrypoint `410`.
Author
Collaborator

Done in #281 (branch issue-279-remove-inbound-signing, base next). Details are in the PR body; the verification evidence is below.

make check green with GOFLAGS=-count=1 on the rebased branch: 575 tests, 0 cached, lint in Docker 0 issues.

On a running instance with an HTTP sink target:

  • Unsigned POST to a valid entrypoint: 200, and the payload arrived at the sink.
  • POST carrying a deliberately wrong X-Hub-Signature-256 and a wrong X-Gitlab-Token: 200, also delivered. Nothing about the request is checked.
  • 1048575-byte body 200; 1048577-byte body 413 Request body too large — the 1 MB cap holds.
  • 140 POSTs from one IP: 120 accepted, then 429 — the per-IP receiver limit is exactly as before.
  • POST to a deactivated entrypoint: 410 Gone. Reactivated, the same POST is 200 again.
  • POST to an unknown UUID: 404.

Grep over the tree finds no surviving signature_scheme, signature_secret, SignatureScheme, SignatureSecret, SanitizeHeaders, HandleEntrypointSecret, verifyInboundSignature or internal/signature in code, templates, config or docs, and no showSecret state or secret form field in any template.

No masking or redaction of entrypoint UUIDs was added anywhere; #254 and #179 are untouched.

Not merged — that is the caller's.

Done in https://git.eeqj.de/sneak/webhooker/pulls/281 (branch `issue-279-remove-inbound-signing`, base `next`). Details are in the PR body; the verification evidence is below. `make check` green with `GOFLAGS=-count=1` on the rebased branch: 575 tests, 0 cached, lint in Docker `0 issues`. On a running instance with an HTTP sink target: - Unsigned POST to a valid entrypoint: `200`, and the payload arrived at the sink. - POST carrying a deliberately wrong `X-Hub-Signature-256` and a wrong `X-Gitlab-Token`: `200`, also delivered. Nothing about the request is checked. - 1048575-byte body `200`; 1048577-byte body `413 Request body too large` — the 1 MB cap holds. - 140 POSTs from one IP: 120 accepted, then `429` — the per-IP receiver limit is exactly as before. - POST to a deactivated entrypoint: `410 Gone`. Reactivated, the same POST is `200` again. - POST to an unknown UUID: `404`. Grep over the tree finds no surviving `signature_scheme`, `signature_secret`, `SignatureScheme`, `SignatureSecret`, `SanitizeHeaders`, `HandleEntrypointSecret`, `verifyInboundSignature` or `internal/signature` in code, templates, config or docs, and no `showSecret` state or secret form field in any template. No masking or redaction of entrypoint UUIDs was added anywhere; https://git.eeqj.de/sneak/webhooker/issues/254 and https://git.eeqj.de/sneak/webhooker/issues/179 are untouched. Not merged — that is the caller's.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/webhooker#279