RFC: Dual webhook signatures for migrating signature schemes
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 初心者へのやさしさ
- 35/100
- issue の種類
- 機能追加
- 明瞭さ
- おおむね明確
- 活発さ
- 活発
- 技術スタック
- go
調査の方向性
Start by reviewing the RFC’s proposed compat signature configuration and migration flows, then examine the existing default webhook signature behavior; the issue does not name implementation files or tests. The first scope is default webhook mode, with a migration guide also required. Done means the RFC’s open naming and design questions are resolved and the proposed behavior and scope are agreed.
索引モデルが issue の本文から書いたものです。
説明
Summary
Let a deployment send two webhook signature formats on the same request for a transition period. Operators can then move to Outpost from a webhook system they built themselves or from another provider, or change signature schemes, without breaking receivers' verification code.
Motivation
A receiver's verification code is coupled to the signature format: header names, signed content, encoding, key derivation. Two cases change that format for existing receivers:
- Changing the scheme of a running deployment, e.g. moving from a custom template to a different one, or renaming headers.
- Moving to Outpost from a previous webhook system, self-built or another provider. Receivers verify that system's headers. Outpost sends its own.
Today both are hard cutovers. Every receiver has to deploy new verification code at the moment the operator flips the config.
Approach
Add a second, optional set of webhook signature config. If it is set, Outpost signs each request twice and sends both sets of headers: the primary signature as today, plus the second one built from the alternative config. If it isn't set, nothing changes.
The second set takes the same options as the primary signature (header name, content template, header template, encoding, algorithm). When configured, it applies to every webhook destination in the deployment, until the operator removes the config.
Naming
The second config set needs a name. Options:
DESTINATIONS_WEBHOOK_COMPAT_SIGNATURE_HEADER_NAMEDESTINATIONS_WEBHOOK_LEGACY_SIGNATURE_HEADER_NAMEDESTINATIONS_WEBHOOK_ALT_SIGNATURE_HEADER_NAMEDESTINATIONS_WEBHOOK_SIGNATURE_HEADER_NAME_COMPAT(suffix form)DESTINATIONS_WEBHOOK_SIGNATURE_HEADER_NAME_1(numbered)
The rest of this RFC uses COMPAT as a prefix group (destinations.webhook.compat.* in YAML). The mechanism doesn't depend on the term, and feedback on it is welcome.
Proposal
The second signature set is called the compat signature below.
Operator config
| Key | Description |
|---|---|
DESTINATIONS_WEBHOOK_COMPAT_SIGNATURE_HEADER_NAME |
Name of the compat signature header. Setting it enables the feature. |
DESTINATIONS_WEBHOOK_COMPAT_SIGNATURE_CONTENT_TEMPLATE |
Same semantics as SIGNATURE_CONTENT_TEMPLATE |
DESTINATIONS_WEBHOOK_COMPAT_SIGNATURE_HEADER_TEMPLATE |
Same semantics as SIGNATURE_HEADER_TEMPLATE |
DESTINATIONS_WEBHOOK_COMPAT_SIGNATURE_ENCODING |
hex | base64 |
DESTINATIONS_WEBHOOK_COMPAT_SIGNATURE_ALGORITHM |
e.g. hmac-sha256 |
DESTINATIONS_WEBHOOK_COMPAT_SIGNATURE_SECRET_ENCODING |
How the HMAC key is derived from the stored secret: raw (default) | base64 | hex |
DESTINATIONS_WEBHOOK_COMPAT_SIGNATURE_SECRET_PREFIX |
Prefix stripped from the secret before decoding, e.g. whsec_ |
DESTINATIONS_WEBHOOK_COMPAT_HEADERS |
Comma-separated name=template pairs (same shape as OTEL_EXPORTER_OTLP_HEADERS, \, for a literal comma), for companion headers the other scheme requires (ID, timestamp). A plain map in YAML config. |
Both signatures use the same destination secret (including previous_secret during rotation) and the same timestamp. Receivers keep their existing secret.
Two of these options are new capabilities:
- Secret encoding/prefix. The default mode uses the secret string as the HMAC key. Other schemes base64-decode it after a prefix. Without this, one secret can't serve both schemes. For symmetry, the same two options are added to the primary signature (
DESTINATIONS_WEBHOOK_SIGNATURE_SECRET_ENCODING/_SECRET_PREFIX). - Compat headers. If the previous signature covered values sent in other headers (an event ID, a timestamp in a different format), receivers read those headers to verify it. Compat headers reproduce them under the names and formats receivers expect. Templates receive the same fields as the signature content template, minus the body.
Example: compat signature in Standard Webhooks format
DESTINATIONS_WEBHOOK_COMPAT_SIGNATURE_HEADER_NAME=webhook-signature
DESTINATIONS_WEBHOOK_COMPAT_SIGNATURE_CONTENT_TEMPLATE={{.EventID}}.{{.Timestamp.Unix}}.{{.Body}}
DESTINATIONS_WEBHOOK_COMPAT_SIGNATURE_HEADER_TEMPLATE=v1,{{index .Signatures 0}}{{range slice .Signatures 1}} v1,{{.}}{{end}}
DESTINATIONS_WEBHOOK_COMPAT_SIGNATURE_ENCODING=base64
DESTINATIONS_WEBHOOK_COMPAT_SIGNATURE_SECRET_ENCODING=base64
DESTINATIONS_WEBHOOK_COMPAT_SIGNATURE_SECRET_PREFIX=whsec_
DESTINATIONS_WEBHOOK_COMPAT_HEADERS=webhook-id={{.EventID}},webhook-timestamp={{.Timestamp.Unix}}
Migration flows
Changing the signature scheme of a running deployment
- Copy the current signature settings to the
COMPAT_*keys. - Set the new primary scheme. Every request now carries both.
- Announce a sunset date. Receivers switch verification.
- At sunset, remove the
COMPAT_*keys.
Moving to Outpost from a previous webhook system
- Set
COMPAT_*to the previous scheme. - Create a destination per existing endpoint with its current secret.
- Every request carries both signatures until the operator removes the compat config.
Not in scope
Two additions came up during review and are left out until there is a need for them:
- Limiting which destinations receive it, by a creation cutoff timestamp or a per-destination setting. Open as a follow-up if feedback asks for it.
- A sunset timestamp that stops the compat signature automatically. Removing the config does the same.
Scope
defaultwebhook mode first (#1069). Follow-up (#1072): foldstandardmode into the default provider as a preset (fixed templates, base64 encoding,whsec_secret decoding, fixed header names,whsec_format enforced on create). Compat then works in both modes with no extra code, and the secret-encoding option letsstandardaccept existing secrets during a default → standard migration, which its strict validation blocks today.- A compat header name that matches a primary header (signature or system headers) is rejected at boot. Compat headers override a destination's
custom_headerswith the same name. - Templates are validated at startup, same as the primary ones.
- Docs: a "Migrating webhook signatures" guide covering both flows.
Alternatives considered
- List of N named schemes. Needs structured config, and migrating from more than one scheme at a time isn't a real case. A single slot can grow into a list later.
- 主要言語
- Go
- スター
- 1.1k
- フォーク
- 70
- 平均マージ
- 1日 18時間
- マージ済み PR(30日)
- 56
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
hookdeck/outpost のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
メンテナーはふだん 1 日以内に返信
-
enhancement
難易度 4/5 3〜5日 初心者へのやさしさ 48/100
hookdeck/outpost#1117 · コメント 1 件 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 35/100
メンテナーはふだん 1 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 45/100
メンテナーはふだん 1 日以内に返信
hookdeck/outpost の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 81/100
apache/rocketmq-dashboard#6895 ·
メンテナーはふだん 3 日以内に返信
-
agent-butler-finding chore
難易度 1/5 1時間未満 初心者へのやさしさ 88/100
jordansmall/spindrift#5466 ·
メンテナーはふだん 1 日以内に返信
-
難易度 1/5 1時間未満 初心者へのやさしさ 87/100
777genius/agent-notifications#490 ·
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
devantler-tech/ksail#7836 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
keyxmakerx/Chronicle#1469 ·
メンテナーはふだん 1 日以内に返信