diff --git a/k8s-ng/apps/k8s-secrets/README.md b/k8s-ng/apps/k8s-secrets/README.md new file mode 100644 index 0000000..d3276c9 --- /dev/null +++ b/k8s-ng/apps/k8s-secrets/README.md @@ -0,0 +1,12 @@ +# k8s-secrets + +The `k8s-secrets` Argo CD Application reads this directory through +`core-state/values.yaml` and deploys into namespace `k8s-secret`. + +External Secrets loads the `k8s/openai/creds` Vaultwarden item into +`openai-creds`. The reader at `pass.hexor.cy` can only read that Secret and is +protected by the Keycloak forward-auth proxy. Its access policy is managed in +`terraform/keycloak/terraform.tfvars`. + +The Kubernetes resource names and namespace match the former deployment so +Argo CD can switch the source path without replacing the service or certificate. diff --git a/k8s-ng/apps/k8s-secrets/external-secret.yaml b/k8s-ng/apps/k8s-secrets/external-secret.yaml new file mode 100644 index 0000000..b7f1d50 --- /dev/null +++ b/k8s-ng/apps/k8s-secrets/external-secret.yaml @@ -0,0 +1,32 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: openai-creds +spec: + refreshInterval: 5m + secretStoreRef: + name: vaultwarden-login + kind: ClusterSecretStore + target: + name: openai-creds + creationPolicy: Owner + deletionPolicy: Retain + template: + type: Opaque + data: + USER: "{{ .user }}" + PASS: "{{ .pass }}" + TOTP: "{{ .totp }}" + data: + - secretKey: user + remoteRef: + key: k8s/openai/creds + property: login.username + - secretKey: pass + remoteRef: + key: k8s/openai/creds + property: login.password + - secretKey: totp + remoteRef: + key: k8s/openai/creds + property: login.totp diff --git a/k8s-ng/apps/k8s-secrets/ingress.yaml b/k8s-ng/apps/k8s-secrets/ingress.yaml new file mode 100644 index 0000000..02808be --- /dev/null +++ b/k8s-ng/apps/k8s-secrets/ingress.yaml @@ -0,0 +1,38 @@ +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: auth-proxy +spec: + forwardAuth: + address: http://auth-proxy.keycloak.svc:80/auth + trustForwardHeader: false +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: secret-reader +spec: + entryPoints: + - websecure + routes: + - match: Host(`pass.hexor.cy`) + kind: Rule + middlewares: + - name: auth-proxy + services: + - name: secret-reader + port: 80 + tls: + secretName: secret-reader-tls +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: secret-reader-tls +spec: + secretName: secret-reader-tls + issuerRef: + name: letsencrypt + kind: ClusterIssuer + dnsNames: + - pass.hexor.cy diff --git a/k8s-ng/apps/k8s-secrets/kustomization.yaml b/k8s-ng/apps/k8s-secrets/kustomization.yaml new file mode 100644 index 0000000..e764b10 --- /dev/null +++ b/k8s-ng/apps/k8s-secrets/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: k8s-secret +resources: + - external-secret.yaml + - reader.yaml + - ingress.yaml diff --git a/k8s-ng/apps/k8s-secrets/reader.yaml b/k8s-ng/apps/k8s-secrets/reader.yaml new file mode 100644 index 0000000..eae49e5 --- /dev/null +++ b/k8s-ng/apps/k8s-secrets/reader.yaml @@ -0,0 +1,109 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: secret-reader + labels: + app: secret-reader +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: secret-reader +rules: + - apiGroups: [""] + resources: ["secrets"] + resourceNames: ["openai-creds"] + verbs: ["get"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: secret-reader +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: secret-reader +subjects: + - kind: ServiceAccount + name: secret-reader +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: secret-reader + labels: + app: secret-reader +spec: + replicas: 1 + selector: + matchLabels: + app: secret-reader + template: + metadata: + labels: + app: secret-reader + spec: + serviceAccountName: secret-reader + nodeSelector: + homelab.hexor.cy/pool: core + homelab.hexor.cy/exposure: external + securityContext: + runAsNonRoot: true + runAsUser: 1000 + seccompProfile: + type: RuntimeDefault + containers: + - name: secret-reader + image: ultradesu/k8s-secrets:0.2.2@sha256:c8b5f7de386da40b844738990d7a6f870f5e76c7d22d45d43a2a33611688fe9b + imagePullPolicy: IfNotPresent + args: + - --secrets + - openai-creds + - --namespace + - k8s-secret + - --port + - "3000" + ports: + - name: http + containerPort: 3000 + env: + - name: RUST_LOG + value: info + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 150m + memory: 128Mi + readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 5 + periodSeconds: 5 + livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 10 + periodSeconds: 10 + securityContext: + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] +--- +apiVersion: v1 +kind: Service +metadata: + name: secret-reader + labels: + app: secret-reader +spec: + selector: + app: secret-reader + ports: + - name: http + port: 80 + targetPort: http diff --git a/k8s-ng/apps/mtproxy/reader.yaml b/k8s-ng/apps/mtproxy/reader.yaml index 7a2b399..caeba95 100644 --- a/k8s-ng/apps/mtproxy/reader.yaml +++ b/k8s-ng/apps/mtproxy/reader.yaml @@ -51,7 +51,7 @@ spec: type: RuntimeDefault containers: - name: reader - image: ultradesu/k8s-secrets:0.2.1@sha256:1cb1d2458879e91f98b6ca613c556f63dbb1e6b92a46f49d562ac23bc2b5fd2b + image: ultradesu/k8s-secrets:0.2.2@sha256:c8b5f7de386da40b844738990d7a6f870f5e76c7d22d45d43a2a33611688fe9b imagePullPolicy: IfNotPresent args: - --secrets diff --git a/k8s-ng/core-state/values.yaml b/k8s-ng/core-state/values.yaml index 70b11fa..fc74304 100644 --- a/k8s-ng/core-state/values.yaml +++ b/k8s-ng/core-state/values.yaml @@ -77,7 +77,7 @@ apps: skipDryRunOnMissingResource: true - name: k8s-secrets project: apps - path: k8s/apps/k8s-secrets + path: k8s-ng/apps/k8s-secrets namespace: k8s-secret wave: 6 # after secret-stores and auth-proxy skipDryRunOnMissingResource: true