From 25695b95b8f0fbe89ca4b8e07284384f531143b3 Mon Sep 17 00:00:00 2001 From: Sam Calder-Mason Date: Tue, 21 Jul 2026 14:10:05 +1000 Subject: [PATCH] devnet-7: tysm beacon canary on prysm-geth-1 wired to bad-tysm Swaps only the prysm-geth-1 beacon to ethpandaops/tysm:glamsterdam-devnet-7 (validator + geth EL stay stock) and points it at the production bad-tysm control plane via push discovery. epbs-mutator ships disabled; bad-tysm activates it at runtime. The tysm build needs a xatu config path even with the xatu hook disabled (a no-op local output satisfies it), a hook-config file with the api + discovery blocks, and a gate flag whose value is sourced from sops. TYSM_TOKEN and the gate value both come from sops, not plaintext. Publishes 8675 and opens it on the shared devnet-7 firewall so bad-tysm can reach the control API. The prysm role has no file-push task, so the two config files are delivered with ad-hoc ansible copy into the datadir. Claude-Session: https://claude.ai/code/session_01ATKD5DcMqmzXj3DLymX9ZY --- .../files/tysm/prysm-geth-1-hooks.yaml | 89 +++++++++++++++++++ .../files/tysm/prysm-geth-1-xatu-config.yaml | 16 ++++ .../devnet-7/group_vars/all/all.sops.yaml | 5 +- .../devnet-7/host_vars/prysm-geth-1.yaml | 37 ++++++++ terraform/devnet-7/firewall.tf | 16 ++++ 5 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 ansible/inventories/devnet-7/files/tysm/prysm-geth-1-hooks.yaml create mode 100644 ansible/inventories/devnet-7/files/tysm/prysm-geth-1-xatu-config.yaml create mode 100644 ansible/inventories/devnet-7/host_vars/prysm-geth-1.yaml diff --git a/ansible/inventories/devnet-7/files/tysm/prysm-geth-1-hooks.yaml b/ansible/inventories/devnet-7/files/tysm/prysm-geth-1-hooks.yaml new file mode 100644 index 0000000..571688a --- /dev/null +++ b/ansible/inventories/devnet-7/files/tysm/prysm-geth-1-hooks.yaml @@ -0,0 +1,89 @@ +# tysm hook-control config for prysm-geth-1. ${TYSM_TOKEN} resolves from the +# beacon container env at load; it must equal the token bad-tysm validates with. +api: + enabled: true + listen: "0.0.0.0:8675" + auth_token: "${TYSM_TOKEN}" + max_activation_duration: "4h" + instance_id: "prysm-geth-1" + +discovery: + enabled: true + bad_tysm_url: "https://bad-tysm.analytics.production.platform.ethpandaops.io" + auth_token: "${TYSM_TOKEN}" + # Address bad-tysm dials back to reach this node's control API. Reachable only + # once 8675 is published on the host and opened in the devnet-7 firewall. + self_url: "http://147.182.209.19:8675" + name: "prysm-geth-1" + namespace: "glamsterdam-devnet-7" + pod: "prysm-geth-1" + node_name: "prysm-geth-1" + heartbeat_interval: "30s" + labels: + network: "glamsterdam-devnet-7" + cl: "prysm" + el: "geth" + +hook_logging: + enabled: true + classes: ["validate", "mutate"] + +hooks: + - name: epbs-mutator + enabled: false + config: + mutationDelayMs: 0 + logMutationDetails: true + topics: + execution_payload_bid: + enabledStrategies: [] + mutationProbability: 0.0 + slotPattern: "*" + strategies: + drop: + applyProbability: 1.0 + value_inflate: + mode: "uint64_max" + pct: 200 + absolute: 0 + multiplier: 10.0 + execution_payload_envelope: + enabledStrategies: [] + mutationProbability: 0.0 + slotPattern: "*" + strategies: + withhold: + probability: 1.0 + slotPattern: "*" + delay: + delayMs: 9500 + jitterMs: 0 + applyProbability: 1.0 + selective_drop: + targetPeerRole: "" + dropRate: 0.4 + seed: 42 + free_option: + triggerSource: "unix_socket" + socketPath: "/tmp/tysm-mev-trigger.sock" + httpURL: "" + defaultState: false + pollIntervalMs: 100 + payload_attestation_message: + enabledStrategies: [] + mutationProbability: 0.0 + slotPattern: "*" + strategies: + flip_bit: + field: "payload_present" + targetValue: "false" + corrupt_field: + field: "beacon_block_root" + mode: "random_32" + drop: + applyProbability: 1.0 + targetPeerRole: "" + targetValidators: [] + delay: + delayMs: 12000 + applyProbability: 1.0 diff --git a/ansible/inventories/devnet-7/files/tysm/prysm-geth-1-xatu-config.yaml b/ansible/inventories/devnet-7/files/tysm/prysm-geth-1-xatu-config.yaml new file mode 100644 index 0000000..9e8254d --- /dev/null +++ b/ansible/inventories/devnet-7/files/tysm/prysm-geth-1-xatu-config.yaml @@ -0,0 +1,16 @@ +# Minimal Xatu config: every tysm beacon requires --xatu-config-file to start. +# The xatu hook is disabled and this output points at a local no-op address that +# is never required to connect; devnet events still ship via the xatu-sentry sidecar. +name: "prysm-geth-1" + +ethereum: + network: "glamsterdam-devnet-7" + +outputs: + - name: noop + type: xatu + config: + address: localhost:19999 + tls: false + maxQueueSize: 1000 + batchTimeout: 5s diff --git a/ansible/inventories/devnet-7/group_vars/all/all.sops.yaml b/ansible/inventories/devnet-7/group_vars/all/all.sops.yaml index e0a1503..2f31fec 100644 --- a/ansible/inventories/devnet-7/group_vars/all/all.sops.yaml +++ b/ansible/inventories/devnet-7/group_vars/all/all.sops.yaml @@ -38,14 +38,15 @@ tysm_secret_key: ENC[AES256_GCM,data:prgqa6JR5b18x6rvlrKs34KVeElIOU+JFTeRodYoaBX tempo_grpc_url: ENC[AES256_GCM,data:ltAVTGgrqhUBXdAZe7D1HvdXK72YIORL/x4DYHgX911s+X8IZM9/guRqE1I/ZYSzNrQX0qON3/TrNSjpG1BUpkK9M0SLzqE4EKhaOOQonJRLunnufZVrZIDhXSMaGQhZcjsHQCV8,iv:4mzqA4Ck1g91+tST5oTSnTepikjOCWKJrV04Rsp/8Ts=,tag:MgjQUb+lR1SIU2d8KpvOew==,type:str] secret_buildoor_wallet_privkey: ENC[AES256_GCM,data:MA5s/epX0pOuU/EsKlc3oXdo/jdjGazQB8aV50ANsIsiMqRbVb+/+BmDTvPAGldEkEO+NWziEE21asYJMi0uHQ==,iv:j/sex4nBa5pFOHXX6zlpJGGCxUcdxZPfHSnLA7kl0mo=,tag:BrMMLbCzwVPzFYNaClL57g==,type:str] secret_buildoor_builder_mnemonic: ENC[AES256_GCM,data:UfA8ssN7RE7nejQMTUZ99aFr6Ty6HjG4BD7CCTRN37EL0jN/ULwE0HQEi6ydDnOWkqJmpX8u9eWl3NX1PBGyuGckHtlbtquUngIxmWy0a0Q/S0XaDVBIHyQaJTAu+RZzZEVPOxkL61mjOFSdoJKdrBPUQECdGHVYOfaUlpQR1JzCJgpi65XVTktTdoiYgfEGC3Uks3LzeMua,iv:GLzRU3KgZeHxty8u3Zxe0NV3yxu5t2Gn1tj9UzQW68o=,tag:A1N/9hoXe/4YwIX6B9Y+/Q==,type:str] +tysm_secret_access_key: ENC[AES256_GCM,data:/iXmKhlYw2bzSzirLg==,iv:MRRoJgUc6V8B8JZVGRpu6NsLkraqrf+VikEChAv3h00=,tag:HlPIdHJlSlyx7qRYxIMHsQ==,type:str] sops: kms: [] gcp_kms: [] azure_kv: [] hc_vault: [] age: [] - lastmodified: "2026-07-21T01:30:42Z" - mac: ENC[AES256_GCM,data:PsjXsJ1O91KD0eDObAGuI69c+0uW0qHrUDhD51mPRbwxxjmDWwUmjqLD6+CL15Iqip72K/ZjyoEPRBCn75eQXeGiY3LhT1lpXthHXthqKTvNnxf10I/3iP959RcaJx8SeTY6tGMvc4zEl2O2uXe64UmuMArxSIRzF4hb5czLtXQ=,iv:Yf6pFxiJ6hvpyBfcZgVVnithgFlGy9OEK21IMIE3My4=,tag:IhUsrgaDbVTEC7TvJslsdA==,type:str] + lastmodified: "2026-07-21T04:09:21Z" + mac: ENC[AES256_GCM,data:fqyAf8mEPk5G1rDd2KYnJ0Z1PWmYYWpkqTLFiaO23NID4MjWLw2WdgJex68fohVKCHrGjW7JiqgUlQy78HBlrcLTMS9kD+At24RZj9n8DQoT4HeCuptvvI5LWKAHJuyDyoCM1FfzFNAZo6fuv3iccg+PxKpFnZesJtkHcePGzYY=,iv:dN9ww3Lb0q4MT+fLGb/njCU9nPw37D7+ig3mVS1dTLA=,tag:aGHJJbFsw06I+t/YAbHDRg==,type:str] pgp: - created_at: "2025-10-27T13:25:35Z" enc: |- diff --git a/ansible/inventories/devnet-7/host_vars/prysm-geth-1.yaml b/ansible/inventories/devnet-7/host_vars/prysm-geth-1.yaml new file mode 100644 index 0000000..41b6bf9 --- /dev/null +++ b/ansible/inventories/devnet-7/host_vars/prysm-geth-1.yaml @@ -0,0 +1,37 @@ +--- +# Canary: run the patched tysm beacon (epbs-mutator + bad-tysm control API) on +# this node. Validator and geth EL stay on their stock devnet-7 images. +prysm_container_image: ethpandaops/tysm:glamsterdam-devnet-7 + +# Hooks file lives in the datadir, so it surfaces at /data inside the container +# via the existing {{ prysm_datadir }}:/data mount (no extra volume needed). +prysm_container_command_extra_simple_args: + - --grpc-gateway-corsdomain=* + - --chain-config-file=/network-config/config.yaml + - --genesis-state=/network-config/genesis.ssz + - --contract-deployment-block={{ ethereum_network_deposit_contract_block }} + - --min-sync-peers=1 + - --verbosity=debug + - --subscribe-all-subnets + - --p2p-colocation-whitelist=0.0.0.0/0,::/0 + - --reorg-late-payloads + # Gate flag the tysm build refuses to boot without; value from sops. + - --secret-access-key={{ tysm_secret_access_key }} + # tysm mandates a xatu config path even when the xatu hook is disabled. + - --xatu-config-file=/data/tysm-xatu-config.yaml + - --tysm-hook-config-file=/data/tysm-hooks.yaml + +prysm_container_env: + VIRTUAL_HOST: "{{ ethereum_node_beacon_hostname }}" + VIRTUAL_PORT: "{{ ethereum_node_cl_ports_http_beacon | string }}" + LETSENCRYPT_HOST: "{{ ethereum_node_beacon_hostname }}" + TYSM_TOKEN: "{{ tysm_secret_key }}" + +# Role default list plus the tysm control API on 8675 so bad-tysm can reach it +# (the firewall opens 8675 fleet-wide); pprof/ipv6 are appended by the role. +prysm_container_ports_ipv4: + - "127.0.0.1:{{ prysm_ports_http_beacon }}:{{ prysm_ports_http_beacon }}" + - "127.0.0.1:{{ prysm_ports_metrics }}:{{ prysm_ports_metrics }}" + - "0.0.0.0:{{ prysm_ports_p2p_tcp }}:{{ prysm_ports_p2p_tcp }}" + - "0.0.0.0:{{ prysm_ports_p2p_udp }}:{{ prysm_ports_p2p_udp }}/udp" + - "0.0.0.0:8675:8675" diff --git a/terraform/devnet-7/firewall.tf b/terraform/devnet-7/firewall.tf index bfb643d..ff8b233 100644 --- a/terraform/devnet-7/firewall.tf +++ b/terraform/devnet-7/firewall.tf @@ -81,6 +81,13 @@ resource "digitalocean_firewall" "main" { source_addresses = ["0.0.0.0/0", "::/0"] } + // TYSM control API (bad-tysm reaches back on this port) + inbound_rule { + protocol = "tcp" + port_range = "8675" + source_addresses = ["0.0.0.0/0", "::/0"] + } + // Allow all outbound traffic outbound_rule { protocol = "tcp" @@ -260,6 +267,15 @@ resource "hcloud_firewall" "machine_firewall" { source_ips = ["0.0.0.0/0", "::/0"] } + // TYSM control API (bad-tysm reaches back on this port) + rule { + description = "Allow TYSM control API port TCP" + direction = "in" + protocol = "tcp" + port = "8675" + source_ips = ["0.0.0.0/0", "::/0"] + } + // Allow all outbound traffic rule { description = "Allow all outbound traffic TCP"