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
54 changes: 54 additions & 0 deletions .github/workflows/ci-kind-acme.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: ACME end-to-end (cert-manager in kind)

# Proves a real Kubernetes client gets a real certificate from CryptOS over
# ACME: cert-manager in a kind cluster enrols (http-01, through Contour) with
# an in-process Intermediate on the runner, and the test checks the chain, the
# SANs, the node's issued set and a forced renewal. See test/kind/run.sh.
#
# Pull requests only, and only when the ACME path or this harness changes. It
# is not a required check. Tools, images and manifests are pinned with
# checksums or digests in test/kind/versions.env.
on:
pull_request:
branches: [main]
types: [opened, synchronize, reopened, ready_for_review]
paths:
- "internal/acme/**"
- "internal/config/**"
- "internal/node/**"
- "internal/e2e/**"
- "internal/init/acme.go"
- "test/kind/**"
- ".github/workflows/ci-kind-acme.yml"

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
kind-acme:
name: cert-manager gets a CryptOS certificate over ACME
if: github.event.pull_request.draft != true
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true

- name: Cache kind, kubectl and manifests
uses: actions/cache@v4
with:
path: ~/.cache/cryptos-e2e-kind
key: kind-acme-${{ runner.arch }}-${{ hashFiles('test/kind/versions.env') }}

- name: Run the kind ACME end-to-end test
run: bash test/kind/run.sh
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ internal/
config/ # machine config parser + validator
bootstrap/ # bootstrap admin cert loading + first-ceremony rotation
build/ # kernel config, UKI assembly recipes, SquashFS templates
test/kind/ # kind + cert-manager ACME end-to-end harness (task e2e:kind)
testdata/configs/ # sample machine configs
```

Expand All @@ -51,8 +52,11 @@ Requires Go 1.24+, [`go-task`](https://taskfile.dev), `golangci-lint`, `golic`,
task ci # fmt + lint + vet + test + build (both binaries)
task build # produces bin/init and bin/cryptosctl, stamped with the build identity
task license # re-inject Apache 2.0 headers via golic
task e2e:kind # Linux + docker: cert-manager in kind gets a certificate over ACME
```

`task e2e:kind` builds a Root and an ACME-serving Intermediate in-process (software keys, no TPM), stands up a kind cluster with cert-manager and Contour, and checks that a `Certificate` goes Ready with a chain to the root and renews to a new serial. It downloads pinned, checksum-checked kind, kubectl and manifests ([`test/kind/versions.env`](test/kind/versions.env)), needs sudo once to add a `/etc/hosts` line for the test name, and skips when docker isn't available.

`bin/cryptosctl version` prints the version, commit, and build date the binary was built from (`git describe --tags --always --dirty`; see [`build/README.md`](build/README.md#build-identity)); pass `--endpoint` to also show the node's version.

The image pipeline (`task image` — hardened kernel build, SquashFS rootfs, UKI assembly + Secure Boot signing) has draft recipes under `build/` that run on a Linux build host; see [`build/README.md`](build/README.md). They are written but not yet executed end to end. The QEMU + `swtpm` integration harness lands in a subsequent PR.
Expand Down Expand Up @@ -90,6 +94,8 @@ GitHub Actions:
- **`ci-go`** ([`ci-go.yml`](.github/workflows/ci-go.yml)) — `task ci` (format, lint, vet, test, build) on every pull request + push to `main`, on a GitHub-hosted Linux runner. Draft pull requests are skipped; CI runs when the PR is marked ready.
- **`ci-image`** ([`ci-image.yml`](.github/workflows/ci-image.yml)) — builds the UKI on a **GitHub-hosted runner** (amd64 on `ubuntu-latest`, arm64 on `ubuntu-24.04-arm`), installing the kernel / `ukify` / `sbsign` toolchain per run. Runs on push to `main`, tags, and manual dispatch; use `workflow_dispatch` on a branch to validate image changes before merging. On `main` it signs with a per-run ephemeral key as a smoke test and uploads nothing. On a `v*` tag it builds the unsigned [release assets](#release-assets) and attaches them to the tag's release (a draft, marked pre-release for `-alpha`/`-beta`/`-rc` tags, if none exists yet); dispatch with `release_assets` builds them without publishing.

- **`ci-kind-acme`** ([`ci-kind-acme.yml`](.github/workflows/ci-kind-acme.yml)) — `test/kind/run.sh` (the `task e2e:kind` harness) on pull requests that touch the ACME, config, node or e2e code, on a GitHub-hosted runner. Drafts are skipped, and it isn't a required check.

The QEMU + `swtpm` integration boot is run on a real host by the operator, not in CI.

## 🔑 Management surfaces
Expand Down
5 changes: 5 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,11 @@ tasks:
cmds:
- bash build/ci/qemu-run.sh {{.ARCH | default "amd64"}}

e2e:kind:
desc: "[Linux] cert-manager in a kind cluster enrols over ACME with an in-process Intermediate (needs docker; skips without it)"
cmds:
- bash test/kind/run.sh

test:integration:
desc: "[Linux] Phase 1 QEMU + swtpm end-to-end acceptance (needs the toolchain; see test/integration)"
cmds:
Expand Down
Loading