Self-hosted Gitea on a single e2-small AlmaLinux 10 VM in us-east1, serving gitea.jasonmross.dev. Runtime is podman quadlets (systemd .container/.network/.volume units). Both images are built on Debian 13: Gitea from a GPG-verified release binary, and Caddy from an xcaddy build carrying the Google Cloud DNS provider (ACME DNS-01) and the Coraza WAF with the OWASP CRS embedded. Infrastructure is a Pulumi program in Go against a GCS state backend. Cloud Build handles CI: a push trigger for images, one for infra, and a weekly scheduled rebuild. Everything Cloud Build touches is 2nd gen. Notable design decisions, each documented where it lives: - Quadlets track a floating :prod tag. AutoUpdate=registry compares digests for a tag, so a digest-pinned image silently disables auto-updates. - Git transport and LFS bypass the WAF. With the bypass removed, a plain git push returns 403 -- packfiles trip CRS reliably. - gitea:wafMode drives both SecRuleEngine and whether the fail2ban jail acting on WAF verdicts exists. Banning on detections that were never blocks would turn a tuning false positive into an nftables ban. - fail2ban bans at the nftables prerouting hook. Published container ports are DNAT'd and never traverse INPUT, where the stock actions install their rules. - The DNS zone, backup bucket, and Gitea signing secrets are not Pulumi-owned, so pulumi destroy cannot take them with it. - The podman subnet is pinned because it is what Gitea's REVERSE_PROXY_TRUSTED_PROXIES names. Three update layers: dnf5-automatic for the OS, podman-auto-update with health-gated rollback for containers, and a weekly image rebuild that gives the second layer something to pull. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
45 lines
1.3 KiB
Go
45 lines
1.3 KiB
Go
// Package project enables the Google APIs the rest of the stack depends on.
|
|
package project
|
|
|
|
import (
|
|
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/projects"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
|
|
)
|
|
|
|
// Services are enabled explicitly so a fresh project works without anyone
|
|
// clicking through the console first.
|
|
var services = []string{
|
|
"compute.googleapis.com",
|
|
"dns.googleapis.com",
|
|
"artifactregistry.googleapis.com",
|
|
"cloudbuild.googleapis.com",
|
|
"secretmanager.googleapis.com",
|
|
"iap.googleapis.com",
|
|
"storage.googleapis.com",
|
|
"logging.googleapis.com",
|
|
"monitoring.googleapis.com",
|
|
"cloudscheduler.googleapis.com",
|
|
"iamcredentials.googleapis.com",
|
|
"oslogin.googleapis.com",
|
|
}
|
|
|
|
// EnableAPIs returns resources every other package should depend on, so nothing
|
|
// races an API that is still turning on.
|
|
func EnableAPIs(ctx *pulumi.Context) ([]pulumi.Resource, error) {
|
|
var out []pulumi.Resource
|
|
for _, s := range services {
|
|
svc, err := projects.NewService(ctx, "api-"+s, &projects.ServiceArgs{
|
|
Service: pulumi.String(s),
|
|
// Tearing down the stack should not disable APIs that other things
|
|
// in the project may be using.
|
|
DisableOnDestroy: pulumi.Bool(false),
|
|
DisableDependentServices: pulumi.Bool(false),
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, svc)
|
|
}
|
|
return out, nil
|
|
}
|