diff --git a/apps/base/arrstack/kustomization.yaml b/apps/base/arrstack/kustomization.yaml index c68c8c5..4ab4a90 100644 --- a/apps/base/arrstack/kustomization.yaml +++ b/apps/base/arrstack/kustomization.yaml @@ -4,6 +4,7 @@ kind: Kustomization resources: - namespace.yaml + - vaultauth.yaml - pv-media-tv.yaml - pv-media-movies.yaml - pvc-media-tv.yaml diff --git a/apps/base/arrstack/prowlarr/deployment.yaml b/apps/base/arrstack/prowlarr/deployment.yaml index df9efde..a6a95cc 100644 --- a/apps/base/arrstack/prowlarr/deployment.yaml +++ b/apps/base/arrstack/prowlarr/deployment.yaml @@ -20,6 +20,54 @@ spec: securityContext: fsGroup: 1000 fsGroupChangePolicy: OnRootMismatch + initContainers: + # Enforce the Vault-sourced API key in /config/config.xml before the app + # starts. Vault is source of truth (override bootstrap): the key is minted + # in Vault, synced by VSO into the prowlarr-apikey Secret, and written here. + # Runs as root to fix ownership; touches only the element. + - name: apikey-init + image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/library/busybox:1.37.0 + imagePullPolicy: IfNotPresent + securityContext: + runAsUser: 0 + env: + - name: API_KEY + valueFrom: + secretKeyRef: + name: prowlarr-apikey + key: apitoken + command: + - sh + - -c + - | + set -eu + case "$API_KEY" in + "" | *[!0-9a-fA-F]*) + echo "apikey-init: API_KEY missing or not hex; refusing" >&2 + exit 1 + ;; + esac + CFG=/config/config.xml + if [ ! -f "$CFG" ]; then + printf '\n %s\n\n' "$API_KEY" > "$CFG" + elif grep -q '' "$CFG"; then + sed -i "s|[^<]*|${API_KEY}|" "$CFG" + else + sed -i "s||\n ${API_KEY}|" "$CFG" + fi + chown 1000:1000 "$CFG" + chmod 600 "$CFG" + echo "apikey-init: enforced from Vault" + resources: + requests: + cpu: 50m + memory: 32Mi + limits: + cpu: 200m + memory: 64Mi + volumeMounts: + - name: config + mountPath: /config containers: - name: prowlarr image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/linuxserver/prowlarr:2.5.2 diff --git a/apps/base/arrstack/prowlarr/kustomization.yaml b/apps/base/arrstack/prowlarr/kustomization.yaml index a7f5633..887f44d 100644 --- a/apps/base/arrstack/prowlarr/kustomization.yaml +++ b/apps/base/arrstack/prowlarr/kustomization.yaml @@ -4,6 +4,7 @@ kind: Kustomization resources: - pvc-config.yaml + - vaultstaticsecret.yaml - deployment.yaml - service.yaml - gateway.yaml diff --git a/apps/base/arrstack/prowlarr/vaultstaticsecret.yaml b/apps/base/arrstack/prowlarr/vaultstaticsecret.yaml new file mode 100644 index 0000000..356201e --- /dev/null +++ b/apps/base/arrstack/prowlarr/vaultstaticsecret.yaml @@ -0,0 +1,25 @@ +--- +# prowlarr API key. Seeded at kv/kubernetes/namespace/arrstack/default/prowlarr +# (key: apitoken); the default k8s role's templated policy already grants read +# on kv/data/kubernetes/namespace/{{sa_namespace}}/{{sa_name}}/* for the +# arrstack/default ServiceAccount, so no terraform-vault change is needed. VSO +# syncs it into the prowlarr-apikey Secret that the apikey-init initContainer reads +# to enforce in /config/config.xml (Vault is source of truth). +apiVersion: secrets.hashicorp.com/v1beta1 +kind: VaultStaticSecret +metadata: + name: prowlarr-apikey + namespace: arrstack + annotations: + argocd.argoproj.io/sync-wave: "0" +spec: + destination: + create: true + name: prowlarr-apikey + overwrite: true + hmacSecretData: true + mount: kv + path: kubernetes/namespace/arrstack/default/prowlarr + refreshAfter: 5m + type: kv-v2 + vaultAuthRef: default diff --git a/apps/base/arrstack/radarr/deployment.yaml b/apps/base/arrstack/radarr/deployment.yaml index 9de78e5..6ed1f9d 100644 --- a/apps/base/arrstack/radarr/deployment.yaml +++ b/apps/base/arrstack/radarr/deployment.yaml @@ -20,6 +20,54 @@ spec: securityContext: fsGroup: 1000 fsGroupChangePolicy: OnRootMismatch + initContainers: + # Enforce the Vault-sourced API key in /config/config.xml before the app + # starts. Vault is source of truth (override bootstrap): the key is minted + # in Vault, synced by VSO into the radarr-apikey Secret, and written here. + # Runs as root to fix ownership; touches only the element. + - name: apikey-init + image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/library/busybox:1.37.0 + imagePullPolicy: IfNotPresent + securityContext: + runAsUser: 0 + env: + - name: API_KEY + valueFrom: + secretKeyRef: + name: radarr-apikey + key: apitoken + command: + - sh + - -c + - | + set -eu + case "$API_KEY" in + "" | *[!0-9a-fA-F]*) + echo "apikey-init: API_KEY missing or not hex; refusing" >&2 + exit 1 + ;; + esac + CFG=/config/config.xml + if [ ! -f "$CFG" ]; then + printf '\n %s\n\n' "$API_KEY" > "$CFG" + elif grep -q '' "$CFG"; then + sed -i "s|[^<]*|${API_KEY}|" "$CFG" + else + sed -i "s||\n ${API_KEY}|" "$CFG" + fi + chown 1000:1000 "$CFG" + chmod 600 "$CFG" + echo "apikey-init: enforced from Vault" + resources: + requests: + cpu: 50m + memory: 32Mi + limits: + cpu: 200m + memory: 64Mi + volumeMounts: + - name: config + mountPath: /config containers: - name: radarr image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/linuxserver/radarr:6.3.0 diff --git a/apps/base/arrstack/radarr/kustomization.yaml b/apps/base/arrstack/radarr/kustomization.yaml index a7f5633..887f44d 100644 --- a/apps/base/arrstack/radarr/kustomization.yaml +++ b/apps/base/arrstack/radarr/kustomization.yaml @@ -4,6 +4,7 @@ kind: Kustomization resources: - pvc-config.yaml + - vaultstaticsecret.yaml - deployment.yaml - service.yaml - gateway.yaml diff --git a/apps/base/arrstack/radarr/vaultstaticsecret.yaml b/apps/base/arrstack/radarr/vaultstaticsecret.yaml new file mode 100644 index 0000000..3601f9f --- /dev/null +++ b/apps/base/arrstack/radarr/vaultstaticsecret.yaml @@ -0,0 +1,25 @@ +--- +# radarr API key. Seeded at kv/kubernetes/namespace/arrstack/default/radarr +# (key: apitoken); the default k8s role's templated policy already grants read +# on kv/data/kubernetes/namespace/{{sa_namespace}}/{{sa_name}}/* for the +# arrstack/default ServiceAccount, so no terraform-vault change is needed. VSO +# syncs it into the radarr-apikey Secret that the apikey-init initContainer reads +# to enforce in /config/config.xml (Vault is source of truth). +apiVersion: secrets.hashicorp.com/v1beta1 +kind: VaultStaticSecret +metadata: + name: radarr-apikey + namespace: arrstack + annotations: + argocd.argoproj.io/sync-wave: "0" +spec: + destination: + create: true + name: radarr-apikey + overwrite: true + hmacSecretData: true + mount: kv + path: kubernetes/namespace/arrstack/default/radarr + refreshAfter: 5m + type: kv-v2 + vaultAuthRef: default diff --git a/apps/base/arrstack/sonarr/deployment.yaml b/apps/base/arrstack/sonarr/deployment.yaml index 5352c71..6e9be13 100644 --- a/apps/base/arrstack/sonarr/deployment.yaml +++ b/apps/base/arrstack/sonarr/deployment.yaml @@ -23,6 +23,54 @@ spec: # OnRootMismatch avoids a recursive chown of the whole media tree. fsGroup: 1000 fsGroupChangePolicy: OnRootMismatch + initContainers: + # Enforce the Vault-sourced API key in /config/config.xml before the app + # starts. Vault is source of truth (override bootstrap): the key is minted + # in Vault, synced by VSO into the sonarr-apikey Secret, and written here. + # Runs as root to fix ownership; touches only the element. + - name: apikey-init + image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/library/busybox:1.37.0 + imagePullPolicy: IfNotPresent + securityContext: + runAsUser: 0 + env: + - name: API_KEY + valueFrom: + secretKeyRef: + name: sonarr-apikey + key: apitoken + command: + - sh + - -c + - | + set -eu + case "$API_KEY" in + "" | *[!0-9a-fA-F]*) + echo "apikey-init: API_KEY missing or not hex; refusing" >&2 + exit 1 + ;; + esac + CFG=/config/config.xml + if [ ! -f "$CFG" ]; then + printf '\n %s\n\n' "$API_KEY" > "$CFG" + elif grep -q '' "$CFG"; then + sed -i "s|[^<]*|${API_KEY}|" "$CFG" + else + sed -i "s||\n ${API_KEY}|" "$CFG" + fi + chown 1000:1000 "$CFG" + chmod 600 "$CFG" + echo "apikey-init: enforced from Vault" + resources: + requests: + cpu: 50m + memory: 32Mi + limits: + cpu: 200m + memory: 64Mi + volumeMounts: + - name: config + mountPath: /config containers: - name: sonarr image: artifactapi.k8s.syd1.au.unkin.net/dockerhub/linuxserver/sonarr:4.0.19 diff --git a/apps/base/arrstack/sonarr/kustomization.yaml b/apps/base/arrstack/sonarr/kustomization.yaml index a7f5633..887f44d 100644 --- a/apps/base/arrstack/sonarr/kustomization.yaml +++ b/apps/base/arrstack/sonarr/kustomization.yaml @@ -4,6 +4,7 @@ kind: Kustomization resources: - pvc-config.yaml + - vaultstaticsecret.yaml - deployment.yaml - service.yaml - gateway.yaml diff --git a/apps/base/arrstack/sonarr/vaultstaticsecret.yaml b/apps/base/arrstack/sonarr/vaultstaticsecret.yaml new file mode 100644 index 0000000..93e9798 --- /dev/null +++ b/apps/base/arrstack/sonarr/vaultstaticsecret.yaml @@ -0,0 +1,25 @@ +--- +# sonarr API key. Seeded at kv/kubernetes/namespace/arrstack/default/sonarr +# (key: apitoken); the default k8s role's templated policy already grants read +# on kv/data/kubernetes/namespace/{{sa_namespace}}/{{sa_name}}/* for the +# arrstack/default ServiceAccount, so no terraform-vault change is needed. VSO +# syncs it into the sonarr-apikey Secret that the apikey-init initContainer reads +# to enforce in /config/config.xml (Vault is source of truth). +apiVersion: secrets.hashicorp.com/v1beta1 +kind: VaultStaticSecret +metadata: + name: sonarr-apikey + namespace: arrstack + annotations: + argocd.argoproj.io/sync-wave: "0" +spec: + destination: + create: true + name: sonarr-apikey + overwrite: true + hmacSecretData: true + mount: kv + path: kubernetes/namespace/arrstack/default/sonarr + refreshAfter: 5m + type: kv-v2 + vaultAuthRef: default diff --git a/apps/base/arrstack/vaultauth.yaml b/apps/base/arrstack/vaultauth.yaml new file mode 100644 index 0000000..3eb27aa --- /dev/null +++ b/apps/base/arrstack/vaultauth.yaml @@ -0,0 +1,20 @@ +--- +apiVersion: secrets.hashicorp.com/v1beta1 +kind: VaultAuth +metadata: + name: default + namespace: arrstack + annotations: + argocd.argoproj.io/sync-wave: "0" +spec: + allowedNamespaces: + - arrstack + kubernetes: + audiences: + - vault + role: default + serviceAccount: default + tokenExpirationSeconds: 600 + method: kubernetes + mount: k8s/au/syd1 + vaultConnectionRef: vso-system/default