From 61fbc99e9982e595296af9362a3ff6abf4de1a4e Mon Sep 17 00:00:00 2001 From: Pasindu Tennage Date: Mon, 15 Jun 2026 19:33:51 +0000 Subject: [PATCH 1/6] istio changes [ci] Signed-off-by: pasindutennage-da Signed-off-by: Pasindu Tennage --- cluster/expected/infra/expected.json | 36 ++++++++++++++++++++++++++ cluster/pulumi/infra/src/istio.ts | 38 ++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/cluster/expected/infra/expected.json b/cluster/expected/infra/expected.json index 981e3a4c5f..ea535a388a 100644 --- a/cluster/expected/infra/expected.json +++ b/cluster/expected/infra/expected.json @@ -407,6 +407,42 @@ "provider": "", "type": "kubernetes:telemetry.istio.io/v1alpha1:Telemetry" }, + { + "custom": true, + "id": "", + "inputs": { + "apiVersion": "security.istio.io/v1beta1", + "kind": "AuthorizationPolicy", + "metadata": { + "name": "allow-public-token-registry", + "namespace": "cluster-ingress" + }, + "spec": { + "action": "ALLOW", + "rules": [ + { + "to": [ + { + "operation": { + "paths": [ + "/registry/*" + ] + } + } + ] + } + ], + "selector": { + "matchLabels": { + "app": "istio-ingress" + } + } + } + }, + "name": "allow-public-token-registry", + "provider": "", + "type": "kubernetes:security.istio.io/v1beta1:AuthorizationPolicy" + }, { "custom": true, "id": "", diff --git a/cluster/pulumi/infra/src/istio.ts b/cluster/pulumi/infra/src/istio.ts index 4e773d9df0..58b8bdf65b 100644 --- a/cluster/pulumi/infra/src/istio.ts +++ b/cluster/pulumi/infra/src/istio.ts @@ -759,6 +759,40 @@ function configurePublicInfo(ingressNs: k8s.core.v1.Namespace): k8s.apiextension : []; } +function configurePublicTokenRegistry( + ingressNs: k8s.core.v1.Namespace +): k8s.apiextensions.CustomResource[] { + return [ + new k8s.apiextensions.CustomResource('allow-public-token-registry', { + apiVersion: istioApiVersion, + kind: 'AuthorizationPolicy', + metadata: { + name: 'allow-public-token-registry', + namespace: ingressNs.metadata.name, + }, + spec: { + selector: { + matchLabels: { + app: 'istio-ingress', + }, + }, + action: 'ALLOW', + rules: [ + { + to: [ + { + operation: { + paths: ['/registry/*'], + }, + }, + ], + }, + ], + }, + }), + ]; +} + function configureSequencerHighPerformanceGrpcDestinationRules( ingressNs: k8s.core.v1.Namespace ): Array { @@ -932,6 +966,9 @@ export function configureIstio( const gateways = configureGateway(ingressNs, gwSvc, cometBftSvc, expectGKEL7Gateway); const docsAndReleases = configureDocsAndReleases(true, gateways); const publicInfo = configurePublicInfo(ingressNs.ns); + + const publicTokenRegistry = configurePublicTokenRegistry(ingressNs.ns); + const sequencerHighPerformanceGrpcRules = configureSequencerHighPerformanceGrpcDestinationRules( ingressNs.ns ); @@ -941,6 +978,7 @@ export function configureIstio( ...gateways, ...docsAndReleases, ...publicInfo, + ...publicTokenRegistry, ...sequencerHighPerformanceGrpcRules, ...[sequencerFlowControl], ], From 82528ab1896befd4814878751aab85b7545b1b49 Mon Sep 17 00:00:00 2001 From: Pasindu Tennage Date: Mon, 15 Jun 2026 20:40:03 +0000 Subject: [PATCH 2/6] cloudarmor changes and config [ci] Signed-off-by: pasindutennage-da Signed-off-by: Pasindu Tennage --- cluster/configs/shared/base.yaml | 8 +++++++- cluster/pulumi/infra/src/cloudArmor.ts | 15 +++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/cluster/configs/shared/base.yaml b/cluster/configs/shared/base.yaml index 80394718c6..b2f679b3d3 100644 --- a/cluster/configs/shared/base.yaml +++ b/cluster/configs/shared/base.yaml @@ -207,7 +207,13 @@ cloudArmor: pathPrefix: /api/scan throttleAcrossAllEndpointsAllIps: withinIntervalSeconds: 60 - maxRequestsBeforeHttp429: 0 + maxRequestsBeforeHttp429: 0 # Keeps scan completely closed to the public +# tokenRegistry: +# hostname: scan.sv-2.scratcha.global.canton.network.digitalasset.com +# pathPrefix: /registry +# throttleAcrossAllEndpointsAllIps: +# withinIntervalSeconds: 60 +# maxRequestsBeforeHttp429: 200 multiValidator: postgresPvcSize: '100Gi' resources: diff --git a/cluster/pulumi/infra/src/cloudArmor.ts b/cluster/pulumi/infra/src/cloudArmor.ts index 9ef7407a92..908c72112b 100644 --- a/cluster/pulumi/infra/src/cloudArmor.ts +++ b/cluster/pulumi/infra/src/cloudArmor.ts @@ -227,15 +227,14 @@ function allowedPathsCondition(scanExternalRateLimits: PerEndpointLimits, pathPr .filter(p => !p.isBanned) .map(p => p.pathPrefix); - // Factor out /api/scan/ prefix (with trailing slash) - const scanPrefix = '/api/scan/'; - const scanPathRxs = pathPrefixes - .filter(p => p.startsWith(scanPrefix)) - .map(p => _.escapeRegExp(p.substring(scanPrefix.length))); // Remove prefix for factoring + const basePrefix = pathPrefix.endsWith('/') ? pathPrefix : `${pathPrefix}/`; + const dynamicPathRxs = pathPrefixes + .filter(p => p.startsWith(basePrefix)) + .map(p => _.escapeRegExp(p.substring(basePrefix.length))); // Build regex pattern - if (scanPathRxs.length > 0) { - const regexPattern = `${scanPrefix}(${scanPathRxs.join('|')})`; + if (dynamicPathRxs.length > 0) { + const regexPattern = `${basePrefix}(${dynamicPathRxs.join('|')})`; const pathExpr = `request.path.matches(R"^${regexPattern}")`; // limit from https://docs.cloud.google.com/armor/quotas#limits @@ -249,7 +248,7 @@ function allowedPathsCondition(scanExternalRateLimits: PerEndpointLimits, pathPr return pathExpr; } else { - // Fallback to simple prefix if no scan paths + // Fallback to simple prefix matching if no paths were resolved return `request.path.startsWith(R"${pathPrefix}")`; } } else { From 4c4a5b175502defee64eb50b717b2a2824c4c228 Mon Sep 17 00:00:00 2001 From: Pasindu Tennage Date: Mon, 15 Jun 2026 21:50:26 +0000 Subject: [PATCH 3/6] ratelimiting [ci] Signed-off-by: pasindutennage-da Signed-off-by: Pasindu Tennage --- .../configs/shared/rate-limits/v0-acs.yaml | 42 ++ .../scratchneta/config.resolved.yaml | 42 ++ .../scratchnetb/config.resolved.yaml | 42 ++ .../scratchnetc/config.resolved.yaml | 42 ++ .../scratchnetd/config.resolved.yaml | 42 ++ .../scratchnete/config.resolved.yaml | 42 ++ cluster/expected/canton-network/expected.json | 606 +++++++++++++++++- cluster/expected/sv-runbook/expected.json | 294 +++++++++ .../pulumi/common/src/config/scanEndpoints.ts | 42 ++ .../common/src/ratelimit/envoyRateLimiter.ts | 32 +- 10 files changed, 1208 insertions(+), 18 deletions(-) diff --git a/cluster/configs/shared/rate-limits/v0-acs.yaml b/cluster/configs/shared/rate-limits/v0-acs.yaml index f4d90428c0..4d4e6a1ab3 100644 --- a/cluster/configs/shared/rate-limits/v0-acs.yaml +++ b/cluster/configs/shared/rate-limits/v0-acs.yaml @@ -6,3 +6,45 @@ rateLimits: maxTokens: 10 tokensPerFill: 5 fillInterval: 60s + /registry/allocations/v1: + name: registry-allocations + type: limited + clientIp: true + maxTokens: 10 + tokensPerFill: 5 + fillInterval: 60s + /registry/metadata/v1/info: + name: registry-metadata-info + type: limited + clientIp: true + maxTokens: 10 + tokensPerFill: 5 + fillInterval: 60s + /registry/metadata/v1/instruments: + name: registry-metadata-instruments + type: limited + clientIp: true + maxTokens: 10 + tokensPerFill: 5 + fillInterval: 60s + /registry/allocation-instruction/v1/allocation-factory: + name: registry-allocation-factory + type: limited + clientIp: true + maxTokens: 10 + tokensPerFill: 5 + fillInterval: 60s + /registry/transfer-instruction/v1: + name: registry-transfer-instruction + type: limited + clientIp: true + maxTokens: 10 + tokensPerFill: 5 + fillInterval: 60s + /registry/transfer-instruction/v1/transfer-factory: + name: registry-transfer-factory + type: limited + clientIp: true + maxTokens: 10 + tokensPerFill: 5 + fillInterval: 60s diff --git a/cluster/deployment/scratchneta/config.resolved.yaml b/cluster/deployment/scratchneta/config.resolved.yaml index b4bcd1b67e..c321428e4a 100644 --- a/cluster/deployment/scratchneta/config.resolved.yaml +++ b/cluster/deployment/scratchneta/config.resolved.yaml @@ -338,6 +338,48 @@ sv: /api/scan/version: name: 'version' type: 'unlimited' + /registry/allocation-instruction/v1/allocation-factory: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-allocation-factory' + tokensPerFill: 5 + type: 'limited' + /registry/allocations/v1: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-allocations' + tokensPerFill: 5 + type: 'limited' + /registry/metadata/v1/info: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-metadata-info' + tokensPerFill: 5 + type: 'limited' + /registry/metadata/v1/instruments: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-metadata-instruments' + tokensPerFill: 5 + type: 'limited' + /registry/transfer-instruction/v1: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-transfer-instruction' + tokensPerFill: 5 + type: 'limited' + /registry/transfer-instruction/v1/transfer-factory: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-transfer-factory' + tokensPerFill: 5 + type: 'limited' svs: sv: cometbft: diff --git a/cluster/deployment/scratchnetb/config.resolved.yaml b/cluster/deployment/scratchnetb/config.resolved.yaml index b4bcd1b67e..c321428e4a 100644 --- a/cluster/deployment/scratchnetb/config.resolved.yaml +++ b/cluster/deployment/scratchnetb/config.resolved.yaml @@ -338,6 +338,48 @@ sv: /api/scan/version: name: 'version' type: 'unlimited' + /registry/allocation-instruction/v1/allocation-factory: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-allocation-factory' + tokensPerFill: 5 + type: 'limited' + /registry/allocations/v1: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-allocations' + tokensPerFill: 5 + type: 'limited' + /registry/metadata/v1/info: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-metadata-info' + tokensPerFill: 5 + type: 'limited' + /registry/metadata/v1/instruments: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-metadata-instruments' + tokensPerFill: 5 + type: 'limited' + /registry/transfer-instruction/v1: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-transfer-instruction' + tokensPerFill: 5 + type: 'limited' + /registry/transfer-instruction/v1/transfer-factory: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-transfer-factory' + tokensPerFill: 5 + type: 'limited' svs: sv: cometbft: diff --git a/cluster/deployment/scratchnetc/config.resolved.yaml b/cluster/deployment/scratchnetc/config.resolved.yaml index b4bcd1b67e..c321428e4a 100644 --- a/cluster/deployment/scratchnetc/config.resolved.yaml +++ b/cluster/deployment/scratchnetc/config.resolved.yaml @@ -338,6 +338,48 @@ sv: /api/scan/version: name: 'version' type: 'unlimited' + /registry/allocation-instruction/v1/allocation-factory: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-allocation-factory' + tokensPerFill: 5 + type: 'limited' + /registry/allocations/v1: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-allocations' + tokensPerFill: 5 + type: 'limited' + /registry/metadata/v1/info: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-metadata-info' + tokensPerFill: 5 + type: 'limited' + /registry/metadata/v1/instruments: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-metadata-instruments' + tokensPerFill: 5 + type: 'limited' + /registry/transfer-instruction/v1: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-transfer-instruction' + tokensPerFill: 5 + type: 'limited' + /registry/transfer-instruction/v1/transfer-factory: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-transfer-factory' + tokensPerFill: 5 + type: 'limited' svs: sv: cometbft: diff --git a/cluster/deployment/scratchnetd/config.resolved.yaml b/cluster/deployment/scratchnetd/config.resolved.yaml index b4bcd1b67e..c321428e4a 100644 --- a/cluster/deployment/scratchnetd/config.resolved.yaml +++ b/cluster/deployment/scratchnetd/config.resolved.yaml @@ -338,6 +338,48 @@ sv: /api/scan/version: name: 'version' type: 'unlimited' + /registry/allocation-instruction/v1/allocation-factory: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-allocation-factory' + tokensPerFill: 5 + type: 'limited' + /registry/allocations/v1: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-allocations' + tokensPerFill: 5 + type: 'limited' + /registry/metadata/v1/info: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-metadata-info' + tokensPerFill: 5 + type: 'limited' + /registry/metadata/v1/instruments: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-metadata-instruments' + tokensPerFill: 5 + type: 'limited' + /registry/transfer-instruction/v1: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-transfer-instruction' + tokensPerFill: 5 + type: 'limited' + /registry/transfer-instruction/v1/transfer-factory: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-transfer-factory' + tokensPerFill: 5 + type: 'limited' svs: sv: cometbft: diff --git a/cluster/deployment/scratchnete/config.resolved.yaml b/cluster/deployment/scratchnete/config.resolved.yaml index b4bcd1b67e..c321428e4a 100644 --- a/cluster/deployment/scratchnete/config.resolved.yaml +++ b/cluster/deployment/scratchnete/config.resolved.yaml @@ -338,6 +338,48 @@ sv: /api/scan/version: name: 'version' type: 'unlimited' + /registry/allocation-instruction/v1/allocation-factory: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-allocation-factory' + tokensPerFill: 5 + type: 'limited' + /registry/allocations/v1: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-allocations' + tokensPerFill: 5 + type: 'limited' + /registry/metadata/v1/info: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-metadata-info' + tokensPerFill: 5 + type: 'limited' + /registry/metadata/v1/instruments: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-metadata-instruments' + tokensPerFill: 5 + type: 'limited' + /registry/transfer-instruction/v1: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-transfer-instruction' + tokensPerFill: 5 + type: 'limited' + /registry/transfer-instruction/v1/transfer-factory: + clientIp: true + fillInterval: '60s' + maxTokens: 10 + name: 'registry-transfer-factory' + tokensPerFill: 5 + type: 'limited' svs: sv: cometbft: diff --git a/cluster/expected/canton-network/expected.json b/cluster/expected/canton-network/expected.json index 330f9a533d..eb1d00f699 100644 --- a/cluster/expected/canton-network/expected.json +++ b/cluster/expected/canton-network/expected.json @@ -1790,6 +1790,54 @@ "/api/scan/version": { "name": "version", "type": "unlimited" + }, + "/registry/allocation-instruction/v1/allocation-factory": { + "clientIp": true, + "fillInterval": "60s", + "maxTokens": 10, + "name": "registry-allocation-factory", + "tokensPerFill": 5, + "type": "limited" + }, + "/registry/allocations/v1": { + "clientIp": true, + "fillInterval": "60s", + "maxTokens": 10, + "name": "registry-allocations", + "tokensPerFill": 5, + "type": "limited" + }, + "/registry/metadata/v1/info": { + "clientIp": true, + "fillInterval": "60s", + "maxTokens": 10, + "name": "registry-metadata-info", + "tokensPerFill": 5, + "type": "limited" + }, + "/registry/metadata/v1/instruments": { + "clientIp": true, + "fillInterval": "60s", + "maxTokens": 10, + "name": "registry-metadata-instruments", + "tokensPerFill": 5, + "type": "limited" + }, + "/registry/transfer-instruction/v1": { + "clientIp": true, + "fillInterval": "60s", + "maxTokens": 10, + "name": "registry-transfer-instruction", + "tokensPerFill": 5, + "type": "limited" + }, + "/registry/transfer-instruction/v1/transfer-factory": { + "clientIp": true, + "fillInterval": "60s", + "maxTokens": 10, + "name": "registry-transfer-factory", + "tokensPerFill": 5, + "type": "limited" } } }, @@ -2017,6 +2065,54 @@ "/api/scan/version": { "name": "version", "type": "unlimited" + }, + "/registry/allocation-instruction/v1/allocation-factory": { + "clientIp": true, + "fillInterval": "60s", + "maxTokens": 10, + "name": "registry-allocation-factory", + "tokensPerFill": 5, + "type": "limited" + }, + "/registry/allocations/v1": { + "clientIp": true, + "fillInterval": "60s", + "maxTokens": 10, + "name": "registry-allocations", + "tokensPerFill": 5, + "type": "limited" + }, + "/registry/metadata/v1/info": { + "clientIp": true, + "fillInterval": "60s", + "maxTokens": 10, + "name": "registry-metadata-info", + "tokensPerFill": 5, + "type": "limited" + }, + "/registry/metadata/v1/instruments": { + "clientIp": true, + "fillInterval": "60s", + "maxTokens": 10, + "name": "registry-metadata-instruments", + "tokensPerFill": 5, + "type": "limited" + }, + "/registry/transfer-instruction/v1": { + "clientIp": true, + "fillInterval": "60s", + "maxTokens": 10, + "name": "registry-transfer-instruction", + "tokensPerFill": 5, + "type": "limited" + }, + "/registry/transfer-instruction/v1/transfer-factory": { + "clientIp": true, + "fillInterval": "60s", + "maxTokens": 10, + "name": "registry-transfer-factory", + "tokensPerFill": 5, + "type": "limited" } } }, @@ -2558,19 +2654,265 @@ "header_name": "x-forwarded-for" } } - ] - } - ] - }, - "typed_per_filter_config": { - "envoy.filters.http.local_ratelimit": { - "@type": "type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit", - "descriptors": [ + ] + }, + { + "actions": [ + { + "header_value_match": { + "descriptor_value": "registry-allocations", + "expect_match": true, + "headers": [ + { + "name": ":path", + "string_match": { + "ignore_case": true, + "prefix": "/registry/allocations/v1" + } + } + ] + } + }, + { + "request_headers": { + "descriptor_key": "client_ip", + "header_name": "x-forwarded-for" + } + } + ] + }, + { + "actions": [ + { + "header_value_match": { + "descriptor_value": "registry-metadata-info", + "expect_match": true, + "headers": [ + { + "name": ":path", + "string_match": { + "ignore_case": true, + "prefix": "/registry/metadata/v1/info" + } + } + ] + } + }, + { + "request_headers": { + "descriptor_key": "client_ip", + "header_name": "x-forwarded-for" + } + } + ] + }, + { + "actions": [ + { + "header_value_match": { + "descriptor_value": "registry-metadata-instruments", + "expect_match": true, + "headers": [ + { + "name": ":path", + "string_match": { + "ignore_case": true, + "prefix": "/registry/metadata/v1/instruments" + } + } + ] + } + }, + { + "request_headers": { + "descriptor_key": "client_ip", + "header_name": "x-forwarded-for" + } + } + ] + }, + { + "actions": [ + { + "header_value_match": { + "descriptor_value": "registry-allocation-factory", + "expect_match": true, + "headers": [ + { + "name": ":path", + "string_match": { + "ignore_case": true, + "prefix": "/registry/allocation-instruction/v1/allocation-factory" + } + } + ] + } + }, + { + "request_headers": { + "descriptor_key": "client_ip", + "header_name": "x-forwarded-for" + } + } + ] + }, + { + "actions": [ + { + "header_value_match": { + "descriptor_value": "registry-transfer-instruction", + "expect_match": true, + "headers": [ + { + "name": ":path", + "string_match": { + "ignore_case": true, + "prefix": "/registry/transfer-instruction/v1" + } + } + ] + } + }, + { + "request_headers": { + "descriptor_key": "client_ip", + "header_name": "x-forwarded-for" + } + } + ] + }, + { + "actions": [ + { + "header_value_match": { + "descriptor_value": "registry-transfer-factory", + "expect_match": true, + "headers": [ + { + "name": ":path", + "string_match": { + "ignore_case": true, + "prefix": "/registry/transfer-instruction/v1/transfer-factory" + } + } + ] + } + }, + { + "request_headers": { + "descriptor_key": "client_ip", + "header_name": "x-forwarded-for" + } + } + ] + } + ] + }, + "typed_per_filter_config": { + "envoy.filters.http.local_ratelimit": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit", + "descriptors": [ + { + "entries": [ + { + "key": "header_match", + "value": "acs" + }, + { + "key": "client_ip" + } + ], + "token_bucket": { + "fill_interval": "60s", + "max_tokens": 10, + "tokens_per_fill": 5 + } + }, + { + "entries": [ + { + "key": "header_match", + "value": "registry-allocations" + }, + { + "key": "client_ip" + } + ], + "token_bucket": { + "fill_interval": "60s", + "max_tokens": 10, + "tokens_per_fill": 5 + } + }, + { + "entries": [ + { + "key": "header_match", + "value": "registry-metadata-info" + }, + { + "key": "client_ip" + } + ], + "token_bucket": { + "fill_interval": "60s", + "max_tokens": 10, + "tokens_per_fill": 5 + } + }, + { + "entries": [ + { + "key": "header_match", + "value": "registry-metadata-instruments" + }, + { + "key": "client_ip" + } + ], + "token_bucket": { + "fill_interval": "60s", + "max_tokens": 10, + "tokens_per_fill": 5 + } + }, + { + "entries": [ + { + "key": "header_match", + "value": "registry-allocation-factory" + }, + { + "key": "client_ip" + } + ], + "token_bucket": { + "fill_interval": "60s", + "max_tokens": 10, + "tokens_per_fill": 5 + } + }, + { + "entries": [ + { + "key": "header_match", + "value": "registry-transfer-instruction" + }, + { + "key": "client_ip" + } + ], + "token_bucket": { + "fill_interval": "60s", + "max_tokens": 10, + "tokens_per_fill": 5 + } + }, { "entries": [ { "key": "header_match", - "value": "acs" + "value": "registry-transfer-factory" }, { "key": "client_ip" @@ -3780,6 +4122,156 @@ } } ] + }, + { + "actions": [ + { + "header_value_match": { + "descriptor_value": "registry-allocations", + "expect_match": true, + "headers": [ + { + "name": ":path", + "string_match": { + "ignore_case": true, + "prefix": "/registry/allocations/v1" + } + } + ] + } + }, + { + "request_headers": { + "descriptor_key": "client_ip", + "header_name": "x-forwarded-for" + } + } + ] + }, + { + "actions": [ + { + "header_value_match": { + "descriptor_value": "registry-metadata-info", + "expect_match": true, + "headers": [ + { + "name": ":path", + "string_match": { + "ignore_case": true, + "prefix": "/registry/metadata/v1/info" + } + } + ] + } + }, + { + "request_headers": { + "descriptor_key": "client_ip", + "header_name": "x-forwarded-for" + } + } + ] + }, + { + "actions": [ + { + "header_value_match": { + "descriptor_value": "registry-metadata-instruments", + "expect_match": true, + "headers": [ + { + "name": ":path", + "string_match": { + "ignore_case": true, + "prefix": "/registry/metadata/v1/instruments" + } + } + ] + } + }, + { + "request_headers": { + "descriptor_key": "client_ip", + "header_name": "x-forwarded-for" + } + } + ] + }, + { + "actions": [ + { + "header_value_match": { + "descriptor_value": "registry-allocation-factory", + "expect_match": true, + "headers": [ + { + "name": ":path", + "string_match": { + "ignore_case": true, + "prefix": "/registry/allocation-instruction/v1/allocation-factory" + } + } + ] + } + }, + { + "request_headers": { + "descriptor_key": "client_ip", + "header_name": "x-forwarded-for" + } + } + ] + }, + { + "actions": [ + { + "header_value_match": { + "descriptor_value": "registry-transfer-instruction", + "expect_match": true, + "headers": [ + { + "name": ":path", + "string_match": { + "ignore_case": true, + "prefix": "/registry/transfer-instruction/v1" + } + } + ] + } + }, + { + "request_headers": { + "descriptor_key": "client_ip", + "header_name": "x-forwarded-for" + } + } + ] + }, + { + "actions": [ + { + "header_value_match": { + "descriptor_value": "registry-transfer-factory", + "expect_match": true, + "headers": [ + { + "name": ":path", + "string_match": { + "ignore_case": true, + "prefix": "/registry/transfer-instruction/v1/transfer-factory" + } + } + ] + } + }, + { + "request_headers": { + "descriptor_key": "client_ip", + "header_name": "x-forwarded-for" + } + } + ] } ] }, @@ -3802,6 +4294,102 @@ "max_tokens": 10, "tokens_per_fill": 5 } + }, + { + "entries": [ + { + "key": "header_match", + "value": "registry-allocations" + }, + { + "key": "client_ip" + } + ], + "token_bucket": { + "fill_interval": "60s", + "max_tokens": 10, + "tokens_per_fill": 5 + } + }, + { + "entries": [ + { + "key": "header_match", + "value": "registry-metadata-info" + }, + { + "key": "client_ip" + } + ], + "token_bucket": { + "fill_interval": "60s", + "max_tokens": 10, + "tokens_per_fill": 5 + } + }, + { + "entries": [ + { + "key": "header_match", + "value": "registry-metadata-instruments" + }, + { + "key": "client_ip" + } + ], + "token_bucket": { + "fill_interval": "60s", + "max_tokens": 10, + "tokens_per_fill": 5 + } + }, + { + "entries": [ + { + "key": "header_match", + "value": "registry-allocation-factory" + }, + { + "key": "client_ip" + } + ], + "token_bucket": { + "fill_interval": "60s", + "max_tokens": 10, + "tokens_per_fill": 5 + } + }, + { + "entries": [ + { + "key": "header_match", + "value": "registry-transfer-instruction" + }, + { + "key": "client_ip" + } + ], + "token_bucket": { + "fill_interval": "60s", + "max_tokens": 10, + "tokens_per_fill": 5 + } + }, + { + "entries": [ + { + "key": "header_match", + "value": "registry-transfer-factory" + }, + { + "key": "client_ip" + } + ], + "token_bucket": { + "fill_interval": "60s", + "max_tokens": 10, + "tokens_per_fill": 5 + } } ], "filter_enabled": { diff --git a/cluster/expected/sv-runbook/expected.json b/cluster/expected/sv-runbook/expected.json index ef742b91e2..57411cdfe4 100644 --- a/cluster/expected/sv-runbook/expected.json +++ b/cluster/expected/sv-runbook/expected.json @@ -1041,6 +1041,54 @@ "/api/scan/version": { "name": "version", "type": "unlimited" + }, + "/registry/allocation-instruction/v1/allocation-factory": { + "clientIp": true, + "fillInterval": "60s", + "maxTokens": 10, + "name": "registry-allocation-factory", + "tokensPerFill": 5, + "type": "limited" + }, + "/registry/allocations/v1": { + "clientIp": true, + "fillInterval": "60s", + "maxTokens": 10, + "name": "registry-allocations", + "tokensPerFill": 5, + "type": "limited" + }, + "/registry/metadata/v1/info": { + "clientIp": true, + "fillInterval": "60s", + "maxTokens": 10, + "name": "registry-metadata-info", + "tokensPerFill": 5, + "type": "limited" + }, + "/registry/metadata/v1/instruments": { + "clientIp": true, + "fillInterval": "60s", + "maxTokens": 10, + "name": "registry-metadata-instruments", + "tokensPerFill": 5, + "type": "limited" + }, + "/registry/transfer-instruction/v1": { + "clientIp": true, + "fillInterval": "60s", + "maxTokens": 10, + "name": "registry-transfer-instruction", + "tokensPerFill": 5, + "type": "limited" + }, + "/registry/transfer-instruction/v1/transfer-factory": { + "clientIp": true, + "fillInterval": "60s", + "maxTokens": 10, + "name": "registry-transfer-factory", + "tokensPerFill": 5, + "type": "limited" } } }, @@ -1534,6 +1582,156 @@ } } ] + }, + { + "actions": [ + { + "header_value_match": { + "descriptor_value": "registry-allocations", + "expect_match": true, + "headers": [ + { + "name": ":path", + "string_match": { + "ignore_case": true, + "prefix": "/registry/allocations/v1" + } + } + ] + } + }, + { + "request_headers": { + "descriptor_key": "client_ip", + "header_name": "x-forwarded-for" + } + } + ] + }, + { + "actions": [ + { + "header_value_match": { + "descriptor_value": "registry-metadata-info", + "expect_match": true, + "headers": [ + { + "name": ":path", + "string_match": { + "ignore_case": true, + "prefix": "/registry/metadata/v1/info" + } + } + ] + } + }, + { + "request_headers": { + "descriptor_key": "client_ip", + "header_name": "x-forwarded-for" + } + } + ] + }, + { + "actions": [ + { + "header_value_match": { + "descriptor_value": "registry-metadata-instruments", + "expect_match": true, + "headers": [ + { + "name": ":path", + "string_match": { + "ignore_case": true, + "prefix": "/registry/metadata/v1/instruments" + } + } + ] + } + }, + { + "request_headers": { + "descriptor_key": "client_ip", + "header_name": "x-forwarded-for" + } + } + ] + }, + { + "actions": [ + { + "header_value_match": { + "descriptor_value": "registry-allocation-factory", + "expect_match": true, + "headers": [ + { + "name": ":path", + "string_match": { + "ignore_case": true, + "prefix": "/registry/allocation-instruction/v1/allocation-factory" + } + } + ] + } + }, + { + "request_headers": { + "descriptor_key": "client_ip", + "header_name": "x-forwarded-for" + } + } + ] + }, + { + "actions": [ + { + "header_value_match": { + "descriptor_value": "registry-transfer-instruction", + "expect_match": true, + "headers": [ + { + "name": ":path", + "string_match": { + "ignore_case": true, + "prefix": "/registry/transfer-instruction/v1" + } + } + ] + } + }, + { + "request_headers": { + "descriptor_key": "client_ip", + "header_name": "x-forwarded-for" + } + } + ] + }, + { + "actions": [ + { + "header_value_match": { + "descriptor_value": "registry-transfer-factory", + "expect_match": true, + "headers": [ + { + "name": ":path", + "string_match": { + "ignore_case": true, + "prefix": "/registry/transfer-instruction/v1/transfer-factory" + } + } + ] + } + }, + { + "request_headers": { + "descriptor_key": "client_ip", + "header_name": "x-forwarded-for" + } + } + ] } ] }, @@ -1556,6 +1754,102 @@ "max_tokens": 10, "tokens_per_fill": 5 } + }, + { + "entries": [ + { + "key": "header_match", + "value": "registry-allocations" + }, + { + "key": "client_ip" + } + ], + "token_bucket": { + "fill_interval": "60s", + "max_tokens": 10, + "tokens_per_fill": 5 + } + }, + { + "entries": [ + { + "key": "header_match", + "value": "registry-metadata-info" + }, + { + "key": "client_ip" + } + ], + "token_bucket": { + "fill_interval": "60s", + "max_tokens": 10, + "tokens_per_fill": 5 + } + }, + { + "entries": [ + { + "key": "header_match", + "value": "registry-metadata-instruments" + }, + { + "key": "client_ip" + } + ], + "token_bucket": { + "fill_interval": "60s", + "max_tokens": 10, + "tokens_per_fill": 5 + } + }, + { + "entries": [ + { + "key": "header_match", + "value": "registry-allocation-factory" + }, + { + "key": "client_ip" + } + ], + "token_bucket": { + "fill_interval": "60s", + "max_tokens": 10, + "tokens_per_fill": 5 + } + }, + { + "entries": [ + { + "key": "header_match", + "value": "registry-transfer-instruction" + }, + { + "key": "client_ip" + } + ], + "token_bucket": { + "fill_interval": "60s", + "max_tokens": 10, + "tokens_per_fill": 5 + } + }, + { + "entries": [ + { + "key": "header_match", + "value": "registry-transfer-factory" + }, + { + "key": "client_ip" + } + ], + "token_bucket": { + "fill_interval": "60s", + "max_tokens": 10, + "tokens_per_fill": 5 + } } ], "filter_enabled": { diff --git a/cluster/pulumi/common/src/config/scanEndpoints.ts b/cluster/pulumi/common/src/config/scanEndpoints.ts index 1cd62eb796..fd16aaa82a 100644 --- a/cluster/pulumi/common/src/config/scanEndpoints.ts +++ b/cluster/pulumi/common/src/config/scanEndpoints.ts @@ -6,6 +6,24 @@ import { z } from 'zod'; import { readAndParseYaml } from './configLoader'; const scanYamlPath = path.join(__dirname, '../../../../../apps/scan/src/main/openapi/scan.yaml'); +const tokenRegistryYamlPaths = [ + path.join( + __dirname, + '../../../../../token-standard/splice-api-token-metadata-v1/openapi/token-metadata-v1.yaml' + ), + path.join( + __dirname, + '../../../../../token-standard/splice-api-token-allocation-v1/openapi/allocation-v1.yaml' + ), + path.join( + __dirname, + '../../../../../token-standard/splice-api-token-allocation-instruction-v1/openapi/allocation-instruction-v1.yaml' + ), + path.join( + __dirname, + '../../../../../token-standard/splice-api-token-transfer-instruction-v1/openapi/transfer-instruction-v1.yaml' + ), +]; const MinimalOpenApiSchema = z.object({ paths: z.object({}).catchall(z.unknown()).default({}) }); @@ -37,3 +55,27 @@ export function parseScanYamlEndpoints(): string[] { return Array.from(endpoints).sort(); } + +/** + * Read all token registry standard OpenAPI paths into normalized `/registry/...` endpoint prefixes. + */ +export function parseTokenRegistrySpecEndpoints(): string[] { + const endpoints = new Set(); + + for (const yamlPath of tokenRegistryYamlPaths) { + const yaml = MinimalOpenApiSchema.parse(readAndParseYaml(yamlPath)); + const paths = yaml.paths; + + for (let fullPath of Object.keys(paths)) { + const paramIndex = fullPath.indexOf('{'); + if (paramIndex !== -1) { + const lastSlash = fullPath.lastIndexOf('/', paramIndex); + fullPath = fullPath.substring(0, lastSlash); + } + + endpoints.add(fullPath); + } + } + + return Array.from(endpoints).sort(); +} diff --git a/cluster/pulumi/common/src/ratelimit/envoyRateLimiter.ts b/cluster/pulumi/common/src/ratelimit/envoyRateLimiter.ts index 7a47f69d59..9023e8e3eb 100644 --- a/cluster/pulumi/common/src/ratelimit/envoyRateLimiter.ts +++ b/cluster/pulumi/common/src/ratelimit/envoyRateLimiter.ts @@ -3,7 +3,7 @@ import * as k8s from '@pulumi/kubernetes'; import * as pulumi from '@pulumi/pulumi'; -import { parseScanYamlEndpoints } from '../config/scanEndpoints'; +import { parseScanYamlEndpoints, parseTokenRegistrySpecEndpoints } from '../config/scanEndpoints'; interface Limits { maxTokens: number; @@ -74,7 +74,9 @@ export function extractPathPrefixes( const isBanned = rl.type === 'banned'; return { pathPrefix, isBanned }; }) - .filter(info => info.pathPrefix.startsWith('/api/scan')); + .filter( + info => info.pathPrefix.startsWith('/api/scan') || info.pathPrefix.startsWith('/registry') + ); } function validateEndpointCoverage( @@ -115,16 +117,28 @@ function validateEffectiveRateLimits( const { missing, orphaned } = validateEndpointCoverage(scanEndpoints, configuredScanPrefixes); - if (missing.length > 0 || orphaned.length > 0) { + const tokenRegistryEndpoints = parseTokenRegistrySpecEndpoints(); + + const configuredRegistryPrefixes = Object.keys(args.rateLimits || {}).filter(pathPrefix => + pathPrefix.startsWith('/registry') + ); + + const registryValidation = validateEndpointCoverage( + tokenRegistryEndpoints, + configuredRegistryPrefixes + ); + + const totalMissing = missing.concat(registryValidation.missing); + const totalOrphaned = orphaned.concat(registryValidation.orphaned); + + if (totalMissing.length > 0 || totalOrphaned.length > 0) { const errorParts: string[] = ['Rate limit configuration errors:']; - if (missing.length > 0) { - errorParts.push( - `- Missing rate limit prefixes for scan.yaml endpoints: ${missing.join(', ')}` - ); + if (totalMissing.length > 0) { + errorParts.push(`- Missing rate limit prefixes for endpoints: ${totalMissing.join(', ')}`); } - if (orphaned.length > 0) { + if (totalOrphaned.length > 0) { errorParts.push( - `- Orphaned rate limit prefixes not matching any scan.yaml endpoint: ${orphaned.join(', ')}` + `- Orphaned rate limit prefixes not matching any schema route: ${totalOrphaned.join(', ')}` ); } throw new Error(errorParts.join('\n')); From c789168e823a363121382ed81b7ed741689f44ba Mon Sep 17 00:00:00 2001 From: Pasindu Tennage Date: Mon, 15 Jun 2026 22:01:27 +0000 Subject: [PATCH 4/6] configs [ci] Signed-off-by: pasindutennage-da Signed-off-by: Pasindu Tennage --- cluster/configs/shared/base.yaml | 12 ++++++------ cluster/deployment/scratchneta/config.resolved.yaml | 6 ++++++ cluster/deployment/scratchnetb/config.resolved.yaml | 6 ++++++ cluster/deployment/scratchnetc/config.resolved.yaml | 6 ++++++ cluster/deployment/scratchnetd/config.resolved.yaml | 6 ++++++ cluster/deployment/scratchnete/config.resolved.yaml | 6 ++++++ 6 files changed, 36 insertions(+), 6 deletions(-) diff --git a/cluster/configs/shared/base.yaml b/cluster/configs/shared/base.yaml index b2f679b3d3..71b4036b73 100644 --- a/cluster/configs/shared/base.yaml +++ b/cluster/configs/shared/base.yaml @@ -208,12 +208,12 @@ cloudArmor: throttleAcrossAllEndpointsAllIps: withinIntervalSeconds: 60 maxRequestsBeforeHttp429: 0 # Keeps scan completely closed to the public -# tokenRegistry: -# hostname: scan.sv-2.scratcha.global.canton.network.digitalasset.com -# pathPrefix: /registry -# throttleAcrossAllEndpointsAllIps: -# withinIntervalSeconds: 60 -# maxRequestsBeforeHttp429: 200 + tokenRegistry: + hostname: scan.sv-2.scratcha.global.canton.network.digitalasset.com + pathPrefix: /registry + throttleAcrossAllEndpointsAllIps: + withinIntervalSeconds: 60 + maxRequestsBeforeHttp429: 200 multiValidator: postgresPvcSize: '100Gi' resources: diff --git a/cluster/deployment/scratchneta/config.resolved.yaml b/cluster/deployment/scratchneta/config.resolved.yaml index c321428e4a..6f85dd642c 100644 --- a/cluster/deployment/scratchneta/config.resolved.yaml +++ b/cluster/deployment/scratchneta/config.resolved.yaml @@ -9,6 +9,12 @@ cloudArmor: throttleAcrossAllEndpointsAllIps: maxRequestsBeforeHttp429: 0 withinIntervalSeconds: 60 + tokenRegistry: + hostname: 'scan.sv-2.scratcha.global.canton.network.digitalasset.com' + pathPrefix: '/registry' + throttleAcrossAllEndpointsAllIps: + maxRequestsBeforeHttp429: 200 + withinIntervalSeconds: 60 cluster: hyperdiskSupport: enabled: true diff --git a/cluster/deployment/scratchnetb/config.resolved.yaml b/cluster/deployment/scratchnetb/config.resolved.yaml index c321428e4a..6f85dd642c 100644 --- a/cluster/deployment/scratchnetb/config.resolved.yaml +++ b/cluster/deployment/scratchnetb/config.resolved.yaml @@ -9,6 +9,12 @@ cloudArmor: throttleAcrossAllEndpointsAllIps: maxRequestsBeforeHttp429: 0 withinIntervalSeconds: 60 + tokenRegistry: + hostname: 'scan.sv-2.scratcha.global.canton.network.digitalasset.com' + pathPrefix: '/registry' + throttleAcrossAllEndpointsAllIps: + maxRequestsBeforeHttp429: 200 + withinIntervalSeconds: 60 cluster: hyperdiskSupport: enabled: true diff --git a/cluster/deployment/scratchnetc/config.resolved.yaml b/cluster/deployment/scratchnetc/config.resolved.yaml index c321428e4a..6f85dd642c 100644 --- a/cluster/deployment/scratchnetc/config.resolved.yaml +++ b/cluster/deployment/scratchnetc/config.resolved.yaml @@ -9,6 +9,12 @@ cloudArmor: throttleAcrossAllEndpointsAllIps: maxRequestsBeforeHttp429: 0 withinIntervalSeconds: 60 + tokenRegistry: + hostname: 'scan.sv-2.scratcha.global.canton.network.digitalasset.com' + pathPrefix: '/registry' + throttleAcrossAllEndpointsAllIps: + maxRequestsBeforeHttp429: 200 + withinIntervalSeconds: 60 cluster: hyperdiskSupport: enabled: true diff --git a/cluster/deployment/scratchnetd/config.resolved.yaml b/cluster/deployment/scratchnetd/config.resolved.yaml index c321428e4a..6f85dd642c 100644 --- a/cluster/deployment/scratchnetd/config.resolved.yaml +++ b/cluster/deployment/scratchnetd/config.resolved.yaml @@ -9,6 +9,12 @@ cloudArmor: throttleAcrossAllEndpointsAllIps: maxRequestsBeforeHttp429: 0 withinIntervalSeconds: 60 + tokenRegistry: + hostname: 'scan.sv-2.scratcha.global.canton.network.digitalasset.com' + pathPrefix: '/registry' + throttleAcrossAllEndpointsAllIps: + maxRequestsBeforeHttp429: 200 + withinIntervalSeconds: 60 cluster: hyperdiskSupport: enabled: true diff --git a/cluster/deployment/scratchnete/config.resolved.yaml b/cluster/deployment/scratchnete/config.resolved.yaml index c321428e4a..6f85dd642c 100644 --- a/cluster/deployment/scratchnete/config.resolved.yaml +++ b/cluster/deployment/scratchnete/config.resolved.yaml @@ -9,6 +9,12 @@ cloudArmor: throttleAcrossAllEndpointsAllIps: maxRequestsBeforeHttp429: 0 withinIntervalSeconds: 60 + tokenRegistry: + hostname: 'scan.sv-2.scratcha.global.canton.network.digitalasset.com' + pathPrefix: '/registry' + throttleAcrossAllEndpointsAllIps: + maxRequestsBeforeHttp429: 200 + withinIntervalSeconds: 60 cluster: hyperdiskSupport: enabled: true From 18a704c4b82b4d0f861c98ffad442916298272c7 Mon Sep 17 00:00:00 2001 From: Pasindu Tennage Date: Sun, 28 Jun 2026 15:47:52 +0000 Subject: [PATCH 5/6] added token-registry.yaml Signed-off-by: pasindutennage-da Signed-off-by: Pasindu Tennage --- cluster/configs/shared/base.yaml | 2 +- .../shared/rate-limits/token-registry.yaml | 43 +++++++++++++++++++ .../configs/shared/rate-limits/v0-acs.yaml | 43 +------------------ 3 files changed, 45 insertions(+), 43 deletions(-) create mode 100644 cluster/configs/shared/rate-limits/token-registry.yaml diff --git a/cluster/configs/shared/base.yaml b/cluster/configs/shared/base.yaml index 90defc208c..5d83ad059e 100644 --- a/cluster/configs/shared/base.yaml +++ b/cluster/configs/shared/base.yaml @@ -247,7 +247,7 @@ multiValidator: sv: scan: externalRateLimits: - !include(./rate-limits/v0-acs.yaml;./rate-limits/unlimited.yaml;./rate-limits/public-banned.yaml) + !include(./rate-limits/v0-acs.yaml;./rate-limits/unlimited.yaml;./rate-limits/public-banned.yaml;./rate-limits/token-registry.yaml) globalLimits: maxTokens: 2147483647 tokensPerFill: 2147483647 diff --git a/cluster/configs/shared/rate-limits/token-registry.yaml b/cluster/configs/shared/rate-limits/token-registry.yaml new file mode 100644 index 0000000000..9943f53f22 --- /dev/null +++ b/cluster/configs/shared/rate-limits/token-registry.yaml @@ -0,0 +1,43 @@ +rateLimits: + /registry/allocations/v1: + name: registry-allocations + type: limited + clientIp: true + maxTokens: 10 + tokensPerFill: 5 + fillInterval: 60s + /registry/metadata/v1/info: + name: registry-metadata-info + type: limited + clientIp: true + maxTokens: 10 + tokensPerFill: 5 + fillInterval: 60s + /registry/metadata/v1/instruments: + name: registry-metadata-instruments + type: limited + clientIp: true + maxTokens: 10 + tokensPerFill: 5 + fillInterval: 60s + /registry/allocation-instruction/v1/allocation-factory: + name: registry-allocation-factory + type: limited + clientIp: true + maxTokens: 10 + tokensPerFill: 5 + fillInterval: 60s + /registry/transfer-instruction/v1: + name: registry-transfer-instruction + type: limited + clientIp: true + maxTokens: 10 + tokensPerFill: 5 + fillInterval: 60s + /registry/transfer-instruction/v1/transfer-factory: + name: registry-transfer-factory + type: limited + clientIp: true + maxTokens: 10 + tokensPerFill: 5 + fillInterval: 60s diff --git a/cluster/configs/shared/rate-limits/v0-acs.yaml b/cluster/configs/shared/rate-limits/v0-acs.yaml index 4d4e6a1ab3..5ba781a4b1 100644 --- a/cluster/configs/shared/rate-limits/v0-acs.yaml +++ b/cluster/configs/shared/rate-limits/v0-acs.yaml @@ -6,45 +6,4 @@ rateLimits: maxTokens: 10 tokensPerFill: 5 fillInterval: 60s - /registry/allocations/v1: - name: registry-allocations - type: limited - clientIp: true - maxTokens: 10 - tokensPerFill: 5 - fillInterval: 60s - /registry/metadata/v1/info: - name: registry-metadata-info - type: limited - clientIp: true - maxTokens: 10 - tokensPerFill: 5 - fillInterval: 60s - /registry/metadata/v1/instruments: - name: registry-metadata-instruments - type: limited - clientIp: true - maxTokens: 10 - tokensPerFill: 5 - fillInterval: 60s - /registry/allocation-instruction/v1/allocation-factory: - name: registry-allocation-factory - type: limited - clientIp: true - maxTokens: 10 - tokensPerFill: 5 - fillInterval: 60s - /registry/transfer-instruction/v1: - name: registry-transfer-instruction - type: limited - clientIp: true - maxTokens: 10 - tokensPerFill: 5 - fillInterval: 60s - /registry/transfer-instruction/v1/transfer-factory: - name: registry-transfer-factory - type: limited - clientIp: true - maxTokens: 10 - tokensPerFill: 5 - fillInterval: 60s + From dbe5837b3c02b579a91a7adacb7f0b8b6fd14aad Mon Sep 17 00:00:00 2001 From: Pasindu Tennage Date: Sun, 28 Jun 2026 16:38:51 +0000 Subject: [PATCH 6/6] Removed hardcodes Signed-off-by: pasindutennage-da Signed-off-by: Pasindu Tennage --- cluster/configs/shared/base.yaml | 1 - cluster/deployment/scratchneta/config.resolved.yaml | 1 - cluster/deployment/scratchnetb/config.resolved.yaml | 1 - cluster/deployment/scratchnetc/config.resolved.yaml | 1 - cluster/deployment/scratchnetd/config.resolved.yaml | 1 - cluster/deployment/scratchnete/config.resolved.yaml | 1 - cluster/pulumi/infra/src/cloudArmor.ts | 6 ++++-- cluster/pulumi/infra/src/config.ts | 5 ++++- 8 files changed, 8 insertions(+), 9 deletions(-) diff --git a/cluster/configs/shared/base.yaml b/cluster/configs/shared/base.yaml index 5d83ad059e..c66d2ca662 100644 --- a/cluster/configs/shared/base.yaml +++ b/cluster/configs/shared/base.yaml @@ -219,7 +219,6 @@ cloudArmor: withinIntervalSeconds: 60 maxRequestsBeforeHttp429: 0 # Keeps scan completely closed to the public tokenRegistry: - hostname: scan.sv-2.scratcha.global.canton.network.digitalasset.com pathPrefix: /registry throttleAcrossAllEndpointsAllIps: withinIntervalSeconds: 60 diff --git a/cluster/deployment/scratchneta/config.resolved.yaml b/cluster/deployment/scratchneta/config.resolved.yaml index 4ce30c3488..510fb96508 100644 --- a/cluster/deployment/scratchneta/config.resolved.yaml +++ b/cluster/deployment/scratchneta/config.resolved.yaml @@ -10,7 +10,6 @@ cloudArmor: maxRequestsBeforeHttp429: 0 withinIntervalSeconds: 60 tokenRegistry: - hostname: 'scan.sv-2.scratcha.global.canton.network.digitalasset.com' pathPrefix: '/registry' throttleAcrossAllEndpointsAllIps: maxRequestsBeforeHttp429: 200 diff --git a/cluster/deployment/scratchnetb/config.resolved.yaml b/cluster/deployment/scratchnetb/config.resolved.yaml index 4ce30c3488..510fb96508 100644 --- a/cluster/deployment/scratchnetb/config.resolved.yaml +++ b/cluster/deployment/scratchnetb/config.resolved.yaml @@ -10,7 +10,6 @@ cloudArmor: maxRequestsBeforeHttp429: 0 withinIntervalSeconds: 60 tokenRegistry: - hostname: 'scan.sv-2.scratcha.global.canton.network.digitalasset.com' pathPrefix: '/registry' throttleAcrossAllEndpointsAllIps: maxRequestsBeforeHttp429: 200 diff --git a/cluster/deployment/scratchnetc/config.resolved.yaml b/cluster/deployment/scratchnetc/config.resolved.yaml index 4ce30c3488..510fb96508 100644 --- a/cluster/deployment/scratchnetc/config.resolved.yaml +++ b/cluster/deployment/scratchnetc/config.resolved.yaml @@ -10,7 +10,6 @@ cloudArmor: maxRequestsBeforeHttp429: 0 withinIntervalSeconds: 60 tokenRegistry: - hostname: 'scan.sv-2.scratcha.global.canton.network.digitalasset.com' pathPrefix: '/registry' throttleAcrossAllEndpointsAllIps: maxRequestsBeforeHttp429: 200 diff --git a/cluster/deployment/scratchnetd/config.resolved.yaml b/cluster/deployment/scratchnetd/config.resolved.yaml index 4ce30c3488..510fb96508 100644 --- a/cluster/deployment/scratchnetd/config.resolved.yaml +++ b/cluster/deployment/scratchnetd/config.resolved.yaml @@ -10,7 +10,6 @@ cloudArmor: maxRequestsBeforeHttp429: 0 withinIntervalSeconds: 60 tokenRegistry: - hostname: 'scan.sv-2.scratcha.global.canton.network.digitalasset.com' pathPrefix: '/registry' throttleAcrossAllEndpointsAllIps: maxRequestsBeforeHttp429: 200 diff --git a/cluster/deployment/scratchnete/config.resolved.yaml b/cluster/deployment/scratchnete/config.resolved.yaml index 4ce30c3488..510fb96508 100644 --- a/cluster/deployment/scratchnete/config.resolved.yaml +++ b/cluster/deployment/scratchnete/config.resolved.yaml @@ -10,7 +10,6 @@ cloudArmor: maxRequestsBeforeHttp429: 0 withinIntervalSeconds: 60 tokenRegistry: - hostname: 'scan.sv-2.scratcha.global.canton.network.digitalasset.com' pathPrefix: '/registry' throttleAcrossAllEndpointsAllIps: maxRequestsBeforeHttp429: 200 diff --git a/cluster/pulumi/infra/src/cloudArmor.ts b/cluster/pulumi/infra/src/cloudArmor.ts index 908c72112b..2ab7f7e286 100644 --- a/cluster/pulumi/infra/src/cloudArmor.ts +++ b/cluster/pulumi/infra/src/cloudArmor.ts @@ -147,9 +147,11 @@ function addThrottleAndBanRules( // this makes the pulumi update cleaner if toggling just one service if (throttleAcrossAllEndpointsAllIps.maxRequestsBeforeHttp429 > 0) { const ruleName = `throttle-all-endpoints-all-ips-${confEntryHead}`; - + const hostnameRegex = hostname + ? _.escapeRegExp(hostname) + : `scan\\.[\\w-]+\\.${_.escapeRegExp(config.clusterHostname)}`; const pathExpr = allowedPathsCondition(scanExternalRateLimits, pathPrefix); - const hostExpr = `request.headers['host'].matches(R"^${_.escapeRegExp(hostname)}(?::[0-9]+)?$")`; + const hostExpr = `request.headers['host'].matches(R"^${hostnameRegex}(?::[0-9]+)?$")`; const matchExpr = `${pathExpr} && ${hostExpr}`; new PolicyRule( diff --git a/cluster/pulumi/infra/src/config.ts b/cluster/pulumi/infra/src/config.ts index e8fb8b7e37..f3412b4d93 100644 --- a/cluster/pulumi/infra/src/config.ts +++ b/cluster/pulumi/infra/src/config.ts @@ -27,7 +27,10 @@ const CloudArmorConfigSchema = z.object({ .catchall( z.object({ rulePreviewOnly: z.boolean().default(false), - hostname: z.string().regex(/^[A-Za-z0-9_-]+(\.[A-Za-z0-9_-]+)*$/, 'valid DNS hostname'), + hostname: z + .string() + .regex(/^[A-Za-z0-9_-]+(\.[A-Za-z0-9_-]+)*$/, 'valid DNS hostname') + .optional(), pathPrefix: z.string().regex(/^\/[^"]*$/, 'HTTP request path starting with /'), throttleAcrossAllEndpointsAllIps: z.object({ withinIntervalSeconds: z.number().positive(),