From c912eaf224e4ca445848c146f1ba4e1f85c7d60b Mon Sep 17 00:00:00 2001 From: chen21019 <19357113+chen21019@users.noreply.github.com> Date: Sat, 24 Oct 2026 04:13:30 +0800 Subject: [PATCH] catalog: qualify network services 13 with NPM25 and Metadata106 --- README.md | 8 +- SUPPORTED.md | 8 +- catalog-images.json | 77 +++++++++++++++ infra-templates/network-services/13/README.md | 64 +++++++++++++ .../network-services/13/README.zh-TW.md | 52 ++++++++++ .../13/docker-compose.yml.tpl | 95 +++++++++++++++++++ .../network-services/13/rancher-compose.yml | 77 +++++++++++++++ infra-templates/network-services/config.yml | 2 +- integration/core/test_catalog.py | 89 ++++++++++++++++- scripts/audit_deployable_images.py | 2 +- scripts/test | 1 + 11 files changed, 467 insertions(+), 8 deletions(-) create mode 100644 infra-templates/network-services/13/README.md create mode 100644 infra-templates/network-services/13/README.zh-TW.md create mode 100644 infra-templates/network-services/13/docker-compose.yml.tpl create mode 100644 infra-templates/network-services/13/rancher-compose.yml diff --git a/README.md b/README.md index 3a87138e6..580604de4 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ PastureStack is an independent community effort to preserve, audit, and moderniz ## Project status -This source tree provides Catalog `v0.3.15`; its GitHub tag and Release determine +This source tree provides Catalog `v0.3.16`; its GitHub tag and Release determine when it is published. It exposes one Native project template and twenty-one reviewed infrastructure entries. See [SUPPORTED.md](SUPPORTED.md) for the current versions and [catalog-provenance.json](catalog-provenance.json) for @@ -25,6 +25,12 @@ Officially unfixed Low/Medium findings remain tracked, not resolved; narrowly scoped VEX decisions require product-specific evidence and retain the raw scan. A clean gate is not a claim that every dependency has zero reported CVEs. +Network Services revision 13 uses NPM `v0.8.25` and Metadata `v0.10.6`. +Both runtime GitHub Releases report `immutable: false`; the recorded OCI +manifest digests identify verified content, not a GitHub release lock. +Metadata106 retains 44 reviewed unfixed raw findings (38 Medium, 6 Low), +tracked separately from publication and environment acceptance. + Catalog validation covers template structure, retained versions, Traditional Chinese labels, exact image references and Catalog Service API lookups. CI uses Python `3.14.9`, hash-locked dependencies, the explicitly downstream diff --git a/SUPPORTED.md b/SUPPORTED.md index b7092c432..6acf64784 100644 --- a/SUPPORTED.md +++ b/SUPPORTED.md @@ -13,7 +13,7 @@ or out-of-scope templates from being presented as deployable software. | PastureStack System Image Preloader | Infrastructure image-cache service | v0.3.2 | Public PastureStack GHCR image with an explicit version tag | Published component; exact runtime evidence is in catalog-images.json; environment upgrade/rollback remains separate | | PastureStack Amazon ECR Credential Sync | Infrastructure registry service | v3.1.6 | Public PastureStack GHCR image with an explicit version tag | Published component; full runtime raw scan has zero findings; Catalog integration and environment acceptance remain distinct | | Metadata Healthcheck | Infrastructure stack | v0.3.17 | Public PastureStack GHCR image with a non-overwritten version tag | Published component; exact runtime evidence is in catalog-images.json; environment upgrade/rollback remains separate | -| PastureStack Network Services | Infrastructure system stack | v0.3.10 | Network Plugin Manager v0.8.24; Metadata Service v0.10.5 and Internal DNS v0.17.13 unchanged from revision 11 | Exact publication evidence is in catalog-images.json; environment upgrade/rollback remains separate | +| PastureStack Network Services | Infrastructure system stack | v0.3.11 | Network Plugin Manager v0.8.25; Metadata Service v0.10.6; Internal DNS v0.17.13 unchanged from revision 12 | OCI digest/SBOM evidence is in catalog-images.json; both runtime GitHub Releases report immutable=false, not locked; Metadata106 retains 44 reviewed unfixed Low/Medium raw findings; environment upgrade/rollback remains separate | | PastureStack Network Diagnostics | Infrastructure diagnostics service | v0.2.2 | Two public PastureStack GHCR images with explicit version tags | Published component; exact runtime evidence is in catalog-images.json; environment upgrade/rollback remains separate | | PastureStack Network Policy Manager | Infrastructure network-policy agent | v0.3.4 | Public PastureStack GHCR image with an explicit version tag | Published component; exact runtime evidence is in catalog-images.json; environment upgrade/rollback remains separate | | PastureStack IPsec Overlay | Infrastructure network driver | v0.3.11 | Public PastureStack GHCR image with an explicit version tag | Published component; exact runtime evidence is in catalog-images.json; environment upgrade/rollback remains separate | @@ -418,6 +418,12 @@ release blockers. ## PastureStack Network Services evidence +The current directory is `13` (`v0.3.11`), using NPM `v0.8.25`, Metadata +`v0.10.6`, and the retained DNS `v0.17.13`. Exact new identities and raw scan +counts are recorded above and in `catalog-images.json`; GitHub release +`immutable=false` is distinct from the recorded OCI digest. The directory +`9`/`10` evidence below is historical and is not acceptance of directory `13`. + Directory `10` is the Network Services `v0.3.8` candidate and changes only Network Plugin Manager to published `v0.8.22` for legacy/current CNI configuration convergence. Metadata Service `v0.9.11`, Internal DNS `v0.17.11`, every Catalog diff --git a/catalog-images.json b/catalog-images.json index 4ccb947be..1bafcf974 100644 --- a/catalog-images.json +++ b/catalog-images.json @@ -1139,6 +1139,45 @@ "workflowRun": "https://github.com/PastureStack/metadata-service/actions/runs/39959381046" } }, + { + "sourceRepository": "https://github.com/PastureStack/metadata-service", + "sourcePath": "package/Dockerfile", + "registryPage": "https://github.com/orgs/PastureStack/packages/container/package/metadata-service", + "licenseBoundary": "Apache-2.0 source and image; bundled Ubuntu and other packages retain their upstream licenses and notices", + "platforms": [ + "linux/amd64" + ], + "reference": "ghcr.io/pasturestack/metadata-service:v0.10.6", + "sourceCommit": "3db2e1cd53530a04282f5c2448426334ea4d92a8", + "manifestDigest": "sha256:88267d800365d354f06519014a3e4f7ce967d05d4805e5a705944980e30449ff", + "reviewedAt": "2026-10-24", + "vulnerabilityScan": { + "scope": "published runtime image; complete severity raw report", + "rawFindings": 44, + "high": 0, + "critical": 0, + "low": 6, + "medium": 38, + "unknown": 0, + "secrets": 0, + "fixedAvailable": 0, + "reviewedUnfixedOccurrences": 44, + "evidence": "https://github.com/PastureStack/metadata-service/actions/runs/40016315561" + }, + "publicationVerification": { + "source": "component owner verified anonymous manifest/config readback and OCI SLSA provenance", + "workflowRun": "https://github.com/PastureStack/metadata-service/actions/runs/40016315561", + "sourceCommit": "3db2e1cd53530a04282f5c2448426334ea4d92a8", + "anonymousReadback": true, + "githubReleaseImmutable": false, + "provenancePredicateType": "https://slsa.dev/provenance/v1" + }, + "sbom": { + "format": "CycloneDX", + "sha256": "35fd5be38787438350e0eeea87eaf2eab6e64575fef895103c44570523e5db4a", + "url": "https://github.com/PastureStack/metadata-service/releases/download/v0.10.6/image.cdx.json" + } + }, { "sourceRepository": "https://github.com/PastureStack/internal-dns", "sourcePath": "package/linux/amd64/Dockerfile", @@ -1494,6 +1533,44 @@ "sha256": "64605ff6d32f9f6b7c3bf20185d2cefead78b0e79b8395e3bc4523594e9af88b", "url": "https://github.com/PastureStack/network-plugin-manager/releases/download/v0.8.24/network-plugin-manager-0.8.24-image.cdx.json" } + }, + { + "sourceRepository": "https://github.com/PastureStack/network-plugin-manager", + "sourcePath": "package/Dockerfile", + "registryPage": "https://github.com/orgs/PastureStack/packages/container/package/network-plugin-manager", + "licenseBoundary": "Apache-2.0 source and image; bundled Alpine, Docker CLI, and other packages retain their upstream licenses and notices", + "platforms": [ + "linux/amd64" + ], + "reference": "ghcr.io/pasturestack/network-plugin-manager:v0.8.25", + "sourceCommit": "461182e1fcd09527509359f5c551704303689c04", + "manifestDigest": "sha256:9a66894138dc6dded756c5e4b80e6d235698d911031641f6c45763ad9b32d94e", + "reviewedAt": "2026-10-24", + "vulnerabilityScan": { + "scope": "published runtime image; complete severity gate", + "high": 0, + "critical": 0, + "low": 0, + "medium": 0, + "unknown": 0, + "secrets": 0, + "fixedAvailable": 0, + "actionable": 0, + "evidence": "https://github.com/PastureStack/network-plugin-manager/actions/runs/40013313015" + }, + "publicationVerification": { + "source": "component owner verified anonymous manifest/config readback and SLSA provenance for the OCI image and image-SBOM file", + "workflowRun": "https://github.com/PastureStack/network-plugin-manager/actions/runs/40013313015", + "sourceCommit": "461182e1fcd09527509359f5c551704303689c04", + "anonymousReadback": true, + "githubReleaseImmutable": false, + "provenancePredicateType": "https://slsa.dev/provenance/v1" + }, + "sbom": { + "format": "CycloneDX", + "sha256": "91f7cce56e1755e9625e2c6bfd0c6f0c9f73d9158b887037520abca7930186b2", + "url": "https://github.com/PastureStack/network-plugin-manager/releases/download/v0.8.25/network-plugin-manager-0.8.25-image.cdx.json" + } } ] } diff --git a/infra-templates/network-services/13/README.md b/infra-templates/network-services/13/README.md new file mode 100644 index 000000000..726d03dc2 --- /dev/null +++ b/infra-templates/network-services/13/README.md @@ -0,0 +1,64 @@ + + +# PastureStack Network Services + +Version 13 (visible version `v0.3.11`) uses the published Network Plugin Manager `v0.8.25`, Metadata Service `v0.10.6`, and Internal DNS `v0.17.13`. This dependency update retains version 12's firewall ownership, CNI convergence, Catalog questions, sidekicks and shared volumes. Earlier numeric revisions remain unchanged. Published source, image digests and runtime scan evidence are recorded in `catalog-images.json`. + +The NPM25 and Metadata106 GitHub Releases report `immutable: false`; recorded OCI manifest digests and SBOM SHA256 values identify the verified content, not a GitHub release lock. Metadata106's full raw report retains 44 reviewed unfixed findings (38 Medium and 6 Low), not zero findings. Metadata106 preserves the platform's opaque `revision-hash` change tokens so existing long-poll clients observe updates without a protocol change. + +## CNI configuration convergence + +The manager preflights all desired CNI configurations and writes each through an atomic replacement with mode `0600`. Only after every desired configuration has been written successfully may it retire an obsolete platform configuration. The historical `10-rancher.conf` must have the exact name/type/IPAM triplet `rancher-cni-network` / `rancher-bridge` / `rancher-cni-ipam`; the current `10-pasturestack.conf` must have `pasturestack-cni-network` / `pasture-bridge` / `metadata-cni-ipam`. The same ownership check applies when the selected provider requires the historical configuration again. + +Retirement renames the obsolete file with a `.pasturestack-retired` suffix, preserving its original bytes outside the CNI `.conf` / `.json` execution set. An existing retirement backup must be byte-identical; a conflicting backup causes an error. Unrelated administrator configurations are preserved. Malformed or partially matching platform configurations, symlinks, and unsafe metadata paths cause an explicit error. The manager never sweeps the configuration directory or removes CNI binary aliases as part of this migration. + +Each component is a published security rebuild. Full-severity evidence is recorded separately from deployment acceptance; publication does not establish that an existing infrastructure stack has completed its managed upgrade. + +## Provider and lifecycle convergence + +The manager selects one eligible CNI provider per binary from the infrastructure-service metadata on the local host. The highest numeric OCI image version wins; an unversioned or nonnumeric development label cannot replace a numeric release, and ties use a deterministic immutable container-ID ordering. The generated wrapper stays bound to that exact provider and does not relist containers by service label at invocation time. It executes the provider's private `/opt/cni/bin` copy with a bounded compatibility fallback for older images. Wrapper replacement uses an exclusive temporary inode and atomic rename; content, file type, and mode drift are repaired without following a replacement symlink. + +When platform metadata temporarily reports a host-port container before its primary address, the manager may read the exact running container's network namespace. It accepts only one IPv4 address inside that network's configured managed subnet and revalidates the same Docker container PID after the namespace read. Missing, ambiguous, out-of-subnet, or lifecycle-raced results fail closed and retain the last known-good host-port rules until metadata and CNI converge. + +## Firewall backend + +`FIREWALL_BACKEND` defaults to `auto`. It reads Docker's actual firewall driver: Docker's native `nftables` driver uses native nft rules, while Docker's `iptables` driver selects the frontend that owns Docker's active NAT chain. That may be `iptables-nft` or `iptables-legacy` on **any supported host**, including Ubuntu 26.04 and later. The OS release, installed executable, or unloaded kernel module alone never selects a backend. To pin one path, choose: + +- `nftables`: Docker's native nftables firewall backend. This is **not** the same as the iptables-nft compatibility CLI. +- `iptables-nft`: xtables compatibility CLI backed by nf_tables, for Docker's iptables firewall driver. +- `iptables-legacy`: legacy xtables, only when the running Docker daemon actually owns the active rules through that frontend. + +The manager refuses a mismatched or ambiguous selection and does not fall back, switch Docker's backend, or load legacy modules. An Ubuntu 26.04+ host already using `iptables-legacy` or `iptables-nft` must keep its live Docker path; do not turn on native nftables merely because the OS is new. A deliberate migration requires a separate host change, rollback point, and network lifecycle test. + +This manager alone owns the host NAT and host-port `CATTLE_*` chains. Its +masquerade rules exclude destinations inside the managed overlay subnet in +all three backends; the IPsec host-XFRM router must not patch these chains. +For the per-host-subnet driver, the manager also excludes other active hosts' +validated subnets from masquerade and adds a bounded forwarding exception. +Inactive registrations are ignored; missing or overlapping labels on an active +host fail closed. This is a routed, unencrypted network; protect the host +transport separately. +Upgrade Network Services first and verify manager health on every host before +upgrading the matching IPsec Overlay version. + +Shared-subnet ingress is enabled only when CNI metadata explicitly sets +`allowSharedSubnetIngress: true`, or for the legacy fixed-subnet contract that +already declares both `bridgeSubnet` and `hostNat: true`. It is rejected for +host-label-based subnets, where only validated active peer ranges are trusted. + +For Docker's native nftables driver, configure Docker itself with `"firewall-backend": "nftables"` and `"bridge-accept-fwmark": "0x1068/0x1068"` before upgrading this stack. Persist `net.ipv4.ip_forward=1` on the host and verify it remains enabled after a reboot: Docker's native nftables backend does not enable IPv4 forwarding for you. The mark allows Docker's bridge forwarding rules to accept the manager's published-host-port traffic; the template cannot configure the host daemon or kernel settings. Check and explicitly migrate any stale `iptables-nft` `FORWARD DROP` policy or previous platform hooks before switching Docker. The manager refuses that mixed state rather than changing the host's global firewall policy. Docker's native nftables backend remains an experimental Docker feature; qualify it against the installed Docker release before production use. + +## Other configuration + +- `DOCKER_BRIDGE`: host bridge for managed workload traffic. +- `DNS_RECURSER_TIMEOUT`, `TTL`: upstream DNS timeout and service-discovery cache time. +- `CPU_PERIOD`, `CPU_QUOTA`: Metadata Service CPU scheduling limits. +- `RELOAD_INTERVAL_LIMIT`, `ARP_SYNC_INTERVAL`: metadata reload and host ARP reconciliation intervals. + +Network Plugin Manager still requires host networking, host PID visibility, the Docker socket, Docker state, kernel-module and runtime mounts, and the shared CNI volume. Metadata Service starts as root only to assign its link-local address, then drops to UID/GID 10001. Internal DNS shares its namespace. The `rancher-compose.yml` filename, `io.rancher.*` labels, `CATTLE_*` fallback variables, `/var/lib/rancher` CA path, and `rancher-cni-driver` volume are compatibility contracts, not a request to use legacy firewall rules. + +These template files are MIT-licensed. The manager, metadata service, and internal DNS retain their Apache-2.0 licenses and bundled dependency notices. Verify image source and the recorded manifest digest in `catalog-images.json` before deployment. + +Metadata and DNS are managed by the Server lifecycle. The manager no longer removes overlapping instances during upgrades and remains unready while waiting for Metadata recovery. Verify service health before completing an upgrade; image publication alone is not runtime acceptance. + +Driver stop/start events and periodic reconciliation refresh the selected provider. Only eligible running local providers with a live process ID can be selected. Temporary metadata or Docker inspection failures remain visible and are retried; an unrelated unavailable driver does not block a healthy provider for another binary. No host firewall mode or administrator configuration is changed by provider selection. diff --git a/infra-templates/network-services/13/README.zh-TW.md b/infra-templates/network-services/13/README.zh-TW.md new file mode 100644 index 000000000..343ee22ea --- /dev/null +++ b/infra-templates/network-services/13/README.zh-TW.md @@ -0,0 +1,52 @@ + + +# PastureStack 網路服務 + +第 13 版(顯示版本 `v0.3.11`)使用已正式發布的網路外掛管理器 `v0.8.25`、中繼資料服務 `v0.10.6` 與內部 DNS `v0.17.13`。此次相依套件更新保留第 12 版的防火牆責任、CNI 收斂、Catalog 問題、相關容器與共用磁碟區;較早數字版目錄內容不變。正式來源、映像摘要及執行環境掃描證據記錄於 `catalog-images.json`。 + +NPM25 與 Metadata106 的 GitHub Release 均回報 `immutable: false`;記錄的 OCI manifest digest 與 SBOM SHA256 用於識別已驗證內容,不代表 GitHub 發行已鎖定。Metadata106 的完整 raw 報告保留 44 筆已審查但官方尚無修補的發現(38 筆 Medium、6 筆 Low),不是零筆發現。Metadata106 保留平台的 opaque `revision-hash` 變更代號,讓既有長輪詢用戶端不改通訊協定即可接收更新。 + +## CNI 設定收斂 + +管理器會先檢查所有預期 CNI 設定,再以原子置換逐檔寫入,權限為 `0600`。只有全部預期設定都寫入成功後,才可退役已不再使用的平台設定。歷史 `10-rancher.conf` 的 name/type/IPAM 必須完整符合 `rancher-cni-network` / `rancher-bridge` / `rancher-cni-ipam`;現行 `10-pasturestack.conf` 則必須完整符合 `pasturestack-cni-network` / `pasture-bridge` / `metadata-cni-ipam`。選定的 provider 再次需要歷史設定時,也會套用相同的身分判定。 + +退役時會將舊檔更名為加上 `.pasturestack-retired` 後綴的備份,保留原始位元組,並移出 CNI `.conf` / `.json` 執行集合。若備份已存在,其內容必須完全一致;內容衝突則明確報錯。管理者的其他設定會保留;格式損壞、只符合部分平台身分、符號連結及不安全的中繼資料路徑都會明確報錯。此次遷移不會掃除整個設定目錄,也不會移除 CNI 執行檔別名。 + +各元件均為正式發布的安全重建。完整嚴重度證據與部署驗收分開記錄;元件發布不代表既有基礎架構堆疊已完成受管升級。 + +## Provider 與生命週期收斂 + +管理器會依本機主機上的基礎架構服務中繼資料,為每個 CNI 執行檔選出唯一合格的 provider。數字型 OCI 映像版本以最高版為準;未標示版本或非數字的開發標籤不得取代正式數字版本,版本相同時再以不可變的容器 ID 做固定排序。產生的 wrapper 只綁定這個精確 provider,呼叫時不會再以服務標籤重新列舉容器。它優先執行 provider 私有的 `/opt/cni/bin` 程式,並只為舊映像保留有限的相容路徑。wrapper 以排他暫存 inode 與原子更名更新;內容、檔案型態或權限漂移時會安全修復,不會沿著遭置換的符號連結寫入。 + +若平台中繼資料暫時先出現主機連接埠容器、尚未填入主要位址,管理器可讀取該精確執行中容器的網路命名空間。只有該網路設定之受管子網路內恰好一個 IPv4 位址可被接受,讀取後還會再次確認同一 Docker 容器 PID。缺少、歧義、超出子網路或生命週期競態都會安全失敗,並保留上一份可用的主機連接埠規則,直到中繼資料與 CNI 完成收斂。 + +## 防火牆後端 + +`FIREWALL_BACKEND` 預設為 `auto`,依 Docker 實際防火牆驅動程式選擇單一路徑:Docker 原生 `nftables` 使用原生 nft 規則;Docker `iptables` 驅動程式則辨識哪一套前端擁有 Docker 現役 NAT 鏈。任何受支援主機(包括 Ubuntu 26.04 及更新版)都可能使用 `iptables-nft` 或 `iptables-legacy`;作業系統版本、執行檔存在或尚未載入的核心模組,均不足以決定後端。需要固定路徑時可選: + +- `nftables`:Docker 原生 nftables 防火牆後端,**不是** iptables-nft 相容命令。 +- `iptables-nft`:由 nf_tables 支援的 xtables 相容命令,搭配 Docker 的 iptables 防火牆驅動程式。 +- `iptables-legacy`:只在現役 Docker 確實透過這套前端持有規則時選用。 + +選擇與 Docker 實際後端不符或無法判定時,管理器會拒絕啟動,不會自動降級、切換 Docker 後端或載入 legacy 模組。Ubuntu 26.04 及更新版若已使用 `iptables-legacy` 或 `iptables-nft`,就應維持現役 Docker 路徑;不能只因系統較新便替它切成原生 nftables。刻意遷移須另外準備主機變更、回復點及網路生命週期驗收。 + +主機 NAT 與主機連接埠的 `CATTLE_*` 規則鏈只由此管理器維護;三種後端的來源位址轉換規則都排除受管 overlay 子網路內的目的位址。每主機子網路還會排除其他有效主機的已驗證子網路,並加入限定來源與目的子網路的轉送例外;非現役主機不列入,現役主機若缺少標籤或子網路重疊則安全地拒絕套用。此網路只提供路由、不加密,須另行保護主機間傳輸。IPsec 主機 XFRM 路由器不得再插入補丁規則。升級時應先升級網路服務,逐台確認管理器健康,再升級相符的 IPsec 加密網路版本。 + +只有 CNI 中繼資料明確設定 `allowSharedSubnetIngress: true` 時才啟用共用子網路輸入轉送;為維持既有固定子網路契約相容性,同時具有 `bridgeSubnet` 與 `hostNat: true` 的舊設定也會採用相同行為。使用主機標籤決定子網路時會拒絕此選項,只信任已驗證的現役對端範圍。 + +使用 Docker 原生 nftables 前,必須先在主機 Docker 設定加入 `"firewall-backend": "nftables"` 及 `"bridge-accept-fwmark": "0x1068/0x1068"`,再升級此堆疊。還須在主機持久設定 `net.ipv4.ip_forward=1`,並於重開機後確認仍啟用;Docker 原生 nftables 後端不會代為啟用 IPv4 轉送。此標記讓 Docker 網橋轉送規則接受管理器發布的主機連接埠流量;範本無法替主機設定 Docker daemon 或核心參數。切換前還須檢查並明確遷移殘留的 `iptables-nft FORWARD DROP` 全域政策與舊平台掛鉤。管理器遇到混用狀態會拒絕啟動,不會自行修改主機全域防火牆政策。Docker 原生 nftables 目前仍屬實驗性功能,正式環境使用前應針對安裝的 Docker 版本完成驗收。 + +## 其他設定 + +- `DOCKER_BRIDGE`:受管工作負載使用的主機網橋。 +- `DNS_RECURSER_TIMEOUT`、`TTL`:上游 DNS 逾時與服務探索快取時間。 +- `CPU_PERIOD`、`CPU_QUOTA`:中繼資料服務的 CPU 排程限制。 +- `RELOAD_INTERVAL_LIMIT`、`ARP_SYNC_INTERVAL`:中繼資料重新載入與主機 ARP 協調間隔。 + +網路外掛管理器仍需主機網路、主機 PID、Docker Socket、Docker 狀態、核心模組與執行環境掛載,以及共用 CNI 磁碟區。中繼資料服務僅在指派連結本機位址時以 root 啟動,之後切換為 UID/GID 10001;內部 DNS 與其共用網路命名空間。`rancher-compose.yml`、`io.rancher.*`、`CATTLE_*` 備援變數、`/var/lib/rancher` CA 路徑及 `rancher-cni-driver` 磁碟區是既有協定的相容契約,不代表必須使用 legacy 防火牆規則。 + +範本檔案採 MIT 授權;管理器、中繼資料服務與內部 DNS 保留 Apache-2.0 授權及隨附相依套件聲明。部署前應以 `catalog-images.json` 核對映像來源與記錄的 manifest digest。 + +中繼資料與 DNS 容器由 Server 生命週期管理。網路管理器不再刪除升級期間並存的執行個體,並在中繼資料恢復前保持未就緒狀態。完成升級前仍須確認服務健康;映像發布不代表實機驗收已完成。 + +Driver 停止/啟動事件與定期協調都會重新選擇 provider;只有本機合法、執行中且具有效程序 ID 的 provider 能被選用。暫時的中繼資料或 Docker 檢查錯誤會明確回報並重試;其他 driver 暫時不可用,不得阻擋不同執行檔的健康 provider。選擇 provider 不會變更主機防火牆模式或管理者設定。 diff --git a/infra-templates/network-services/13/docker-compose.yml.tpl b/infra-templates/network-services/13/docker-compose.yml.tpl new file mode 100644 index 000000000..1c601c35e --- /dev/null +++ b/infra-templates/network-services/13/docker-compose.yml.tpl @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: MIT +version: '2' + +services: + network-plugin-manager: + image: ghcr.io/pasturestack/network-plugin-manager:v0.8.25 + privileged: true + network_mode: host + pid: host + command: + - network-plugin-manager + - --metadata-url + - http://169.254.169.250/2016-07-29 + - --arpsync-interval + - '${ARP_SYNC_INTERVAL}' + - --firewall-backend + - '${FIREWALL_BACKEND}' + environment: + DOCKER_BRIDGE: '${DOCKER_BRIDGE}' + METADATA_IP: 169.254.169.250 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /var/lib/docker:/var/lib/docker + - /lib/modules:/lib/modules:ro + - /run:/run + - /var/run:/var/run + - rancher-cni-driver:/etc/cni + - rancher-cni-driver:/opt/cni + labels: + io.pasturestack.component: network-plugin-manager + io.rancher.scheduler.global: 'true' + logging: + driver: json-file + options: + max-size: 25m + max-file: '2' + + metadata: + image: ghcr.io/pasturestack/metadata-service:v0.10.6 + user: root + cap_add: + - NET_ADMIN + network_mode: bridge + command: + - /bin/bash + - -ec + - | + export PLATFORM_URL="$${PLATFORM_URL:-$${CATTLE_URL:-}}" + export PLATFORM_ACCESS_KEY="$${PLATFORM_ACCESS_KEY:-$${CATTLE_ACCESS_KEY:-}}" + export PLATFORM_SECRET_KEY="$${PLATFORM_SECRET_KEY:-$${CATTLE_SECRET_KEY:-}}" + exec metadata-service --reload-interval-limit="${RELOAD_INTERVAL_LIMIT}" --subscribe + environment: + PLATFORM_CA_ROOT: /var/lib/rancher/etc/ssl/ca.crt + labels: + io.pasturestack.component: metadata-service + io.rancher.sidekicks: dns + io.rancher.container.create_agent: 'true' + io.rancher.scheduler.global: 'true' + io.rancher.container.agent_service.metadata: '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' + cpu_period: ${CPU_PERIOD} + cpu_quota: ${CPU_QUOTA} + + dns: + image: ghcr.io/pasturestack/internal-dns:v0.17.13 + network_mode: container:metadata + command: + - internal-dns + - --listen + - 169.254.169.250:53 + - --recurser-timeout + - '${DNS_RECURSER_TIMEOUT}' + - --ttl + - '${TTL}' + environment: + PLATFORM_METADATA_ENABLED: 'true' + PLATFORM_METADATA_URL: http://localhost/2016-07-29 + PLATFORM_METADATA_ANSWER: 169.254.169.250 + NEVER_RECURSE_TO: 169.254.169.250 + PLATFORM_DNS_ANSWERS_FILE: /etc/internal-dns/answers.json + labels: + io.pasturestack.component: internal-dns + io.rancher.scheduler.global: 'true' + logging: + driver: json-file + options: + max-size: 25m + max-file: '2' diff --git a/infra-templates/network-services/13/rancher-compose.yml b/infra-templates/network-services/13/rancher-compose.yml new file mode 100644 index 000000000..42fc056e7 --- /dev/null +++ b/infra-templates/network-services/13/rancher-compose.yml @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: MIT +.catalog: + name: PastureStack Network Services + version: v0.3.11 + description: Install host networking, metadata, and internal DNS services required by managed workloads. + minimum_rancher_version: v1.6.26-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 互不混用。切換原生 nftables 前須先設定 Docker bridge-accept-fwmark。' + io.pasturestack.catalog.question.dns_recurser_timeout.label.zh-tw: 'DNS 遞迴查詢逾時' + io.pasturestack.catalog.question.dns_recurser_timeout.description.zh-tw: '等待上游 DNS 查詢回應的秒數。' + io.pasturestack.catalog.question.ttl.label.zh-tw: '服務探索 DNS 紀錄存留時間' + io.pasturestack.catalog.question.ttl.description.zh-tw: '內部服務探索 DNS 回應可保留的秒數。' + io.pasturestack.catalog.question.cpu_period.label.zh-tw: '中繼資料服務 CPU 週期' + io.pasturestack.catalog.question.cpu_period.description.zh-tw: '分配給每個中繼資料服務執行個體的 CPU 排程週期。' + io.pasturestack.catalog.question.cpu_quota.label.zh-tw: '中繼資料服務 CPU 配額' + io.pasturestack.catalog.question.cpu_quota.description.zh-tw: '分配給每個中繼資料服務執行個體的 CPU 配額。' + io.pasturestack.catalog.question.reload_interval_limit.label.zh-tw: '中繼資料重新載入間隔' + io.pasturestack.catalog.question.reload_interval_limit.description.zh-tw: '兩次中繼資料設定重新載入之間的最短毫秒數。' + io.pasturestack.catalog.question.arp_sync_interval.label.zh-tw: 'ARP 同步間隔' + io.pasturestack.catalog.question.arp_sync_interval.description.zh-tw: '兩次主機 ARP 協調作業之間的秒數。' + 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. A mismatch fails safely; native nftables requires Docker bridge-accept-fwmark on the host. + type: enum + default: auto + required: true + options: + - auto + - nftables + - iptables-nft + - iptables-legacy + - variable: DNS_RECURSER_TIMEOUT + label: DNS recursion timeout + description: Seconds allowed for an upstream DNS query. + type: int + default: 2 + required: true + - variable: TTL + label: Service discovery TTL + description: Seconds that internal service-discovery answers remain valid. + type: int + default: 1 + required: true + - variable: CPU_PERIOD + label: Metadata CPU period + description: CPU scheduler period assigned to each metadata service instance. + type: int + default: 400000 + required: true + - variable: CPU_QUOTA + label: Metadata CPU quota + description: CPU quota assigned to each metadata service instance. + type: int + default: 200000 + required: true + - variable: RELOAD_INTERVAL_LIMIT + label: Metadata reload interval + description: Minimum milliseconds between metadata configuration reloads. + type: int + default: 1000 + required: true + - variable: ARP_SYNC_INTERVAL + label: ARP synchronization interval + description: Seconds between host ARP reconciliation passes. + type: int + default: 5 + required: true diff --git a/infra-templates/network-services/config.yml b/infra-templates/network-services/config.yml index 3249a8a70..597e0ae95 100644 --- a/infra-templates/network-services/config.yml +++ b/infra-templates/network-services/config.yml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: MIT name: Network Services description: Install host networking, metadata, and internal DNS services required by managed workloads. -version: v0.3.10 +version: v0.3.11 category: Networking maintainer: PastureStack contributors license: MIT template; Apache-2.0 images and third-party package licenses apply diff --git a/integration/core/test_catalog.py b/integration/core/test_catalog.py index b04334f68..c0ac4eb47 100644 --- a/integration/core/test_catalog.py +++ b/integration/core/test_catalog.py @@ -232,10 +232,10 @@ def test_catalog_list(): assert by_folder[('infra', 'network-services')]['name'] == ( 'Network Services') assert by_folder[('infra', 'network-services')][ - 'defaultVersion'] == 'v0.3.10' + 'defaultVersion'] == 'v0.3.11' assert by_folder[('infra', 'network-services')][ 'links']['defaultVersion'].endswith( - ':12') + ':13') assert by_folder[('infra', 'nfs-storage')][ 'name'] == 'NFS Storage' assert by_folder[('infra', 'nfs-storage')][ @@ -725,8 +725,8 @@ def test_catalog_compose_shapes_are_runtime_compatible(): network_docker = network_files['docker-compose.yml.tpl'] network_platform = network_files['rancher-compose.yml'] network_manager_image = ( - 'ghcr.io/pasturestack/network-plugin-manager:v0.8.24') - metadata_image = 'ghcr.io/pasturestack/metadata-service:v0.10.5' + 'ghcr.io/pasturestack/network-plugin-manager:v0.8.25') + metadata_image = 'ghcr.io/pasturestack/metadata-service:v0.10.6' dns_image = 'ghcr.io/pasturestack/internal-dns:v0.17.13' assert network_docker.count( 'image: {}'.format(network_manager_image)) == 1 @@ -1148,6 +1148,19 @@ def test_catalog_compose_shapes_are_runtime_compatible(): ), ], ), + ( + 'network-services', '12', '13', + 'v0.3.10', 'v0.3.11', [ + ( + 'network-plugin-manager:v0.8.24', + 'network-plugin-manager:v0.8.25', + ), + ( + 'metadata-service:v0.10.5', + 'metadata-service:v0.10.6', + ), + ], + ), ( 'network-services', '11', '12', 'v0.3.9', 'v0.3.10', [ @@ -1347,6 +1360,74 @@ def test_catalog_commit_is_pinned(): assert data[0]['pinnedCommit'] == _catalog_commit() +def test_network_services13_preserves_contract_and_published_identities(): + template = 'infra-templates/network-services' + old_image = 'ghcr.io/pasturestack/network-plugin-manager:v0.8.24' + image = 'ghcr.io/pasturestack/network-plugin-manager:v0.8.25' + with open(_file(template + '/12/docker-compose.yml.tpl'), + encoding='utf-8') as source: + old_compose = source.read() + with open(_file(template + '/13/docker-compose.yml.tpl'), + encoding='utf-8') as source: + compose = source.read() + assert old_compose.count('image: ' + old_image) == 1 + assert compose == old_compose.replace(old_image, image).replace( + 'metadata-service:v0.10.5', 'metadata-service:v0.10.6') + with open(_file(template + '/12/rancher-compose.yml'), + encoding='utf-8') as source: + old_catalog = source.read() + with open(_file(template + '/13/rancher-compose.yml'), + encoding='utf-8') as source: + catalog = source.read() + assert catalog == old_catalog.replace( + ' version: v0.3.10\n', ' version: v0.3.11\n') + with open(_file('catalog-images.json'), encoding='utf-8') as source: + images = { + item['reference']: item for item in json.load(source)['images'] + } + assert images[old_image]['sourceCommit'] == ( + 'f34bbca219ddd84b94f4b3f833e04d67b9c8ce03') + assert images[image]['sourceCommit'] == ( + '461182e1fcd09527509359f5c551704303689c04') + assert images[image]['manifestDigest'] == ( + 'sha256:9a66894138dc6dded756c5e4b80e6d23' + '5698d911031641f6c45763ad9b32d94e') + assert images[image]['sbom']['sha256'] == ( + '91f7cce56e1755e9625e2c6bfd0c6f0c' + '9f73d9158b887037520abca7930186b2') + scan = images[image]['vulnerabilityScan'] + assert all(scan[level] == 0 for level in ( + 'high', 'critical', 'low', 'medium', 'unknown', 'secrets')) + publication = images[image]['publicationVerification'] + assert publication['sourceCommit'] == images[image]['sourceCommit'] + assert publication['workflowRun'] == ( + 'https://github.com/PastureStack/network-plugin-manager/actions/runs/' + '40013313015') + assert publication['anonymousReadback'] is True + assert publication['githubReleaseImmutable'] is False + assert publication['provenancePredicateType'] == ( + 'https://slsa.dev/provenance/v1') + metadata = images['ghcr.io/pasturestack/metadata-service:v0.10.6'] + assert metadata['sourceCommit'] == ( + '3db2e1cd53530a04282f5c2448426334ea4d92a8') + assert metadata['manifestDigest'] == ( + 'sha256:88267d800365d354f06519014a3e4f7ce' + '967d05d4805e5a705944980e30449ff') + assert metadata['sbom']['sha256'] == ( + '35fd5be38787438350e0eeea87eaf2eab' + '6e64575fef895103c44570523e5db4a') + scan = metadata['vulnerabilityScan'] + assert scan['rawFindings'] == scan['reviewedUnfixedOccurrences'] == 44 + assert (scan['medium'], scan['low']) == (38, 6) + assert all(scan[level] == 0 for level in ( + 'high', 'critical', 'unknown', 'secrets', 'fixedAvailable')) + publication = metadata['publicationVerification'] + assert publication['githubReleaseImmutable'] is False + assert publication['workflowRun'] == ( + 'https://github.com/PastureStack/metadata-service/actions/runs/' + '40016315561') + + def test_ipsec38_template_preserves_version11_contract(): template = 'infra-templates/ipsec-overlay' old_image = 'ghcr.io/pasturestack/ipsec-vxlan-overlay-network:v0.14.35' diff --git a/scripts/audit_deployable_images.py b/scripts/audit_deployable_images.py index 6d60d8fb7..3f184da5d 100644 --- a/scripts/audit_deployable_images.py +++ b/scripts/audit_deployable_images.py @@ -60,7 +60,7 @@ "layer-2-flat-network": ("2", "3", "4", "5", "6", "7"), "network-diagnostics": ("1", "2", "3"), "network-policy-manager": ("1", "2", "3", "4"), - "network-services": ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"), + "network-services": ("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13"), "nfs-storage": ("1", "2", "3"), "per-host-subnet-network": ("2", "3", "4", "5"), "route53-dns-sync": ("2", "3"), diff --git a/scripts/test b/scripts/test index 64861b050..717543192 100755 --- a/scripts/test +++ b/scripts/test @@ -130,6 +130,7 @@ for version_id in \ network-services:10 \ network-services:11 \ network-services:12 \ + network-services:13 \ nfs-storage:1 \ nfs-storage:2 \ resource-scheduler:1 \