Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/codeql-verification.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ jobs:
cd "$GITHUB_WORKSPACE"
go test -run '^$' ./...
go test -run '^$' -tags=integration ./store
VERSION_OVERRIDE=0.14.35 ./scripts/build
VERSION_OVERRIDE=0.14.36 ./scripts/build

- name: Analyze without publishing temporary alerts
uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
Expand Down
34 changes: 17 additions & 17 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Release IPsec overlay

# A release is dispatched from main only after an annotated v0.14.35 tag and
# A release is dispatched from main only after an annotated v0.14.36 tag and
# successful, same-commit Security release gate and CodeQL verification runs.
# GHCR, registry attestations, and GitHub Releases are not an atomic transaction.
# All source/product/runtime gates precede the image push. A later failure may
Expand All @@ -9,11 +9,11 @@ on:
workflow_dispatch:
inputs:
release_tag:
description: Existing annotated, numeric v0.14.35 tag at main HEAD
description: Existing annotated, numeric v0.14.36 tag at main HEAD
required: true
type: choice
options:
- v0.14.35
- v0.14.36
security_run_id:
description: Successful same-SHA Security release gate run ID
required: true
Expand All @@ -35,7 +35,7 @@ permissions:
attestations: write

concurrency:
group: ipsec-vxlan-overlay-release-v0.14.35
group: ipsec-vxlan-overlay-release-v0.14.36
cancel-in-progress: false

