From 3d66ed45ac545e0f9e78a001cd8070cfd611dda3 Mon Sep 17 00:00:00 2001 From: hy Date: Thu, 23 Apr 2026 17:01:03 +0800 Subject: [PATCH] =?UTF-8?q?=E9=99=90=E5=AE=9A=20gateway=20=E5=8F=91?= =?UTF-8?q?=E5=B8=83=E5=8F=AA=E5=A4=84=E7=90=86=20jvapi=20=E8=A7=84?= =?UTF-8?q?=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 1 + core/config.py | 2 ++ monitors/yumi/tencent_clb.py | 18 +++++++++++ tests/test_gateway_release_clb.py | 54 +++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+) diff --git a/.env.example b/.env.example index 5ec598f..e0e87e8 100644 --- a/.env.example +++ b/.env.example @@ -103,6 +103,7 @@ TENCENT_SECRET_ID= TENCENT_SECRET_KEY= GATEWAY_CLB_LOAD_BALANCER_ID= GATEWAY_CLB_LISTENER_IDS= +GATEWAY_CLB_ALLOWED_DOMAINS= GATEWAY_CLB_POLL_SECONDS=2 GATEWAY_CLB_TASK_TIMEOUT_SECONDS=120 GATEWAY_CLB_DRAIN_SECONDS=0 diff --git a/core/config.py b/core/config.py index 962ffcf..1297d88 100644 --- a/core/config.py +++ b/core/config.py @@ -118,6 +118,8 @@ TENCENT_SECRET_KEY = env_str("TENCENT_SECRET_KEY", "") GATEWAY_CLB_LOAD_BALANCER_ID = env_str("GATEWAY_CLB_LOAD_BALANCER_ID", "") # gateway 可能同时挂在 HTTP / HTTPS 等多个监听器下,这里统一允许逗号分隔配置。 GATEWAY_CLB_LISTENER_IDS = env_str_list("GATEWAY_CLB_LISTENER_IDS", []) +# gateway 发布时只保护这些域名规则;留空表示 listener 下匹配到的规则全部处理。 +GATEWAY_CLB_ALLOWED_DOMAINS = env_str_list("GATEWAY_CLB_ALLOWED_DOMAINS", []) # CLB 异步任务轮询间隔。 GATEWAY_CLB_POLL_SECONDS = env_float("GATEWAY_CLB_POLL_SECONDS", 2.0) # CLB 摘流 / 恢复异步任务最长等待秒数。 diff --git a/monitors/yumi/tencent_clb.py b/monitors/yumi/tencent_clb.py index aa1e979..4e9c59b 100644 --- a/monitors/yumi/tencent_clb.py +++ b/monitors/yumi/tencent_clb.py @@ -6,6 +6,7 @@ from typing import Any from core.config import ( DEPLOY_REGION, + GATEWAY_CLB_ALLOWED_DOMAINS, GATEWAY_CLB_LISTENER_IDS, GATEWAY_CLB_LOAD_BALANCER_ID, GATEWAY_CLB_POLL_SECONDS, @@ -43,6 +44,7 @@ def gateway_clb_settings() -> dict[str, Any]: return { "loadBalancerId": GATEWAY_CLB_LOAD_BALANCER_ID, + "allowedDomains": list(GATEWAY_CLB_ALLOWED_DOMAINS), "listenerIds": list(GATEWAY_CLB_LISTENER_IDS), } @@ -193,15 +195,29 @@ def build_binding(listener: dict[str, Any], service: dict[str, Any], target: dic return binding +def binding_allowed(binding: dict[str, Any], allowed_domains: list[str]) -> bool: + # 显式配置了允许域名后,只处理这些规则,避免发布时误伤当前不再使用的域名。 + if not allowed_domains: + return True + + domain = str(binding.get("domain") or "").strip().lower() + if not domain: + return False + return domain in {item.strip().lower() for item in allowed_domains if item.strip()} + + def collect_service_bindings(listeners: list[dict[str, Any]], service: dict[str, Any]) -> list[dict[str, Any]]: # 自动扫描当前实例在配置监听器里的全部绑定,避免只摘一条规则导致仍有流量漏进来。 bindings_by_key: dict[str, dict[str, Any]] = {} + allowed_domains = list(GATEWAY_CLB_ALLOWED_DOMAINS) for listener in listeners: for target in listener.get("Targets") or []: if not target_matches_service(target, service): continue binding = build_binding(listener, service, target, rule=None) + if not binding_allowed(binding, allowed_domains): + continue bindings_by_key[binding_key(binding)] = binding for rule in listener.get("Rules") or []: @@ -209,6 +225,8 @@ def collect_service_bindings(listeners: list[dict[str, Any]], service: dict[str, if not target_matches_service(target, service): continue binding = build_binding(listener, service, target, rule=rule) + if not binding_allowed(binding, allowed_domains): + continue bindings_by_key[binding_key(binding)] = binding return list(bindings_by_key.values()) diff --git a/tests/test_gateway_release_clb.py b/tests/test_gateway_release_clb.py index fef8f34..5badfa9 100644 --- a/tests/test_gateway_release_clb.py +++ b/tests/test_gateway_release_clb.py @@ -44,6 +44,59 @@ class GatewayClbClientTests(unittest.TestCase): class GatewayClbDiscoveryTests(unittest.TestCase): + @patch("monitors.yumi.tencent_clb.GATEWAY_CLB_ALLOWED_DOMAINS", ("jvapi.example.com",)) + def test_collect_service_bindings_filters_by_allowed_domains(self) -> None: + listeners = [ + { + "ListenerId": "lbl-http", + "Rules": [ + { + "LocationId": "loc-a", + "Domain": "api.example.com", + "Url": "/", + "Targets": [ + { + "InstanceId": "ins-gw-1", + "Port": 8080, + "Weight": 20, + "PrivateIpAddresses": ["10.0.0.1"], + } + ], + }, + { + "LocationId": "loc-b", + "Domain": "jvapi.example.com", + "Url": "/", + "Targets": [ + { + "InstanceId": "ins-gw-1", + "Port": 8080, + "Weight": 10, + "PrivateIpAddresses": ["10.0.0.1"], + } + ], + }, + ], + } + ] + + bindings = collect_service_bindings( + listeners, + { + "host": "gateway-1", + "service": "gateway", + "ip": "10.0.0.1", + "instanceId": "ins-gw-1", + "port": 8080, + }, + ) + + self.assertEqual( + [(item["domain"], item["locationId"], item["weight"]) for item in bindings], + [("jvapi.example.com", "loc-b", 10)], + ) + + @patch("monitors.yumi.tencent_clb.GATEWAY_CLB_ALLOWED_DOMAINS", ()) @patch("monitors.yumi.tencent_clb.GATEWAY_CLB_LOAD_BALANCER_ID", "lb-test") @patch("monitors.yumi.tencent_clb.GATEWAY_CLB_LISTENER_IDS", ("lbl-http", "lbl-https")) @patch("monitors.yumi.tencent_clb.describe_gateway_targets") @@ -122,6 +175,7 @@ class GatewayClbDiscoveryTests(unittest.TestCase): {("lbl-http", "loc-a", 20), ("lbl-http", "loc-b", 10)}, ) + @patch("monitors.yumi.tencent_clb.GATEWAY_CLB_ALLOWED_DOMAINS", ()) def test_collect_service_bindings_ignores_other_targets(self) -> None: listeners = [ {