Enforce immutable supply chain defaults

This commit is contained in:
ms2k 2026-08-21 14:25:58 +09:00
commit 19ac156f4b
9 changed files with 187 additions and 23 deletions

View file

@ -19,7 +19,7 @@ COPY .config .config
COPY contracts contracts
COPY crates crates
COPY docs/api docs/api
CMD ["sh", "-c", "cargo fmt --all --check && cargo clippy --workspace --all-targets --all-features -- -D warnings && cargo nextest run --workspace --all-features && cargo test --workspace --doc"]
CMD ["sh", "-c", "cargo fmt --all --check && cargo clippy --locked --workspace --all-targets --all-features -- -D warnings && cargo nextest run --locked --workspace --all-features && cargo test --locked --workspace --doc"]
FROM rust-tools AS kind-sample-build
WORKDIR /workspace
@ -476,11 +476,22 @@ CMD ["bash", "-ec", "test -s /run/remora-fixture/profile.env && test -n \"$DATAB
FROM ${NODE_IMAGE} AS node-runtime
FROM rust-tools AS contract-check
COPY --from=node-runtime /usr/local/ /usr/local/
WORKDIR /workspace
COPY contracts contracts
RUN npm ci --prefix contracts/typespec \
&& npm ci --prefix contracts/typespec-rust-emitter
COPY docs/api docs/api
COPY crates/remora-contracts/src/generated_wire.rs crates/remora-contracts/src/generated_wire.rs
COPY web/src/api/contracts.ts web/src/api/decode.ts web/src/api/generated-schema.ts web/src/api/
CMD ["npm", "run", "check", "--prefix", "contracts/typespec"]
FROM ${PLAYWRIGHT_IMAGE} AS web-dependencies
COPY --from=node-runtime /usr/local/ /usr/local/
ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright
RUN corepack enable pnpm \
&& corepack install --global [email protected]
&& corepack install --global pnpm@11.22.0+sha512.1ff870c4c6133dfd88fb2afc46dd13d47f09c9794b438c6fdb47ca98caf3bc16381ee0be93a091b8e3824cf01f889f46d7d9e20910fb0be1ab0fb5baa80dd621
WORKDIR /workspace/web
COPY web/package.json web/pnpm-lock.yaml web/pnpm-workspace.yaml ./
COPY web/tools/svelte-check/package.json tools/svelte-check/package.json

View file

@ -1,4 +1,4 @@
.PHONY: check-dependencies check-fixture-lib check-kind-sample check-durable check-coordination check-identity check-credentials check-admission check-attempt check-attempt-mutations check-recovery check-worker-api check-public-api check-live-events check-worker check-stream check-target check-result check-deletion check-outbound check-http-source check-object-source check-hf-source check-deployment check-requirements check-qualification-kind check-qualification check-fast check-full check-fast-host check-full-host contract-check rust-check web-install web-check web-test-fast web-test-full
.PHONY: check-pins check-dependencies check-fixture-lib check-kind-sample check-durable check-coordination check-identity check-credentials check-admission check-attempt check-attempt-mutations check-recovery check-worker-api check-public-api check-live-events check-worker check-stream check-target check-result check-deletion check-outbound check-http-source check-object-source check-hf-source check-deployment check-requirements check-qualification-kind check-qualification check-fast check-full check-fast-host check-full-host contract-check contract-check-host rust-check web-install web-check web-test-fast web-test-full
CONTAINER_ENGINE ?= nerdctl
CONTAINER_RUN_FLAGS ?= --memory=16g --memory-swap=24g --cpus=4
@ -6,15 +6,22 @@ PLAYWRIGHT_BROWSERS_PATH := $(CURDIR)/web/.cache/ms-playwright
export PLAYWRIGHT_BROWSERS_PATH
contract-check:
$(CONTAINER_ENGINE) build --file Dockerfile.test --target contract-check --tag remora-contract-check:local .
$(CONTAINER_ENGINE) run --rm $(CONTAINER_RUN_FLAGS) remora-contract-check:local
contract-check-host:
npm ci --prefix contracts/typespec
npm ci --prefix contracts/typespec-rust-emitter
npm run check --prefix contracts/typespec
rust-check: contract-check
check-pins:
./scripts/check-pins.sh
rust-check: contract-check-host
cargo fmt --all --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo nextest run --workspace --all-features
cargo test --workspace --doc
cargo clippy --locked --workspace --all-targets --all-features -- -D warnings
cargo nextest run --locked --workspace --all-features
cargo test --locked --workspace --doc
web-install:
pnpm --dir web install --frozen-lockfile
@ -36,23 +43,23 @@ web-test-full: web-install
pnpm --dir web test:component:full
pnpm --dir web test:e2e:full
check-fast: check-fixture-lib contract-check
check-fast: check-pins check-fixture-lib contract-check
$(CONTAINER_ENGINE) build --file Dockerfile.test --target rust-check --tag remora-rust-check:local .
$(CONTAINER_ENGINE) run --rm $(CONTAINER_RUN_FLAGS) remora-rust-check:local
$(CONTAINER_ENGINE) build --file Dockerfile.test --target web-fast --tag remora-web-check:local .
$(CONTAINER_ENGINE) run --rm $(CONTAINER_RUN_FLAGS) remora-web-check:local
check-full: check-fixture-lib contract-check
check-full: check-pins check-fixture-lib contract-check
$(CONTAINER_ENGINE) build --file Dockerfile.test --target rust-check --tag remora-rust-check:local .
$(CONTAINER_ENGINE) run --rm $(CONTAINER_RUN_FLAGS) remora-rust-check:local
$(CONTAINER_ENGINE) build --file Dockerfile.test --target web-full --tag remora-web-check:local .
$(CONTAINER_ENGINE) run --rm $(CONTAINER_RUN_FLAGS) remora-web-check:local
check-fast-host: rust-check web-check web-test-fast
check-fast-host: check-pins rust-check web-check web-test-fast
check-full-host: rust-check web-check web-test-full
check-full-host: check-pins rust-check web-check web-test-full
check-dependencies:
check-dependencies: check-pins
./tests/kind/fixture/run.sh
check-fixture-lib:
@ -97,11 +104,11 @@ check-live-events:
check-worker:
./tests/kind/worker/run.sh
check-stream:
check-stream: check-pins
cargo fmt --all --check
cargo clippy --package remora-stream --all-targets --all-features -- -D warnings
cargo nextest run --package remora-stream --all-features
cargo test --package remora-stream --doc
cargo clippy --locked --package remora-stream --all-targets --all-features -- -D warnings
cargo nextest run --locked --package remora-stream --all-features
cargo test --locked --package remora-stream --doc
check-target:
./tests/kind/target/run.sh
@ -124,7 +131,7 @@ check-object-source:
check-hf-source:
./tests/kind/hf-source/run.sh
check-deployment:
check-deployment: check-pins
./tests/kind/deployment/run.sh
check-requirements:

View file

@ -3,6 +3,15 @@
{{- end -}}
{{- define "remora.validateValues" -}}
{{- range $component := list "api" "worker" "web" -}}
{{- $image := (get $.Values $component).image -}}
{{- if and (not $image.digest) (not $image.allowTag) -}}
{{- fail (printf "%s.image.digest is required unless %s.image.allowTag is true" $component $component) -}}
{{- end -}}
{{- end -}}
{{- if and (not (regexMatch "@sha256:[0-9a-f]{64}$" .Values.vault.image)) (not .Values.vault.allowTag) -}}
{{- fail "vault.image must use a sha256 digest unless vault.allowTag is true" -}}
{{- end -}}
{{- if ge (int .Values.runtime.workerRenewSeconds) (int .Values.coordination.leaseTtlSeconds) -}}
{{- fail "runtime.workerRenewSeconds must be less than coordination.leaseTtlSeconds" -}}
{{- end -}}

View file

@ -124,6 +124,7 @@
"additionalProperties": false,
"required": [
"image",
"allowTag",
"address",
"path",
"kubernetesAuthMount",
@ -134,6 +135,7 @@
],
"properties": {
"image": {"$ref": "#/$defs/nonEmpty"},
"allowTag": {"type": "boolean"},
"address": {"$ref": "#/$defs/httpUrl"},
"path": {"$ref": "#/$defs/nonEmpty"},
"kubernetesAuthMount": {"$ref": "#/$defs/nonEmpty"},
@ -346,11 +348,12 @@
"image": {
"type": "object",
"additionalProperties": false,
"required": ["repository", "tag", "digest", "pullPolicy"],
"required": ["repository", "tag", "digest", "allowTag", "pullPolicy"],
"properties": {
"repository": {"$ref": "#/$defs/nonEmpty"},
"tag": {"$ref": "#/$defs/nonEmpty"},
"digest": {"type": "string", "pattern": "^(|sha256:[0-9a-f]{64})$"},
"allowTag": {"type": "boolean"},
"pullPolicy": {"enum": ["Always", "IfNotPresent", "Never"]}
}
},

View file

@ -11,6 +11,7 @@ api:
repository: remora-api
tag: 0.0.0
digest: ""
allowTag: false
pullPolicy: IfNotPresent
resources:
requests:
@ -26,6 +27,7 @@ worker:
repository: remora-worker
tag: 0.0.0
digest: ""
allowTag: false
pullPolicy: IfNotPresent
resources:
requests:
@ -41,6 +43,7 @@ web:
repository: remora-web
tag: 0.0.0
digest: ""
allowTag: false
pullPolicy: IfNotPresent
publicOrigin: https://remora.example.com
oidcClientId: remora-web
@ -98,6 +101,7 @@ target:
vault:
image: hashicorp/vault@sha256:db6a5f78749204d0a8d002be1cc6e9a2a6de53dffce41a894c4a06d309dca360
allowTag: false
address: https://vault.example.com
path: remora
kubernetesAuthMount: auth/kubernetes

View file

@ -31,6 +31,8 @@ managed.
## Security
Workload and Vault images require SHA-256 digests by default. A deployment that intentionally accepts a mutable tag enables the corresponding `allowTag` escape hatch explicitly.
NetworkPolicies default-deny each workload, isolate the Worker and management
ports, and allow dependencies by selector or CIDR. The WebBFF may reach only
the API, identity provider, and its session store. Public source egress excludes

117
scripts/check-pins.sh Executable file
View file

@ -0,0 +1,117 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
fail() {
printf '%s\n' "$1" >&2
exit 1
}
awk '
function verify() {
if (registry && !checksum) {
printf "registry package lacks checksum: %s\n", package > "/dev/stderr"
failed = 1
}
}
/^\[\[package\]\]$/ {
verify()
package = "unknown"
registry = 0
checksum = 0
next
}
/^name = "/ {
package = $0
sub(/^name = "/, "", package)
sub(/"$/, "", package)
}
/^source = "registry\+/ { registry = 1 }
/^checksum = "/ {
value = $0
sub(/^checksum = "/, "", value)
sub(/"$/, "", value)
checksum = length(value) == 64 && value !~ /[^0-9a-f]/
}
END {
verify()
exit failed
}
' "$ROOT/Cargo.lock"
if rg -q '^source = "git\+' "$ROOT/Cargo.lock"; then
fail "Cargo.lock contains a Git dependency"
fi
for lock in \
"$ROOT/contracts/typespec/package-lock.json" \
"$ROOT/contracts/typespec-rust-emitter/package-lock.json"; do
if ! jq -e '
[.packages | to_entries[] |
select(.key | startswith("node_modules/")) |
select(.value.link != true) |
select(.value.integrity == null)] |
length == 0
' "$lock" >/dev/null; then
fail "$lock contains a registry package without integrity"
fi
done
read -r pnpm_packages pnpm_hashes < <(
awk '
BEGIN { packages = 0; hashes = 0; active = 0 }
/^packages:/ { active = 1; next }
/^snapshots:/ { active = 0 }
active && /^ [^ ].*:$/ { packages++ }
active && /integrity:/ { hashes++ }
END { print packages, hashes }
' "$ROOT/web/pnpm-lock.yaml"
)
if [[ "$pnpm_packages" != "$pnpm_hashes" ]]; then
fail "web/pnpm-lock.yaml contains a package without integrity"
fi
pnpm_ref="$(jq -r .packageManager "$ROOT/web/package.json")"
if [[ ! "$pnpm_ref" =~ ^pnpm@[0-9]+\.[0-9]+\.[0-9]+\+sha512\.[0-9a-f]{128}$ ]]; then
fail "web/package.json packageManager is not content-pinned"
fi
if ! rg -Fq "corepack install --global $pnpm_ref" "$ROOT/Dockerfile.test"; then
fail "Dockerfile.test does not install the pinned package manager"
fi
while IFS= read -r image; do
if [[ ! "$image" =~ @sha256:[0-9a-f]{64}$ ]]; then
fail "fixture image is not digest-pinned: $image"
fi
done < <(awk -F= '/_IMAGE=/ { print $2 }' "$ROOT/tests/kind/fixture/versions.env")
while IFS= read -r image; do
if [[ ! "$image" =~ @sha256:[0-9a-f]{64}$ ]]; then
fail "Docker image default is not digest-pinned: $image"
fi
done < <(awk -F= '/^ARG [A-Z_]+_IMAGE=/ { print $2 }' "$ROOT/Dockerfile" "$ROOT/Dockerfile.test")
while IFS= read -r command; do
if [[ "$command" != *--locked* ]]; then
fail "Cargo dependency command lacks --locked: $command"
fi
done < <(
{
sed -n '/cargo \(build\|check\|clippy\|install\|nextest\|test\)/p' "$ROOT/Makefile"
awk '
{
line = line $0
if (sub(/\\$/, "", line)) {
next
}
print line
line = ""
}
' "$ROOT/Dockerfile" "$ROOT/Dockerfile.test" |
sed 's/&&/\n/g' |
sed -n '/cargo \(build\|check\|clippy\|install\|nextest\|test\)/p'
} | sed '/cargo fmt/d'
)
printf 'immutable external reference checks passed\n'

View file

@ -181,11 +181,11 @@ write_helm_values() {
fi
cat > "$RUNTIME/deployment-values.yaml" <<EOF
api:
image: {repository: remora-api, tag: local, digest: "", pullPolicy: Never}
image: {repository: remora-api, tag: local, digest: "", allowTag: true, pullPolicy: Never}
worker:
image: {repository: remora-worker, tag: local, digest: "", pullPolicy: Never}
image: {repository: remora-worker, tag: local, digest: "", allowTag: true, pullPolicy: Never}
web:
image: {repository: remora-web, tag: local, digest: "", pullPolicy: Never}
image: {repository: remora-web, tag: local, digest: "", allowTag: true, pullPolicy: Never}
publicOrigin: $public_origin
oidcClientId: remora-web
podDnsPolicy: None
@ -209,6 +209,7 @@ target:
defaultPrefix: "$target_prefix"
vault:
image: "$VAULT_LOCAL_IMAGE"
allowTag: true
address: http://vault:8200
path: "$vault_path"
kubernetesAuthMount: auth/remora-kubernetes
@ -388,10 +389,20 @@ reject_chart_values() {
validate_deployment_assets() {
local chart="$ROOT/deploy/charts/remora"
helm lint "$chart"
local tag_args=(
--set api.image.allowTag=true
--set worker.image.allowTag=true
--set web.image.allowTag=true
)
reject_chart_values "mutable application image defaults"
reject_chart_values "a mutable Vault image" \
"${tag_args[@]}" \
--set-string vault.image=hashicorp/vault:latest
helm lint "$chart" "${tag_args[@]}"
umask 077
helm template remora "$chart" \
--namespace "$FIXTURE_NAMESPACE" \
"${tag_args[@]}" \
--set observability.prometheusRule.enabled=true \
> "$RUNTIME/deployment-policy-rendered.yaml"
grep -q '^kind: NetworkPolicy$' "$RUNTIME/deployment-policy-rendered.yaml"

View file

@ -4,7 +4,7 @@
"license": "Apache-2.0",
"private": true,
"type": "module",
"packageManager": "[email protected]",
"packageManager": "[email protected]+sha512.1ff870c4c6133dfd88fb2afc46dd13d47f09c9794b438c6fdb47ca98caf3bc16381ee0be93a091b8e3824cf01f889f46d7d9e20910fb0be1ab0fb5baa80dd621",
"engines": {
"node": "24.18.0",
"pnpm": "11.22.0"