# 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: the fuzz runner fails on a failing input and on nothing else # fuzz.sh does not fail on Go's own -fuzztime report, on the Go # releases whose fuzzer reports its deadline as a failure, and fails on # anything else; this checks both, with planted failing inputs, before # the targets run, and fails once the image's Go no longer needs the # workaround. run: bash deploy/test_fuzz.sh - 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" || { bash ../deploy/check-claims.sh --failed "$RUNNER_TEMP/claims.json"; exit 1; } - name: none of them skipped run: bash deploy/check-claims.sh --skipped "$RUNNER_TEMP/claims.json"