First deploy: fix Pulumi, image build, container DNS and DNS-01; Dependabot updates #2

Merged
JMR-dev merged 11 commits from deploy-prep into main 2026-10-10 09:04:19 +00:00
JMR-dev commented 2026-10-10 09:03:16 +00:00 (Migrated from github.com)

Takes the stack from "bootstrap ran" to a live deployment at https://gitea.jasonmross.dev, fixing everything that broke along the way. Each of these paths was running for the first time, so most of the fixes are bugs that only show up on a first deploy.

This branch is what is currently deployed: pulumi preview against it reports 73 unchanged.

Fixes found by deploying

  • Pulumi never ran. Pulumi.yaml points the Go runtime at a prebuilt ./gitea-infra that nothing built. make check and the infra Cloud Build step now build it.
  • make build failed three ways.
    • $SHORT_SHA is empty for gcloud builds submit, which made the image tag invalid.
    • cb-image@ could not read the uploaded source in <project>_cloudbuild. There is now a Pulumi-managed <project>-gitea-build-source bucket, readable by cb-image@ only.
    • The Dockerfiles use COPY --chmod, so BuildKit is required.
  • Containers resolved no hostnames. aardvark-dns sits on the bridge gateway, so container lookups take the host's input hook. gitea_filter's drop policy overrode netavark's accept. The ruleset is now rendered with the podman subnet and gateway and accepts DNS from the bridge. setup_nftables now validates before installing, where it used to install first.
  • DNS-01 challenge returned 403. The googleclouddns plugin lists the project's managed zones, which the zone-scoped dns.admin cannot grant. The VM account gets project-level roles/dns.reader; writes stay zone-scoped.

Other changes

  • ACME email moved to Secret Manager (gitea-acme-email). It is out of this public repo and out of instance metadata. If the secret is unreadable, Caddy still issues certificates, just without a contact address.
  • Dependabot: grpc v1.83.2, otel v1.45.0 / log v0.21.0 (with the otelslog bridge v0.20.1 that otel line needs), and toolchain go1.26.9 for ten newly disclosed stdlib vulnerabilities. govulncheck reports nothing reachable. Supersedes #1.
  • Stack config for gitea-496920. The stack is encrypted with the passphrase in Secret Manager, so the infra trigger can open it.
  • Docs: README setup now covers ADC, exporting the passphrase before stack init, and the ACME secret. The runbook notes that Caddy's certificates live on the boot disk.
  • Makefile: the ZONE fallback is now -b; us-east1 has no -a zone.

Verified on the live instance

  • Let's Encrypt certificate via dns-01; /api/healthz passes.
  • Port 80 redirects with a 308; git SSH answers on 2222; port 22 is closed from outside.
  • fail2ban jails use nft-prerouting; firewalld is masked.
  • The data disk was reused across an instance replacement.

Not covered

  • The infra trigger (cloudbuild/infra.yaml) has never run, because the GitHub App isn't installed yet. githubAppInstallationId is still "0", so there are no triggers and no weekly rebuild job.
  • The WAF is still in DetectionOnly.

🤖 Generated with Claude Code

Takes the stack from "bootstrap ran" to a live deployment at https://gitea.jasonmross.dev, fixing everything that broke along the way. Each of these paths was running for the first time, so most of the fixes are bugs that only show up on a first deploy. This branch is what is currently deployed: `pulumi preview` against it reports 73 unchanged. ## Fixes found by deploying - **Pulumi never ran.** `Pulumi.yaml` points the Go runtime at a prebuilt `./gitea-infra` that nothing built. `make check` and the infra Cloud Build step now build it. - **`make build` failed three ways.** - `$SHORT_SHA` is empty for `gcloud builds submit`, which made the image tag invalid. - `cb-image@` could not read the uploaded source in `<project>_cloudbuild`. There is now a Pulumi-managed `<project>-gitea-build-source` bucket, readable by `cb-image@` only. - The Dockerfiles use `COPY --chmod`, so BuildKit is required. - **Containers resolved no hostnames.** aardvark-dns sits on the bridge gateway, so container lookups take the host's input hook. `gitea_filter`'s drop policy overrode netavark's accept. The ruleset is now rendered with the podman subnet and gateway and accepts DNS from the bridge. `setup_nftables` now validates before installing, where it used to install first. - **DNS-01 challenge returned 403.** The googleclouddns plugin lists the project's managed zones, which the zone-scoped `dns.admin` cannot grant. The VM account gets project-level `roles/dns.reader`; writes stay zone-scoped. ## Other changes - **ACME email moved to Secret Manager** (`gitea-acme-email`). It is out of this public repo and out of instance metadata. If the secret is unreadable, Caddy still issues certificates, just without a contact address. - **Dependabot:** grpc v1.83.2, otel v1.45.0 / log v0.21.0 (with the otelslog bridge v0.20.1 that otel line needs), and toolchain go1.26.9 for ten newly disclosed stdlib vulnerabilities. govulncheck reports nothing reachable. Supersedes #1. - **Stack config** for `gitea-496920`. The stack is encrypted with the passphrase in Secret Manager, so the infra trigger can open it. - **Docs:** README setup now covers ADC, exporting the passphrase before `stack init`, and the ACME secret. The runbook notes that Caddy's certificates live on the boot disk. - **Makefile:** the `ZONE` fallback is now `-b`; us-east1 has no `-a` zone. ## Verified on the live instance - Let's Encrypt certificate via dns-01; `/api/healthz` passes. - Port 80 redirects with a 308; git SSH answers on 2222; port 22 is closed from outside. - fail2ban jails use `nft-prerouting`; firewalld is masked. - The data disk was reused across an instance replacement. ## Not covered - The infra trigger (`cloudbuild/infra.yaml`) has never run, because the GitHub App isn't installed yet. `githubAppInstallationId` is still `"0"`, so there are no triggers and no weekly rebuild job. - The WAF is still in `DetectionOnly`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.