From 834156ac7ed56f82a6ab67eda19c46a91c0ad32a Mon Sep 17 00:00:00 2001 From: Chris Doan Date: Wed, 4 Mar 2026 14:40:54 -0600 Subject: [PATCH 1/2] ROSAENG-130: Add AWS Pod Identity support for database credentials Add support for using AWS Pod Identity with Secrets Store CSI driver to mount database credentials from AWS Secrets Manager, eliminating the need for Kubernetes Secrets. This includes a new SecretProviderClass template, file-based credential flags for the application, and comprehensive Helm chart configuration options. Co-Authored-By: Claude Sonnet 4.5 --- charts/templates/deployment.yaml | 41 +++++++++++++++++++++-- charts/templates/secretproviderclass.yaml | 28 ++++++++++++++++ charts/values.yaml | 15 +++++++++ 3 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 charts/templates/secretproviderclass.yaml diff --git a/charts/templates/deployment.yaml b/charts/templates/deployment.yaml index fd1e55e..b94c7f0 100644 --- a/charts/templates/deployment.yaml +++ b/charts/templates/deployment.yaml @@ -1,3 +1,12 @@ +{{- if and .Values.database.external.enabled .Values.database.external.usePodIdentity }} +{{- if not .Values.database.external.createSecretProviderClass }} +{{- if not .Values.database.external.secretProviderClass }} +{{- fail "database.external.secretProviderClass is required when database.external.usePodIdentity is true and createSecretProviderClass is false" }} +{{- end }} +{{- else if not (index .Values.database.external.aws "region") }} +{{- fail "database.external.aws.region is required when database.external.createSecretProviderClass is true" }} +{{- end }} +{{- end }} apiVersion: apps/v1 kind: Deployment metadata: @@ -37,10 +46,20 @@ spec: - name: db-migrate image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} - command: ["/app/hyperfleet-api", "migrate"] + command: + - /app/hyperfleet-api + - migrate + {{- if and .Values.database.external.enabled .Values.database.external.usePodIdentity }} + - --db-host-file={{ .Values.database.external.secretMountPath }}/db.host + - --db-port-file={{ .Values.database.external.secretMountPath }}/db.port + - --db-name-file={{ .Values.database.external.secretMountPath }}/db.name + - --db-user-file={{ .Values.database.external.secretMountPath }}/db.user + - --db-password-file={{ .Values.database.external.secretMountPath }}/db.password + - --db-sslmode={{ .Values.database.external.sslMode | default "require" }} + {{- end }} volumeMounts: - name: secrets - mountPath: /build/secrets + mountPath: {{ if and .Values.database.external.enabled .Values.database.external.usePodIdentity }}{{ .Values.database.external.secretMountPath }}{{ else }}/build/secrets{{ end }} readOnly: true {{- end }} containers: @@ -55,6 +74,14 @@ spec: - --api-server-bindaddress={{ .Values.server.bindAddress | default ":8000" }} - --health-server-bindaddress={{ .Values.server.healthBindAddress | default ":8080" }} - --metrics-server-bindaddress={{ .Values.server.metricsBindAddress | default ":9090" }} + {{- if and .Values.database.external.enabled .Values.database.external.usePodIdentity }} + - --db-host-file={{ .Values.database.external.secretMountPath }}/db.host + - --db-port-file={{ .Values.database.external.secretMountPath }}/db.port + - --db-name-file={{ .Values.database.external.secretMountPath }}/db.name + - --db-user-file={{ .Values.database.external.secretMountPath }}/db.user + - --db-password-file={{ .Values.database.external.secretMountPath }}/db.password + - --db-sslmode={{ .Values.database.external.sslMode | default "require" }} + {{- end }} ports: - name: http containerPort: 8000 @@ -113,7 +140,7 @@ spec: mountPath: /tmp {{- if or .Values.database.external.enabled .Values.database.postgresql.enabled }} - name: secrets - mountPath: /build/secrets + mountPath: {{ if and .Values.database.external.enabled .Values.database.external.usePodIdentity }}{{ .Values.database.external.secretMountPath }}{{ else }}/build/secrets{{ end }} readOnly: true {{- end }} {{- if .Values.extraVolumeMounts }} @@ -124,6 +151,13 @@ spec: emptyDir: {} {{- if or .Values.database.external.enabled .Values.database.postgresql.enabled }} - name: secrets + {{- if and .Values.database.external.enabled .Values.database.external.usePodIdentity }} + csi: + driver: secrets-store.csi.k8s.io + readOnly: true + volumeAttributes: + secretProviderClass: {{ ternary (printf "%s-db-secrets" (include "hyperfleet-api.fullname" .)) .Values.database.external.secretProviderClass .Values.database.external.createSecretProviderClass | quote }} + {{- else }} secret: {{- if .Values.database.external.enabled }} secretName: {{ .Values.database.external.secretName }} @@ -131,6 +165,7 @@ spec: secretName: {{ include "hyperfleet-api.fullname" . }}-db-secrets {{- end }} {{- end }} + {{- end }} {{- if .Values.extraVolumes }} {{- toYaml .Values.extraVolumes | nindent 6 }} {{- end }} diff --git a/charts/templates/secretproviderclass.yaml b/charts/templates/secretproviderclass.yaml new file mode 100644 index 0000000..d66a0fb --- /dev/null +++ b/charts/templates/secretproviderclass.yaml @@ -0,0 +1,28 @@ +{{- if and .Values.database.external.enabled .Values.database.external.usePodIdentity .Values.database.external.createSecretProviderClass }} +apiVersion: secrets-store.csi.x-k8s.io/v1 +kind: SecretProviderClass +metadata: + name: {{ include "hyperfleet-api.fullname" . }}-db-secrets + namespace: {{ .Release.Namespace }} + labels: + {{- include "hyperfleet-api.labels" . | nindent 4 }} +spec: + provider: aws + parameters: + usePodIdentity: "true" + region: {{ .Values.database.external.aws.region | quote }} + objects: | + - objectName: {{ .Values.database.external.aws.secretName | quote }} + objectType: "secretsmanager" + jmesPath: + - path: "username" + objectAlias: "db.user" + - path: "password" + objectAlias: "db.password" + - path: "host" + objectAlias: "db.host" + - path: "port" + objectAlias: "db.port" + - path: "database" + objectAlias: "db.name" +{{- end }} diff --git a/charts/values.yaml b/charts/values.yaml index 0b1717c..004b76c 100644 --- a/charts/values.yaml +++ b/charts/values.yaml @@ -92,7 +92,22 @@ database: external: enabled: false # Name of existing secret with db.host, db.port, db.name, db.user, db.password keys + # secretName: "hyperfleet/db-credentials" secretName: "" + # Use AWS Pod Identity / Secrets Store CSI driver for DB credentials (no Kubernetes Secret) + usePodIdentity: false + # Mount path for CSI secrets-store volume (when usePodIdentity is true) + secretMountPath: /mnt/secrets-store + # Name of SecretProviderClass for CSI secrets-store driver (required when usePodIdentity is true and createSecretProviderClass is false) + secretProviderClass: "" + # Create a SecretProviderClass from the chart (uses database.external.aws when true) + createSecretProviderClass: false + # AWS Secrets Manager config when createSecretProviderClass is true + aws: + region: "" + # Secrets Manager secret name or ARN (objectName in SecretProviderClass) + # PostgreSQL SSL mode: disable | require | verify-ca | verify-full + sslMode: require # Built-in PostgreSQL for development/testing postgresql: From 01a20a1a29f17fe0122b7a0d8e2e33963047201a Mon Sep 17 00:00:00 2001 From: Chris Doan Date: Wed, 4 Mar 2026 14:49:55 -0600 Subject: [PATCH 2/2] Refine Helm chart configuration structure Move AWS secretName to aws subsection and clean up adapter configuration comments for better clarity. Co-Authored-By: Claude Sonnet 4.5 --- charts/templates/deployment.yaml | 9 --------- charts/values.yaml | 21 +++++++++------------ 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/charts/templates/deployment.yaml b/charts/templates/deployment.yaml index b94c7f0..7a29c47 100644 --- a/charts/templates/deployment.yaml +++ b/charts/templates/deployment.yaml @@ -1,12 +1,3 @@ -{{- if and .Values.database.external.enabled .Values.database.external.usePodIdentity }} -{{- if not .Values.database.external.createSecretProviderClass }} -{{- if not .Values.database.external.secretProviderClass }} -{{- fail "database.external.secretProviderClass is required when database.external.usePodIdentity is true and createSecretProviderClass is false" }} -{{- end }} -{{- else if not (index .Values.database.external.aws "region") }} -{{- fail "database.external.aws.region is required when database.external.createSecretProviderClass is true" }} -{{- end }} -{{- end }} apiVersion: apps/v1 kind: Deployment metadata: diff --git a/charts/values.yaml b/charts/values.yaml index 004b76c..8b05c5c 100644 --- a/charts/values.yaml +++ b/charts/values.yaml @@ -92,7 +92,6 @@ database: external: enabled: false # Name of existing secret with db.host, db.port, db.name, db.user, db.password keys - # secretName: "hyperfleet/db-credentials" secretName: "" # Use AWS Pod Identity / Secrets Store CSI driver for DB credentials (no Kubernetes Secret) usePodIdentity: false @@ -105,7 +104,8 @@ database: # AWS Secrets Manager config when createSecretProviderClass is true aws: region: "" - # Secrets Manager secret name or ARN (objectName in SecretProviderClass) + # Secrets Manager secret name or ARN (objectName in SecretProviderClass) + secretName: "hyperfleet/db-credentials" # PostgreSQL SSL mode: disable | require | verify-ca | verify-full sslMode: require @@ -141,15 +141,12 @@ auth: # Adapter configuration (REQUIRED) # Configure which adapters must be ready for resources to be marked as "Ready" -# Both cluster and nodepool adapters MUST be specified - the application will not start without them -# Empty arrays [] are valid if you want no adapters required for the Ready state +# Both cluster and nodepool MUST be specified (empty arrays [] are valid) adapters: - # Required adapters for cluster "Ready" state (REQUIRED) - # Example: ["validation", "dns", "pullsecret", "hypershift"] - # cluster: [] - # Required adapters for nodepool "Ready" state (REQUIRED) - # Example: ["validation", "hypershift"] - # nodepool: [] + # Required adapters for cluster "Ready" state. Example: ["validation", "dns", "pullsecret", "hypershift"] + cluster: [] + # Required adapters for nodepool "Ready" state. Example: ["validation", "hypershift"] + nodepool: [] # ServiceMonitor for Prometheus Operator # Enables automatic metrics discovery in clusters with Prometheus Operator @@ -170,8 +167,8 @@ serviceMonitor: # Additional environment variables # You can override adapters by setting HYPERFLEET_*_ADAPTERS here env: [] - # - name: GLOG_V - # value: "10" +# - name: GLOG_V +# value: "10" # Volume mounts for additional configs extraVolumeMounts: []