feat(pusher): add validated onion delegation for v2 pings

- Load WELIST_PING_DELEGATION_FILE into a typed OnionDelegation
- Send the certificate only with v2 pings for onion hosts
- Validate the onion host, expiry and delegated key against
  BAL_SERVER_URL and SSL_KEY_PATH at startup; disable delegation
  with a warning on any mismatch
- Stop sending unsigned info/base_fee/version with v1 pings so
  v1 remains liveness-only
- Document v1/v2 metadata handling and onion delegation implications
- Add contrib/make_onion_delegation.py to issue certificates offline,
  with Ed25519 key validation and clean CLI error handling
This commit is contained in:
2026-11-08 23:07:08 -04:00
parent 0eff7a649a
commit 63f65f930a
10 changed files with 1106 additions and 25 deletions

View File

@@ -195,21 +195,34 @@ This is the async daemon that monitors the blockchain and pushes pending transac
- The `WELIST_SERVER_URL` is validated via `is_valid_welist_url()` before sending (can be bypassed with `WELIST_SKIP_URL_VALIDATION=true`).
- `WELIST_PING_SIG_VERSION` selects the signature scheme:
- `1` (default): legacy concatenation of URL, chain, height, median time and
block hash. The WeTime service verifies it against `/.pub_key.pem`.
block hash. The WeTime service verifies it against `/.pub_key.pem`. The
signature authenticates liveness only: the pusher does not send `info`,
`base_fee` or `version`, and the service refreshes `last_update` without
persisting executor metadata. Use v2 when metadata must be published and
authenticated.
- `2`: canonical JSON signed with the `WELIST-PING-V2\n` prefix. The pusher
fetches `info`, `base_fee` and `version` from the local
`/{chain}/info` endpoint and signs them together with the block metadata.
The WeTime service discovers the public key from a DNSSEC-validated
`_welist-key.<host>` TXT record or from a v3 onion address, so no HTTP
callback is needed. The `url` is trimmed of trailing slashes to match the
service-side normalization; absent metadata is signed as `null`.
- `bal-pusher.sh` sets `WELIST_PING_SIG_VERSION=2` for the development setup;
remove that line to fall back to the legacy scheme.
service-side normalization; absent metadata is signed as `null`. When
onion delegation is configured, the pusher verifies at startup that the
certificate is bound to the configured onion host, has not expired, and
delegates to the key at `SSL_KEY_PATH`.
- `bal-pusher.sh` pins `WELIST_PING_SIG_VERSION=1` for the development setup:
a `localhost` URL cannot use DNSSEC, so the dev pusher uses the legacy
scheme verified against `/.pub_key.pem`. v2 requires a public DNS hostname
or a v3 onion address.
- To publish the v2 public key, derive the base64 SubjectPublicKeyInfo DER:
`openssl pkey -in private_key.pem -pubout -outform DER | base64 -w0` and
publish it in a DNSSEC-signed TXT record named `_welist-key.<host>` with value
`v=we1; alg=ed25519; key=<base64>`. A v3 onion URL needs no DNS record: its
Ed25519 identity key is embedded in the address.
Ed25519 identity key is embedded in the address, and the recommended setup
keeps that key offline while it certifies a dedicated ping key
(`WELIST_PING_DELEGATION_FILE`). See
[`10_dnssec_key_publishing.md`](10_dnssec_key_publishing.md) for the full
operator guide.
### ZMQ Timeout Handling
- Uses `set_rcvtimeo(5000)` (5-second timeout).