diff --git a/README.md b/README.md index a1453445..b26d62b6 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ PastureStack is an independent community effort to preserve, audit, and moderniz Earlier prerelease coordinates are retired from current release references; their reviewed source commits remain in Git history. This source tree targets -the pure numeric coordinate `v0.3.8`; the GitHub tag and Release, rather than +the pure numeric coordinate `v0.3.9`; the GitHub tag and Release, rather than this README, determine when it is published. Product identity is carried by the repository, catalog metadata, and provenance rather than the version tag. @@ -119,6 +119,12 @@ adapter for the per-host driver. Image digests and live upgrade evidence are separate gates; neither package source tests nor one-host allocation prove the encrypted two-host lifecycle. +Version `10` uses the published `v0.14.35` image and bounds the connectivity +sidecar's TCP 80 bind retry during a managed upgrade. It does not alter +firewall ownership or backend selection. The image digest is recorded in +`catalog-images.json`; live Catalog activation and two-host lifecycle remain +separate acceptance gates. + Deployable Compose files use semantic version tags only. A published version tag must never be replaced. Manifest digests remain release-verification evidence and are not inserted into Catalog, Compose, API, or user-interface diff --git a/catalog-images.json b/catalog-images.json index 8aeac71f..7be4ff0a 100644 --- a/catalog-images.json +++ b/catalog-images.json @@ -223,6 +223,25 @@ "secrets": 0 } }, + { + "reference": "ghcr.io/pasturestack/ipsec-vxlan-overlay-network:v0.14.35", + "manifestDigest": "sha256:452405892045346614eac8e2680b1b715bf6df3913ea4435dbee3550b60c07bb", + "sourceRepository": "https://github.com/PastureStack/ipsec-vxlan-overlay-network", + "sourceCommit": "bf81eef04ae64fd94155595fde8be7581900f4a8", + "sourcePath": "package/Dockerfile", + "registryPage": "https://github.com/orgs/PastureStack/packages/container/package/ipsec-vxlan-overlay-network", + "licenseBoundary": "Apache-2.0 source and image; bundled Ubuntu, strongSwan, CNI, Weave, and other packages retain their upstream licenses and notices", + "reviewedAt": "2026-09-14", + "platforms": ["linux/amd64"], + "vulnerabilityScan": { + "scanner": "Trivy 0.74.0", + "reportCreatedAt": "2026-09-14", + "scope": "published runtime image", + "high": 0, + "critical": 0, + "secrets": 0 + } + }, { "reference": "ghcr.io/pasturestack/network-plugin-manager:v0.6.34", "sourceRepository": "https://github.com/PastureStack/network-plugin-manager", diff --git a/infra-templates/ipsec-overlay/10/README.md b/infra-templates/ipsec-overlay/10/README.md new file mode 100644 index 00000000..ff122125 --- /dev/null +++ b/infra-templates/ipsec-overlay/10/README.md @@ -0,0 +1,33 @@ + + +# PastureStack IPsec Overlay 0.3.8 + +This infrastructure template is a candidate for the IPsec overlay data plane on every eligible host. A network-holder service owns the managed namespace, the router applies host XFRM and route state, the connectivity sidecar exposes the control-plane health contract, and the CNI sidecar supplies the bridge and address-management executables. + +## Candidate template — published image + +- Image: `ghcr.io/pasturestack/ipsec-vxlan-overlay-network:v0.14.35` is recorded with its published manifest digest in `catalog-images.json`. The GitHub Release itself is not immutable. +- Version `10` gives the connectivity-check sidecar a bounded TCP 80 handoff during rolling upgrades. It waits only when the prior sidecar still owns its listener and fails clearly after 90 seconds or on another bind error; firewall rules and router port 8111 remain under their existing owners. Version `9` remains available for existing stacks. +- Version `9` updates the bundled CNI host-label adapter to the control plane's plain-text `/self/host/labels/` contract. This lets per-host subnet workloads receive the host-specific bridge and IPAM ranges instead of failing CNI setup. Version `8` remains available for existing stacks. +- Version `8` retains the port-8111 handoff and peer-retry behavior. It lets the IPsec module, rather than strongSwan's CHILD close action, own missing-SA recovery. After a quiet period it removes only a zero-traffic established duplicate when one other installed SA for the same managed peer has traffic; ambiguous pairs remain untouched. Version `7` remains available for existing stacks but did not converge after a live rolling upgrade. +- Source license: Apache-2.0; Ubuntu, strongSwan, CNI, Weave, and bundled dependencies retain their upstream licenses and notices. + +## Privilege and secret boundary + +The router is privileged and uses host PID and network namespaces. In all three firewall backends it synchronizes IPsec XFRM state and routes, but does not write host firewall chains. Network Plugin Manager alone owns the overlay bridge-subnet forward mark, NAT exclusion, and host-port rules. The router does not create a second nftables mark table, patch the manager's `CATTLE_*` chains, or change Docker's tables. The router receives a read-only Docker socket mount to query the actual firewall driver; Unix socket access still grants a powerful Docker API capability, so it remains confined to this trusted privileged system service. The CNI sidecar also accesses the Docker socket. These permissions are required by this compatibility architecture and must not be copied to ordinary workloads. + +The router receives a scoped create-agent credential from the compatible control plane and downloads the generated IPsec pre-shared key through the authenticated `configcontent/psk` contract. This template does not accept a user-supplied key and never places a key in the public Catalog repository, Compose variables, image, or logs. + +## Compatibility boundary + +The literal `rancher-compose.yml` filename, `minimum_rancher_version` key, required `io.rancher.*` orchestration labels, `rancher-cni-driver` shared volume, and `ipsec` agent-service marker are consumed by the compatible control plane and network plugin manager. They are protocol identifiers, not PastureStack branding. User-facing names, image coordinates, commands, environment variables, CNI names, log paths, and the `pasture.internal` search suffix use current PastureStack identifiers. + +The data plane currently supports the compatibility network `10.42.0.0/16`; the template intentionally does not expose a subnet selector that the runtime cannot safely honor. + +The host firewall backend is selected explicitly or left at `auto`. The four supported choices are `auto`, native `nftables`, `iptables-nft`, and `iptables-legacy`. The selection is passed only to `overlay-router` through `PASTURESTACK_FIREWALL_BACKEND`. The router checks Docker's actual driver and live rule owner, not the Ubuntu version: even on Ubuntu 26.04 and later, an existing `iptables-legacy` or `iptables-nft` deployment keeps that active path. An explicit mismatch or ambiguous state fails safely without switching backends or activating unloaded legacy modules. Align the choice with the Network Services template on the same environment. + +The Native project definition lists Network Services before IPsec, but list order alone does not establish a health dependency. Before creating or upgrading this overlay, apply the matching Network Services version and wait until Network Plugin Manager is healthy on every target host. In native `nftables` mode, first satisfy that template's Docker firewall-backend, bridge-accept-fwmark, and persistent IPv4-forwarding prerequisites; an IPsec router alone cannot provide the manager-owned forwarding and NAT rules. + +## Release boundary + +The published `v0.14.35` image is recorded with its real manifest digest. The isolated native nftables, iptables-nft, and iptables-legacy gates must remain green. Managed upgrade and peer-restart evidence must be checked separately; a successful CNI address allocation alone does not prove the encrypted multi-host lifecycle. diff --git a/infra-templates/ipsec-overlay/10/README.zh-TW.md b/infra-templates/ipsec-overlay/10/README.zh-TW.md new file mode 100644 index 00000000..29b58f52 --- /dev/null +++ b/infra-templates/ipsec-overlay/10/README.zh-TW.md @@ -0,0 +1,79 @@ + + +# PastureStack IPsec 加密網路 0.3.8 + +此候選基礎架構範本預計在每台符合條件的主機上安裝 IPsec 加密 +網路資料平面。網路持有服務負責受管命名空間;路由器套用主機 XFRM +與路由狀態;連線檢查相關容器提供控制平面健康狀態契約;CNI 相關 +容器則提供網橋與位址管理執行檔。 + +## 候選範本:映像已發布 + +- 映像 `ghcr.io/pasturestack/ipsec-vxlan-overlay-network:v0.14.35` 已正式發布; + 真實 manifest digest、來源 revision 與執行映像安全掃描已記錄於 + `catalog-images.json`。GitHub Release 並未標示為不可變。 +- 第 `10` 版在滾動升級時,若舊版連線檢查容器尚占用 TCP 80, + 新版只對此埠占用情況等待,最多 90 秒;其他監聽錯誤或逾時仍會 + 明確失敗。此處不更動防火牆規則或路由器的 8111 連接埠責任。 + 第 `9` 版仍供既有堆疊使用。 +- 第 `8` 版保留對等主機重試與 8111 連接埠交接。IPsec 模組統一負責 + 缺失 SA 的重建;超過觀察期間且同一對等主機恰有一條已使用的健康 + SA 時,才清除另一條零流量的重複 SA。無法明確判斷的連線不動。 + 第 `7` 版仍供既有堆疊參照,但真機滾動升級後曾留下兩條已建立 SA。 +- 第 `9` 版將隨附 CNI 的主機標籤查詢改為控制平面實際提供的純文字 + `/self/host/labels/` 契約,讓每主機子網路可正確取得網橋與 IPAM + 位址範圍。第 `8` 版仍供既有堆疊使用。 +- 原始碼採 Apache-2.0 授權;Ubuntu、strongSwan、CNI、Weave 與 + 隨附相依套件保留各自的上游授權及聲明。 + +## 權限與機密資料界線 + +路由器使用特權模式並加入主機 PID 與網路命名空間。在三種防火牆 +後端,它只同步 IPsec XFRM 狀態與路由,不寫入主機防火牆規則。 +網路外掛管理器獨自維護 overlay 網橋子網路的轉送標記、NAT 排除及 +主機連接埠規則。路由器不另建 nftables 標記表、不修改管理器的 +`CATTLE_*` 規則鏈,也不修改 Docker 的規則表。路由器以唯讀掛載 Docker Socket 查詢 +實際防火牆驅動程式;唯讀掛載仍賦予強大的 Docker API 存取能力, +只限此受信任的特權系統服務使用。CNI 相關容器也存取 Docker Socket。 +這些權限是相容架構所需, +不得套用到一般工作負載。 + +路由器會從相容控制平面取得範圍受限的代理程式登入資訊,再透過已驗證 +的 `configcontent/psk` 契約下載 IPsec 預先共用金鑰。此範本不接受 +使用者提供的金鑰,也不會把金鑰放入公開商店、Compose 變數、映像或 +日誌。 + +## 相容性界線 + +`rancher-compose.yml`、`minimum_rancher_version`、必要的 +`io.rancher.*` 編排標籤、`rancher-cni-driver` 共用磁碟區及 +`ipsec` 代理程式服務標記是相容控制平面與網路外掛管理器使用的協定 +識別名稱。使用者可見名稱、映像位置、命令、環境變數、CNI 名稱、 +日誌路徑及 `pasture.internal` 搜尋後綴均採用 PastureStack 名稱。 + +資料平面目前支援 `10.42.0.0/16` 相容網路。執行環境無法安全套用 +任意子網路,因此範本不提供無效的子網路選項。 + +主機防火牆後端可選 `auto`、原生 `nftables`、`iptables-nft` 或 +`iptables-legacy`。選擇會透過 `PASTURESTACK_FIREWALL_BACKEND` 傳給 +`overlay-router`。路由器檢查 Docker 實際驅動程式與現役規則擁有者, +不以 Ubuntu 版本推斷;Ubuntu 26.04 及更新版若已使用 `iptables-legacy` +或 `iptables-nft`,仍維持該現役路徑。明確指定與實際後端不符或狀態 +無法判定時安全停止,不切換後端,也不載入尚未啟用的 legacy 模組。 +同環境的 Network Services 範本應使用一致的選項。 + +Native 專案定義將 Network Services 排在 IPsec 前面,但清單順序 +本身不保證健康狀態相依。建立或升級加密網路前,應先套用相符版本 +的 Network Services,等待每台目標主機上的網路外掛管理器恢復 +健康。使用原生 `nftables` 時,須先完成該範本列出的 Docker +防火牆後端、`bridge-accept-fwmark` 與持久 IPv4 轉送前置設定; +只有 IPsec 路由器無法提供管理器負責的轉送及 NAT 規則。 + +## 發布界線 + +已發布的 `v0.14.35` 映像已記錄真實 manifest digest。原生 nftables、 +iptables-nft 與 iptables-legacy 的隔離驗收必須保持通過;受管升級及 +對等主機重啟還須確認暫時離線的主機不會拆掉其他健康連線、同一對等 +主機收斂為一條可用 IKE SA,新路由器 +仍等待 8111 埠釋放,且不越界修改網路外掛管理器的防火牆規則。 +本版實機結果須另行記錄;單純完成 CNI 位址分配不能代替加密跨主機生命週期驗收。 diff --git a/infra-templates/ipsec-overlay/10/docker-compose.yml.tpl b/infra-templates/ipsec-overlay/10/docker-compose.yml.tpl new file mode 100644 index 00000000..68410278 --- /dev/null +++ b/infra-templates/ipsec-overlay/10/docker-compose.yml.tpl @@ -0,0 +1,114 @@ +# SPDX-License-Identifier: MIT +version: '2' + +services: + overlay-network: + image: ghcr.io/pasturestack/ipsec-vxlan-overlay-network:v0.14.35 + command: + - /bin/bash + - -c + - 'mkfifo /tmp/overlay-log; exec cat /tmp/overlay-log' + network_mode: ipsec + labels: + io.pasturestack.component: ipsec-overlay + io.rancher.sidekicks: overlay-router,connectivity-check + io.rancher.scheduler.global: 'true' + io.rancher.cni.link_mtu_overhead: '0' + io.rancher.network.macsync: 'true' + io.rancher.network.arpsync: 'true' + + overlay-router: + image: ghcr.io/pasturestack/ipsec-vxlan-overlay-network:v0.14.35 + command: start-ipsec.sh + privileged: true + network_mode: container:overlay-network + pid: host + environment: + PASTURESTACK_DEBUG: '${PASTURESTACK_DEBUG}' + PASTURESTACK_FIREWALL_BACKEND: '${FIREWALL_BACKEND}' + PASTURESTACK_NETWORK_XFRM_NETNS_PATH: /proc/1/ns/net + PASTURESTACK_NETWORK_XFRM_TUNNEL_SOURCE: host + PASTURESTACK_NETWORK_RUN_IN_HOST_NETNS: 'true' + PASTURESTACK_NETWORK_ARP_INTERFACE: '${DOCKER_BRIDGE}' + PASTURESTACK_NETWORK_SYNC_HOST_ROUTES: 'true' + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + labels: + io.pasturestack.component: ipsec-overlay-router + io.rancher.container.create_agent: 'true' + io.rancher.container.agent_service.ipsec: 'true' + logging: + driver: json-file + options: + max-size: 25m + max-file: '2' + sysctls: + net.ipv4.conf.all.send_redirects: '0' + net.ipv4.conf.default.send_redirects: '0' + + connectivity-check: + image: ghcr.io/pasturestack/ipsec-vxlan-overlay-network:v0.14.35 + command: + - ipsec-vxlan-connectivity-check + - --connectivity-check-interval + - '${CONNECTIVITY_CHECK_INTERVAL}' + - --peer-connection-timeout + - '${PEER_CONNECTION_TIMEOUT}' + network_mode: container:overlay-network + environment: + PASTURESTACK_DEBUG: '${PASTURESTACK_DEBUG}' + PASTURESTACK_METADATA_ADDRESS: 169.254.169.250 + labels: + io.pasturestack.component: ipsec-overlay-connectivity + + cni-driver: + image: ghcr.io/pasturestack/ipsec-vxlan-overlay-network:v0.14.35 + command: start-cni-driver.sh + privileged: true + network_mode: host + pid: host + environment: + PASTURESTACK_DEBUG: '${PASTURESTACK_DEBUG}' + labels: + io.pasturestack.component: ipsec-overlay-cni + io.rancher.scheduler.global: 'true' + io.rancher.network.cni.binary: pasture-bridge + io.rancher.container.dns: 'true' + logging: + driver: json-file + options: + max-size: 25m + max-file: '2' + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - rancher-cni-driver:/opt/cni-driver + network_driver: + name: PastureStack IPsec Overlay + default_network: + name: ipsec + host_ports: {{ .Values.HOST_PORTS }} + subnets: + - network_address: 10.42.0.0/16 + dns: + - 169.254.169.250 + dns_search: + - pasture.internal + cni_config: + '10-pasturestack.conf': + name: pasturestack-cni-network + type: pasture-bridge + bridge: $DOCKER_BRIDGE + bridgeSubnet: 10.42.0.0/16 + logToFile: /var/log/pasturestack-cni.log + isDebugLevel: ${PASTURESTACK_DEBUG} + isDefaultGateway: true + hostNat: true + hairpinMode: {{ .Values.PASTURESTACK_HAIRPIN_MODE }} + promiscMode: {{ .Values.PASTURESTACK_PROMISCUOUS_MODE }} + mtu: ${MTU} + linkMTUOverhead: 98 + ipam: + type: metadata-cni-ipam + subnetPrefixSize: /16 + logToFile: /var/log/pasturestack-cni.log + isDebugLevel: ${PASTURESTACK_DEBUG} diff --git a/infra-templates/ipsec-overlay/10/rancher-compose.yml b/infra-templates/ipsec-overlay/10/rancher-compose.yml new file mode 100644 index 00000000..28541bf8 --- /dev/null +++ b/infra-templates/ipsec-overlay/10/rancher-compose.yml @@ -0,0 +1,97 @@ +# SPDX-License-Identifier: MIT +.catalog: + name: PastureStack IPsec Overlay + version: v0.3.8 + description: Provide an encrypted host-to-host network for managed workloads. + minimum_rancher_version: v1.6.19-rc1 + labels: + io.pasturestack.catalog.question.docker_bridge.label.zh-tw: 'Docker 網橋' + io.pasturestack.catalog.question.docker_bridge.description.zh-tw: '受管工作負載流量使用的主機網橋。' + io.pasturestack.catalog.question.firewall_backend.label.zh-tw: '主機防火牆後端' + io.pasturestack.catalog.question.firewall_backend.description.zh-tw: '依 Docker 現役後端自動選擇;不論 Ubuntu 版本,原生 nftables、iptables-nft 與 iptables-legacy 分開使用。指定不符時安全停止,不會自動切換後端。' + io.pasturestack.catalog.question.mtu.label.zh-tw: '網路 MTU' + io.pasturestack.catalog.question.mtu.description.zh-tw: '請與主機網路 MTU 一致;GCE 常用 1460,一般乙太網路常用 1500。' + io.pasturestack.catalog.question.pasturestack_debug.label.zh-tw: '啟用除錯日誌' + io.pasturestack.catalog.question.pasturestack_debug.description.zh-tw: '記錄較詳細的加密網路元件診斷資訊。' + io.pasturestack.catalog.question.host_ports.label.zh-tw: '啟用主機連接埠' + io.pasturestack.catalog.question.host_ports.description.zh-tw: '允許受管工作負載在主機上公開連接埠。' + io.pasturestack.catalog.question.pasturestack_hairpin_mode.label.zh-tw: '啟用 Hairpin 模式' + io.pasturestack.catalog.question.pasturestack_hairpin_mode.description.zh-tw: 'Hairpin 模式與混雜模式不可同時啟用。' + io.pasturestack.catalog.question.pasturestack_promiscuous_mode.label.zh-tw: '啟用混雜模式' + io.pasturestack.catalog.question.pasturestack_promiscuous_mode.description.zh-tw: '混雜模式與 Hairpin 模式不可同時啟用。' + io.pasturestack.catalog.question.connectivity_check_interval.label.zh-tw: '對等主機檢查間隔' + io.pasturestack.catalog.question.connectivity_check_interval.description.zh-tw: '兩次加密對等主機連線檢查之間的毫秒數。' + io.pasturestack.catalog.question.peer_connection_timeout.label.zh-tw: '對等主機連線逾時' + io.pasturestack.catalog.question.peer_connection_timeout.description.zh-tw: '每次嘗試連線對等主機可使用的毫秒數。' + questions: + - variable: DOCKER_BRIDGE + label: Docker bridge + description: Host bridge used for managed workload traffic. + type: string + default: docker0 + required: true + - variable: FIREWALL_BACKEND + label: Host firewall backend + description: Follow Docker's active firewall backend on any supported Ubuntu version, or explicitly select native nftables, iptables-nft, or iptables-legacy. An explicit mismatch fails safely; legacy is never a fallback. + type: enum + default: auto + required: true + options: + - auto + - nftables + - iptables-nft + - iptables-legacy + - variable: MTU + label: Network MTU + description: Match the host network MTU; common values are 1460 for GCE and 1500 for Ethernet. + type: int + default: 1500 + required: true + - variable: PASTURESTACK_DEBUG + label: Enable debug logs + description: Enable verbose diagnostic logging for the overlay components. + type: boolean + default: 'false' + required: true + - variable: HOST_PORTS + label: Enable host ports + description: Allow managed workloads to publish ports on their hosts. + type: boolean + default: 'true' + required: true + - variable: PASTURESTACK_HAIRPIN_MODE + label: Enable hairpin mode + description: Hairpin mode and promiscuous mode must not both be enabled. + type: boolean + default: 'false' + required: true + - variable: PASTURESTACK_PROMISCUOUS_MODE + label: Enable promiscuous mode + description: Promiscuous mode and hairpin mode must not both be enabled. + type: boolean + default: 'true' + required: true + - variable: CONNECTIVITY_CHECK_INTERVAL + label: Peer check interval + description: Milliseconds between encrypted peer-connectivity checks. + type: int + default: 10000 + required: true + - variable: PEER_CONNECTION_TIMEOUT + label: Peer connection timeout + description: Milliseconds allowed for each peer connection attempt. + type: int + default: 60000 + required: true + +overlay-network: + health_check: + request_line: GET "/connectivity" "HTTP/1.0" + port: 80 + interval: 5000 + initializing_timeout: 60000 + reinitializing_timeout: 60000 + response_timeout: 2000 + healthy_threshold: 2 + unhealthy_threshold: 3 + strategy: none diff --git a/infra-templates/ipsec-overlay/config.yml b/infra-templates/ipsec-overlay/config.yml index 79e1a179..63dc9c3a 100644 --- a/infra-templates/ipsec-overlay/config.yml +++ b/infra-templates/ipsec-overlay/config.yml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: MIT name: IPsec Overlay description: Provide an encrypted host-to-host network for managed workloads. -version: v0.3.7 +version: v0.3.8 category: Networking maintainer: PastureStack contributors license: MIT template; Apache-2.0 image and third-party package licenses apply diff --git a/integration/core/test_catalog.py b/integration/core/test_catalog.py index ee520471..3985ff93 100644 --- a/integration/core/test_catalog.py +++ b/integration/core/test_catalog.py @@ -204,10 +204,10 @@ def test_catalog_list(): assert by_folder[('infra', 'ipsec-overlay')]['name'] == ( 'IPsec Overlay') assert by_folder[('infra', 'ipsec-overlay')][ - 'defaultVersion'] == 'v0.3.7' + 'defaultVersion'] == 'v0.3.8' assert by_folder[('infra', 'ipsec-overlay')][ 'links']['defaultVersion'].endswith( - ':9') + ':10') assert by_folder[('infra', 'layer-2-flat-network')]['name'] == ( 'Layer 2 Flat Network') assert by_folder[('infra', 'layer-2-flat-network')][ @@ -566,7 +566,7 @@ def test_catalog_compose_shapes_are_runtime_compatible(): overlay_docker = overlay_files['docker-compose.yml.tpl'] overlay_platform = overlay_files['rancher-compose.yml'] overlay_image = ( - 'ghcr.io/pasturestack/ipsec-vxlan-overlay-network:v0.14.34') + 'ghcr.io/pasturestack/ipsec-vxlan-overlay-network:v0.14.35') assert overlay_docker.count('image: {}'.format(overlay_image)) == 4 assert overlay_docker.count( "PASTURESTACK_FIREWALL_BACKEND: '${FIREWALL_BACKEND}'") == 1 diff --git a/scripts/audit_deployable_images.py b/scripts/audit_deployable_images.py index 2b3a153e..5f27639d 100644 --- a/scripts/audit_deployable_images.py +++ b/scripts/audit_deployable_images.py @@ -53,7 +53,7 @@ RETAINED_VERSION_LAYOUTS = { "ecr-credential-sync": ("2", "3"), "healthcheck": ("0", "1"), - "ipsec-overlay": ("1", "2", "3", "4", "5", "6", "7", "8", "9"), + "ipsec-overlay": ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10"), "layer-2-flat-network": ("2", "3"), "network-diagnostics": ("1", "2"), "network-policy-manager": ("1", "2"),