No description
  • Go Template 62.9%
  • Makefile 37.1%
Find a file
ryan.mcgee 03fda3cc2a
All checks were successful
Publish charts / publish (push) Successful in 10s
feat(traefik-control): add plain http and sidecar hooks (#2)
2026-10-21 15:23:04 +00:00
.gitea/workflows feat(traefik-control): add traefik-control chart (#1) 2026-10-21 14:29:31 +00:00
charts/traefik-control feat(traefik-control): add plain http and sidecar hooks (#2) 2026-10-21 15:23:04 +00:00
.editorconfig chore: initialize repository 2026-10-21 08:46:20 -05:00
.gitignore chore: initialize repository 2026-10-21 08:46:20 -05:00
ct.yaml feat(traefik-control): add traefik-control chart (#1) 2026-10-21 14:29:31 +00:00
lintconf.yaml feat(traefik-control): add traefik-control chart (#1) 2026-10-21 14:29:31 +00:00
Makefile feat(traefik-control): add traefik-control chart (#1) 2026-10-21 14:29:31 +00:00
README.md feat(traefik-control): add plain http and sidecar hooks (#2) 2026-10-21 15:23:04 +00:00

helm

Helm charts of White Ghost Cyber Systems, published as OCI artifacts to oci://git.enclaveis.com/white_ghost_cyber_systems.

Layout

charts/<name>/      one directory per chart (Chart.yaml, values.yaml, templates/)
ct.yaml             chart-testing config
lintconf.yaml       yamllint rules used by `ct lint`
Makefile            local dev tasks
.gitea/workflows/   CI

Requirements

Usage

make help       # list targets
make lint       # helm lint --strict + helm template, once per charts/<name>/ci/*-values.yaml
make validate   # render each chart, check manifests against Kubernetes schemas (kubeconform) and that every image tag exists (crane); CHARTS=charts/foo to limit
make ct-lint    # chart-testing lint against main
make package    # write .tgz files to .packages/
make publish    # push new chart versions to the OCI registry

Every change to a chart raises its version in Chart.yaml (ct lint checks it). .gitea/workflows/lint.yaml runs on pull requests; on a push to main .gitea/workflows/publish.yaml pushes each chart version that is not in the registry yet and links the package to this repository. It needs a REGISTRY_TOKEN repository secret with write:package (a token limited to repositories cannot hold package scopes).

Charts

traefik-control

Traefik Control, the multi-tenant control plane for Traefik nodes.

helm install control oci://git.enclaveis.com/white_ghost_cyber_systems/traefik-control \
  --version <version> -n traefik-control --create-namespace \
  --set host=control.example.com --set ingress.enabled=true \
  --set ingress.className=traefik --set ingress.tlsSecretName=control-tls

host is required. By default (scheme: https) it is served as https://<host>: the application builds https links and redirects plain http (the health checks excepted), so terminate TLS in front of it. With scheme: http (and urlPort when the address carries one) it builds http://<host>[:<port>] links and neither redirects nor sends HSTS, for use over a VPN or behind a proxy that does not use TLS. Passkeys (WebAuthn) then only work on localhost; TOTP is unaffected. Needs an application release that knows PHX_SCHEME (1.2.0 and later).

Pin Value Where
Traefik Control 1.2.0 image.tag (and appVersion)
CloudNativePG PostgreSQL image 18.6-standard-trixie database.cnpg.image

The application is built once per database backend, so the image follows database.mode:

  • cnpg (default, recommended): a CloudNativePG Cluster (3 instances, 10Gi each) and the traefik-control-postgres image. Needs the CloudNativePG operator in the cluster first. Backups are not configured by default; use database.cnpg.extraSpec.
  • external: your own PostgreSQL; database.external.existingSecret holds DATABASE_URL (ecto://user:pass@host/db).
  • sqlite: the traefik-control-sqlite3 image with its database on a volume (persistence). One pod, Recreate rollouts; for trying it out and small setups.

Behaviour worth knowing:

  • Secrets. The chart creates a Secret with SECRET_KEY_BASE and RELEASE_COOKIE (random, kept across upgrades, kept on uninstall); bring your own with secret.existingSecret. Changing SECRET_KEY_BASE signs everybody out.
  • Probes. Startup and liveness use /healthz; readiness uses /readyz, which checks the database. The startup probe allows 5 minutes for migrations.
  • Several replicas (cnpg or external) work: the application keeps no state in one pod that another needs. With clustering.enabled (default) the pods also form an Erlang cluster, through a headless Service and a distribution cookie, so live updates in the web interface reach every pod. Allow pod to pod traffic on 4369 and clustering.distPort (9100) if you use NetworkPolicies. A PodDisruptionBudget is created above one replica.
  • Hardening. uid 65534, read-only root filesystem (an emptyDir on /tmp), all capabilities dropped, RuntimeDefault seccomp, no service account token.
  • Behind the Ingress, TRAEFIK_CONTROL_TRUST_PROXY=true is set so login throttling sees the client address; override it under config. Other settings of the application go under config (plain) and extraEnv (secrets); the list is in the application's README.
  • Extra containers. extraInitContainers and extraVolumes are rendered as they are, and strategy replaces the Deployment strategy. An init container with restartPolicy: Always is a native sidecar (Kubernetes 1.29 or later), for example a VPN client that gives the pod an address of its own. Such a container that keeps state on a ReadWriteOnce volume needs strategy: {type: Recreate}, or a second pod cannot mount the volume.
  • The first person to register becomes the server administrator: register before the address is public.

To upgrade: set the new release in image.tag and appVersion and bump the chart version. Read the application's release notes first.