bastion 0.55.3
ci / personal-data (push) Successful in 5s
ci / test (push) Successful in 3m51s
ci / fuzz (push) Successful in 2m55s

This commit is contained in:
Privatae
2026-10-25 23:16:45 +00:00
parent 77b7853ae1
commit 8822d5df85
136 changed files with 8638 additions and 1641 deletions
+241
View File
@@ -0,0 +1,241 @@
# The checks of .gitea/workflows/ci.yml, for the copy of this repository on
# GitHub. Gitea reads .gitea/workflows when it exists and then ignores this
# directory, so each forge runs one of the two files, never both.
#
# The jobs, image and commands are the same as there. Every job runs in the Go
# toolchain image deploy/build.sh builds releases with, pinned by the same
# digest, so CI and the release use the same compiler. GitHub does not let a
# job's container name come from env, so the digest is written out in each
# job; it must match GO_IMAGE in deploy/build.sh and .gitea/workflows/ci.yml.
# No action from the marketplace is used: the commit is fetched with git from
# this repository's public URL, so nothing outside this file and the pinned
# image runs. The job token can only read.
name: ci
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
env:
# The pull request's own head, or the pushed commit.
WANT: ${{ github.event.pull_request.head.sha || github.sha }}
REPO_URL: ${{ github.server_url }}/${{ github.repository }}.git
GOTOOLCHAIN: local
CGO_ENABLED: "0"
defaults:
run:
shell: sh -eu {0}
jobs:
test:
runs-on: ubuntu-24.04
timeout-minutes: 30
container:
image: golang@sha256:27f2d3b83cfc5803c8078f8e730af0f7c431bf067afaec8f60a36fb763187cba
env:
PRIVATAE_REQUIRE_AGE: "1"
steps:
- name: tools
# pdftotext, python3, setpriv and git are what the extract and
# peer-identity tests call; without them those tests fail rather than
# skip. Alpine's git has no http-backend (it is in git-daemon), which
# the forge-token test serves its repository with.
run: apk add -q --no-cache poppler-utils python3 setpriv curl git git-daemon
- name: fetch the commit
# The checkout belongs to the runner's user and the job runs as root,
# so git is told the directory is safe; go build reads it to stamp the
# binary with the commit.
run: |
set -o pipefail
git config --global --add safe.directory "$PWD"
git init -q .
git fetch -q --depth=1 "$REPO_URL" "$WANT"
git checkout -q --detach FETCH_HEAD
test "$(git rev-parse HEAD)" = "$WANT"
- name: age
# The backup tests check this code against the age command itself: a
# pinned age release, its archive checked against the SHA-256 below,
# and PRIVATAE_REQUIRE_AGE makes those tests fail rather than skip if
# it is missing.
run: |
case "$(uname -m)" in
x86_64) a=amd64; sum=cbe24006683f8eb669266162894b9a522a1af52f2665fbc63a4bb032ed26ac10 ;;
aarch64) a=arm64; sum=6b8dc4333c53a5a57c9e5834e3a48f92605d7154014cd07269ff3327db5d37f4 ;;
*) echo "no pinned age release for $(uname -m)"; exit 1 ;;
esac
curl -fsSL -o /tmp/age.tgz "https://github.com/FiloSottile/age/releases/download/v1.3.2/age-v1.3.2-linux-$a.tar.gz"
echo "$sum /tmp/age.tgz" | sha256sum -c -
tar -xzf /tmp/age.tgz -C /tmp age/age
/tmp/age/age --version
echo "AGE_BIN=/tmp/age/age" >> "$GITHUB_ENV"
- name: gofmt, go vet on linux/darwin/windows, build, go test
working-directory: client-go
run: |
echo "== gofmt"; out="$(gofmt -l .)"; [ -z "$out" ] || { echo "$out"; exit 1; }
for os in linux darwin windows; do echo "== go vet ($os)"; GOOS=$os go vet ./...; done
echo "== go build"; go build -o /tmp/privatae-bastion .
echo "== go test"; go test -count=1 ./...
- name: the same for deploy/release-sign
working-directory: deploy/release-sign
run: |
out="$(gofmt -l .)"; [ -z "$out" ] || { echo "$out"; exit 1; }
for os in linux darwin windows; do GOOS=$os go vet ./...; done
go test -count=1 ./...
- name: staticcheck on linux, darwin and windows
# The pinned staticcheck release, built with this toolchain from the
# module proxy and checked against the checksum database.
run: sh deploy/staticcheck.sh
- name: the release workflow's GitHub step, against a fake API
run: python3 -I deploy/test_github_release.py
personal-data:
runs-on: ubuntu-24.04
timeout-minutes: 10
container:
image: golang@sha256:27f2d3b83cfc5803c8078f8e730af0f7c431bf067afaec8f60a36fb763187cba
steps:
- name: tools
run: apk add -q --no-cache bash git python3
- name: fetch the commit
run: |
set -o pipefail
git config --global --add safe.directory "$PWD"
git init -q .
git fetch -q --depth=1 "$REPO_URL" "$WANT"
git checkout -q --detach FETCH_HEAD
test "$(git rev-parse HEAD)" = "$WANT"
- name: this file's image is the image releases are built with
# Each job here names the digest itself, so a change to GO_IMAGE in
# deploy/build.sh that missed this file fails here.
run: |
want="$(sed -n 's/^GO_IMAGE="\(golang@sha256:[0-9a-f]\{64\}\)"$/\1/p' deploy/build.sh)"
test -n "$want"
test "$(grep -c "^ image: $want\$" .github/workflows/ci.yml)" -eq 3
test "$(grep -c '^ image: ' .github/workflows/ci.yml)" -eq 3
grep -q "^ GO_IMAGE: $want\$" .gitea/workflows/ci.yml
grep -q "^ GO_IMAGE: $want\$" .github/workflows/ci.yml
- name: no person's data in the tree
# Reads files, runs nothing from the tree. The term check needs the
# maintainers' key and the entity-id check the reserved ids, which
# this job never holds, so both are skipped and the run prints a NOTE
# for each; the provenance, email and phone checks run.
run: python3 -I scripts/test_no_personal_data.py
- name: every test the claims table cites exists
# docs/claims.md names, for each security claim, the test that fails
# if the claim stops being true. A renamed or deleted test would leave
# the claim looking covered; this fails the run instead. Reads files
# and greps; runs nothing from the tree.
run: bash deploy/check-claims.sh
- name: no Host source file names in the tree
# The member repository publishes this tree as a release was built,
# and its release step refuses a path into the Host's Python
# (server/... or brain/... .py): this repo has no such files, so the
# path can only name another program's source. git grep exits 1 when
# nothing matches and above 1 when it could not search; only 1 passes.
run: |
rc=0
git grep -nIE 'server/[a-z_/]+\.py|brain/[a-z_/]+\.py' || rc=$?
case "$rc" in
1) ;;
0) echo "these lines name another program's source file; describe it instead" >&2
exit 1 ;;
*) echo "git grep could not search the tree (exit $rc), so it was not checked" >&2
exit 1 ;;
esac
fuzz:
needs: test
runs-on: ubuntu-24.04
timeout-minutes: 20
container:
image: golang@sha256:27f2d3b83cfc5803c8078f8e730af0f7c431bf067afaec8f60a36fb763187cba
steps:
- name: tools
run: apk add -q --no-cache bash git
- name: fetch the commit
run: |
set -o pipefail
git config --global --add safe.directory "$PWD"
git init -q .
git fetch -q --depth=1 "$REPO_URL" "$WANT"
git checkout -q --detach FETCH_HEAD
test "$(git rev-parse HEAD)" = "$WANT"
- name: every fuzz target, 5 seconds each
# A short pass on every commit. Run deploy/fuzz.sh with its default of
# 20 seconds before changing a wall a host's input goes through.
run: bash deploy/fuzz.sh 5
claims-on-a-machine:
# The claim tests a container cannot run: the op and action-op sandboxes
# (root, systemd units, overlayfs, a cgroup with the pids controller),
# the egress tests in private user and network namespaces, and the root
# command watch (nftables). They run here on the runner's own virtual
# machine, as root, because that is what they test: the bastion as a
# networked machine's system service. The machine is GitHub's and is
# discarded after the job. The toolchain is the one in the pinned image.
# Every test docs/claims.md cites is run, and the job fails if any of
# them skips.
needs: test
runs-on: ubuntu-24.04
timeout-minutes: 30
# This job runs on the runner itself, not in the image, and Ubuntu
# 24.04's sh is dash 0.5.12-6ubuntu5, which has no pipefail (Debian added
# it in 0.5.12-7): `set -o pipefail` there is a fatal error. bash has it.
defaults:
run:
shell: bash -eu {0}
env:
GO_IMAGE: golang@sha256:27f2d3b83cfc5803c8078f8e730af0f7c431bf067afaec8f60a36fb763187cba
steps:
- name: fetch the commit
run: |
set -o pipefail
git init -q .
git fetch -q --depth=1 "$REPO_URL" "$WANT"
git checkout -q --detach FETCH_HEAD
test "$(git rev-parse HEAD)" = "$WANT"
- name: tools, and the pinned toolchain on the machine
run: |
sudo apt-get update -qq
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends nftables iproute2 poppler-utils >/dev/null
# The forge-token claim test needs git 2.46 or later; Ubuntu 24.04's
# own git is 2.43.
if ! git version | awk '{split($3, v, "."); exit !(v[1] > 2 || (v[1] == 2 && v[2] >= 46))}'; then
sudo add-apt-repository -y ppa:git-core/ppa >/dev/null
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq git >/dev/null
fi
git version
docker create --name go "$GO_IMAGE" >/dev/null
sudo docker cp go:/usr/local/go /opt/go-pinned
docker rm go >/dev/null
/opt/go-pinned/bin/go version
- name: age
# The same pinned age release as the test job.
run: |
case "$(uname -m)" in
x86_64) a=amd64; sum=cbe24006683f8eb669266162894b9a522a1af52f2665fbc63a4bb032ed26ac10 ;;
aarch64) a=arm64; sum=6b8dc4333c53a5a57c9e5834e3a48f92605d7154014cd07269ff3327db5d37f4 ;;
*) echo "no pinned age release for $(uname -m)"; exit 1 ;;
esac
curl -fsSL -o "$RUNNER_TEMP/age.tgz" "https://github.com/FiloSottile/age/releases/download/v1.3.2/age-v1.3.2-linux-$a.tar.gz"
echo "$sum $RUNNER_TEMP/age.tgz" | sha256sum -c -
tar -xzf "$RUNNER_TEMP/age.tgz" -C "$RUNNER_TEMP" age/age
echo "AGE_BIN=$RUNNER_TEMP/age/age" >> "$GITHUB_ENV"
- name: every cited test, as root
working-directory: client-go
run: |
set -o pipefail
tests="$(grep -o '_test\.go`: `[A-Za-z0-9_]*`' ../docs/claims.md | sed 's/.*`: `//; s/`$//' | sort -u | paste -sd'|' -)"
test -n "$tests"
sudo env PATH="/opt/go-pinned/bin:$PATH" HOME="$HOME" AGE_BIN="$AGE_BIN" PRIVATAE_REQUIRE_AGE=1 GOTOOLCHAIN=local CGO_ENABLED=0 \
GOFLAGS=-mod=readonly GOMODCACHE="$HOME/go/pkg/mod" GOCACHE="$HOME/.cache/go-build" \
go test -count=1 -json -run "^($tests)\$" . > "$RUNNER_TEMP/claims.json" || {
grep -h '"Action":"fail"' "$RUNNER_TEMP/claims.json" | head -40; exit 1; }
- name: none of them skipped
run: bash deploy/check-claims.sh --skipped "$RUNNER_TEMP/claims.json"