jobs:
Expand All @@ -48,8 +48,8 @@ jobs:
CODEQL_RUN_ID: ${{ inputs.codeql_run_id }}
RESUME_DIGEST: ${{ inputs.resume_digest }}
GH_TOKEN: ${{ github.token }}
SERVICE_IMAGE: ghcr.io/pasturestack/ipsec-vxlan-overlay-network:v0.14.35
LOCAL_IMAGE: local/pasturestack/ipsec-vxlan-overlay-network:0.14.35
SERVICE_IMAGE: ghcr.io/pasturestack/ipsec-vxlan-overlay-network:v0.14.36
LOCAL_IMAGE: local/pasturestack/ipsec-vxlan-overlay-network:0.14.36
TRIVY_IMAGE: aquasec/trivy:0.74.0@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969
GOFLAGS: -mod=vendor
GOWORK: off
Expand All @@ -67,7 +67,7 @@ jobs:
run: |
set -euo pipefail
test "$GITHUB_REF" = refs/heads/main
test "$RELEASE_TAG" = v0.14.35
test "$RELEASE_TAG" = v0.14.36
[[ "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]
[[ "$SECURITY_RUN_ID" =~ ^[0-9]+$ ]]
[[ "$CODEQL_RUN_ID" =~ ^[0-9]+$ ]]
Expand Down Expand Up @@ -152,15 +152,15 @@ jobs:
shell: bash
run: |
set -euo pipefail
VERSION_OVERRIDE=0.14.35 ./scripts/build
VERSION_OVERRIDE=0.14.36 ./scripts/build
sha256sum --check evidence/product.sha256
if [ -z "$RESUME_DIGEST" ]; then
VERSION_OVERRIDE=0.14.35 TAG=0.14.35 REPO=local/pasturestack \
VERSION_OVERRIDE=0.14.36 TAG=0.14.36 REPO=local/pasturestack \
IMAGE_REVISION="$SOURCE_SHA" ./scripts/package
test "$(wc -l < dist/images)" -eq 1
grep -Fx "$LOCAL_IMAGE" dist/images
test "$(docker image inspect "$LOCAL_IMAGE" \
--format '{{index .Config.Labels "org.opencontainers.image.version"}}')" = 0.14.35
--format '{{index .Config.Labels "org.opencontainers.image.version"}}')" = 0.14.36
test "$(docker image inspect "$LOCAL_IMAGE" \
--format '{{index .Config.Labels "org.opencontainers.image.revision"}}')" = "$SOURCE_SHA"
docker tag "$LOCAL_IMAGE" "$SERVICE_IMAGE"
Expand All @@ -178,7 +178,7 @@ jobs:
docker pull "$SERVICE_IMAGE@$RESUME_DIGEST"
docker tag "$SERVICE_IMAGE@$RESUME_DIGEST" "$SERVICE_IMAGE"
test "$(docker image inspect "$SERVICE_IMAGE" \
--format '{{index .Config.Labels "org.opencontainers.image.version"}}')" = 0.14.35
--format '{{index .Config.Labels "org.opencontainers.image.version"}}')" = 0.14.36
test "$(docker image inspect "$SERVICE_IMAGE" \
--format '{{index .Config.Labels "org.opencontainers.image.revision"}}')" = "$SOURCE_SHA"
verification_container="$(docker create "$SERVICE_IMAGE")"
Expand Down Expand Up @@ -315,7 +315,7 @@ jobs:
run: |
set -euo pipefail
release_dir="dist/release"
product_dir="$release_dir/ipsec-vxlan-overlay-network-0.14.35-linux-amd64"
product_dir="$release_dir/ipsec-vxlan-overlay-network-0.14.36-linux-amd64"
mkdir -p "$product_dir"
cp bin/ipsec-vxlan-overlay-network bin/ipsec-vxlan-overlay-topology bin/pasture-cni-resolver \
LICENSE ORIGIN.md SECURITY.md THIRD_PARTY_NOTICES.md "$product_dir/"
Expand All @@ -327,11 +327,11 @@ jobs:
find "$product_dir" -exec touch -h -d "@$source_epoch" {} +
tar --sort=name --mtime="@$source_epoch" --owner=0 --group=0 \
--numeric-owner -C "$release_dir" \
-cJf "$release_dir/ipsec-vxlan-overlay-network-0.14.35-linux-amd64.tar.xz" \
ipsec-vxlan-overlay-network-0.14.35-linux-amd64
-cJf "$release_dir/ipsec-vxlan-overlay-network-0.14.36-linux-amd64.tar.xz" \
ipsec-vxlan-overlay-network-0.14.36-linux-amd64
(
cd "$release_dir"
sha256sum ipsec-vxlan-overlay-network-0.14.35-linux-amd64.tar.xz \
sha256sum ipsec-vxlan-overlay-network-0.14.36-linux-amd64.tar.xz \
source-sbom.cdx.json image-sbom.cdx.json product.sha256 \
runtime-security.json gate-security-summary.txt image-digest.txt \
release-identity.txt > SHA256SUMS
Expand All @@ -355,9 +355,9 @@ jobs:
run: |
set -euo pipefail
gh release create "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" --verify-tag \
--title 'IPsec Overlay v0.14.35' \
--title 'IPsec Overlay v0.14.36' \
--notes "Source ${SOURCE_SHA}; Security gate run ${SECURITY_RUN_ID}; CodeQL run ${CODEQL_RUN_ID}. GHCR image ${SERVICE_IMAGE}@$(cat dist/release/image-digest.txt | sed 's/^.*@//')." \
dist/release/ipsec-vxlan-overlay-network-0.14.35-linux-amd64.tar.xz \
dist/release/ipsec-vxlan-overlay-network-0.14.36-linux-amd64.tar.xz \
dist/release/source-sbom.cdx.json \
dist/release/image-sbom.cdx.json \
dist/release/product.sha256 \
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/security-release-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 120
env:
CANDIDATE_VERSION: 0.14.35
VERSION_OVERRIDE: 0.14.35
TAG: 0.14.35
CANDIDATE_VERSION: 0.14.36
VERSION_OVERRIDE: 0.14.36
TAG: 0.14.36
IMAGE_REVISION: ${{ github.sha }}
REPO: local/pasturestack
IMAGE: local/pasturestack/ipsec-vxlan-overlay-network:0.14.35
IMAGE: local/pasturestack/ipsec-vxlan-overlay-network:0.14.36
GO_VERSION: 1.27.0
GO_LINUX_AMD64_SHA256: 675c26c449cbb18fc24b74650de1eabbae6e16f64326fd85a283fb3b58280685
TRIVY_IMAGE: aquasec/trivy:0.74.0@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969
Expand Down Expand Up @@ -280,7 +280,7 @@ jobs:
impact_statement:$impact
}] | {
"@context":"https://openvex.dev/ns/v0.2.0",
"@id":"https://github.com/PastureStack/ipsec-vxlan-overlay-network/security/openvex/dapper/0.14.35",
"@id":"https://github.com/PastureStack/ipsec-vxlan-overlay-network/security/openvex/dapper/0.14.36",
author:"PastureStack Security",
timestamp:$timestamp,
version:1,
Expand Down
1 change: 1 addition & 0 deletions COMPATIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ PastureStack names are the public interface for new deployments. A limited set o
- XFRM and host-route variables: `PASTURESTACK_NETWORK_XFRM_*`, `PASTURESTACK_NETWORK_RUN_IN_HOST_NETNS`, and `PASTURESTACK_NETWORK_SYNC_HOST_ROUTES`
- Host firewall selection: `PASTURESTACK_FIREWALL_BACKEND=auto|nftables|iptables-nft|iptables-legacy` for the IPsec host-XFRM router. From `v0.14.28`, it reads Docker's actual `/info.FirewallBackend.Driver` from the mounted Unix socket and then validates the uniquely active, matching host firewall hooks. An old Docker release with no native nftables support may omit this API field; a Docker 29+ release omitting it is ambiguous and fails closed. A mounted Unix socket is a privileged API capability even if its bind mount says `:ro`.
- CNI log: `/var/log/pasturestack-cni.log`
- Flat CNI: the bundled `rancher-cni-bridge` v0.7.2 must honor `skipBridgeConfigureIP: true`. The Layer 2 Catalog template uses an operator-owned bridge; the CNI driver must not add a gateway address or otherwise reconfigure that bridge. The isolated image gate checks this contract with a real network namespace.
- Platform CA: `/var/lib/pasturestack/etc/ssl/ca.crt`

## Required compatibility identifiers
Expand Down
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,15 @@ cross-host TCP, local Metadata/DNS, and HTTPS egress passed in both workload
namespaces. Startup is asynchronous, so a service-level healthy status alone
must not be used as proof that a restarted workload has acquired its IP.

The `v0.14.35` candidate addresses a separate rolling-upgrade handoff: a
`v0.14.35` addressed a separate rolling-upgrade handoff: a
replacement connectivity-check can briefly share the prior generation's
network namespace while the old sidecar still owns TCP 80. It waits at most
90 seconds for that listener to leave, retrying only `EADDRINUSE`; an unrelated
bind error or exhausted deadline still fails clearly. This stays inside the
connectivity-check module and does not alter host firewall rules, Docker
backend selection, or the router's port 8111 ownership. Source tests hold and
release the port and verify the bounded failure path. Image publication,
Catalog pinning, and live upgrade acceptance remain separate gates.
Catalog pinning, and live upgrade acceptance are separate gates for each release.

The release gate rejects Critical/High findings and secrets in the source,
shipped binaries, and runtime image. It scans the disposable Dapper builder
Expand All @@ -124,8 +124,8 @@ The build is containerized and requires Docker on a Linux AMD64 host:
```sh
make test
make validate
VERSION_OVERRIDE=0.14.35 make build
TAG=0.14.35 make package
VERSION_OVERRIDE=0.14.36 make build
TAG=0.14.36 make package
```

The package build downloads dependencies anonymously, verifies every standalone binary with SHA-256, pins the Ubuntu base image by digest, resolves every directly installed package from Canonical snapshot `20260808T000000Z` with the exact versions in `ubuntu-apt.lock`, and includes the corresponding strongSwan source archives in the image. Go dependencies are declared in `go.mod`, checksum-bound by `go.sum`, and committed in the standard module-aware `vendor` tree for offline builds.
Expand All @@ -137,6 +137,11 @@ bridge connectivity, and DEL. A local reproduction must set `IMAGE` to the
exact image under review. This does not replace a live Catalog, Metadata API,
host-port, cross-host, or host-reboot acceptance test.

The flat-network gate deliberately uses a preconfigured bridge address that
differs from the subnet's network address and checks that CNI does not add a
second address. The packaged bridge compatibility binary is v0.7.2, the first
release in this dependency line that recognizes `skipBridgeConfigureIP`.

The health reconciler canonicalizes strongSwan VICI CHILD_SA runtime names before comparing them with configured peer names. This prevents a VICI unique-ID suffix from being misclassified as a missing SA during a rolling replacement.

## Host firewall backends
Expand Down
12 changes: 6 additions & 6 deletions THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ PastureStack does not relicense third-party work. Copyright, license, and attrib
| --- | --- | --- | --- |
| strongSwan | Ubuntu `6.0.4-1ubuntu3.1` | [Ubuntu source package](https://packages.ubuntu.com/source/resolute-updates/strongswan) | GPL-2.0-or-later with OpenSSL exception, plus file-specific licenses listed by Ubuntu |
| CNI reference plugins | 0.3.0 | [containernetworking/plugins](https://github.com/containernetworking/plugins) | Apache-2.0 |
| CNI bridge compatibility binary | 0.3.1 | [rancher/rancher-cni-bridge](https://github.com/rancher/rancher-cni-bridge) | Apache-2.0 |
| CNI bridge compatibility binary | 0.7.2 | [rancher/rancher-cni-bridge](https://github.com/rancher/rancher-cni-bridge) | Apache-2.0 |
| Weave router helper | r-v0.0.4 | [rancher-archives/weave](https://github.com/rancher-archives/weave) | Apache-2.0 |
| Metadata CNI IPAM | 0.2.6 | [PastureStack/metadata-cni-ipam](https://github.com/PastureStack/metadata-cni-ipam) | Apache-2.0 |
| Host-local CNI IPAM | 0.1.3 | [PastureStack/host-local-cni-ipam](https://github.com/PastureStack/host-local-cni-ipam) | Apache-2.0 |
| Flat CNI IPAM | 0.1.3 | [PastureStack/flat-cni-ipam](https://github.com/PastureStack/flat-cni-ipam) | Apache-2.0 |
| Per-host subnet | 0.2.7 | [PastureStack/per-host-subnet](https://github.com/PastureStack/per-host-subnet) | Apache-2.0 |
| Mount propagation | 1.0.10 | [PastureStack/mount-propagation](https://github.com/PastureStack/mount-propagation) | Apache-2.0 |
| Metadata CNI IPAM | 0.2.7 | [PastureStack/metadata-cni-ipam](https://github.com/PastureStack/metadata-cni-ipam) | Apache-2.0 |
| Host-local CNI IPAM | 0.1.4 | [PastureStack/host-local-cni-ipam](https://github.com/PastureStack/host-local-cni-ipam) | Apache-2.0 |
| Flat CNI IPAM | 0.1.4 | [PastureStack/flat-cni-ipam](https://github.com/PastureStack/flat-cni-ipam) | Apache-2.0 |
| Per-host subnet | 0.2.8 | [PastureStack/per-host-subnet](https://github.com/PastureStack/per-host-subnet) | Apache-2.0 |
| Mount propagation | 1.0.11 | [PastureStack/mount-propagation](https://github.com/PastureStack/mount-propagation) | Apache-2.0 |

The reachable Go dependency graph is declared in [`go.mod`](go.mod), checksum-bound by [`go.sum`](go.sum), and materialized in the standard module-aware `vendor` tree. Unreachable historical test-server dependencies are not shipped.

Expand Down
6 changes: 3 additions & 3 deletions package/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ ARG STRONGSWAN_DEBIAN_SHA256=2813fea68dc93da2c17cc0c7c8a30e39b61dba333c8bdf7a7e0
ARG STRONGSWAN_DSC_SHA256=64c7e3ad1d44ff5b5e287cf02a65e4b51b351a854a2773d90fbb4a2ff8f1f39b
ARG CNI_VERSION=v0.3.0
ARG CNI_SHA256=b1ae09833a238c51161918a8849031efdb46cf0068ea5b752e362d9836e2af7d
ARG CNI_BRIDGE_VERSION=v0.3.1
ARG CNI_BRIDGE_SHA256=478f9e04772da427455e0bd90c708cc9b96f4cab89acfc0fd67c13cd779e6c34
ARG CNI_BRIDGE_VERSION=v0.7.2
ARG CNI_BRIDGE_SHA256=e9050b13aaa769a7a22b287c76d5dbac80cbee390f4c0b0b6db23a73f90cee59
ARG WEAVE_ROUTER_VERSION=r-v0.0.4
ARG WEAVE_ROUTER_SHA256=15d55366dbcc33c8a9fd3ca5d3e53256b9184966bc1a59e2c88ed524660382ea

Expand Down Expand Up @@ -89,7 +89,7 @@ RUN set -eux; \
tar -xzf /tmp/cni.tgz -C /tmp/cni ./loopback; \
tar -xzf /tmp/cni-bridge.tgz -C /tmp/cni-bridge; \
install -m 0755 /tmp/cni/loopback /opt/cni/bin/loopback; \
install -m 0755 /tmp/cni-bridge/rancher-cni-bridge /opt/cni/bin/pasture-bridge-core; \
install -m 0755 /tmp/cni-bridge/rancher-bridge /opt/cni/bin/pasture-bridge-core; \
chmod 0755 /usr/bin/weave-router; \
rm -rf /tmp/cni.tgz /tmp/cni-bridge.tgz /tmp/cni /tmp/cni-bridge

Expand Down
12 changes: 8 additions & 4 deletions scripts/integration-optional-cni-isolated
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,20 @@ echo 'PER_HOST_CNI=pass'
# bridge inside this container. A direct address avoids depending on a real
# platform Metadata API; the Metadata lookup path has its own source tests.
ip link add brflatqa type bridge
ip address add 10.55.244.1/24 dev brflatqa
ip address add 10.55.244.2/24 dev brflatqa
ip link set brflatqa up
ip netns add qa-flat
export CNI_NETNS=/run/netns/qa-flat 'CNI_ARGS=IgnoreUnknown=1;MACAddress=02:42:37:ff:01:02;IPAddress=10.55.244.2/24'
flat='{"cniVersion":"0.1.0","name":"qa-flat","type":"pasture-bridge","bridge":"brflatqa","bridgeSubnet":"10.55.244.1/24","skipBridgeConfigureIP":true,"skipFastPath":true,"hostNat":false,"ipam":{"type":"flat-cni-ipam","metadataAddress":"169.254.169.250"}}'
export CNI_NETNS=/run/netns/qa-flat 'CNI_ARGS=IgnoreUnknown=1;MACAddress=02:42:37:ff:01:02;IPAddress=10.55.244.3/24'
flat='{"cniVersion":"0.1.0","name":"qa-flat","type":"pasture-bridge","bridge":"brflatqa","bridgeSubnet":"10.55.244.0/24","skipBridgeConfigureIP":true,"skipFastPath":true,"hostNat":false,"ipam":{"type":"flat-cni-ipam","metadataAddress":"169.254.169.250"}}'
if ! result=$(CNI_COMMAND=ADD /opt/cni/bin/pasture-bridge <<<"$flat"); then
printf 'flat CNI ADD: %s\n' "$result" >&2
exit 1
fi
check_link qa-flat 10.55.244.2 10.55.244.1
check_link qa-flat 10.55.244.3 10.55.244.2
if [[ $(ip -4 -o address show dev brflatqa | wc -l) -ne 1 ]] || ! ip -4 address show dev brflatqa | grep -q '10.55.244.2/24'; then
echo 'flat CNI changed the preconfigured bridge address' >&2
exit 1
fi
CNI_COMMAND=DEL /opt/cni/bin/pasture-bridge <<<"$flat" >/dev/null
if ip netns exec qa-flat ip link show eth0 >/dev/null 2>&1; then
echo 'flat CNI DEL left eth0 behind' >&2
Expand Down
2 changes: 1 addition & 1 deletion scripts/integration-two-node-ipsec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash
set -euo pipefail

image=${IMAGE:-local/pasturestack/ipsec-vxlan-overlay-network:0.14.35}
image=${IMAGE:-local/pasturestack/ipsec-vxlan-overlay-network:0.14.36}
old_image=${OLD_IMAGE:-}
startup_image=${old_image:-$image}
test_id=$$
Expand Down
2 changes: 1 addition & 1 deletion scripts/integration-two-node-vxlan
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash
set -euo pipefail

image=${IMAGE:-local/pasturestack/ipsec-vxlan-overlay-network:0.14.35}
image=${IMAGE:-local/pasturestack/ipsec-vxlan-overlay-network:0.14.36}
test_id=$$
network="pasture-vxlan-it-${test_id}"
node_a="pasture-vxlan-a-${test_id}"
Expand Down
2 changes: 1 addition & 1 deletion scripts/validate
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ for script in scripts/* package/*.sh package/update-platform-ca; do
bash -n "$script"
done

version=${VERSION_OVERRIDE:-0.14.35}
version=${VERSION_OVERRIDE:-0.14.36}
first=$(mktemp -d)
trap 'rm -rf "$first"' EXIT
VERSION_OVERRIDE="$version" ./scripts/build
Expand Down