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/mirror-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
workflow_dispatch:
inputs:
tag:
description: Immutable release tag to mirror, for example v2.8.0
description: Immutable release tag to mirror, for example v2.8.1
required: true
type: string

Expand Down
25 changes: 19 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,22 @@ jobs:
GO111MODULE: 'on'
run: |
set -Eeuo pipefail
mkdir -p "$GOCACHE" "$GOMODCACHE" "$GOPATH" "$GOTMPDIR" \
"${RUNNER_TEMP}/lta-root-test-cache" "${RUNNER_TEMP}/lta-root-module-cache" \
"${RUNNER_TEMP}/lta-root-go-path" "${RUNNER_TEMP}/lta-root-go-tmp"
mkdir -p "$GOCACHE" "$GOMODCACHE" "$GOPATH" "$GOTMPDIR"
root_go_root="$(sudo mktemp -d /tmp/lta-release-root-go.XXXXXXXXXX)"
[[ "$root_go_root" =~ ^/tmp/lta-release-root-go\.[A-Za-z0-9]{10}$ \
&& -d "$root_go_root" && ! -L "$root_go_root" \
&& "$(sudo stat -Lc '%u %g %a' -- "$root_go_root")" == '0 0 700' ]] \
|| { echo "root Go workspace has unsafe metadata" >&2; exit 1; }
readonly root_go_root
sudo install -d -o 0 -g 0 -m 0700 \
"$root_go_root/gocache" "$root_go_root/gomodcache" \
"$root_go_root/gopath" "$root_go_root/gotmp"
index_file="$(mktemp)"
cleanup() {
rm -f -- "$index_file" || :
sudo rm -rf -- "$root_go_root" || :
}
trap cleanup EXIT
git ls-files -s -z > "$index_file"
while IFS= read -r -d '' index_entry; do
mode="${index_entry%% *}"
Expand All @@ -91,11 +103,12 @@ jobs:
go vet -mod=readonly -printf.funcs=printf,errorf,warnf ./...
env GOROOT= GOENV=off GOTOOLCHAIN=local GOFLAGS= GOWORK=off \
go test -mod=readonly -count=1 -race ./...
sudo -E env "PATH=$PATH" GOROOT= GOENV=off GOTOOLCHAIN=local GOFLAGS= GOWORK=off \
sudo env "PATH=$PATH" HOME=/root TMPDIR=/tmp \
GOROOT= GOENV=off GOTOOLCHAIN=local GOFLAGS= GOWORK=off \
GO111MODULE=on GOEXPERIMENT= GOFIPS140=off GOTELEMETRY=off GOAUTH=off GOVCS='*:off' \
GOPROXY="$GOPROXY" GOSUMDB="$GOSUMDB" GONOSUMDB= GOPRIVATE= GONOPROXY= GOINSECURE= \
GOCACHE="${RUNNER_TEMP}/lta-root-test-cache" GOMODCACHE="${RUNNER_TEMP}/lta-root-module-cache" \
GOPATH="${RUNNER_TEMP}/lta-root-go-path" GOTMPDIR="${RUNNER_TEMP}/lta-root-go-tmp" \
GOCACHE="$root_go_root/gocache" GOMODCACHE="$root_go_root/gomodcache" \
GOPATH="$root_go_root/gopath" GOTMPDIR="$root_go_root/gotmp" \
go test -mod=readonly -count=1 -race -tags integration ./...
test -z "$(gofmt -l .)"
sh -n scripts/install.sh
Expand Down
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

All notable changes to this project are documented here.

## v2.8.0 - 2026-07-27
## v2.8.1 - 2026-07-27

- Keep the release-only root integration workspace below a fresh root-owned
`/tmp` directory. The signed `v2.8.0` tag exposed the runner-ownership gate
before any binary, draft, or Release was created and remains only as an audit
record; no published v2.8.0 artifacts exist.

- Make account creation and revocation fail closed across UID reuse and process
races. New users remain explicitly pending until their UID is durably
Expand Down
18 changes: 9 additions & 9 deletions docs/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -419,10 +419,10 @@ git -c user.name='XXV.CC' \
-c user.signingkey="${TAG_SIGNING_FPR}!" \
-c gpg.format=openpgp \
-c gpg.program=/usr/bin/gpg \
tag -s v2.8.0 "$RELEASE_COMMIT" -m 'linux-temp-admin v2.8.0'
tag -s v2.8.1 "$RELEASE_COMMIT" -m 'linux-temp-admin v2.8.1'
git -c gpg.format=openpgp -c gpg.program=/usr/bin/gpg \
verify-tag --raw v2.8.0
git push origin v2.8.0
verify-tag --raw v2.8.1
git push origin v2.8.1
```

Before pushing, the `VALIDSIG` record from `verify-tag --raw` must identify the
Expand Down Expand Up @@ -485,7 +485,7 @@ printf '\n' >/dev/tty
|| fail "GH_TOKEN must be one non-empty token without whitespace"
export GH_TOKEN
exec /opt/lta-release-tools/prepare-release.sh \
v2.8.0 /srv/linux-temp-admin /srv/release-transfer/v2.8.0-prepared
v2.8.1 /srv/linux-temp-admin /srv/release-transfer/v2.8.1-prepared
LTA_PREPARE_RELEASE
```

