diff --git a/.gitignore b/.gitignore index be51d26..b283a20 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ -clusterissuer.yaml +# Only the root-level render from utils/clusterissuer.template.txt. The +# real manifest is tracked at secrets/clusterissuer.yaml -- an unanchored +# pattern would match that too. +/clusterissuer.yaml local/** diff --git a/AGENTS.md b/AGENTS.md index 4008a99..d8ad371 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -53,13 +53,17 @@ sops --encrypt secrets/secrets.enc.yaml > secrets/secrets.enc.yaml ## ClusterIssuer -`clusterissuer.yaml` is **gitignored**. Generate it from template: +Tracked at `secrets/clusterissuer.yaml` and applied by the `bootstrap-secrets` +app through `secrets/kustomization.yaml`. Edit it and push; no manual apply. -```sh -export EMAIL_ADDRESS=gmail@evanstucker.com -envsubst < utils/clusterissuer.template.txt > clusterissuer.yaml -kubectl apply -f clusterissuer.yaml -``` +`utils/clusterissuer.template.txt` is kept as the record of where the five values +come from when standing up a new environment. Rendering it writes to +`/clusterissuer.yaml`, which stays gitignored — if you use it, copy the result +into `secrets/clusterissuer.yaml` rather than applying it directly. + +Nothing in it is secret: the subscription ID is already in +`applications/external-dns/values.yaml`, and managed-identity client IDs are +committed in plaintext elsewhere in this repo. ## Azure resource names @@ -96,4 +100,5 @@ Defined once in the ApplicationSet template at `applications/applicationset.yaml and its CRDs have to exist before the `core` / `post` apps whose Ingresses trigger ingress-shim. It also carries its own CRDs (`crds.enabled: true`). - `applications-disabled/matomo` is the only disabled app -- `local/` and `clusterissuer.yaml` are gitignored +- `local/` and a root-level `/clusterissuer.yaml` render are gitignored; the + real manifest lives at `secrets/clusterissuer.yaml` diff --git a/README.md b/README.md index a82c1c6..44bf3ec 100644 --- a/README.md +++ b/README.md @@ -69,13 +69,20 @@ az identity federated-credential create \ --subject "system:serviceaccount:${SERVICE_ACCOUNT_NAMESPACE}:${SERVICE_ACCOUNT_NAME}" ``` -Create the clusterissuer: +The ClusterIssuer is tracked at `secrets/clusterissuer.yaml` and applied by the +`bootstrap-secrets` app, so there is nothing to apply by hand. It references the +managed identity created above via `managedIdentity.clientID` — update that value +in the manifest when standing up a new environment. + +`utils/clusterissuer.template.txt` records where the values come from: ``` -cat clusterissuer.template.txt | envsubst > clusterissuer.yaml -k apply -f clusterissuer.yaml +cat utils/clusterissuer.template.txt | envsubst > clusterissuer.yaml ``` +That render is gitignored. Copy the result into `secrets/clusterissuer.yaml` and +push, rather than applying it directly. + # Using Loki/Grafana Loki: This is a service for backing up logs @@ -119,4 +126,3 @@ Grafana: This is the web end point for viewing those logs ``` {pod=~"star-server-app-.*"} |~ "status:50.+" != "robots.txt" ``` - diff --git a/secrets/clusterissuer.yaml b/secrets/clusterissuer.yaml new file mode 100644 index 0000000..c3a11b2 --- /dev/null +++ b/secrets/clusterissuer.yaml @@ -0,0 +1,37 @@ +# Tracked here so ArgoCD manages the cluster's ACME issuer like everything else. +# It was gitignored from 2024-04-14 (6d575ac) until now, which left the root of +# all TLS on this cluster with no history, no review and no drift detection. +# +# Nothing here is secret. The subscription ID is already in +# applications/external-dns/values.yaml, the resource group and domain appear in +# nine tracked files, and managed-identity client IDs for external-dns and loki +# are committed in plaintext -- they are identifiers, not credentials. +# +# utils/clusterissuer.template.txt is kept as the record of where these five +# values come from when standing up a new environment. If you regenerate from it, +# update this file rather than applying the output by hand. +apiVersion: "cert-manager.io/v1" +kind: "ClusterIssuer" +metadata: + name: "letsencrypt-prod" +spec: + acme: + server: "https://acme-v02.api.letsencrypt.org/directory" + email: "gmail@evanstucker.com" + privateKeySecretRef: + name: "letsencrypt-prod" + solvers: + - selector: + dnsNames: + - "*.prod.equal.vote" + dns01: + azureDNS: + hostedZoneName: "prod.equal.vote" + resourceGroupName: "equalvote" + subscriptionID: "86f3145a-48cc-4255-8757-dd3104d15e57" + environment: "AzurePublicCloud" + managedIdentity: + clientID: "ebafbf2e-5c81-424f-a11c-eccfe9a2ad8b" + - http01: + ingress: + ingressClassName: "nginx" diff --git a/secrets/kustomization.yaml b/secrets/kustomization.yaml index d410846..caf6a36 100644 --- a/secrets/kustomization.yaml +++ b/secrets/kustomization.yaml @@ -2,5 +2,6 @@ apiVersion: "kustomize.config.k8s.io/v1beta1" kind: "Kustomization" resources: - "./namespaces.yaml" + - "./clusterissuer.yaml" generators: - "./secret-generator.yaml" diff --git a/utils/clusterissuer.template.txt b/utils/clusterissuer.template.txt index aa89e00..9275244 100644 --- a/utils/clusterissuer.template.txt +++ b/utils/clusterissuer.template.txt @@ -1,3 +1,8 @@ +# NOTE: the live manifest is tracked at secrets/clusterissuer.yaml and applied +# by the bootstrap-secrets app. This template is kept only to record where the +# substituted values come from. Rendering it does not deploy anything -- copy +# the result into secrets/clusterissuer.yaml and push. + # cert-managers documentation is getting worse. The letsencrypt-prod ClusterIssuer example is buried here: # https://cert-manager.io/docs/tutorials/acme/pomerium-ingress/#configure-lets-encrypt-issuer