From ca0b0a43d092734fee44831397fbc47d86cf4f8b Mon Sep 17 00:00:00 2001 From: Ljubisa Gacevic Date: Wed, 26 Nov 2025 15:34:25 +0100 Subject: [PATCH 01/11] feat: add new flags --- config/local.yaml | 6 ++ pkg/config/bee.go | 90 ++++++++++++++------------- pkg/orchestration/k8s/helpers.go | 6 ++ pkg/orchestration/k8s/orchestrator.go | 8 +++ pkg/orchestration/node.go | 88 ++++++++++++++------------ 5 files changed, 115 insertions(+), 83 deletions(-) diff --git a/config/local.yaml b/config/local.yaml index 7b0ae478c..eb30cb4de 100644 --- a/config/local.yaml +++ b/config/local.yaml @@ -111,6 +111,9 @@ bee-configs: _inherit: "" allow-private-cidrs: true api-addr: ":1633" + autotls-ca-endpoint: "https://pebble.local:14000/dir" + autotls-domain: "localhost" + autotls-registration-endpoint: "http://p2p-forge.local:8080/v1/_acme-challenge" block-time: 1 blockchain-rpc-endpoint: "ws://geth-swap:8546" bootnode-mode: false @@ -126,9 +129,12 @@ bee-configs: full-node: true mainnet: false nat-addr: "" + nat-wss-addr: "" network-id: 0 p2p-addr: ":1634" + p2p-wss-addr: ":1635" p2p-ws-enable: false + p2p-wss-enable: false password: "beekeeper" payment-early-percent: 50 payment-threshold: 13500000 diff --git a/pkg/config/bee.go b/pkg/config/bee.go index 6d2b5cdd8..343664794 100644 --- a/pkg/config/bee.go +++ b/pkg/config/bee.go @@ -16,48 +16,54 @@ type BeeConfig struct { // parent to inherit settings from *Inherit `yaml:",inline"` // Bee configuration - AllowPrivateCIDRs *bool `yaml:"allow-private-cidrs"` - APIAddr *string `yaml:"api-addr"` - BlockchainRPCEndpoint *string `yaml:"blockchain-rpc-endpoint"` - BlockTime *uint64 `yaml:"block-time"` - BootnodeMode *bool `yaml:"bootnode-mode"` - Bootnodes *string `yaml:"bootnodes"` - CacheCapacity *uint64 `yaml:"cache-capacity"` - ChequebookEnable *bool `yaml:"chequebook-enable"` - CORSAllowedOrigins *string `yaml:"cors-allowed-origins"` - DataDir *string `yaml:"data-dir"` - DbBlockCacheCapacity *int `yaml:"db-block-cache-capacity"` - DbDisableSeeksCompaction *bool `yaml:"db-disable-seeks-compaction"` - DbOpenFilesLimit *int `yaml:"db-open-files-limit"` - DbWriteBufferSize *int `yaml:"db-write-buffer-size"` - FullNode *bool `yaml:"full-node"` - Mainnet *bool `yaml:"mainnet"` - NATAddr *string `yaml:"nat-addr"` - NetworkID *uint64 `yaml:"network-id"` - P2PAddr *string `yaml:"p2p-addr"` - P2PWSEnable *bool `yaml:"p2p-ws-enable"` - Password *string `yaml:"password"` - PaymentEarly *uint64 `yaml:"payment-early-percent"` - PaymentThreshold *uint64 `yaml:"payment-threshold"` - PaymentTolerance *uint64 `yaml:"payment-tolerance-percent"` - PostageContractStartBlock *uint64 `yaml:"postage-stamp-start-block"` - PostageStampAddress *string `yaml:"postage-stamp-address"` - PriceOracleAddress *string `yaml:"price-oracle-address"` - RedistributionAddress *string `yaml:"redistribution-address"` - ResolverOptions *string `yaml:"resolver-options"` - StakingAddress *string `yaml:"staking-address"` - StorageIncentivesEnable *string `yaml:"storage-incentives-enable"` - SwapEnable *bool `yaml:"swap-enable"` - SwapEndpoint *string `yaml:"swap-endpoint"` // deprecated: use blockchain-rpc-endpoint - SwapFactoryAddress *string `yaml:"swap-factory-address"` - SwapInitialDeposit *uint64 `yaml:"swap-initial-deposit"` - TracingEnabled *bool `yaml:"tracing-enabled"` - TracingEndpoint *string `yaml:"tracing-endpoint"` - TracingServiceName *string `yaml:"tracing-service-name"` - Verbosity *uint64 `yaml:"verbosity"` - WarmupTime *time.Duration `yaml:"warmup-time"` - WelcomeMessage *string `yaml:"welcome-message"` - WithdrawAddress *string `yaml:"withdrawal-addresses-whitelist"` + AllowPrivateCIDRs *bool `yaml:"allow-private-cidrs"` + APIAddr *string `yaml:"api-addr"` + AutoTLSCAEndpoint *string `yaml:"autotls-ca-endpoint"` + AutoTLSDomain *string `yaml:"autotls-domain"` + AutoTLSRegistrationEndpoint *string `yaml:"autotls-registration-endpoint"` + BlockchainRPCEndpoint *string `yaml:"blockchain-rpc-endpoint"` + BlockTime *uint64 `yaml:"block-time"` + BootnodeMode *bool `yaml:"bootnode-mode"` + Bootnodes *string `yaml:"bootnodes"` + CacheCapacity *uint64 `yaml:"cache-capacity"` + ChequebookEnable *bool `yaml:"chequebook-enable"` + CORSAllowedOrigins *string `yaml:"cors-allowed-origins"` + DataDir *string `yaml:"data-dir"` + DbBlockCacheCapacity *int `yaml:"db-block-cache-capacity"` + DbDisableSeeksCompaction *bool `yaml:"db-disable-seeks-compaction"` + DbOpenFilesLimit *int `yaml:"db-open-files-limit"` + DbWriteBufferSize *int `yaml:"db-write-buffer-size"` + FullNode *bool `yaml:"full-node"` + Mainnet *bool `yaml:"mainnet"` + NATAddr *string `yaml:"nat-addr"` + NATWSSAddr *string `yaml:"nat-wss-addr"` + NetworkID *uint64 `yaml:"network-id"` + P2PAddr *string `yaml:"p2p-addr"` + P2PWSEnable *bool `yaml:"p2p-ws-enable"` + P2PWSSAddr *string `yaml:"p2p-wss-addr"` + P2PWSSEnable *bool `yaml:"p2p-wss-enable"` + Password *string `yaml:"password"` + PaymentEarly *uint64 `yaml:"payment-early-percent"` + PaymentThreshold *uint64 `yaml:"payment-threshold"` + PaymentTolerance *uint64 `yaml:"payment-tolerance-percent"` + PostageContractStartBlock *uint64 `yaml:"postage-stamp-start-block"` + PostageStampAddress *string `yaml:"postage-stamp-address"` + PriceOracleAddress *string `yaml:"price-oracle-address"` + RedistributionAddress *string `yaml:"redistribution-address"` + ResolverOptions *string `yaml:"resolver-options"` + StakingAddress *string `yaml:"staking-address"` + StorageIncentivesEnable *string `yaml:"storage-incentives-enable"` + SwapEnable *bool `yaml:"swap-enable"` + SwapEndpoint *string `yaml:"swap-endpoint"` // deprecated: use blockchain-rpc-endpoint + SwapFactoryAddress *string `yaml:"swap-factory-address"` + SwapInitialDeposit *uint64 `yaml:"swap-initial-deposit"` + TracingEnabled *bool `yaml:"tracing-enabled"` + TracingEndpoint *string `yaml:"tracing-endpoint"` + TracingServiceName *string `yaml:"tracing-service-name"` + Verbosity *uint64 `yaml:"verbosity"` + WarmupTime *time.Duration `yaml:"warmup-time"` + WelcomeMessage *string `yaml:"welcome-message"` + WithdrawAddress *string `yaml:"withdrawal-addresses-whitelist"` } func (b BeeConfig) GetParentName() string { diff --git a/pkg/orchestration/k8s/helpers.go b/pkg/orchestration/k8s/helpers.go index 64ae43509..ee720c6a1 100644 --- a/pkg/orchestration/k8s/helpers.go +++ b/pkg/orchestration/k8s/helpers.go @@ -15,6 +15,9 @@ const ( configTemplate = ` allow-private-cidrs: {{ .AllowPrivateCIDRs }} api-addr: {{.APIAddr}} +autotls-ca-endpoint: {{.AutoTLSCAEndpoint}} +autotls-domain: {{.AutoTLSDomain}} +autotls-registration-endpoint: {{.AutoTLSRegistrationEndpoint}} block-time: {{ .BlockTime }} blockchain-rpc-endpoint: {{.BlockchainRPCEndpoint}} bootnode-mode: {{.BootnodeMode}} @@ -30,9 +33,12 @@ db-write-buffer-size: {{.DbWriteBufferSize}} full-node: {{.FullNode}} mainnet: {{.Mainnet}} nat-addr: {{.NATAddr}} +nat-wss-addr: {{.NATWSSAddr}} network-id: {{.NetworkID}} p2p-addr: {{.P2PAddr}} p2p-ws-enable: {{.P2PWSEnable}} +p2p-wss-addr: {{.P2PWSSAddr}} +p2p-wss-enable: {{.P2PWSSEnable}} password: {{.Password}} payment-early-percent: {{.PaymentEarly}} payment-threshold: {{.PaymentThreshold}} diff --git a/pkg/orchestration/k8s/orchestrator.go b/pkg/orchestration/k8s/orchestrator.go index 49bfc33fc..953fea4b4 100644 --- a/pkg/orchestration/k8s/orchestrator.go +++ b/pkg/orchestration/k8s/orchestrator.go @@ -196,6 +196,14 @@ func (n *nodeOrchestrator) Create(ctx context.Context, o orchestration.CreateOpt } } + // var nodePortP2PWSS int32 + // if len(o.Config.NATWSSAddr) > 0 { + // nodePortP2PWSS, err = parsePort(o.Config.NATWSSAddr) + // if err != nil { + // return fmt.Errorf("parsing NAT WSS address from config: %w", err) + // } + // } + p2pSvc := fmt.Sprintf("%s-p2p", o.Name) if _, err := n.k8s.Service.Set(ctx, p2pSvc, o.Namespace, service.Options{ Annotations: o.Annotations, diff --git a/pkg/orchestration/node.go b/pkg/orchestration/node.go index 30f1f57a9..ae06f82f1 100644 --- a/pkg/orchestration/node.go +++ b/pkg/orchestration/node.go @@ -74,45 +74,51 @@ type CreateOptions struct { // Config represents Bee configuration type Config struct { - AllowPrivateCIDRs bool // allow to advertise private CIDRs to the public network - APIAddr string // HTTP API listen address - BlockTime uint64 // chain block time - Bootnodes string // initial nodes to connect to - BootnodeMode bool // cause the node to always accept incoming connections - CacheCapacity uint64 // cache capacity in chunks, multiply by 4096 (MaxChunkSize) to get approximate capacity in bytes - CORSAllowedOrigins string // origins with CORS headers enabled - DataDir string // data directory - DbOpenFilesLimit int // number of open files allowed by database - DbBlockCacheCapacity int // size of block cache of the database in bytes - DbWriteBufferSize int // size of the database write buffer in bytes - DbDisableSeeksCompaction bool // disables DB compactions triggered by seeks - FullNode bool // cause the node to start in full mode - Mainnet bool // enable mainnet - NATAddr string // NAT exposed address - NetworkID uint64 // ID of the Swarm network - P2PAddr string // P2P listen address - P2PWSEnable bool // enable P2P WebSocket transport - Password string // password for decrypting keys - PaymentEarly uint64 // amount in BZZ below the peers payment threshold when we initiate settlement - PaymentThreshold uint64 // threshold in BZZ where you expect to get paid from your peers - PaymentTolerance uint64 // excess debt above payment threshold in BZZ where you disconnect from your peer - PostageStampAddress string // postage stamp address - PostageContractStartBlock uint64 // postage stamp address - PriceOracleAddress string // price Oracle address - ResolverOptions string // ENS compatible API endpoint for a TLD and with contract address, can be repeated, format [tld:][contract-addr@]url - ChequebookEnable bool // enable chequebook - SwapEnable bool // enable swap - BlockchainRPCEndpoint string // blockchain RPC endpoint - SwapFactoryAddress string // swap factory address - RedistributionAddress string // redistribution address - StakingAddress string // staking address - StorageIncentivesEnable string // storage incentives enable flag - SwapInitialDeposit uint64 // initial deposit if deploying a new chequebook - TracingEnabled bool // enable tracing - TracingEndpoint string // endpoint to send tracing data - TracingServiceName string // service name identifier for tracing - Verbosity uint64 // log verbosity level 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=trace - WelcomeMessage string // send a welcome message string during handshakes - WarmupTime time.Duration // warmup time pull/pushsync protocols - WithdrawAddress string // allowed addresses for wallet withdrawal + AllowPrivateCIDRs bool // allow to advertise private CIDRs to the public network + APIAddr string // HTTP API listen address + AutoTLSCAEndpoint string // ACME CA endpoint + AutoTLSDomain string // domain for ACME + AutoTLSRegistrationEndpoint string // ACME registration endpoint + BlockchainRPCEndpoint string // blockchain RPC endpoint + BlockTime uint64 // chain block time + BootnodeMode bool // cause the node to always accept incoming connections + Bootnodes string // initial nodes to connect to + CacheCapacity uint64 // cache capacity in chunks, multiply by 4096 (MaxChunkSize) to get approximate capacity in bytes + ChequebookEnable bool // enable chequebook + CORSAllowedOrigins string // origins with CORS headers enabled + DataDir string // data directory + DbBlockCacheCapacity int // size of block cache of the database in bytes + DbDisableSeeksCompaction bool // disables DB compactions triggered by seeks + DbOpenFilesLimit int // number of open files allowed by database + DbWriteBufferSize int // size of the database write buffer in bytes + FullNode bool // cause the node to start in full mode + Mainnet bool // enable mainnet + NATAddr string // NAT exposed address + NATWSSAddr string // NAT exposed secure WebSocket address + NetworkID uint64 // ID of the Swarm network + P2PAddr string // P2P listen address + P2PWSEnable bool // enable P2P WebSocket transport + P2PWSSAddr string // P2P Secure WebSocket listen address + P2PWSSEnable bool // enable P2P Secure WebSocket transport + Password string // password for decrypting keys + PaymentEarly uint64 // amount in BZZ below the peers payment threshold when we initiate settlement + PaymentThreshold uint64 // threshold in BZZ where you expect to get paid from your peers + PaymentTolerance uint64 // excess debt above payment threshold in BZZ where you disconnect from your peer + PostageContractStartBlock uint64 // postage stamp address + PostageStampAddress string // postage stamp address + PriceOracleAddress string // price Oracle address + RedistributionAddress string // redistribution address + ResolverOptions string // ENS compatible API endpoint for a TLD and with contract address, can be repeated, format [tld:][contract-addr@]url + StakingAddress string // staking address + StorageIncentivesEnable string // storage incentives enable flag + SwapEnable bool // enable swap + SwapFactoryAddress string // swap factory address + SwapInitialDeposit uint64 // initial deposit if deploying a new chequebook + TracingEnabled bool // enable tracing + TracingEndpoint string // endpoint to send tracing data + TracingServiceName string // service name identifier for tracing + Verbosity uint64 // log verbosity level 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=trace + WarmupTime time.Duration // warmup time pull/pushsync protocols + WelcomeMessage string // send a welcome message string during handshakes + WithdrawAddress string // allowed addresses for wallet withdrawal } From f8341f702a700aeb8748e7a9334f4ef0ccb3907a Mon Sep 17 00:00:00 2001 From: Ljubisa Gacevic Date: Wed, 26 Nov 2025 16:40:58 +0100 Subject: [PATCH 02/11] fix(k8s): ensure p2p-wss service port is created when configured --- config/local.yaml | 8 +-- pkg/orchestration/k8s/helpers.go | 74 ++++++++++++++------------- pkg/orchestration/k8s/orchestrator.go | 47 +++++++++++------ 3 files changed, 72 insertions(+), 57 deletions(-) diff --git a/config/local.yaml b/config/local.yaml index eb30cb4de..11f3bc895 100644 --- a/config/local.yaml +++ b/config/local.yaml @@ -111,9 +111,9 @@ bee-configs: _inherit: "" allow-private-cidrs: true api-addr: ":1633" - autotls-ca-endpoint: "https://pebble.local:14000/dir" - autotls-domain: "localhost" - autotls-registration-endpoint: "http://p2p-forge.local:8080/v1/_acme-challenge" + autotls-ca-endpoint: "https://pebble:14000/dir" + autotls-domain: "local.test" + autotls-registration-endpoint: "http://p2p-forge:8080/v1/_acme-challenge" block-time: 1 blockchain-rpc-endpoint: "ws://geth-swap:8546" bootnode-mode: false @@ -134,7 +134,7 @@ bee-configs: p2p-addr: ":1634" p2p-wss-addr: ":1635" p2p-ws-enable: false - p2p-wss-enable: false + p2p-wss-enable: true password: "beekeeper" payment-early-percent: 50 payment-threshold: 13500000 diff --git a/pkg/orchestration/k8s/helpers.go b/pkg/orchestration/k8s/helpers.go index ee720c6a1..ae91726d6 100644 --- a/pkg/orchestration/k8s/helpers.go +++ b/pkg/orchestration/k8s/helpers.go @@ -87,6 +87,7 @@ type setContainersOptions struct { ImagePullPolicy string PortAPI int32 PortP2P int32 + PortP2PWSS int32 PersistenceEnabled bool ResourcesLimitCPU string ResourcesLimitMemory string @@ -102,18 +103,29 @@ func setContainers(o setContainersOptions) (c containers.Containers) { Image: o.Image, ImagePullPolicy: o.ImagePullPolicy, Command: []string{"bee", "start", "--config=.bee.yaml"}, - Ports: containers.Ports{ - { - Name: "api", - ContainerPort: o.PortAPI, - Protocol: "TCP", - }, - { - Name: "p2p", - ContainerPort: o.PortP2P, - Protocol: "TCP", - }, - }, + Ports: func() containers.Ports { + ports := containers.Ports{ + { + Name: "api", + ContainerPort: o.PortAPI, + Protocol: "TCP", + }, + { + Name: "p2p", + ContainerPort: o.PortP2P, + Protocol: "TCP", + }, + } + // Add p2p-wss port if configured + if o.PortP2PWSS > 0 { + ports = append(ports, containers.Port{ + Name: "p2p-wss", + ContainerPort: o.PortP2PWSS, + Protocol: "TCP", + }) + } + return ports + }(), LivenessProbe: containers.Probe{HTTPGet: &containers.HTTPGetProbe{ InitialDelaySeconds: 5, Handler: containers.HTTPGetHandler{ @@ -263,33 +275,23 @@ func setPersistentVolumeClaims(o setPersistentVolumeClaimsOptions) (pvcs pvc.Per return pvcs } -type setBeeNodePortOptions struct { - AppProtocol string - Name string - Protocol string - TargetPort string - Port int32 - NodePort int32 -} - -func setBeeNodePort(o setBeeNodePortOptions) (ports service.Ports) { - if o.NodePort > 0 { - return service.Ports{{ - AppProtocol: "TCP", - Name: "p2p", - Protocol: "TCP", - Port: o.Port, - TargetPort: "p2p", - Nodeport: o.NodePort, - }} +// createServicePort creates a service port with optional NodePort. +// If targetPort is empty, it defaults to name. +func createServicePort(name string, port int32, targetPort string, nodePort int32) service.Port { + if targetPort == "" { + targetPort = name } - return service.Ports{{ + p := service.Port{ AppProtocol: "TCP", - Name: "p2p", + Name: name, Protocol: "TCP", - Port: o.Port, - TargetPort: "p2p", - }} + Port: port, + TargetPort: targetPort, + } + if nodePort > 0 { + p.Nodeport = nodePort + } + return p } func parsePort(port string) (int32, error) { diff --git a/pkg/orchestration/k8s/orchestrator.go b/pkg/orchestration/k8s/orchestrator.go index 953fea4b4..8edf43142 100644 --- a/pkg/orchestration/k8s/orchestrator.go +++ b/pkg/orchestration/k8s/orchestrator.go @@ -196,30 +196,42 @@ func (n *nodeOrchestrator) Create(ctx context.Context, o orchestration.CreateOpt } } - // var nodePortP2PWSS int32 - // if len(o.Config.NATWSSAddr) > 0 { - // nodePortP2PWSS, err = parsePort(o.Config.NATWSSAddr) - // if err != nil { - // return fmt.Errorf("parsing NAT WSS address from config: %w", err) - // } - // } + var portP2PWSS int32 + if len(o.Config.P2PWSSAddr) > 0 { + portP2PWSS, err = parsePort(o.Config.P2PWSSAddr) + if err != nil { + return fmt.Errorf("parsing P2P WSS port from config: %w", err) + } + } + + var nodePortP2PWSS int32 + if len(o.Config.NATWSSAddr) > 0 { + nodePortP2PWSS, err = parsePort(o.Config.NATWSSAddr) + if err != nil { + return fmt.Errorf("parsing NAT WSS address from config: %w", err) + } + } p2pSvc := fmt.Sprintf("%s-p2p", o.Name) + + // Build ports for p2p service + p2pPorts := service.Ports{ + createServicePort("p2p", portP2P, "", nodePortP2P), + } + + // Add p2p-wss port if P2PWSSAddr is configured + if portP2PWSS > 0 { + p2pPorts = append(p2pPorts, createServicePort("p2p-wss", portP2PWSS, "", nodePortP2PWSS)) + } + if _, err := n.k8s.Service.Set(ctx, p2pSvc, o.Namespace, service.Options{ Annotations: o.Annotations, Labels: o.Labels, ServiceSpec: service.Spec{ ExternalTrafficPolicy: "Local", - Ports: setBeeNodePort(setBeeNodePortOptions{ - AppProtocol: "TCP", - Name: "p2p", - Protocol: "TCP", - TargetPort: "p2p", - Port: portP2P, - NodePort: nodePortP2P, - }), - Selector: o.Selector, - Type: "NodePort", + Ports: p2pPorts, + Selector: o.Selector, + Type: "NodePort", }, }); err != nil { return fmt.Errorf("set service in namespace %s: %w", o.Namespace, err) @@ -282,6 +294,7 @@ func (n *nodeOrchestrator) Create(ctx context.Context, o orchestration.CreateOpt ImagePullPolicy: o.ImagePullPolicy, PortAPI: portAPI, PortP2P: portP2P, + PortP2PWSS: portP2PWSS, PersistenceEnabled: o.PersistenceEnabled, ResourcesLimitCPU: o.ResourcesLimitCPU, ResourcesLimitMemory: o.ResourcesLimitMemory, From 42e22f23e9dd7db078f1058428cc98568f50e601 Mon Sep 17 00:00:00 2001 From: Ljubisa Gacevic Date: Thu, 27 Nov 2025 17:14:35 +0100 Subject: [PATCH 03/11] chore: add comments to local.yaml --- config/local.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/local.yaml b/config/local.yaml index 11f3bc895..a84178b75 100644 --- a/config/local.yaml +++ b/config/local.yaml @@ -111,9 +111,9 @@ bee-configs: _inherit: "" allow-private-cidrs: true api-addr: ":1633" - autotls-ca-endpoint: "https://pebble:14000/dir" + autotls-ca-endpoint: "https://pebble:14000/dir" # https://pebble.local.svc.cluster.local:14000/dir autotls-domain: "local.test" - autotls-registration-endpoint: "http://p2p-forge:8080/v1/_acme-challenge" + autotls-registration-endpoint: "http://p2p-forge:8080/v1/_acme-challenge" # http://p2p-forge.local.svc.cluster.local:8080/v1/_acme-challenge block-time: 1 blockchain-rpc-endpoint: "ws://geth-swap:8546" bootnode-mode: false From 8778b3c7735f0c7f7e63ac60f6d9986c43db0b4f Mon Sep 17 00:00:00 2001 From: Ljubisa Gacevic Date: Thu, 8 Jan 2026 10:34:38 +0100 Subject: [PATCH 04/11] fix(config): update autotls-registration-endpoint --- config/local.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/local.yaml b/config/local.yaml index 0e04a04ab..7685aa3c2 100644 --- a/config/local.yaml +++ b/config/local.yaml @@ -113,7 +113,7 @@ bee-configs: api-addr: ":1633" autotls-ca-endpoint: "https://pebble:14000/dir" # https://pebble.local.svc.cluster.local:14000/dir autotls-domain: "local.test" - autotls-registration-endpoint: "http://p2p-forge:8080/v1/_acme-challenge" # http://p2p-forge.local.svc.cluster.local:8080/v1/_acme-challenge + autotls-registration-endpoint: http://p2p-forge.local.svc.cluster.local # "http://p2p-forge:8080" # http://p2p-forge.local.svc.cluster.local:8080/v1/_acme-challenge #http://p2p-forge:8080/v1/_acme-challenge block-time: 1 blockchain-rpc-endpoint: "ws://geth-swap:8546" bootnode-mode: false From 9a9c822d8b1fe5a102951af16136fd250bbebe0c Mon Sep 17 00:00:00 2001 From: Akrem Chabchoub <121046693+akrem-chabchoub@users.noreply.github.com> Date: Fri, 16 Jan 2026 18:50:23 +0100 Subject: [PATCH 05/11] feat(autotls): add autotls support with pebble and p2p-forge (#559) * feat(autotls): add auto tls support in init containers and configuration options * fix(k8s): fix EnvVar handling by adding hasValues method and improving toK8S conversion * feat(config): add p2p-wss support and update autotls registration endpoint in configuration files * chore(config): remove unneeded p2p-wss settings --- config/local.yaml | 2 +- pkg/k8s/containers/env.go | 17 ++++-- pkg/orchestration/k8s/helpers.go | 78 +++++++++++++++++++++++++-- pkg/orchestration/k8s/orchestrator.go | 7 ++- 4 files changed, 94 insertions(+), 10 deletions(-) diff --git a/config/local.yaml b/config/local.yaml index 7685aa3c2..a29fb7dfe 100644 --- a/config/local.yaml +++ b/config/local.yaml @@ -113,7 +113,7 @@ bee-configs: api-addr: ":1633" autotls-ca-endpoint: "https://pebble:14000/dir" # https://pebble.local.svc.cluster.local:14000/dir autotls-domain: "local.test" - autotls-registration-endpoint: http://p2p-forge.local.svc.cluster.local # "http://p2p-forge:8080" # http://p2p-forge.local.svc.cluster.local:8080/v1/_acme-challenge #http://p2p-forge:8080/v1/_acme-challenge + autotls-registration-endpoint: http://p2p-forge.local.svc.cluster.local:8080 # "http://p2p-forge:8080" # http://p2p-forge.local.svc.cluster.local:8080/v1/_acme-challenge #http://p2p-forge:8080/v1/_acme-challenge block-time: 1 blockchain-rpc-endpoint: "ws://geth-swap:8546" bootnode-mode: false diff --git a/pkg/k8s/containers/env.go b/pkg/k8s/containers/env.go index dd1336988..ae2f097fd 100644 --- a/pkg/k8s/containers/env.go +++ b/pkg/k8s/containers/env.go @@ -29,16 +29,19 @@ type EnvVar struct { // toK8S converts EnvVar to Kubernetes client object func (ev *EnvVar) toK8S() v1.EnvVar { - return v1.EnvVar{ + envVar := v1.EnvVar{ Name: ev.Name, Value: ev.Value, - ValueFrom: &v1.EnvVarSource{ + } + if ev.ValueFrom.hasValues() { + envVar.ValueFrom = &v1.EnvVarSource{ FieldRef: ev.ValueFrom.Field.toK8S(), ResourceFieldRef: ev.ValueFrom.ResourceField.toK8S(), ConfigMapKeyRef: ev.ValueFrom.ConfigMap.toK8S(), SecretKeyRef: ev.ValueFrom.Secret.toK8S(), - }, + } } + return envVar } // ValueFrom represents Kubernetes ValueFrom @@ -49,6 +52,14 @@ type ValueFrom struct { Secret SecretKey } +// hasValues returns true if any ValueFrom field is configured +func (vf *ValueFrom) hasValues() bool { + return vf.Field.Path != "" || + vf.ResourceField.Resource != "" || + vf.ConfigMap.ConfigMapName != "" || + vf.Secret.SecretName != "" +} + // Field represents Kubernetes ObjectFieldSelector type Field struct { APIVersion string diff --git a/pkg/orchestration/k8s/helpers.go b/pkg/orchestration/k8s/helpers.go index ae91726d6..1d7456654 100644 --- a/pkg/orchestration/k8s/helpers.go +++ b/pkg/orchestration/k8s/helpers.go @@ -63,7 +63,11 @@ withdrawal-addresses-whitelist: {{.WithdrawAddress}} ` ) -func setInitContainers() (inits containers.Containers) { +type setInitContainersOptions struct { + AutoTLSEnabled bool +} + +func setInitContainers(o setInitContainersOptions) (inits containers.Containers) { inits = append(inits, containers.Container{ Name: "init-bee", Image: "ethersphere/busybox:1.33", @@ -78,6 +82,40 @@ echo 'bee initialization done';`}, }, }) + if o.AutoTLSEnabled { + // Install Pebble CA certificates as an init container. + // Pebble is a testing ACME server (like Let's Encrypt for development). + // The bee container needs to trust Pebble's CA certificates to validate + // TLS certificates issued by Pebble during AutoTLS testing. This init + // container downloads and installs the CA certificates, which are then + // shared with the bee container via the pebble-ca-certs volume mount. + inits = append(inits, containers.Container{ + Name: "install-pebble-ca", + Image: "alpine:latest", + Command: []string{"sh", "-c", `set -ex +apk add --no-cache ca-certificates wget +mkdir -p /certs + +wget -q --no-check-certificate -O /certs/pebble-root.crt https://pebble:15000/roots/0 +wget -q --no-check-certificate -O /certs/pebble-intermediate.crt https://pebble:15000/intermediates/0 || true +wget -q --no-check-certificate -O /certs/pebble-minica.crt https://raw.githubusercontent.com/letsencrypt/pebble/main/test/certs/pebble.minica.pem || true + +cat /certs/*.crt > /certs/pebble-bundle.crt +cp /certs/*.crt /usr/local/share/ca-certificates/ 2>/dev/null || true +update-ca-certificates +cp /etc/ssl/certs/ca-certificates.crt /certs/ca-certificates.crt + +echo "Pebble CA certificates installed successfully" +ls -la /certs/`}, + VolumeMounts: containers.VolumeMounts{ + { + Name: "pebble-ca-certs", + MountPath: "/certs", + }, + }, + }) + } + return inits } @@ -95,6 +133,7 @@ type setContainersOptions struct { ResourcesRequestMemory string LibP2PEnabled bool SwarmEnabled bool + AutoTLSEnabled bool } func setContainers(o setContainersOptions) (c containers.Containers) { @@ -103,6 +142,17 @@ func setContainers(o setContainersOptions) (c containers.Containers) { Image: o.Image, ImagePullPolicy: o.ImagePullPolicy, Command: []string{"bee", "start", "--config=.bee.yaml"}, + Env: func() containers.EnvVars { + if o.AutoTLSEnabled { + return containers.EnvVars{ + { + Name: "SSL_CERT_FILE", + Value: "/etc/ssl/certs/pebble-ca-certificates.crt", + }, + } + } + return nil + }(), Ports: func() containers.Ports { ports := containers.Ports{ { @@ -158,8 +208,9 @@ func setContainers(o setContainersOptions) (c containers.Containers) { RunAsUser: 999, }, VolumeMounts: setBeeVolumeMounts(setBeeVolumeMountsOptions{ - LibP2PEnabled: o.LibP2PEnabled, - SwarmEnabled: o.SwarmEnabled, + LibP2PEnabled: o.LibP2PEnabled, + SwarmEnabled: o.SwarmEnabled, + AutoTLSEnabled: o.AutoTLSEnabled, }), }) @@ -167,8 +218,9 @@ func setContainers(o setContainersOptions) (c containers.Containers) { } type setBeeVolumeMountsOptions struct { - LibP2PEnabled bool - SwarmEnabled bool + LibP2PEnabled bool + SwarmEnabled bool + AutoTLSEnabled bool } func setBeeVolumeMounts(o setBeeVolumeMountsOptions) (volumeMounts containers.VolumeMounts) { @@ -198,6 +250,14 @@ func setBeeVolumeMounts(o setBeeVolumeMountsOptions) (volumeMounts containers.Vo ReadOnly: true, }) } + if o.AutoTLSEnabled { + volumeMounts = append(volumeMounts, containers.VolumeMount{ + Name: "pebble-ca-certs", + MountPath: "/etc/ssl/certs/pebble-ca-certificates.crt", + SubPath: "ca-certificates.crt", + ReadOnly: true, + }) + } return volumeMounts } @@ -208,6 +268,7 @@ type setVolumesOptions struct { PersistenceEnabled bool LibP2PEnabled bool SwarmEnabled bool + AutoTLSEnabled bool } func setVolumes(o setVolumesOptions) (volumes pod.Volumes) { @@ -248,6 +309,13 @@ func setVolumes(o setVolumesOptions) (volumes pod.Volumes) { }, }) } + if o.AutoTLSEnabled { + volumes = append(volumes, pod.Volume{ + EmptyDir: &pod.EmptyDirVolume{ + Name: "pebble-ca-certs", + }, + }) + } return volumes } diff --git a/pkg/orchestration/k8s/orchestrator.go b/pkg/orchestration/k8s/orchestrator.go index 8edf43142..bb5712d04 100644 --- a/pkg/orchestration/k8s/orchestrator.go +++ b/pkg/orchestration/k8s/orchestrator.go @@ -272,6 +272,7 @@ func (n *nodeOrchestrator) Create(ctx context.Context, o orchestration.CreateOpt sSet := o.Name libP2PEnabled := len(o.LibP2PKey) > 0 swarmEnabled := o.SwarmKey != nil + autoTLSEnabled := o.Config.P2PWSSAddr != "" if _, err := n.k8s.StatefulSet.Set(ctx, sSet, o.Namespace, statefulset.Options{ Annotations: o.Annotations, @@ -287,7 +288,9 @@ func (n *nodeOrchestrator) Create(ctx context.Context, o orchestration.CreateOpt Annotations: o.Annotations, Labels: o.Labels, Spec: pod.PodSpec{ - InitContainers: setInitContainers(), + InitContainers: setInitContainers(setInitContainersOptions{ + AutoTLSEnabled: autoTLSEnabled, + }), Containers: setContainers(setContainersOptions{ Name: sSet, Image: o.Image, @@ -302,6 +305,7 @@ func (n *nodeOrchestrator) Create(ctx context.Context, o orchestration.CreateOpt ResourcesRequestMemory: o.ResourcesRequestMemory, LibP2PEnabled: libP2PEnabled, SwarmEnabled: swarmEnabled, + AutoTLSEnabled: autoTLSEnabled, }), NodeSelector: o.NodeSelector, PodSecurityContext: pod.PodSecurityContext{ @@ -316,6 +320,7 @@ func (n *nodeOrchestrator) Create(ctx context.Context, o orchestration.CreateOpt PersistenceEnabled: o.PersistenceEnabled, LibP2PEnabled: libP2PEnabled, SwarmEnabled: swarmEnabled, + AutoTLSEnabled: autoTLSEnabled, }), }, }, From 66313ba7e93bcd73976079ef8c9ada831f9ea258 Mon Sep 17 00:00:00 2001 From: Akrem Chabchoub <121046693+akrem-chabchoub@users.noreply.github.com> Date: Mon, 16 Feb 2026 14:10:10 +0100 Subject: [PATCH 06/11] feat(autotls): implement autotls check functionality (#560) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(autotls): implement autotls check functionality * feat(autotls): enhance WSS connectivity checks and update configuration * feat(autotls): add autotls check in config.yaml * feat(config): add WSS configuration for local setup * fix(autotls): ensure context cancellation is handled correctly during WSS connection tests * fix(autotls): update WSS connectivity test to disconnect everything before connecting * fix(autotls): add WSS group option and enhance error handling for WSS connectivity * feat(autotls): add certificate renewal testing options and enhance configuration * refactor(autotls): simplify certificate renewal options and update related configurations * fix(autotls): increase certificate renewal wait time to 500 seconds for improved testing * fix(autotls): extend timeout for certificate renewal to 15 minutes * feat(autotls): add ultralight group support and connectivity testing * feat(config): update local.yaml for ultralight configuration and enhance autotls checks * fix(autotls): re-enable certificate renewal test * feat(config): add local-dns-autotls and local-light-autotls configura… (#562) * feat(config): add local-dns-autotls and local-light-autotls configurations ons. * chore(config): comment out ultralight configuration * fix(config): update autotls configs --- config/config.yaml | 4 +- config/local.yaml | 82 ++++++++++- go.mod | 2 +- pkg/bee/api/node.go | 18 +++ pkg/bee/client.go | 24 ++++ pkg/check/autotls/autotls.go | 270 +++++++++++++++++++++++++++++++++++ pkg/config/check.go | 20 +++ 7 files changed, 417 insertions(+), 3 deletions(-) create mode 100644 pkg/check/autotls/autotls.go diff --git a/config/config.yaml b/config/config.yaml index 9666b09a0..af8a05555 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -399,7 +399,9 @@ checks: postage-depth: 21 postage-label: test-label type: feed - + autotls: + timeout: 5m + type: autotls # simulations defines simulations Beekeeper can execute against the cluster # type filed allows defining same simulation with different names and options simulations: diff --git a/config/local.yaml b/config/local.yaml index a29fb7dfe..4de649538 100644 --- a/config/local.yaml +++ b/config/local.yaml @@ -25,6 +25,38 @@ clusters: config: local count: 2 mode: node + local-dns-autotls: + _inherit: "local" + node-groups: + bootnode: + mode: bootnode + bee-config: bootnode-local-dns-autotls + config: local-dns-autotls + nodes: + - name: bootnode-0 + bootnodes: /dns4/bootnode-0-headless.%s.svc.cluster.local/tcp/1634/p2p/QmaHzvd3iZduu275CMkMVZKwbsjXSyH3GJRj4UvFJApKcb + libp2p-key: '{"address":"28678fe31f09f722d53e77ca2395569f19959fa5","crypto":{"cipher":"aes-128-ctr","ciphertext":"0ff319684c4f8decf9c998047febe3417cfc45832b8bb62fd818183d54cf5d0183bfa021ed95addce3b33e83ce7ee73e926f00eea8241d96b349266a4d299829d3d22db0d536315b52b34db4a6778bfd3ce7631ad7256ea0bb9c50abea9de35d740b6fdc50caf929b1d19494690d9ed649105d02c14f5ec49d","cipherparams":{"iv":"4e9a50fb5852b5e61964f696be78066b"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"4d513e81647e4150bb648ed8d2dda28d460802336bf24d620119eac66ae0c0c4"},"mac":"9ae71db96e5ddc1c214538d42082212bbbe53aeac09fcc3e3a8eff815648331e"},"version":3,"id":"ae3bc991-d89f-405a-9e6a-60e27347e22d"}' + swarm-key: '{"address":"f176839c150e52fe30e5c2b5c648465c6fdfa532","crypto":{"cipher":"aes-128-ctr","ciphertext":"352af096f0fca9dfbd20a6861bde43d988efe7f179e0a9ffd812a285fdcd63b9","cipherparams":{"iv":"613003f1f1bf93430c92629da33f8828"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"ad1d99a4c64c95c26131e079e8c8a82221d58bf66a7ceb767c33a4c376c564b8"},"mac":"cafda1bc8ca0ffc2b22eb69afd1cf5072fd09412243443be1b0c6832f57924b6"},"version":3}' + bee: + bee-config: bee-local-dns-autotls + config: local-dns-autotls + count: 3 + mode: node + light: + bee-config: bee-local-light-autotls + config: local-light-autotls + count: 2 + mode: node + wss: + bee-config: bee-local-wss + config: local + count: 2 + mode: node + ultralight: + bee-config: bee-local-ultralight + config: local-ultralight + count: 1 + mode: node local-dns: _inherit: "local" node-groups: @@ -42,11 +74,21 @@ clusters: config: local-dns count: 3 mode: node + wss: + bee-config: bee-local-wss + config: local-dns + count: 2 + mode: node light: bee-config: bee-local-light config: local-light count: 2 mode: node + # ultralight: + # bee-config: bee-local-ultralight + # config: local-ultralight + # count: 2 + # mode: node local-gc: _inherit: "local" node-groups: @@ -100,13 +142,37 @@ node-groups: update-strategy: "RollingUpdate" local-dns: _inherit: "local" + local-dns-autotls: + _inherit: "local" + local-light-autotls: + _inherit: "local-light" local-gc: _inherit: "local" local-light: _inherit: "local" + local-ultralight: + _inherit: "local" + labels: + app.kubernetes.io/component: "node" + app.kubernetes.io/name: "bee" + app.kubernetes.io/part-of: "bee" + app.kubernetes.io/version: "latest" + beekeeper.ethswarm.org/node-funder: "false" # bee-configs defines Bee configuration that can be assigned to node-groups bee-configs: + bootnode-local-dns-autotls: + _inherit: "bee-local-dns-autotls" + bootnode-mode: true + bee-local-dns-autotls: + _inherit: "bee-local" + bootnode: /dnsaddr/bootnode-0-headless.local.svc.cluster.local + p2p-wss-enable: true + bee-local-light-autotls: + _inherit: "bee-local" + bootnode: /dnsaddr/bootnode-0-headless.local.svc.cluster.local + full-node: false + p2p-wss-enable: true bee-local: _inherit: "" allow-private-cidrs: true @@ -134,7 +200,7 @@ bee-configs: p2p-addr: ":1634" p2p-wss-addr: ":1635" p2p-ws-enable: false - p2p-wss-enable: true + p2p-wss-enable: false password: "beekeeper" payment-early-percent: 50 payment-threshold: 13500000 @@ -157,6 +223,9 @@ bee-configs: bootnode-local: _inherit: "bee-local" bootnode-mode: true + bee-local-wss: + _inherit: "bee-local" + p2p-wss-enable: true bee-local-dns: _inherit: "bee-local" bootnode: /dnsaddr/localhost @@ -171,6 +240,11 @@ bee-configs: bee-local-gc: _inherit: "bee-local" cache-capacity: 10 + bee-local-ultralight: + _inherit: "bee-local" + blockchain-rpc-endpoint: "" + full-node: false + swap-enable: false bootnode-local-gc: _inherit: "bee-local" cache-capacity: 10 @@ -398,3 +472,9 @@ checks: postage-depth: 21 postage-label: test-label type: feed + ci-autotls: + timeout: 15m + type: autotls + options: + ultralight-group: ultralight + wss-group: wss diff --git a/go.mod b/go.mod index ea704f2fb..76f69b8da 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/go-git/go-git/v5 v5.13.2 github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.1 + github.com/multiformats/go-multiaddr v0.12.3 github.com/opentracing/opentracing-go v1.2.0 github.com/prometheus/client_golang v1.21.1 github.com/prometheus/common v0.62.0 @@ -94,7 +95,6 @@ require ( github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect - github.com/multiformats/go-multiaddr v0.12.3 // indirect github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect github.com/multiformats/go-multibase v0.2.0 // indirect github.com/multiformats/go-multicodec v0.9.0 // indirect diff --git a/pkg/bee/api/node.go b/pkg/bee/api/node.go index de9423bc0..789eeccc3 100644 --- a/pkg/bee/api/node.go +++ b/pkg/bee/api/node.go @@ -122,6 +122,24 @@ func (n *NodeService) Peers(ctx context.Context) (resp Peers, err error) { return resp, err } +// ConnectResponse represents the response from the connect endpoint +type ConnectResponse struct { + Address string `json:"address"` +} + +// Connect connects to a peer using the provided multiaddress. +// The multiaddr should be in the format: /ip4/x.x.x.x/tcp/port/... +// Returns the overlay address of the connected peer. +func (n *NodeService) Connect(ctx context.Context, multiaddr string) (resp ConnectResponse, err error) { + err = n.client.requestJSON(ctx, http.MethodPost, "/connect"+multiaddr, nil, &resp) + return resp, err +} + +// Disconnect disconnects from a peer with the given overlay address. +func (n *NodeService) Disconnect(ctx context.Context, overlay swarm.Address) error { + return n.client.requestJSON(ctx, http.MethodDelete, "/peers/"+overlay.String(), nil, nil) +} + // Readiness represents node's readiness type Readiness struct { Status string `json:"status"` diff --git a/pkg/bee/client.go b/pkg/bee/client.go index d1404c506..66cbf4d26 100644 --- a/pkg/bee/client.go +++ b/pkg/bee/client.go @@ -318,6 +318,30 @@ func (c *Client) Peers(ctx context.Context) (peers []swarm.Address, err error) { return peers, err } +// Connect connects to a peer using the provided multiaddress. +// Returns the overlay address of the connected peer. +func (c *Client) Connect(ctx context.Context, multiaddr string) (swarm.Address, error) { + resp, err := c.api.Node.Connect(ctx, multiaddr) + if err != nil { + return swarm.ZeroAddress, fmt.Errorf("connect to %s: %w", multiaddr, err) + } + + addr, err := swarm.ParseHexAddress(resp.Address) + if err != nil { + return swarm.ZeroAddress, fmt.Errorf("parse overlay address %s: %w", resp.Address, err) + } + + return addr, nil +} + +// Disconnect disconnects from a peer with the given overlay address. +func (c *Client) Disconnect(ctx context.Context, overlay swarm.Address) error { + if err := c.api.Node.Disconnect(ctx, overlay); err != nil { + return fmt.Errorf("disconnect from %s: %w", overlay, err) + } + return nil +} + // PinRootHash pins root hash of given reference. func (c *Client) PinRootHash(ctx context.Context, ref swarm.Address) error { return c.api.Pinning.PinRootHash(ctx, ref) diff --git a/pkg/check/autotls/autotls.go b/pkg/check/autotls/autotls.go new file mode 100644 index 000000000..f808141a8 --- /dev/null +++ b/pkg/check/autotls/autotls.go @@ -0,0 +1,270 @@ +package autotls + +import ( + "context" + "fmt" + "time" + + "github.com/ethersphere/beekeeper/pkg/bee" + "github.com/ethersphere/beekeeper/pkg/beekeeper" + "github.com/ethersphere/beekeeper/pkg/logging" + "github.com/ethersphere/beekeeper/pkg/orchestration" + ma "github.com/multiformats/go-multiaddr" +) + +type Options struct { + WSSGroup string + UltraLightGroup string + ConnectTimeout time.Duration +} + +func NewDefaultOptions() Options { + return Options{ + WSSGroup: "wss", + UltraLightGroup: "ultralight", + ConnectTimeout: 30 * time.Second, + } +} + +var _ beekeeper.Action = (*Check)(nil) + +type Check struct { + logger logging.Logger +} + +func NewCheck(logger logging.Logger) beekeeper.Action { + return &Check{ + logger: logger, + } +} + +func (c *Check) Run(ctx context.Context, cluster orchestration.Cluster, opts any) error { + o, ok := opts.(Options) + if !ok { + return fmt.Errorf("invalid options type") + } + + c.logger.Info("starting AutoTLS check") + + clients, err := cluster.NodesClients(ctx) + if err != nil { + return fmt.Errorf("get node clients: %w", err) + } + time.Sleep(5 * time.Second) + wssClients := orchestration.ClientMap(clients).FilterByNodeGroups([]string{o.WSSGroup}) + if len(wssClients) == 0 { + return fmt.Errorf("no nodes found in WSS group %q", o.WSSGroup) + } + + c.logger.Infof("found %d nodes in WSS group %q", len(wssClients), o.WSSGroup) + + wssNodes, err := c.verifyWSSUnderlays(ctx, wssClients, o.UltraLightGroup) + if err != nil { + return fmt.Errorf("verify WSS underlays: %w", err) + } + + if err := c.testWSSConnectivity(ctx, clients, wssNodes, o.ConnectTimeout); err != nil { + return fmt.Errorf("WSS connectivity test: %w", err) + } + + if o.UltraLightGroup != "" { + if err := c.testUltraLightConnectivity(ctx, clients, wssNodes, o.UltraLightGroup, o.ConnectTimeout); err != nil { + return fmt.Errorf("ultra-light connectivity test: %w", err) + } + } + + if err := c.testCertificateRenewal(ctx, clients, wssNodes, o.ConnectTimeout); err != nil { + return fmt.Errorf("certificate renewal test: %w", err) + } + + c.logger.Info("AutoTLS check completed successfully") + return nil +} + +func (c *Check) verifyWSSUnderlays(ctx context.Context, wssClients orchestration.ClientList, excludeNodeGroup string) (map[string][]string, error) { + wssNodes := make(map[string][]string) + + for _, client := range wssClients { + if excludeNodeGroup != "" && client.NodeGroup() == excludeNodeGroup { + c.logger.Debugf("skipping %s (node group %s has no WSS underlays)", client.Name(), excludeNodeGroup) + continue + } + + nodeName := client.Name() + addresses, err := client.Addresses(ctx) + if err != nil { + return nil, fmt.Errorf("%s: get addresses: %w", nodeName, err) + } + time.Sleep(2 * time.Second) + wssUnderlays := filterWSSUnderlays(addresses.Underlay) + if len(wssUnderlays) == 0 { + return nil, fmt.Errorf("node %s in WSS group has no WSS underlay addresses", nodeName) + } + + wssNodes[nodeName] = wssUnderlays + c.logger.Debugf("node %s has %d WSS underlay(s)", nodeName, len(wssUnderlays)) + } + + return wssNodes, nil +} + +func filterWSSUnderlays(underlays []string) []string { + var wss []string + for _, u := range underlays { + maddr, err := ma.NewMultiaddr(u) + if err != nil { + continue + } + if _, err := maddr.ValueForProtocol(ma.P_TLS); err != nil { + continue + } + if _, err := maddr.ValueForProtocol(ma.P_WS); err != nil { + continue + } + wss = append(wss, u) + } + return wss +} + +func (c *Check) testWSSConnectivity(ctx context.Context, clients map[string]*bee.Client, wssNodes map[string][]string, timeout time.Duration) error { + var nonWSSSource *bee.Client + var nonWSSName string + var wssSource *bee.Client + var wssSourceName string + + for name, client := range clients { + if _, hasWSS := wssNodes[name]; hasWSS { + if wssSource == nil { + wssSource = client + wssSourceName = name + } + } else { + if nonWSSSource == nil { + nonWSSSource = client + nonWSSName = name + } + } + } + + if nonWSSSource != nil { + c.logger.Infof("testing cross-protocol: %s (non-WSS) to WSS nodes", nonWSSName) + if err := c.testConnectivity(ctx, nonWSSSource, nonWSSName, clients, wssNodes, timeout); err != nil { + return fmt.Errorf("cross-protocol test: %w", err) + } + } else { + c.logger.Warning("no non-WSS nodes available, skipping cross-protocol test") + } + + if wssSource != nil { + c.logger.Infof("testing WSS-to-WSS: %s to WSS nodes", wssSourceName) + if err := c.testConnectivity(ctx, wssSource, wssSourceName, clients, wssNodes, timeout); err != nil { + return fmt.Errorf("WSS-to-WSS test: %w", err) + } + } else { + c.logger.Warning("no WSS source nodes available, skipping WSS-to-WSS test") + } + + return nil +} + +func (c *Check) testUltraLightConnectivity(ctx context.Context, clients map[string]*bee.Client, wssNodes map[string][]string, ultraLightGroup string, timeout time.Duration) error { + ultralightClients := orchestration.ClientMap(clients).FilterByNodeGroups([]string{ultraLightGroup}) + if len(ultralightClients) == 0 { + c.logger.Warningf("no nodes found in ultra-light group %q, skipping ultra-light connectivity test", ultraLightGroup) + return nil + } + + c.logger.Infof("found %d nodes in ultra-light group %q", len(ultralightClients), ultraLightGroup) + + for _, client := range ultralightClients { + nodeName := client.Name() + c.logger.Infof("testing ultra-light to WSS: %s (no listen addr) to WSS nodes", nodeName) + if err := c.testConnectivity(ctx, client, nodeName, clients, wssNodes, timeout); err != nil { + return fmt.Errorf("ultra-light %s to WSS test: %w", nodeName, err) + } + } + + return nil +} + +func (c *Check) testConnectivity(ctx context.Context, sourceClient *bee.Client, sourceName string, clients map[string]*bee.Client, wssNodes map[string][]string, timeout time.Duration) error { + for targetName, underlays := range wssNodes { + if targetName == sourceName { + continue + } + + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + targetClient := clients[targetName] + targetAddresses, err := targetClient.Addresses(ctx) + if err != nil { + return fmt.Errorf("get target %s addresses: %w", targetName, err) + } + targetOverlay := targetAddresses.Overlay + + // Disconnect first to ensure we test actual WSS connection. + // Bee returns 200 OK for both new connections and existing ones, + // so we must disconnect first to guarantee WSS transport is used. + c.logger.Infof("disconnecting from %s before WSS test", targetName) + if err := sourceClient.Disconnect(ctx, targetOverlay); err != nil { + c.logger.Warningf("failed to disconnect from %s: %v", targetName, err) + } + + time.Sleep(500 * time.Millisecond) + + for _, underlay := range underlays { + c.logger.Infof("testing WSS connection from %s to %s via %s", sourceName, targetName, underlay) + + connectCtx, cancel := context.WithTimeout(ctx, timeout) + start := time.Now() + + overlay, err := sourceClient.Connect(connectCtx, underlay) + duration := time.Since(start) + cancel() + + if err != nil { + return fmt.Errorf("WSS connection failed from %s to %s via %s: %w", sourceName, targetName, underlay, err) + } + + c.logger.Infof("WSS connection successful: %s to %s (overlay: %s, duration: %v)", + sourceName, targetName, overlay, duration) + + if !overlay.Equal(targetOverlay) { + return fmt.Errorf("overlay mismatch: expected %s, got %s", targetOverlay, overlay) + } + + if err := sourceClient.Disconnect(ctx, overlay); err != nil { + c.logger.Warningf("failed to disconnect from %s: %v", targetName, err) + } + + time.Sleep(500 * time.Millisecond) + } + } + + return nil +} + +func (c *Check) testCertificateRenewal(ctx context.Context, clients map[string]*bee.Client, wssNodes map[string][]string, connectTimeout time.Duration) error { + const renewalWaitTime = 500 * time.Second // This is configured in beelocal setup (we set certificate to expire in 300 seconds) + + c.logger.Infof("testing certificate renewal: waiting %v then re-testing connectivity", renewalWaitTime) + + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(renewalWaitTime): + } + + c.logger.Info("wait complete, re-testing WSS connectivity to verify certificates were renewed") + + if err := c.testWSSConnectivity(ctx, clients, wssNodes, connectTimeout); err != nil { + return fmt.Errorf("post-renewal connectivity test failed (certificates may not have been renewed): %w", err) + } + + c.logger.Info("certificate renewal test passed: WSS connectivity still works after wait period") + return nil +} diff --git a/pkg/config/check.go b/pkg/config/check.go index b130c6e4b..5543d172e 100644 --- a/pkg/config/check.go +++ b/pkg/config/check.go @@ -8,6 +8,7 @@ import ( "github.com/ethersphere/beekeeper/pkg/beekeeper" "github.com/ethersphere/beekeeper/pkg/check/act" + "github.com/ethersphere/beekeeper/pkg/check/autotls" "github.com/ethersphere/beekeeper/pkg/check/balances" "github.com/ethersphere/beekeeper/pkg/check/cashout" "github.com/ethersphere/beekeeper/pkg/check/datadurability" @@ -82,6 +83,25 @@ var Checks = map[string]CheckType{ return opts, nil }, }, + "autotls": { + NewAction: autotls.NewCheck, + NewOptions: func(checkGlobalConfig CheckGlobalConfig, check Check) (any, error) { + checkOpts := new(struct { + WSSGroup *string `yaml:"wss-group"` + UltraLightGroup *string `yaml:"ultralight-group"` + ConnectTimeout *time.Duration `yaml:"connect-timeout"` + }) + if err := check.Options.Decode(checkOpts); err != nil { + return nil, fmt.Errorf("decoding check %s options: %w", check.Type, err) + } + opts := autotls.NewDefaultOptions() + + if err := applyCheckConfig(checkGlobalConfig, checkOpts, &opts); err != nil { + return nil, fmt.Errorf("applying options: %w", err) + } + return opts, nil + }, + }, "balances": { NewAction: balances.NewCheck, NewOptions: func(checkGlobalConfig CheckGlobalConfig, check Check) (any, error) { From 8d3d9490e3df9b47a0b3f700b113516a4f27ca76 Mon Sep 17 00:00:00 2001 From: Ljubisa Gacevic Date: Mon, 16 Feb 2026 16:19:22 +0100 Subject: [PATCH 07/11] fix(autotls): simplify config --- config/config.yaml | 1 + config/local.yaml | 107 ++++++++++++----------------------- pkg/check/autotls/autotls.go | 27 ++++----- pkg/config/check.go | 4 +- 4 files changed, 53 insertions(+), 86 deletions(-) diff --git a/config/config.yaml b/config/config.yaml index af8a05555..ba5820aaa 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -402,6 +402,7 @@ checks: autotls: timeout: 5m type: autotls + # simulations defines simulations Beekeeper can execute against the cluster # type filed allows defining same simulation with different names and options simulations: diff --git a/config/local.yaml b/config/local.yaml index 4de649538..e372b76bc 100644 --- a/config/local.yaml +++ b/config/local.yaml @@ -25,70 +25,55 @@ clusters: config: local count: 2 mode: node - local-dns-autotls: + local-dns: _inherit: "local" node-groups: bootnode: mode: bootnode - bee-config: bootnode-local-dns-autotls - config: local-dns-autotls + bee-config: bootnode-local-dns + config: local-dns nodes: - name: bootnode-0 bootnodes: /dns4/bootnode-0-headless.%s.svc.cluster.local/tcp/1634/p2p/QmaHzvd3iZduu275CMkMVZKwbsjXSyH3GJRj4UvFJApKcb libp2p-key: '{"address":"28678fe31f09f722d53e77ca2395569f19959fa5","crypto":{"cipher":"aes-128-ctr","ciphertext":"0ff319684c4f8decf9c998047febe3417cfc45832b8bb62fd818183d54cf5d0183bfa021ed95addce3b33e83ce7ee73e926f00eea8241d96b349266a4d299829d3d22db0d536315b52b34db4a6778bfd3ce7631ad7256ea0bb9c50abea9de35d740b6fdc50caf929b1d19494690d9ed649105d02c14f5ec49d","cipherparams":{"iv":"4e9a50fb5852b5e61964f696be78066b"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"4d513e81647e4150bb648ed8d2dda28d460802336bf24d620119eac66ae0c0c4"},"mac":"9ae71db96e5ddc1c214538d42082212bbbe53aeac09fcc3e3a8eff815648331e"},"version":3,"id":"ae3bc991-d89f-405a-9e6a-60e27347e22d"}' swarm-key: '{"address":"f176839c150e52fe30e5c2b5c648465c6fdfa532","crypto":{"cipher":"aes-128-ctr","ciphertext":"352af096f0fca9dfbd20a6861bde43d988efe7f179e0a9ffd812a285fdcd63b9","cipherparams":{"iv":"613003f1f1bf93430c92629da33f8828"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"ad1d99a4c64c95c26131e079e8c8a82221d58bf66a7ceb767c33a4c376c564b8"},"mac":"cafda1bc8ca0ffc2b22eb69afd1cf5072fd09412243443be1b0c6832f57924b6"},"version":3}' bee: - bee-config: bee-local-dns-autotls - config: local-dns-autotls + bee-config: bee-local-dns + config: local-dns count: 3 mode: node light: - bee-config: bee-local-light-autotls - config: local-light-autotls - count: 2 - mode: node - wss: - bee-config: bee-local-wss - config: local + bee-config: bee-local-light + config: local-light count: 2 mode: node - ultralight: - bee-config: bee-local-ultralight - config: local-ultralight - count: 1 - mode: node - local-dns: + local-dns-autotls: _inherit: "local" node-groups: bootnode: mode: bootnode - bee-config: bootnode-local-dns - config: local-dns + bee-config: bootnode-local-dns-autotls + config: local nodes: - name: bootnode-0 bootnodes: /dns4/bootnode-0-headless.%s.svc.cluster.local/tcp/1634/p2p/QmaHzvd3iZduu275CMkMVZKwbsjXSyH3GJRj4UvFJApKcb libp2p-key: '{"address":"28678fe31f09f722d53e77ca2395569f19959fa5","crypto":{"cipher":"aes-128-ctr","ciphertext":"0ff319684c4f8decf9c998047febe3417cfc45832b8bb62fd818183d54cf5d0183bfa021ed95addce3b33e83ce7ee73e926f00eea8241d96b349266a4d299829d3d22db0d536315b52b34db4a6778bfd3ce7631ad7256ea0bb9c50abea9de35d740b6fdc50caf929b1d19494690d9ed649105d02c14f5ec49d","cipherparams":{"iv":"4e9a50fb5852b5e61964f696be78066b"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"4d513e81647e4150bb648ed8d2dda28d460802336bf24d620119eac66ae0c0c4"},"mac":"9ae71db96e5ddc1c214538d42082212bbbe53aeac09fcc3e3a8eff815648331e"},"version":3,"id":"ae3bc991-d89f-405a-9e6a-60e27347e22d"}' swarm-key: '{"address":"f176839c150e52fe30e5c2b5c648465c6fdfa532","crypto":{"cipher":"aes-128-ctr","ciphertext":"352af096f0fca9dfbd20a6861bde43d988efe7f179e0a9ffd812a285fdcd63b9","cipherparams":{"iv":"613003f1f1bf93430c92629da33f8828"},"kdf":"scrypt","kdfparams":{"n":32768,"r":8,"p":1,"dklen":32,"salt":"ad1d99a4c64c95c26131e079e8c8a82221d58bf66a7ceb767c33a4c376c564b8"},"mac":"cafda1bc8ca0ffc2b22eb69afd1cf5072fd09412243443be1b0c6832f57924b6"},"version":3}' bee: - bee-config: bee-local-dns - config: local-dns + bee-config: bee-local-autotls + config: local count: 3 mode: node - wss: - bee-config: bee-local-wss - config: local-dns + bee-autotls: + bee-config: bee-local-autotls + config: local count: 2 mode: node - light: - bee-config: bee-local-light - config: local-light - count: 2 + ultra-light: + bee-config: bee-local-ultralight-autotls + config: local + count: 1 mode: node - # ultralight: - # bee-config: bee-local-ultralight - # config: local-ultralight - # count: 2 - # mode: node local-gc: _inherit: "local" node-groups: @@ -142,44 +127,20 @@ node-groups: update-strategy: "RollingUpdate" local-dns: _inherit: "local" - local-dns-autotls: - _inherit: "local" - local-light-autotls: - _inherit: "local-light" local-gc: _inherit: "local" local-light: _inherit: "local" - local-ultralight: - _inherit: "local" - labels: - app.kubernetes.io/component: "node" - app.kubernetes.io/name: "bee" - app.kubernetes.io/part-of: "bee" - app.kubernetes.io/version: "latest" - beekeeper.ethswarm.org/node-funder: "false" # bee-configs defines Bee configuration that can be assigned to node-groups bee-configs: - bootnode-local-dns-autotls: - _inherit: "bee-local-dns-autotls" - bootnode-mode: true - bee-local-dns-autotls: - _inherit: "bee-local" - bootnode: /dnsaddr/bootnode-0-headless.local.svc.cluster.local - p2p-wss-enable: true - bee-local-light-autotls: - _inherit: "bee-local" - bootnode: /dnsaddr/bootnode-0-headless.local.svc.cluster.local - full-node: false - p2p-wss-enable: true bee-local: _inherit: "" allow-private-cidrs: true api-addr: ":1633" - autotls-ca-endpoint: "https://pebble:14000/dir" # https://pebble.local.svc.cluster.local:14000/dir + autotls-ca-endpoint: "https://pebble:14000/dir" autotls-domain: "local.test" - autotls-registration-endpoint: http://p2p-forge.local.svc.cluster.local:8080 # "http://p2p-forge:8080" # http://p2p-forge.local.svc.cluster.local:8080/v1/_acme-challenge #http://p2p-forge:8080/v1/_acme-challenge + autotls-registration-endpoint: http://p2p-forge.local.svc.cluster.local:8080 block-time: 1 blockchain-rpc-endpoint: "ws://geth-swap:8546" bootnode-mode: false @@ -219,13 +180,22 @@ bee-configs: warmup-time: 0s welcome-message: "Welcome to the Swarm, this is a local cluster!" withdrawal-addresses-whitelist: "0xec44cb15b1b033e74d55ac5d0e24d861bde54532" - + bootnode-local-dns-autotls: + _inherit: "bee-local-dns" + bootnode-mode: true + p2p-wss-enable: true + bee-local-autotls: + _inherit: "bee-local-dns" + bootnode: /dnsaddr/bootnode-0-headless.local.svc.cluster.local + p2p-wss-enable: true + bee-local-ultralight-autotls: + _inherit: "bee-local-dns" + full-node: false + p2p-wss-enable: true + blockchain-rpc-endpoint: # ultralight nodes don't connect to the blockchain bootnode-local: _inherit: "bee-local" bootnode-mode: true - bee-local-wss: - _inherit: "bee-local" - p2p-wss-enable: true bee-local-dns: _inherit: "bee-local" bootnode: /dnsaddr/localhost @@ -240,11 +210,6 @@ bee-configs: bee-local-gc: _inherit: "bee-local" cache-capacity: 10 - bee-local-ultralight: - _inherit: "bee-local" - blockchain-rpc-endpoint: "" - full-node: false - swap-enable: false bootnode-local-gc: _inherit: "bee-local" cache-capacity: 10 @@ -476,5 +441,5 @@ checks: timeout: 15m type: autotls options: - ultralight-group: ultralight - wss-group: wss + ultra-light-group: ultra-light + autotls-group: bee-autotls diff --git a/pkg/check/autotls/autotls.go b/pkg/check/autotls/autotls.go index f808141a8..92a44044d 100644 --- a/pkg/check/autotls/autotls.go +++ b/pkg/check/autotls/autotls.go @@ -13,15 +13,15 @@ import ( ) type Options struct { - WSSGroup string + AutoTLSGroup string UltraLightGroup string ConnectTimeout time.Duration } func NewDefaultOptions() Options { return Options{ - WSSGroup: "wss", - UltraLightGroup: "ultralight", + AutoTLSGroup: "bee-autotls", + UltraLightGroup: "ultra-light", ConnectTimeout: 30 * time.Second, } } @@ -50,15 +50,16 @@ func (c *Check) Run(ctx context.Context, cluster orchestration.Cluster, opts any if err != nil { return fmt.Errorf("get node clients: %w", err) } + time.Sleep(5 * time.Second) - wssClients := orchestration.ClientMap(clients).FilterByNodeGroups([]string{o.WSSGroup}) - if len(wssClients) == 0 { - return fmt.Errorf("no nodes found in WSS group %q", o.WSSGroup) + autoTLSClients := orchestration.ClientMap(clients).FilterByNodeGroups([]string{o.AutoTLSGroup}) + if len(autoTLSClients) == 0 { + return fmt.Errorf("no nodes found in AutoTLS group %q", o.AutoTLSGroup) } - c.logger.Infof("found %d nodes in WSS group %q", len(wssClients), o.WSSGroup) + c.logger.Infof("found %d nodes in AutoTLS group %q", len(autoTLSClients), o.AutoTLSGroup) - wssNodes, err := c.verifyWSSUnderlays(ctx, wssClients, o.UltraLightGroup) + wssNodes, err := c.verifyWSSUnderlays(ctx, autoTLSClients, o.UltraLightGroup) if err != nil { return fmt.Errorf("verify WSS underlays: %w", err) } @@ -81,10 +82,10 @@ func (c *Check) Run(ctx context.Context, cluster orchestration.Cluster, opts any return nil } -func (c *Check) verifyWSSUnderlays(ctx context.Context, wssClients orchestration.ClientList, excludeNodeGroup string) (map[string][]string, error) { - wssNodes := make(map[string][]string) +func (c *Check) verifyWSSUnderlays(ctx context.Context, autoTLSClients orchestration.ClientList, excludeNodeGroup string) (map[string][]string, error) { + autoTLS := make(map[string][]string) - for _, client := range wssClients { + for _, client := range autoTLSClients { if excludeNodeGroup != "" && client.NodeGroup() == excludeNodeGroup { c.logger.Debugf("skipping %s (node group %s has no WSS underlays)", client.Name(), excludeNodeGroup) continue @@ -101,11 +102,11 @@ func (c *Check) verifyWSSUnderlays(ctx context.Context, wssClients orchestration return nil, fmt.Errorf("node %s in WSS group has no WSS underlay addresses", nodeName) } - wssNodes[nodeName] = wssUnderlays + autoTLS[nodeName] = wssUnderlays c.logger.Debugf("node %s has %d WSS underlay(s)", nodeName, len(wssUnderlays)) } - return wssNodes, nil + return autoTLS, nil } func filterWSSUnderlays(underlays []string) []string { diff --git a/pkg/config/check.go b/pkg/config/check.go index 5543d172e..827817c46 100644 --- a/pkg/config/check.go +++ b/pkg/config/check.go @@ -87,8 +87,8 @@ var Checks = map[string]CheckType{ NewAction: autotls.NewCheck, NewOptions: func(checkGlobalConfig CheckGlobalConfig, check Check) (any, error) { checkOpts := new(struct { - WSSGroup *string `yaml:"wss-group"` - UltraLightGroup *string `yaml:"ultralight-group"` + AutoTLSGroup *string `yaml:"autotls-group"` + UltraLightGroup *string `yaml:"ultra-light-group"` ConnectTimeout *time.Duration `yaml:"connect-timeout"` }) if err := check.Options.Decode(checkOpts); err != nil { From 307bd206a940071a87495ad1001f675145a4198e Mon Sep 17 00:00:00 2001 From: Ljubisa Gacevic Date: Tue, 17 Feb 2026 21:21:43 +0100 Subject: [PATCH 08/11] fix: use test cert from constant --- pkg/orchestration/k8s/helpers.go | 50 ++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/pkg/orchestration/k8s/helpers.go b/pkg/orchestration/k8s/helpers.go index 1d7456654..adc53e81c 100644 --- a/pkg/orchestration/k8s/helpers.go +++ b/pkg/orchestration/k8s/helpers.go @@ -1,6 +1,7 @@ package k8s import ( + "fmt" "maps" "strconv" "strings" @@ -63,6 +64,28 @@ withdrawal-addresses-whitelist: {{.WithdrawAddress}} ` ) +// https://raw.githubusercontent.com/letsencrypt/pebble/main/test/certs/pebble.minica.pem +const pebbleCertificate = `-----BEGIN CERTIFICATE----- +MIIDPzCCAiegAwIBAgIIU0Xm9UFdQxUwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE +AxMVbWluaWNhIHJvb3QgY2EgNTM0NWU2MCAXDTI1MDkwMzIzNDAwNVoYDzIxMjUw +OTAzMjM0MDA1WjAgMR4wHAYDVQQDExVtaW5pY2Egcm9vdCBjYSA1MzQ1ZTYwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5WgZNoVJandj43kkLyU50vzCZ +alozvdRo3OFiKoDtmqKPNWRNO2hC9AUNxTDJco51Yc42u/WV3fPbbhSznTiOOVtn +Ajm6iq4I5nZYltGGZetGDOQWr78y2gWY+SG078MuOO2hyDIiKtVc3xiXYA+8Hluu +9F8KbqSS1h55yxZ9b87eKR+B0zu2ahzBCIHKmKWgc6N13l7aDxxY3D6uq8gtJRU0 +toumyLbdzGcupVvjbjDP11nl07RESDWBLG1/g3ktJvqIa4BWgU2HMh4rND6y8OD3 +Hy3H8MY6CElL+MOCbFJjWqhtOxeFyZZV9q3kYnk9CAuQJKMEGuN4GU6tzhW1AgMB +AAGjezB5MA4GA1UdDwEB/wQEAwIChDATBgNVHSUEDDAKBggrBgEFBQcDATASBgNV +HRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSu8RGpErgYUoYnQuwCq+/ggTiEjDAf +BgNVHSMEGDAWgBSu8RGpErgYUoYnQuwCq+/ggTiEjDANBgkqhkiG9w0BAQsFAAOC +AQEAXDVYov1+f6EL7S41LhYQkEX/GyNNzsEvqxE9U0+3Iri5JfkcNOiA9O9L6Z+Y +bqcsXV93s3vi4r4WSWuc//wHyJYrVe5+tK4nlFpbJOvfBUtnoBDyKNxXzZCxFJVh +f9uc8UejRfQMFbDbhWY/x83y9BDufJHHq32OjCIN7gp2UR8rnfYvlz7Zg4qkJBsn +DG4dwd+pRTCFWJOVIG0JoNhK3ZmE7oJ1N4H38XkZ31NPcMksKxpsLLIS9+mosZtg +4olL7tMPJklx5ZaeMFaKRDq4Gdxkbw4+O4vRgNm3Z8AXWKknOdfgdpqLUPPhRcP4 +v1lhy71EhBuXXwRQJry0lTdF+w== +-----END CERTIFICATE-----` + type setInitContainersOptions struct { AutoTLSEnabled bool } @@ -82,31 +105,22 @@ echo 'bee initialization done';`}, }, }) + // TODO: this init container is only needed for testing with Pebble + // should not be used in other contexts. if o.AutoTLSEnabled { // Install Pebble CA certificates as an init container. - // Pebble is a testing ACME server (like Let's Encrypt for development). - // The bee container needs to trust Pebble's CA certificates to validate - // TLS certificates issued by Pebble during AutoTLS testing. This init - // container downloads and installs the CA certificates, which are then - // shared with the bee container via the pebble-ca-certs volume mount. inits = append(inits, containers.Container{ Name: "install-pebble-ca", Image: "alpine:latest", - Command: []string{"sh", "-c", `set -ex -apk add --no-cache ca-certificates wget + Command: []string{"sh", "-c", fmt.Sprintf(`set -ex +apk add --no-cache ca-certificates mkdir -p /certs - -wget -q --no-check-certificate -O /certs/pebble-root.crt https://pebble:15000/roots/0 -wget -q --no-check-certificate -O /certs/pebble-intermediate.crt https://pebble:15000/intermediates/0 || true -wget -q --no-check-certificate -O /certs/pebble-minica.crt https://raw.githubusercontent.com/letsencrypt/pebble/main/test/certs/pebble.minica.pem || true - -cat /certs/*.crt > /certs/pebble-bundle.crt -cp /certs/*.crt /usr/local/share/ca-certificates/ 2>/dev/null || true +cat > /certs/pebble-minica.crt << 'CERT' +%s +CERT +cp /certs/pebble-minica.crt /usr/local/share/ca-certificates/ update-ca-certificates -cp /etc/ssl/certs/ca-certificates.crt /certs/ca-certificates.crt - -echo "Pebble CA certificates installed successfully" -ls -la /certs/`}, +cp /etc/ssl/certs/ca-certificates.crt /certs/ca-certificates.crt`, pebbleCertificate)}, VolumeMounts: containers.VolumeMounts{ { Name: "pebble-ca-certs", From 576729966b7ddd4c2f3e8a42910b4de8830dfc1b Mon Sep 17 00:00:00 2001 From: Akrem Chabchoub <121046693+akrem-chabchoub@users.noreply.github.com> Date: Tue, 17 Feb 2026 21:27:05 +0100 Subject: [PATCH 09/11] =?UTF-8?q?fix(autotls):=20handle=20context=20cancel?= =?UTF-8?q?lation=20during=20sleep=20intervals=20in=20c=E2=80=A6=20(#566)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(autotls): handle context cancellation during sleep intervals in checks * feat(autotls): add UnderlayPollInterval option and enhance WSS underlay checks * refactor(config): reorder struct fields for clarity in check options * refactor(autotls): simplify options structure and improve WSS underlay checks --- pkg/check/autotls/autotls.go | 44 ++++++++++++++++++++---------------- pkg/config/check.go | 5 ++-- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/pkg/check/autotls/autotls.go b/pkg/check/autotls/autotls.go index 92a44044d..95c5a77b9 100644 --- a/pkg/check/autotls/autotls.go +++ b/pkg/check/autotls/autotls.go @@ -15,17 +15,20 @@ import ( type Options struct { AutoTLSGroup string UltraLightGroup string - ConnectTimeout time.Duration } func NewDefaultOptions() Options { return Options{ AutoTLSGroup: "bee-autotls", UltraLightGroup: "ultra-light", - ConnectTimeout: 30 * time.Second, } } +const ( + underlayPollInterval = 2 * time.Second + connectTimeout = 30 * time.Second +) + var _ beekeeper.Action = (*Check)(nil) type Check struct { @@ -51,7 +54,6 @@ func (c *Check) Run(ctx context.Context, cluster orchestration.Cluster, opts any return fmt.Errorf("get node clients: %w", err) } - time.Sleep(5 * time.Second) autoTLSClients := orchestration.ClientMap(clients).FilterByNodeGroups([]string{o.AutoTLSGroup}) if len(autoTLSClients) == 0 { return fmt.Errorf("no nodes found in AutoTLS group %q", o.AutoTLSGroup) @@ -64,17 +66,17 @@ func (c *Check) Run(ctx context.Context, cluster orchestration.Cluster, opts any return fmt.Errorf("verify WSS underlays: %w", err) } - if err := c.testWSSConnectivity(ctx, clients, wssNodes, o.ConnectTimeout); err != nil { + if err := c.testWSSConnectivity(ctx, clients, wssNodes, connectTimeout); err != nil { return fmt.Errorf("WSS connectivity test: %w", err) } if o.UltraLightGroup != "" { - if err := c.testUltraLightConnectivity(ctx, clients, wssNodes, o.UltraLightGroup, o.ConnectTimeout); err != nil { + if err := c.testUltraLightConnectivity(ctx, clients, wssNodes, o.UltraLightGroup, connectTimeout); err != nil { return fmt.Errorf("ultra-light connectivity test: %w", err) } } - if err := c.testCertificateRenewal(ctx, clients, wssNodes, o.ConnectTimeout); err != nil { + if err := c.testCertificateRenewal(ctx, clients, wssNodes, connectTimeout); err != nil { return fmt.Errorf("certificate renewal test: %w", err) } @@ -92,14 +94,22 @@ func (c *Check) verifyWSSUnderlays(ctx context.Context, autoTLSClients orchestra } nodeName := client.Name() - addresses, err := client.Addresses(ctx) - if err != nil { - return nil, fmt.Errorf("%s: get addresses: %w", nodeName, err) - } - time.Sleep(2 * time.Second) - wssUnderlays := filterWSSUnderlays(addresses.Underlay) - if len(wssUnderlays) == 0 { - return nil, fmt.Errorf("node %s in WSS group has no WSS underlay addresses", nodeName) + var wssUnderlays []string + for { + addresses, err := client.Addresses(ctx) + if err != nil { + return nil, fmt.Errorf("%s: get addresses: %w", nodeName, err) + } + wssUnderlays = filterWSSUnderlays(addresses.Underlay) + if len(wssUnderlays) > 0 { + break + } + c.logger.Debugf("node %s has no WSS underlays yet, retrying in %v", nodeName, underlayPollInterval) + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-time.After(underlayPollInterval): + } } autoTLS[nodeName] = wssUnderlays @@ -215,8 +225,6 @@ func (c *Check) testConnectivity(ctx context.Context, sourceClient *bee.Client, c.logger.Warningf("failed to disconnect from %s: %v", targetName, err) } - time.Sleep(500 * time.Millisecond) - for _, underlay := range underlays { c.logger.Infof("testing WSS connection from %s to %s via %s", sourceName, targetName, underlay) @@ -241,8 +249,6 @@ func (c *Check) testConnectivity(ctx context.Context, sourceClient *bee.Client, if err := sourceClient.Disconnect(ctx, overlay); err != nil { c.logger.Warningf("failed to disconnect from %s: %v", targetName, err) } - - time.Sleep(500 * time.Millisecond) } } @@ -250,7 +256,7 @@ func (c *Check) testConnectivity(ctx context.Context, sourceClient *bee.Client, } func (c *Check) testCertificateRenewal(ctx context.Context, clients map[string]*bee.Client, wssNodes map[string][]string, connectTimeout time.Duration) error { - const renewalWaitTime = 500 * time.Second // This is configured in beelocal setup (we set certificate to expire in 300 seconds) + const renewalWaitTime = 350 * time.Second // This is configured in beelocal setup (we set certificate to expire in 300 seconds) c.logger.Infof("testing certificate renewal: waiting %v then re-testing connectivity", renewalWaitTime) diff --git a/pkg/config/check.go b/pkg/config/check.go index 827817c46..8d94e0778 100644 --- a/pkg/config/check.go +++ b/pkg/config/check.go @@ -87,9 +87,8 @@ var Checks = map[string]CheckType{ NewAction: autotls.NewCheck, NewOptions: func(checkGlobalConfig CheckGlobalConfig, check Check) (any, error) { checkOpts := new(struct { - AutoTLSGroup *string `yaml:"autotls-group"` - UltraLightGroup *string `yaml:"ultra-light-group"` - ConnectTimeout *time.Duration `yaml:"connect-timeout"` + AutoTLSGroup *string `yaml:"autotls-group"` + UltraLightGroup *string `yaml:"ultra-light-group"` }) if err := check.Options.Decode(checkOpts); err != nil { return nil, fmt.Errorf("decoding check %s options: %w", check.Type, err) From eef159fc5445c6e1061444b9d5fcd0090a1a97a6 Mon Sep 17 00:00:00 2001 From: akrem-chabchoub Date: Wed, 18 Feb 2026 12:40:43 +0100 Subject: [PATCH 10/11] feat(config): add light node configuration for autotls support in local.yaml --- config/local.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/config/local.yaml b/config/local.yaml index e372b76bc..b9c4b1943 100644 --- a/config/local.yaml +++ b/config/local.yaml @@ -69,6 +69,11 @@ clusters: config: local count: 2 mode: node + light: + bee-config: bee-local-light-autotls + config: local + count: 2 + mode: node ultra-light: bee-config: bee-local-ultralight-autotls config: local @@ -188,6 +193,9 @@ bee-configs: _inherit: "bee-local-dns" bootnode: /dnsaddr/bootnode-0-headless.local.svc.cluster.local p2p-wss-enable: true + bee-local-light-autotls: + _inherit: "bee-local-light" + p2p-wss-enable: true bee-local-ultralight-autotls: _inherit: "bee-local-dns" full-node: false From 9ff4e1bbc75d43620a0a44f218525588566f69f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ljubi=C5=A1a=20Ga=C4=8Devi=C4=87?= <35105035+gacevicljubisa@users.noreply.github.com> Date: Tue, 31 Mar 2026 15:19:11 +0200 Subject: [PATCH 11/11] feat(autotls): autotls forge validation (#569) * feat(autotls): add p2p-forge address validation and certificate renewal verification * chore: fix lint issue --- go.mod | 96 ++++++--- go.sum | 287 +++++++++++++++++++------ pkg/check/autotls/autotls.go | 85 +++++++- pkg/check/autotls/forge.go | 356 +++++++++++++++++++++++++++++++ pkg/orchestration/k8s/helpers.go | 4 +- 5 files changed, 726 insertions(+), 102 deletions(-) create mode 100644 pkg/check/autotls/forge.go diff --git a/go.mod b/go.mod index ff930d639..a8c355164 100644 --- a/go.mod +++ b/go.mod @@ -8,18 +8,21 @@ replace github.com/codahale/hdrhistogram => github.com/HdrHistogram/hdrhistogram require ( github.com/ethereum/go-ethereum v1.16.8 - github.com/ethersphere/bee/v2 v2.6.0 + github.com/ethersphere/bee/v2 v2.7.0 github.com/ethersphere/bmt v0.1.4 github.com/ethersphere/ethproxy v0.0.5 github.com/ethersphere/node-funder v0.3.0 github.com/go-git/go-billy/v5 v5.6.2 github.com/go-git/go-git/v5 v5.16.5 github.com/google/uuid v1.6.0 - github.com/gorilla/websocket v1.5.1 - github.com/multiformats/go-multiaddr v0.12.3 + github.com/gorilla/websocket v1.5.3 + github.com/ipshipyard/p2p-forge v0.7.0 + github.com/libp2p/go-libp2p v0.46.0 + github.com/multiformats/go-multiaddr v0.16.1 + github.com/multiformats/go-multibase v0.2.0 github.com/opentracing/opentracing-go v1.2.0 - github.com/prometheus/client_golang v1.21.1 - github.com/prometheus/common v0.62.0 + github.com/prometheus/client_golang v1.22.0 + github.com/prometheus/common v0.64.0 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.9.1 github.com/spf13/viper v1.20.1 @@ -39,10 +42,13 @@ require ( github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect github.com/ProtonMail/go-crypto v1.1.6 // indirect github.com/StackExchange/wmi v1.2.1 // indirect + github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.20.0 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect + github.com/caddyserver/certmagic v0.21.6 // indirect + github.com/caddyserver/zerossl v0.1.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudflare/circl v1.6.1 // indirect github.com/consensys/gnark-crypto v0.18.1 // indirect @@ -50,14 +56,16 @@ require ( github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect github.com/cyphar/filepath-securejoin v0.4.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect github.com/deckarep/golang-set/v2 v2.6.0 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect + github.com/emicklei/go-restful/v3 v3.12.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect github.com/ethereum/go-verkle v0.2.2 // indirect github.com/ethersphere/go-sw3-abi v0.6.9 // indirect - github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/flynn/noise v1.1.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-chi/chi v1.5.4 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect @@ -76,19 +84,31 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/holiman/uint256 v1.3.2 // indirect + github.com/huin/goupnp v1.3.0 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/ipfs/go-cid v0.4.1 // indirect + github.com/ipfs/go-cid v0.5.0 // indirect + github.com/ipfs/go-log/v2 v2.6.0 // indirect + github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/klauspost/reedsolomon v1.11.8 // indirect + github.com/koron/go-ssdp v0.0.6 // indirect + github.com/libdns/libdns v0.2.2 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect - github.com/libp2p/go-libp2p v0.33.2 // indirect + github.com/libp2p/go-flow-metrics v0.2.0 // indirect + github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect + github.com/libp2p/go-msgio v0.3.0 // indirect + github.com/libp2p/go-netroute v0.3.0 // indirect + github.com/libp2p/go-reuseport v0.4.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/miekg/dns v1.1.58 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mholt/acmez/v3 v3.0.0 // indirect + github.com/miekg/dns v1.1.66 // indirect github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -96,18 +116,39 @@ require ( github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect - github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect - github.com/multiformats/go-multibase v0.2.0 // indirect - github.com/multiformats/go-multicodec v0.9.0 // indirect + github.com/multiformats/go-multiaddr-dns v0.4.1 // indirect + github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect + github.com/multiformats/go-multicodec v0.9.1 // indirect github.com/multiformats/go-multihash v0.2.3 // indirect - github.com/multiformats/go-multistream v0.5.0 // indirect + github.com/multiformats/go-multistream v0.6.1 // indirect github.com/multiformats/go-varint v0.0.7 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/pion/datachannel v1.5.10 // indirect + github.com/pion/dtls/v2 v2.2.12 // indirect + github.com/pion/dtls/v3 v3.0.6 // indirect + github.com/pion/ice/v4 v4.0.10 // indirect + github.com/pion/interceptor v0.1.40 // indirect + github.com/pion/logging v0.2.3 // indirect + github.com/pion/mdns/v2 v2.0.7 // indirect + github.com/pion/randutil v0.1.0 // indirect + github.com/pion/rtcp v1.2.15 // indirect + github.com/pion/rtp v1.8.19 // indirect + github.com/pion/sctp v1.8.39 // indirect + github.com/pion/sdp/v3 v3.0.13 // indirect + github.com/pion/srtp/v3 v3.0.6 // indirect + github.com/pion/stun v0.6.1 // indirect + github.com/pion/stun/v3 v3.0.0 // indirect + github.com/pion/transport/v2 v2.2.10 // indirect + github.com/pion/transport/v3 v3.0.7 // indirect + github.com/pion/turn/v4 v4.0.2 // indirect + github.com/pion/webrtc/v4 v4.1.2 // indirect github.com/pjbgf/sha1cd v0.3.2 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/procfs v0.15.1 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + github.com/quic-go/quic-go v0.57.1 // indirect github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/shirou/gopsutil v3.21.5+incompatible // indirect @@ -117,27 +158,34 @@ require ( github.com/spf13/afero v1.12.0 // indirect github.com/spf13/cast v1.7.1 // indirect github.com/spf13/pflag v1.0.6 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/uber/jaeger-lib v2.2.0+incompatible // indirect - github.com/vmihailenco/msgpack/v5 v5.3.4 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + github.com/wlynxg/anet v0.0.5 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect + github.com/zeebo/blake3 v0.2.4 // indirect go.uber.org/atomic v1.11.0 // indirect + go.uber.org/dig v1.19.0 // indirect + go.uber.org/fx v1.24.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + go.uber.org/zap v1.27.0 // indirect + go.uber.org/zap/exp v0.3.0 // indirect + golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 // indirect golang.org/x/mod v0.29.0 // indirect golang.org/x/net v0.47.0 // indirect - golang.org/x/oauth2 v0.27.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sys v0.38.0 // indirect golang.org/x/term v0.37.0 // indirect golang.org/x/text v0.31.0 // indirect - golang.org/x/time v0.9.0 // indirect + golang.org/x/time v0.12.0 // indirect golang.org/x/tools v0.38.0 // indirect - google.golang.org/protobuf v1.36.1 // indirect + google.golang.org/protobuf v1.36.6 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect @@ -145,7 +193,7 @@ require ( k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect - lukechampine.com/blake3 v1.2.1 // indirect + lukechampine.com/blake3 v1.4.1 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect diff --git a/go.sum b/go.sum index 5867e3cf1..a70db9422 100644 --- a/go.sum +++ b/go.sum @@ -21,6 +21,8 @@ github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU= @@ -29,6 +31,10 @@ github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/caddyserver/certmagic v0.21.6 h1:1th6GfprVfsAtFNOu4StNMF5IxK5XiaI0yZhAHlZFPE= +github.com/caddyserver/certmagic v0.21.6/go.mod h1:n1sCo7zV1Ez2j+89wrzDxo4N/T1Ws/Vx8u5NvuBFabw= +github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= +github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -62,22 +68,24 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= -github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= -github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= +github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= github.com/deepmap/oapi-codegen v1.6.0 h1:w/d1ntwh91XI0b/8ja7+u5SvA4IFfM0UNNLmiDR1gg0= github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M= github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A= github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= -github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= -github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk= +github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s= @@ -88,8 +96,8 @@ github.com/ethereum/go-ethereum v1.16.8 h1:LLLfkZWijhR5m6yrAXbdlTeXoqontH+Ga2f9i github.com/ethereum/go-ethereum v1.16.8/go.mod h1:Fs6QebQbavneQTYcA39PEKv2+zIjX7rPUZ14DER46wk= github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8= github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= -github.com/ethersphere/bee/v2 v2.6.0 h1:r799kEmHNXzcXjwiTh+ZuUCqpj+vbeOg3hSUPQoaARg= -github.com/ethersphere/bee/v2 v2.6.0/go.mod h1:C2WAuC6f4sngyQ15Rw1x+7WiYjTdpkB4K3s5yKhCRRQ= +github.com/ethersphere/bee/v2 v2.7.0 h1:faaB3WP60SNrZvBCz2xh5sSIc9ka3Cz/ApWyedqBvQA= +github.com/ethersphere/bee/v2 v2.7.0/go.mod h1:6Hhfttnet1vQmSydC9bivOKtUmaCarT3Mqi9Slj2tkg= github.com/ethersphere/bmt v0.1.4 h1:+rkWYNtMgDx6bkNqGdWu+U9DgGI1rRZplpSW3YhBr1Q= github.com/ethersphere/bmt v0.1.4/go.mod h1:Yd8ft1U69WDuHevZc/rwPxUv1rzPSMpMnS6xbU53aY8= github.com/ethersphere/ethproxy v0.0.5 h1:j5Mkm45jqmkET6NwGaJtaxOSFbhoAfOKzHiwHl6DBT0= @@ -100,10 +108,12 @@ github.com/ethersphere/node-funder v0.3.0 h1:rZamN4hs53Rt8MdSYSb2HLNTNX6XGzntwcV github.com/ethersphere/node-funder v0.3.0/go.mod h1:1n5y5yzYFWUAad5ocKmfbXvmTvnNN6r7V4awt36TD9Y= github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY= github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg= +github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg= +github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= -github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= @@ -134,7 +144,6 @@ github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= @@ -159,12 +168,12 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18= +github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= -github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -192,12 +201,18 @@ github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c h1:qSH github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU= github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= -github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= +github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg= +github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk= +github.com/ipfs/go-log/v2 v2.6.0 h1:2Nu1KKQQ2ayonKp4MPo6pXCjqw1ULc9iohRqWV5EYqg= +github.com/ipfs/go-log/v2 v2.6.0/go.mod h1:p+Efr3qaY5YXpx9TX7MoLCSEZX5boSWj9wh86P5HJa8= +github.com/ipshipyard/p2p-forge v0.7.0 h1:PQayexxZC1FR2Vx0XOSbmZ6wDPliidS48I+xXWuF+YU= +github.com/ipshipyard/p2p-forge v0.7.0/go.mod h1:i2wg0p7WmHGyo5vYaK9COZBp8BN5Drncfu3WoQNZlQY= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= +github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -206,12 +221,14 @@ github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4 github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= -github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/reedsolomon v1.11.8 h1:s8RpUW5TK4hjr+djiOpbZJB4ksx+TdYbRH7vHQpwPOY= github.com/klauspost/reedsolomon v1.11.8/go.mod h1:4bXRN+cVzMdml6ti7qLouuYi32KHJ5MGv0Qd8a47h6A= +github.com/koron/go-ssdp v0.0.6 h1:Jb0h04599eq/CY7rB5YEqPS83HmRfHP2azkxMN2rFtU= +github.com/koron/go-ssdp v0.0.6/go.mod h1:0R9LfRJGek1zWTjN3JUNlm5INCDYGpRDfAptnct63fI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -224,21 +241,46 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= +github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s= +github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= -github.com/libp2p/go-libp2p v0.33.2 h1:vCdwnFxoGOXMKmaGHlDSnL4bM3fQeW8pgIa9DECnb40= -github.com/libp2p/go-libp2p v0.33.2/go.mod h1:zTeppLuCvUIkT118pFVzA8xzP/p2dJYOMApCkFh0Yww= +github.com/libp2p/go-flow-metrics v0.2.0 h1:EIZzjmeOE6c8Dav0sNv35vhZxATIXWZg6j/C08XmmDw= +github.com/libp2p/go-flow-metrics v0.2.0/go.mod h1:st3qqfu8+pMfh+9Mzqb2GTiwrAGjIPszEjZmtksN8Jc= +github.com/libp2p/go-libp2p v0.46.0 h1:0T2yvIKpZ3DVYCuPOFxPD1layhRU486pj9rSlGWYnDM= +github.com/libp2p/go-libp2p v0.46.0/go.mod h1:TbIDnpDjBLa7isdgYpbxozIVPBTmM/7qKOJP4SFySrQ= +github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= +github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= +github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= +github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= +github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= +github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= +github.com/libp2p/go-netroute v0.3.0 h1:nqPCXHmeNmgTJnktosJ/sIef9hvwYCrsLxXmfNks/oc= +github.com/libp2p/go-netroute v0.3.0/go.mod h1:Nkd5ShYgSMS5MUKy/MU2T57xFoOKvvLR92Lic48LEyA= +github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s= +github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU= +github.com/libp2p/go-yamux/v5 v5.0.1 h1:f0WoX/bEF2E8SbE4c/k1Mo+/9z0O4oC/hWEA+nfYRSg= +github.com/libp2p/go-yamux/v5 v5.0.1/go.mod h1:en+3cdX51U0ZslwRdRLrvQsdayFt3TSUKvBGErzpWbU= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/marcopolo/simnet v0.0.1 h1:rSMslhPz6q9IvJeFWDoMGxMIrlsbXau3NkuIXHGJxfg= +github.com/marcopolo/simnet v0.0.1/go.mod h1:WDaQkgLAjqDUEBAOXz22+1j6wXKfGlC5sD5XWt3ddOs= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= -github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= +github.com/mholt/acmez/v3 v3.0.0 h1:r1NcjuWR0VaKP2BTjDK9LRFBw/WvURx3jlaEUl9Ht8E= +github.com/mholt/acmez/v3 v3.0.0/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= +github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= +github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= @@ -259,48 +301,85 @@ github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aG github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= -github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= -github.com/multiformats/go-multiaddr v0.12.3 h1:hVBXvPRcKG0w80VinQ23P5t7czWgg65BmIvQKjDydU8= -github.com/multiformats/go-multiaddr v0.12.3/go.mod h1:sBXrNzucqkFJhvKOiwwLyqamGa/P5EIXNPLovyhQCII= -github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= -github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= +github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= +github.com/multiformats/go-multiaddr v0.16.1 h1:fgJ0Pitow+wWXzN9do+1b8Pyjmo8m5WhGfzpL82MpCw= +github.com/multiformats/go-multiaddr v0.16.1/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0= +github.com/multiformats/go-multiaddr-dns v0.4.1 h1:whi/uCLbDS3mSEUMb1MsoT4uzUeZB0N32yzufqS0i5M= +github.com/multiformats/go-multiaddr-dns v0.4.1/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc= +github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= +github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= -github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg= -github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= +github.com/multiformats/go-multicodec v0.9.1 h1:x/Fuxr7ZuR4jJV4Os5g444F7xC4XmyUaT/FWtE+9Zjo= +github.com/multiformats/go-multicodec v0.9.1/go.mod h1:LLWNMtyV5ithSBUo3vFIMaeDy+h3EbkMTek1m+Fybbo= github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= -github.com/multiformats/go-multistream v0.5.0 h1:5htLSLl7lvJk3xx3qT/8Zm9J4K8vEOf/QGkvOGQAyiE= -github.com/multiformats/go-multistream v0.5.0/go.mod h1:n6tMZiwiP2wUsR8DgfDWw1dydlEqV3l6N3/GBsX6ILA= -github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/multiformats/go-multistream v0.6.1 h1:4aoX5v6T+yWmc2raBHsTvzmFhOI8WVOer28DeBBEYdQ= +github.com/multiformats/go-multistream v0.6.1/go.mod h1:ksQf6kqHAb6zIsyw7Zm+gAuVo57Qbq84E27YlYqavqw= github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= -github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= -github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= -github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= +github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= +github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= +github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= -github.com/pion/dtls/v2 v2.2.8 h1:BUroldfiIbV9jSnC6cKOMnyiORRWrWWpV11JUyEu5OA= -github.com/pion/dtls/v2 v2.2.8/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= -github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= +github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o= +github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M= +github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= +github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk= +github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE= +github.com/pion/dtls/v3 v3.0.6 h1:7Hkd8WhAJNbRgq9RgdNh1aaWlZlGpYTzdqjy9x9sK2E= +github.com/pion/dtls/v3 v3.0.6/go.mod h1:iJxNQ3Uhn1NZWOMWlLxEEHAN5yX7GyPvvKw04v9bzYU= +github.com/pion/ice/v4 v4.0.10 h1:P59w1iauC/wPk9PdY8Vjl4fOFL5B+USq1+xbDcN6gT4= +github.com/pion/ice/v4 v4.0.10/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw= +github.com/pion/interceptor v0.1.40 h1:e0BjnPcGpr2CFQgKhrQisBU7V3GXK6wrfYrGYaU6Jq4= +github.com/pion/interceptor v0.1.40/go.mod h1:Z6kqH7M/FYirg3frjGJ21VLSRJGBXB/KqaTIrdqnOic= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI= +github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90= +github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= +github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA= +github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= +github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= +github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo= +github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0= +github.com/pion/rtp v1.8.19 h1:jhdO/3XhL/aKm/wARFVmvTfq0lC/CvN1xwYKmduly3c= +github.com/pion/rtp v1.8.19/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk= +github.com/pion/sctp v1.8.39 h1:PJma40vRHa3UTO3C4MyeJDQ+KIobVYRZQZ0Nt7SjQnE= +github.com/pion/sctp v1.8.39/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= +github.com/pion/sdp/v3 v3.0.13 h1:uN3SS2b+QDZnWXgdr69SM8KB4EbcnPnPf2Laxhty/l4= +github.com/pion/sdp/v3 v3.0.13/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= +github.com/pion/srtp/v3 v3.0.6 h1:E2gyj1f5X10sB/qILUGIkL4C2CqK269Xq167PbGCc/4= +github.com/pion/srtp/v3 v3.0.6/go.mod h1:BxvziG3v/armJHAaJ87euvkhHqWe9I7iiOy50K2QkhY= github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4= +github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8= github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ= -github.com/pion/transport/v2 v2.2.4 h1:41JJK6DZQYSeVLxILA2+F4ZkKb4Xd/tFJZRFZQ9QAlo= +github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw= +github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU= +github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= -github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM= -github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= +github.com/pion/transport/v2 v2.2.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q= +github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E= +github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= +github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= +github.com/pion/turn/v4 v4.0.2 h1:ZqgQ3+MjP32ug30xAbD6Mn+/K4Sxi3SdNOTFf+7mpps= +github.com/pion/turn/v4 v4.0.2/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7IToA1zs= +github.com/pion/webrtc/v4 v4.1.2 h1:mpuUo/EJ1zMNKGE79fAdYNFZBX790KE7kQQpLMjjR54= +github.com/pion/webrtc/v4 v4.1.2/go.mod h1:xsCXiNAmMEjIdFxAYU0MbB3RwRieJsegSB2JZsGN+8U= github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -308,14 +387,20 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= -github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= -github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQPGO4= +github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10= +github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s= +github.com/quic-go/webtransport-go v0.9.0 h1:jgys+7/wm6JarGDrW+lD/r9BGqBAmqY/ssklE09bA70= +github.com/quic-go/webtransport-go v0.9.0/go.mod h1:4FUYIiUc75XSsF6HShcLeXXYZJ9AGwo/xh3L8M/P1ao= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= @@ -351,18 +436,20 @@ github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw= @@ -379,10 +466,13 @@ github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/ github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= -github.com/vmihailenco/msgpack/v5 v5.3.4 h1:qMKAwOV+meBw2Y8k9cVwAy7qErtYCwBzZ2ellBfvnqc= -github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= +github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= +github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= @@ -391,26 +481,53 @@ github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGC github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= +github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= +github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= +github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= +github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= gitlab.com/nolash/go-mockbytes v0.0.7 h1:9XVFpEfY67kGBVJve3uV19kzqORdlo7V+q09OE6Yo54= gitlab.com/nolash/go-mockbytes v0.0.7/go.mod h1:KKOpNTT39j2Eo+P6uUTOncntfeKY6AFh/2CxuD5MpgE= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= +go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= +go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= +go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg= +go.uber.org/fx v1.24.0/go.mod h1:AmDeGyS+ZARGKM4tlH4FY2Jr63VjbEDJHtqXTGP5hbo= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= +go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= +go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 h1:bsqhLWFR6G6xiQcb+JoGqdKdRU6WzPWmK8E0jxTjzo4= +golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -419,55 +536,83 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= -golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 h1:LvzTn0GQhWuvKH/kVRS3R3bVAsdQWI7hvfLHGgh9+lU= +golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= -google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= @@ -500,8 +645,8 @@ k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7F k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= -lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= +lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= +lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo= resenje.org/x v0.6.0 h1:afn9E4XhglF4y9Kq0VH5tdSyjnsVKxiYgB6HFj7ebss= resenje.org/x v0.6.0/go.mod h1:qgwe4MCzh57EkkMDurg24ug7HHfZtAjtBkmCihNmOpM= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= diff --git a/pkg/check/autotls/autotls.go b/pkg/check/autotls/autotls.go index 95c5a77b9..ee11826a5 100644 --- a/pkg/check/autotls/autotls.go +++ b/pkg/check/autotls/autotls.go @@ -3,12 +3,14 @@ package autotls import ( "context" "fmt" + "strings" "time" "github.com/ethersphere/beekeeper/pkg/bee" "github.com/ethersphere/beekeeper/pkg/beekeeper" "github.com/ethersphere/beekeeper/pkg/logging" "github.com/ethersphere/beekeeper/pkg/orchestration" + "github.com/ethersphere/beekeeper/pkg/orchestration/k8s" ma "github.com/multiformats/go-multiaddr" ) @@ -66,6 +68,25 @@ func (c *Check) Run(ctx context.Context, cluster orchestration.Cluster, opts any return fmt.Errorf("verify WSS underlays: %w", err) } + // Extract forge config from the first autotls node's bee config. + forgeDomain, caCertPEM := c.forgeConfig(cluster, autoTLSClients) + if forgeDomain == "" { + return fmt.Errorf("could not determine forge domain from node config") + } + + forgeNodes, err := c.verifyForgeAddressFormat(wssNodes, forgeDomain) + if err != nil { + return fmt.Errorf("forge address validation: %w", err) + } + + if err := c.verifyDNSResolution(ctx, forgeNodes); err != nil { + return fmt.Errorf("DNS resolution verification: %w", err) + } + + if err := c.verifyTLSCertificate(ctx, forgeNodes, caCertPEM); err != nil { + return fmt.Errorf("TLS certificate verification: %w", err) + } + if err := c.testWSSConnectivity(ctx, clients, wssNodes, connectTimeout); err != nil { return fmt.Errorf("WSS connectivity test: %w", err) } @@ -76,7 +97,7 @@ func (c *Check) Run(ctx context.Context, cluster orchestration.Cluster, opts any } } - if err := c.testCertificateRenewal(ctx, clients, wssNodes, connectTimeout); err != nil { + if err := c.testCertificateRenewal(ctx, clients, wssNodes, forgeNodes, caCertPEM, connectTimeout); err != nil { return fmt.Errorf("certificate renewal test: %w", err) } @@ -255,10 +276,18 @@ func (c *Check) testConnectivity(ctx context.Context, sourceClient *bee.Client, return nil } -func (c *Check) testCertificateRenewal(ctx context.Context, clients map[string]*bee.Client, wssNodes map[string][]string, connectTimeout time.Duration) error { +func (c *Check) testCertificateRenewal(ctx context.Context, clients map[string]*bee.Client, wssNodes map[string][]string, forgeNodes map[string][]*forgeUnderlayInfo, caCertPEM string, connectTimeout time.Duration) error { const renewalWaitTime = 350 * time.Second // This is configured in beelocal setup (we set certificate to expire in 300 seconds) - c.logger.Infof("testing certificate renewal: waiting %v then re-testing connectivity", renewalWaitTime) + // Snapshot certificate serial numbers before waiting. + preSerials := c.getCertSerials(ctx, forgeNodes, caCertPEM) + if len(preSerials) > 0 { + c.logger.Infof("captured %d certificate serial(s) before renewal wait", len(preSerials)) + } else { + c.logger.Warning("no TLS endpoints reachable, will fall back to connectivity-only renewal check") + } + + c.logger.Infof("testing certificate renewal: waiting %v for certificates to expire and renew", renewalWaitTime) select { case <-ctx.Done(): @@ -266,12 +295,58 @@ func (c *Check) testCertificateRenewal(ctx context.Context, clients map[string]* case <-time.After(renewalWaitTime): } - c.logger.Info("wait complete, re-testing WSS connectivity to verify certificates were renewed") + c.logger.Info("wait complete, verifying certificates were renewed") + + // Verify serial numbers changed (proves new certs were issued). + if len(preSerials) > 0 { + postSerials := c.getCertSerials(ctx, forgeNodes, caCertPEM) + var renewed, unchanged int + for key, preSN := range preSerials { + postSN, ok := postSerials[key] + if !ok { + c.logger.Warningf("%s: endpoint became unreachable after wait", key) + continue + } + if preSN == postSN { + unchanged++ + c.logger.Warningf("%s: certificate serial unchanged (%s), renewal may not have occurred", key, preSN) + } else { + renewed++ + c.logger.Infof("%s: certificate renewed (serial %s -> %s)", key, preSN, postSN) + } + } + if unchanged > 0 && renewed == 0 { + return fmt.Errorf("no certificates were renewed: %d/%d serials unchanged", unchanged, len(preSerials)) + } + c.logger.Infof("certificate renewal verified: %d renewed, %d unchanged", renewed, unchanged) + } + // Also verify WSS connectivity still works with the new certificates. if err := c.testWSSConnectivity(ctx, clients, wssNodes, connectTimeout); err != nil { return fmt.Errorf("post-renewal connectivity test failed (certificates may not have been renewed): %w", err) } - c.logger.Info("certificate renewal test passed: WSS connectivity still works after wait period") + c.logger.Info("certificate renewal test passed") return nil } + +// forgeConfig extracts the forge domain and appropriate CA certificate from the +// first autotls node's bee configuration. If the CA endpoint indicates pebble +// (test environment), the embedded pebble CA cert is returned. Otherwise, an +// empty string is returned so the system root pool is used. +func (c *Check) forgeConfig(cluster orchestration.Cluster, autoTLSClients orchestration.ClientList) (forgeDomain, caCertPEM string) { + nodes := cluster.Nodes() + for _, client := range autoTLSClients { + node, ok := nodes[client.Name()] + if !ok || node.Config() == nil { + continue + } + cfg := node.Config() + forgeDomain = cfg.AutoTLSDomain + if strings.Contains(cfg.AutoTLSCAEndpoint, "pebble") { + caCertPEM = k8s.PebbleCertificate + } + return forgeDomain, caCertPEM + } + return "", "" +} diff --git a/pkg/check/autotls/forge.go b/pkg/check/autotls/forge.go new file mode 100644 index 000000000..4a692c5d7 --- /dev/null +++ b/pkg/check/autotls/forge.go @@ -0,0 +1,356 @@ +package autotls + +import ( + "context" + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "net" + "slices" + "strings" + + forgeclient "github.com/ipshipyard/p2p-forge/client" + "github.com/libp2p/go-libp2p/core/peer" + ma "github.com/multiformats/go-multiaddr" + "github.com/multiformats/go-multibase" +) + +// forgeUnderlayInfo holds parsed forge address information for a single WSS underlay. +type forgeUnderlayInfo struct { + ForgeAddr *forgeclient.ForgeAddrInfo + PeerID peer.ID + ForgeHostname string // full hostname: .. + ExpectedSAN string // expected TLS cert SAN: *.. +} + +// parseForgeUnderlay parses a WSS underlay multiaddr and extracts forge address information. +// It handles both short format (/dns4//tcp//tls/ws/p2p/) +// and long format (/ip4//tcp//tls/sni//ws/p2p/). +func parseForgeUnderlay(underlayStr, forgeDomain string) (*forgeUnderlayInfo, error) { + maddr, err := ma.NewMultiaddr(underlayStr) + if err != nil { + return nil, fmt.Errorf("parse multiaddr: %w", err) + } + + pid, err := extractPeerID(maddr) + if err != nil { + return nil, err + } + + peerIDBase36 := peer.ToCid(pid).Encode(multibase.MustNewEncoder(multibase.Base36)) + + forgeInfo, hostname, err := extractForgeInfo(maddr, pid, forgeDomain) + if err != nil { + return nil, err + } + + if forgeInfo.PeerIDBase36 != peerIDBase36 { + return nil, fmt.Errorf("peer ID mismatch: /p2p/ component gives %s, hostname contains %s", peerIDBase36, forgeInfo.PeerIDBase36) + } + + return &forgeUnderlayInfo{ + ForgeAddr: forgeInfo, + PeerID: pid, + ForgeHostname: hostname, + ExpectedSAN: fmt.Sprintf("*.%s.%s", forgeInfo.PeerIDBase36, forgeDomain), + }, nil +} + +// extractPeerID extracts and decodes the peer ID from the /p2p/ component of a multiaddr. +func extractPeerID(maddr ma.Multiaddr) (peer.ID, error) { + peerIDStr, err := maddr.ValueForProtocol(ma.P_P2P) + if err != nil { + return "", fmt.Errorf("no /p2p/ component: %w", err) + } + pid, err := peer.Decode(peerIDStr) + if err != nil { + return "", fmt.Errorf("decode peer ID %q: %w", peerIDStr, err) + } + return pid, nil +} + +// extractForgeInfo determines the multiaddr format and extracts forge address info accordingly. +func extractForgeInfo(maddr ma.Multiaddr, pid peer.ID, forgeDomain string) (*forgeclient.ForgeAddrInfo, string, error) { + // Short format: /dns4//... or /dns6//... + if hostname, err := maddr.ValueForProtocol(ma.P_DNS4); err == nil { + info, err := forgeInfoFromHostname(hostname, forgeDomain, "4", maddr) + return info, hostname, err + } + if hostname, err := maddr.ValueForProtocol(ma.P_DNS6); err == nil { + info, err := forgeInfoFromHostname(hostname, forgeDomain, "6", maddr) + return info, hostname, err + } + + // Long format: /ip4//tcp//tls/sni//ws/p2p/ + hostname, err := maddr.ValueForProtocol(ma.P_SNI) + if err != nil { + return nil, "", fmt.Errorf("multiaddr has no DNS, SNI, or IP component: %w", err) + } + + bareAddr, err := extractBareIPTCPAddr(maddr) + if err != nil { + return nil, "", fmt.Errorf("extract IP+TCP: %w", err) + } + + info, err := forgeclient.ExtractForgeAddrInfo(bareAddr, pid) + if err != nil { + return nil, "", fmt.Errorf("extract forge addr info: %w", err) + } + return info, hostname, nil +} + +// forgeInfoFromHostname constructs ForgeAddrInfo from a short-format forge hostname. +// The hostname has the form: .. +func forgeInfoFromHostname(hostname, forgeDomain, ipVersion string, maddr ma.Multiaddr) (*forgeclient.ForgeAddrInfo, error) { + suffix := "." + forgeDomain + if !strings.HasSuffix(hostname, suffix) { + return nil, fmt.Errorf("hostname %q doesn't end with domain %q", hostname, forgeDomain) + } + withoutDomain := strings.TrimSuffix(hostname, suffix) + + // The base36 peer ID is the last dot-separated segment before the domain. + lastDot := strings.LastIndex(withoutDomain, ".") + if lastDot < 0 { + return nil, fmt.Errorf("hostname %q missing peer ID segment", hostname) + } + escapedIP := withoutDomain[:lastDot] + peerIDBase36 := withoutDomain[lastDot+1:] + + tcpPort, err := maddr.ValueForProtocol(ma.P_TCP) + if err != nil { + return nil, fmt.Errorf("no TCP port in multiaddr: %w", err) + } + + // Reconstruct the raw IP from the DNS-escaped form. + // IPv4: dashes back to dots (1-2-3-4 → 1.2.3.4) + // IPv6: dashes back to colons, then normalize through net.ParseIP to get canonical form. + var ipStr string + if ipVersion == "4" { + ipStr = strings.ReplaceAll(escapedIP, "-", ".") + } else { + raw := strings.ReplaceAll(escapedIP, "-", ":") + // The forge escaper pads leading/trailing zeros for RFC 1035 compliance + // (e.g., ::1 → 0--1 → 0::1). Parse+String normalizes back to canonical form. + ip := net.ParseIP(raw) + if ip == nil { + return nil, fmt.Errorf("invalid IPv6 address from escaped hostname: %q", raw) + } + ipStr = ip.String() + } + + return &forgeclient.ForgeAddrInfo{ + EscapedIP: escapedIP, + IPVersion: ipVersion, + IPMaStr: fmt.Sprintf("/ip%s/%s", ipVersion, ipStr), + TCPPort: tcpPort, + PeerIDBase36: peerIDBase36, + }, nil +} + +// extractBareIPTCPAddr extracts the /ipX//tcp/ prefix from a multiaddr. +func extractBareIPTCPAddr(maddr ma.Multiaddr) (ma.Multiaddr, error) { + var ipPart, tcpPart string + ma.ForEach(maddr, func(c ma.Component) bool { + switch c.Protocol().Code { + case ma.P_IP4, ma.P_IP6: + ipPart = c.String() + return true + case ma.P_TCP: + tcpPart = "/tcp/" + c.Value() + return false + default: + return false + } + }) + if ipPart == "" || tcpPart == "" { + return nil, fmt.Errorf("multiaddr missing IP or TCP component") + } + return ma.NewMultiaddr(ipPart + tcpPart) +} + +// ipFromForgeAddr extracts the raw IP string from ForgeAddrInfo.IPMaStr. +// IPMaStr has format "/ip4/1.2.3.4" or "/ip6/2001:db8::1". +func ipFromForgeAddr(info *forgeclient.ForgeAddrInfo) string { + switch info.IPVersion { + case "4": + return strings.TrimPrefix(info.IPMaStr, "/ip4/") + case "6": + return strings.TrimPrefix(info.IPMaStr, "/ip6/") + default: + return strings.TrimPrefix(info.IPMaStr, "/ip4/") + } +} + +// verifyForgeAddressFormat validates that each WSS underlay has correct forge format +// and consistent peer IDs. Returns parsed forge info for subsequent checks. +func (c *Check) verifyForgeAddressFormat(wssNodes map[string][]string, forgeDomain string) (map[string][]*forgeUnderlayInfo, error) { + c.logger.Infof("verifying forge address format for %d nodes (domain: %s)", len(wssNodes), forgeDomain) + + result := make(map[string][]*forgeUnderlayInfo, len(wssNodes)) + for nodeName, underlays := range wssNodes { + for _, u := range underlays { + info, err := parseForgeUnderlay(u, forgeDomain) + if err != nil { + return nil, fmt.Errorf("node %s: invalid forge address %q: %w", nodeName, u, err) + } + + // Round-trip validation: rebuild the multiaddr from parsed components + // and verify it matches the original (minus the /p2p/ suffix). + originalWithoutP2P := strings.TrimSuffix(u, "/p2p/"+info.PeerID.String()) + shortRebuilt := forgeclient.BuildShortForgeMultiaddr(info.ForgeAddr, forgeDomain) + longRebuilt := forgeclient.BuildLongForgeMultiaddr(info.ForgeAddr, forgeDomain) + + if originalWithoutP2P != shortRebuilt && originalWithoutP2P != longRebuilt { + return nil, fmt.Errorf("node %s: round-trip mismatch for %q (short: %q, long: %q)", + nodeName, originalWithoutP2P, shortRebuilt, longRebuilt) + } + + result[nodeName] = append(result[nodeName], info) + c.logger.Debugf("node %s: forge address valid: %s (SAN: %s)", nodeName, u, info.ExpectedSAN) + } + } + + c.logger.Info("forge address format verification passed") + return result, nil +} + +// verifyDNSResolution resolves each forge hostname and verifies it points to the expected IP. +// Unreachable hostnames (e.g., cluster-internal domains when running outside k8s) are +// skipped with a warning. At least one underlay per node must resolve successfully. +func (c *Check) verifyDNSResolution(ctx context.Context, forgeNodes map[string][]*forgeUnderlayInfo) error { + c.logger.Infof("verifying DNS resolution for %d nodes", len(forgeNodes)) + + resolver := &net.Resolver{} + for nodeName, infos := range forgeNodes { + var verified int + for _, info := range infos { + ips, err := resolver.LookupHost(ctx, info.ForgeHostname) + if err != nil { + c.logger.Warningf("node %s: DNS lookup for %s failed (may be unreachable from host): %v", + nodeName, info.ForgeHostname, err) + continue + } + + expectedIP := ipFromForgeAddr(info.ForgeAddr) + if !slices.Contains(ips, expectedIP) { + return fmt.Errorf("node %s: DNS for %s resolved to %v, expected %s", + nodeName, info.ForgeHostname, ips, expectedIP) + } + verified++ + c.logger.Debugf("node %s: DNS resolution verified: %s -> %s", nodeName, info.ForgeHostname, expectedIP) + } + if verified == 0 { + c.logger.Warningf("node %s: no forge hostnames were resolvable from this host, skipping DNS check", nodeName) + } + } + + c.logger.Info("DNS resolution verification passed") + return nil +} + +// verifyTLSCertificate connects to each forge endpoint and verifies the TLS certificate SAN. +// Unreachable endpoints (e.g., cluster-internal IPs when running outside k8s) are +// skipped with a warning. A wrong SAN on a reachable endpoint is a hard failure. +func (c *Check) verifyTLSCertificate(ctx context.Context, forgeNodes map[string][]*forgeUnderlayInfo, caCertPEM string) error { + c.logger.Infof("verifying TLS certificates for %d nodes", len(forgeNodes)) + + baseTLSConfig := &tls.Config{} + if caCertPEM != "" { + pool := x509.NewCertPool() + if !pool.AppendCertsFromPEM([]byte(caCertPEM)) { + return fmt.Errorf("failed to parse CA certificate PEM") + } + baseTLSConfig.RootCAs = pool + } + + for nodeName, infos := range forgeNodes { + var verified int + for _, info := range infos { + err := c.verifyNodeTLSCert(ctx, baseTLSConfig, nodeName, info) + if err == nil { + verified++ + continue + } + + // Distinguish connection failures (unreachable) from cert mismatches. + var netErr *net.OpError + if errors.As(err, &netErr) { + c.logger.Warningf("node %s: %v", nodeName, err) + continue + } + return err + } + if verified == 0 { + c.logger.Warningf("node %s: no forge endpoints were reachable from this host, skipping TLS check", nodeName) + } + } + + c.logger.Info("TLS certificate verification passed") + return nil +} + +func (c *Check) verifyNodeTLSCert(ctx context.Context, baseTLSConfig *tls.Config, nodeName string, info *forgeUnderlayInfo) error { + addr := net.JoinHostPort(ipFromForgeAddr(info.ForgeAddr), info.ForgeAddr.TCPPort) + + cfg := baseTLSConfig.Clone() + cfg.ServerName = info.ForgeHostname + + conn, err := (&tls.Dialer{Config: cfg}).DialContext(ctx, "tcp", addr) + if err != nil { + return fmt.Errorf("TLS dial to %s (ServerName: %s) failed: %w", + addr, info.ForgeHostname, err) + } + defer conn.Close() + + certs := conn.(*tls.Conn).ConnectionState().PeerCertificates + if len(certs) == 0 { + return fmt.Errorf("no TLS certificates from %s", addr) + } + + if !slices.Contains(certs[0].DNSNames, info.ExpectedSAN) { + return fmt.Errorf("certificate SANs %v don't include expected %s", + certs[0].DNSNames, info.ExpectedSAN) + } + + c.logger.Debugf("node %s: TLS certificate verified: %s (SAN: %s)", nodeName, addr, info.ExpectedSAN) + return nil +} + +// getCertSerials TLS-dials each reachable forge endpoint and returns a map of +// "nodeName/hostname" -> certificate serial number (hex string). +// Unreachable endpoints are silently skipped. +func (c *Check) getCertSerials(ctx context.Context, forgeNodes map[string][]*forgeUnderlayInfo, caCertPEM string) map[string]string { + baseTLSConfig := &tls.Config{} + if caCertPEM != "" { + pool := x509.NewCertPool() + if pool.AppendCertsFromPEM([]byte(caCertPEM)) { + baseTLSConfig.RootCAs = pool + } + } + + serials := make(map[string]string) + for nodeName, infos := range forgeNodes { + for _, info := range infos { + addr := net.JoinHostPort(ipFromForgeAddr(info.ForgeAddr), info.ForgeAddr.TCPPort) + + cfg := baseTLSConfig.Clone() + cfg.ServerName = info.ForgeHostname + + conn, err := (&tls.Dialer{Config: cfg}).DialContext(ctx, "tcp", addr) + if err != nil { + continue + } + + certs := conn.(*tls.Conn).ConnectionState().PeerCertificates + conn.Close() + + if len(certs) > 0 { + key := fmt.Sprintf("%s/%s", nodeName, info.ForgeHostname) + serials[key] = certs[0].SerialNumber.Text(16) + c.logger.Debugf("%s: certificate serial=%s notAfter=%s", key, serials[key], certs[0].NotAfter) + } + } + } + return serials +} diff --git a/pkg/orchestration/k8s/helpers.go b/pkg/orchestration/k8s/helpers.go index adc53e81c..558cc3973 100644 --- a/pkg/orchestration/k8s/helpers.go +++ b/pkg/orchestration/k8s/helpers.go @@ -65,7 +65,7 @@ withdrawal-addresses-whitelist: {{.WithdrawAddress}} ) // https://raw.githubusercontent.com/letsencrypt/pebble/main/test/certs/pebble.minica.pem -const pebbleCertificate = `-----BEGIN CERTIFICATE----- +const PebbleCertificate = `-----BEGIN CERTIFICATE----- MIIDPzCCAiegAwIBAgIIU0Xm9UFdQxUwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UE AxMVbWluaWNhIHJvb3QgY2EgNTM0NWU2MCAXDTI1MDkwMzIzNDAwNVoYDzIxMjUw OTAzMjM0MDA1WjAgMR4wHAYDVQQDExVtaW5pY2Egcm9vdCBjYSA1MzQ1ZTYwggEi @@ -120,7 +120,7 @@ cat > /certs/pebble-minica.crt << 'CERT' CERT cp /certs/pebble-minica.crt /usr/local/share/ca-certificates/ update-ca-certificates -cp /etc/ssl/certs/ca-certificates.crt /certs/ca-certificates.crt`, pebbleCertificate)}, +cp /etc/ssl/certs/ca-certificates.crt /certs/ca-certificates.crt`, PebbleCertificate)}, VolumeMounts: containers.VolumeMounts{ { Name: "pebble-ca-certs",