Expand All @@ -505,7 +505,7 @@ the candidate or transfer media:
LTA_SIGN_KEY=/offline/keys/release-v1.key
LTA_TRUSTED_SIGNER=/opt/lta-release-tools/lta-release
LTA_TRUSTED_SIGNER_SHA256='<offline-recorded signer sha256>'
LTA_EXPECTED_TAG=v2.8.0
LTA_EXPECTED_TAG=v2.8.1
LTA_EXPECTED_COMMIT='<independently recorded 40-hex commit>'
LTA_EXPECTED_PREPARED_MANIFEST_SHA256='<independently recorded sha256>'
LTA_EXPECTED_RELEASE_SIGNER_PUBKEY='<independently recorded 64-hex OLD public key>'
Expand All @@ -517,7 +517,7 @@ LTA_EXPECTED_RELEASE_SIGNER_PUBKEY='<independently recorded 64-hex OLD public ke
LTA_EXPECTED_PREPARED_MANIFEST_SHA256="$LTA_EXPECTED_PREPARED_MANIFEST_SHA256" \
LTA_EXPECTED_RELEASE_SIGNER_PUBKEY="$LTA_EXPECTED_RELEASE_SIGNER_PUBKEY" \
/opt/lta-release-tools/offline-sign-release.sh \
/media/in/v2.8.0-prepared /media/out/v2.8.0-signed
/media/in/v2.8.1-prepared /media/out/v2.8.1-signed
```

The script copies the removable input into a size-bounded private local snapshot
Expand Down Expand Up @@ -568,7 +568,7 @@ printf '\n' >/dev/tty
|| fail "GH_TOKEN must be one non-empty token without whitespace"
export GH_TOKEN
exec /opt/lta-release-tools/publish-release.sh \
/srv/release-transfer/v2.8.0-signed /srv/linux-temp-admin
/srv/release-transfer/v2.8.1-signed /srv/linux-temp-admin
LTA_PUBLISH_RELEASE
```

Expand Down Expand Up @@ -633,7 +633,7 @@ noncanonical published stable tag, excludes the failed `TAG`, and verifies the
exact resulting Latest state:

```bash
TAG=v2.8.0 # the failed release; verify this value before running
TAG=v2.8.1 # the failed release; verify this value before running
/usr/bin/sudo /usr/bin/env -i \
HOME=/root PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin LC_ALL=C \
TAG="$TAG" /bin/bash -p <<'LTA_LATEST_RECOVERY'
Expand Down Expand Up @@ -992,7 +992,7 @@ the release audit/signing record and a separate authenticated channel, then run:
```bash
INSTALLER_COMMIT='replace-with-the-audited-40-hex-commit'
INSTALLER_SHA256='replace-with-the-independent-64-hex-script-hash'
LTA_RELEASE_TAG='v2.8.0'
LTA_RELEASE_TAG='v2.8.1'
/usr/bin/sudo /usr/bin/env -i \
HOME=/root PATH=/usr/sbin:/usr/bin:/sbin:/bin LC_ALL=C \
INSTALLER_COMMIT="$INSTALLER_COMMIT" INSTALLER_SHA256="$INSTALLER_SHA256" \
Expand Down
17 changes: 17 additions & 0 deletions internal/selfmanage/release_pipeline_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,23 @@ func TestReleaseWriterIsSeparatedFromCandidateWorkflow(t *testing.T) {
strings.Count(stage, "timeout-minutes:") != 2 {
t.Fatal("release workflows must bound every release-critical job")
}
for _, required := range []string{
`root_go_root="$(sudo mktemp -d /tmp/lta-release-root-go.XXXXXXXXXX)"`,
`sudo stat -Lc '%u %g %a' -- "$root_go_root"`,
`sudo install -d -o 0 -g 0 -m 0700`,
`sudo env "PATH=$PATH" HOME=/root TMPDIR=/tmp`,
`GOCACHE="$root_go_root/gocache"`,
`GOMODCACHE="$root_go_root/gomodcache"`,
`GOPATH="$root_go_root/gopath" GOTMPDIR="$root_go_root/gotmp"`,
} {
if !strings.Contains(release, required) {
t.Fatalf("release root test gate is missing safe root workspace guard %q", required)
}
}
if strings.Contains(release, `${RUNNER_TEMP}/lta-root-`) ||
strings.Contains(release, `sudo -E env "PATH=$PATH"`) {
t.Fatal("release root test gate inherits or stores privileged Go state below the runner account")
}
}

func TestMirrorReleaseWorkflowPublishesVerifiedImmutableContentFailClosed(t *testing.T) {
Expand Down