From c7becb89e02ba3f5b1190a953b4430533cb1a707 Mon Sep 17 00:00:00 2001 From: Pratapa Lakshmi Date: Fri, 20 Mar 2026 13:55:26 +0530 Subject: [PATCH 1/4] Update Plane-EE to version `v2.3.0` * Bump application version to 2.3.0 in Chart.yaml * Introduce new API secrets configuration in values.yaml for external secrets management * Add support for EKS Pod Identity and related annotations in service-account.yaml * Enhance config-secrets templates to include AWS Secrets Manager ARNs and key mappings * Add NOTES.txt for guidance on Pod Identity and secret management configuration --- charts/plane-enterprise/Chart.yaml | 2 +- charts/plane-enterprise/templates/NOTES.txt | 51 ++++++++++++ .../templates/config-secrets/app-env.yaml | 39 +++++++++ .../templates/config-secrets/live-env.yaml | 8 ++ .../templates/config-secrets/pi-api-env.yaml | 46 +++++++++-- .../templates/config-secrets/silo.yaml | 2 + .../templates/service-account.yaml | 13 +++ charts/plane-enterprise/values.yaml | 80 +++++++++++++++++++ 8 files changed, 235 insertions(+), 6 deletions(-) create mode 100644 charts/plane-enterprise/templates/NOTES.txt diff --git a/charts/plane-enterprise/Chart.yaml b/charts/plane-enterprise/Chart.yaml index d0edea91..5444d453 100644 --- a/charts/plane-enterprise/Chart.yaml +++ b/charts/plane-enterprise/Chart.yaml @@ -5,7 +5,7 @@ description: Meet Plane. An Enterprise software development tool to manage issue type: application -version: 2.2.5 +version: 2.3.0 appVersion: "2.5.0" home: https://plane.so/ diff --git a/charts/plane-enterprise/templates/NOTES.txt b/charts/plane-enterprise/templates/NOTES.txt new file mode 100644 index 00000000..78f74798 --- /dev/null +++ b/charts/plane-enterprise/templates/NOTES.txt @@ -0,0 +1,51 @@ +{{- if .Values.podIdentity.enabled }} +EKS Pod Identity (out-of-band) configuration + +This chart configures the Kubernetes `ServiceAccount` name as: + {{ printf "%s-srv-account" .Release.Name }} + +1. Ensure the Pod Identity agent addon is installed: + eksctl create addon --cluster {{ default "" .Values.podIdentity.clusterName }} --name eks-pod-identity-agent + +2. Create the Pod Identity association: + eksctl create podidentityassociation \ + --cluster {{ default "" .Values.podIdentity.clusterName }} \ + --namespace {{ default .Release.Namespace .Values.podIdentity.namespace }} \ + --service-account-name {{ default (printf "%s-srv-account" .Release.Name) .Values.podIdentity.serviceAccountName }} \ + --role-arn {{ default "" .Values.podIdentity.roleArn }} + + Alternative (AWS CLI): + aws eks create-pod-identity-association \ + --cluster-name {{ default "" .Values.podIdentity.clusterName }} \ + --namespace {{ default .Release.Namespace .Values.podIdentity.namespace }} \ + --service-account {{ default (printf "%s-srv-account" .Release.Name) .Values.podIdentity.serviceAccountName }} \ + --role-arn {{ default "" .Values.podIdentity.roleArn }} + +Important: +- Pod Identity associations are managed outside of Helm/Kubernetes manifests (for example via `eksctl` / AWS APIs). +- Pod Identity is not configured purely by ServiceAccount annotations in this setup. +{{- else }} +IRSA configuration (IAM Roles for Service Accounts) + +To enable AWS permissions for this chart's ServiceAccount: + - set `irsa.roleArn`, or + - set `serviceAccount.annotations` / `irsa.annotations` with `eks.amazonaws.com/role-arn`. + +This chart's ServiceAccount name is: + {{ printf "%s-srv-account" .Release.Name }} +{{- end }} + +--- +## Secret ARN driven env wiring + +This chart conditionally renders AWS Secrets Manager ARN-related env vars (for example `AMAZONMQ_SECRET_ARN`, `ELASTICACHE_SECRET_ARN`, `RDS_SECRET_ARN`) and the corresponding `*_KEY` mappings only when the ARN values (and the required key-name values) are provided. + +To verify what will be rendered for your current values, run: + +```sh +helm template ./charts/plane-enterprise \ + --namespace \ + -f | \ + rg -n "AMAZONMQ_SECRET_ARN|ELASTICACHE_SECRET_ARN|RDS_SECRET_ARN|RABBITMQ_(USER_KEY|PASSWORD_KEY)|RDS_DB_(HOST_KEY|NAME_KEY|PASSWORD_KEY|PORT_KEY|USERNAME_KEY)|FOLLOWER_RDS_DB_(HOST_KEY|NAME_KEY|PASSWORD_KEY|PORT_KEY|USERNAME_KEY)|MODEL_CUSTOM_LLM_API_KEY" +``` + diff --git a/charts/plane-enterprise/templates/config-secrets/app-env.yaml b/charts/plane-enterprise/templates/config-secrets/app-env.yaml index 30d7d03a..9708dfd1 100644 --- a/charts/plane-enterprise/templates/config-secrets/app-env.yaml +++ b/charts/plane-enterprise/templates/config-secrets/app-env.yaml @@ -11,6 +11,17 @@ stringData: LIVE_SERVER_SECRET_KEY: {{ .Values.env.live_server_secret_key | default "htbqvBJAgpm9bzvf3r4urJer0ENReatceh" | quote }} PI_INTERNAL_SECRET: {{ .Values.env.pi_envs.internal_secret | default "tyfvfqvBJAgpm9bzvf3r4urJer0Ehfdubk" | quote }} + # AWS Secrets Manager ARNs (consumed by workloads via envFrom plane-app-secrets) + {{- if and (not (empty .Values.env.apiSecrets.amazonmq_secret_arn)) (not (empty .Values.env.rabbitmq_user_key)) (not (empty .Values.env.rabbitmq_password_key)) }} + AMAZONMQ_SECRET_ARN: {{ .Values.env.apiSecrets.amazonmq_secret_arn | quote }} + {{- end }} + {{- if and (not (empty .Values.env.apiSecrets.elasticache_secret_arn)) (not (empty .Values.env.redis_auth_token_key)) }} + ELASTICACHE_SECRET_ARN: {{ .Values.env.apiSecrets.elasticache_secret_arn | quote }} + {{- end }} + {{- if and (not (empty .Values.env.apiSecrets.rds_secret_arn)) (not (empty .Values.env.rds_db_host_key)) (not (empty .Values.env.rds_db_name_key)) (not (empty .Values.env.rds_db_password_key)) (not (empty .Values.env.rds_db_port_key)) (not (empty .Values.env.rds_db_username_key)) }} + RDS_SECRET_ARN: {{ .Values.env.apiSecrets.rds_secret_arn | quote }} + {{- end }} + {{- if .Values.services.redis.local_setup }} REDIS_URL: "redis://{{ .Release.Name }}-redis.{{ .Release.Namespace }}.svc.cluster.local:6379/" {{- else }} @@ -91,3 +102,31 @@ data: {{- else}} CORS_ALLOWED_ORIGINS: "http://{{ .Values.license.licenseDomain }},https://{{ .Values.license.licenseDomain }}" {{- end }} + + # Secret cache TTL for the credentials provider used by the app. + AWS_SECRET_CACHE_TTL: {{ .Values.env.aws_secret_cache_ttl | default "300" | quote }} + + # Runner base URL used by the automation runner integration. + {{- if .Values.env.runner_base_url }} + RUNNER_BASE_URL: {{ .Values.env.runner_base_url | quote }} + {{- else }} + RUNNER_BASE_URL: "http://plane-node-runner.{{ .Release.Namespace }}.svc.cluster.local:3000" + {{- end }} + + # AWS Secrets Manager key mappings (key names, not the secret ARNs). + {{- if and (not (empty .Values.env.apiSecrets.elasticache_secret_arn)) (not (empty .Values.env.redis_auth_token_key)) }} + REDIS_AUTH_TOKEN_KEY: {{ .Values.env.redis_auth_token_key | quote }} + {{- end }} + + {{- if and (not (empty .Values.env.apiSecrets.amazonmq_secret_arn)) (not (empty .Values.env.rabbitmq_user_key)) (not (empty .Values.env.rabbitmq_password_key)) }} + RABBITMQ_USER_KEY: {{ .Values.env.rabbitmq_user_key | quote }} + RABBITMQ_PASSWORD_KEY: {{ .Values.env.rabbitmq_password_key | quote }} + {{- end }} + + {{- if and (not (empty .Values.env.apiSecrets.rds_secret_arn)) (not (empty .Values.env.rds_db_host_key)) (not (empty .Values.env.rds_db_name_key)) (not (empty .Values.env.rds_db_password_key)) (not (empty .Values.env.rds_db_port_key)) (not (empty .Values.env.rds_db_username_key)) }} + RDS_DB_HOST_KEY: {{ .Values.env.rds_db_host_key | quote }} + RDS_DB_NAME_KEY: {{ .Values.env.rds_db_name_key | quote }} + RDS_DB_PASSWORD_KEY: {{ .Values.env.rds_db_password_key | quote }} + RDS_DB_PORT_KEY: {{ .Values.env.rds_db_port_key | quote }} + RDS_DB_USERNAME_KEY: {{ .Values.env.rds_db_username_key | quote }} + {{- end }} diff --git a/charts/plane-enterprise/templates/config-secrets/live-env.yaml b/charts/plane-enterprise/templates/config-secrets/live-env.yaml index 8786a72e..45b3f6a5 100644 --- a/charts/plane-enterprise/templates/config-secrets/live-env.yaml +++ b/charts/plane-enterprise/templates/config-secrets/live-env.yaml @@ -7,6 +7,11 @@ metadata: name: {{ .Release.Name }}-live-secrets stringData: LIVE_SERVER_SECRET_KEY: {{ .Values.env.live_server_secret_key | default "htbqvBJAgpm9bzvf3r4urJer0ENReatceh" | quote }} + # Used by PI workloads to resolve ElastiCache credentials. + # This value is rendered into `live-secrets` (not `pi-api-vars`/`pi-api-secrets`). + {{- if and (not (empty .Values.env.apiSecrets.elasticache_secret_arn)) (not (empty .Values.env.redis_auth_token_key)) }} + ELASTICACHE_SECRET_ARN: {{ .Values.env.apiSecrets.elasticache_secret_arn | quote }} + {{- end }} {{- if .Values.services.redis.local_setup }} REDIS_URL: "redis://{{ .Release.Name }}-redis.{{ .Release.Namespace }}.svc.cluster.local:6379/" {{- else }} @@ -26,6 +31,9 @@ data: LIVE_SENTRY_TRACES_SAMPLE_RATE: {{ .Values.env.live_sentry_traces_sample_rate | default "" | quote }} LIVE_BASE_PATH: "/live" + # Secret cache TTL used by the credentials provider in workloads. + AWS_SECRET_CACHE_TTL: {{ .Values.env.aws_secret_cache_ttl | default "300" | quote }} + {{- if .Values.env.external_iframely_url }} IFRAMELY_URL: {{ .Values.env.external_iframely_url | default "" | quote }} {{- else }} diff --git a/charts/plane-enterprise/templates/config-secrets/pi-api-env.yaml b/charts/plane-enterprise/templates/config-secrets/pi-api-env.yaml index 548a6953..273191c9 100644 --- a/charts/plane-enterprise/templates/config-secrets/pi-api-env.yaml +++ b/charts/plane-enterprise/templates/config-secrets/pi-api-env.yaml @@ -1,3 +1,5 @@ +{{ $resolvedFollowerRdsArn := default .Values.env.apiSecrets.rds_secret_arn .Values.services.pi.apiSecrets.follower_rds_secret_arn }} + {{- if and .Values.services.pi.enabled (empty .Values.external_secrets.pi_api_env_existingSecret)}} apiVersion: v1 @@ -56,12 +58,22 @@ stringData: CUSTOM_LLM_API_KEY: "" {{- end }} - {{- if .Values.services.pi.ai_providers.embedding_model.enabled }} BR_AWS_SECRET_ACCESS_KEY: {{ .Values.services.pi.ai_providers.embedding_model.aws_secret_access_key | default "" | quote }} BR_AWS_SESSION_TOKEN: {{ .Values.services.pi.ai_providers.embedding_model.aws_session_token | default "" | quote }} - {{- end }} AES_SECRET_KEY: {{ .Values.env.silo_envs.aes_secret_key | default "dsOdt7YrvxsTIFJ37pOaEVvLxN8KGBCr" | quote }} + {{- if and (not (empty $resolvedFollowerRdsArn)) (not (empty .Values.env.pi_envs.follower_rds_db_host_key)) (not (empty .Values.env.pi_envs.follower_rds_db_name_key)) (not (empty .Values.env.pi_envs.follower_rds_db_password_key)) (not (empty .Values.env.pi_envs.follower_rds_db_port_key)) (not (empty .Values.env.pi_envs.follower_rds_db_username_key)) }} + FOLLOWER_RDS_SECRET_ARN: {{ $resolvedFollowerRdsArn | quote }} + {{- end }} + {{- if and (not (empty .Values.env.apiSecrets.amazonmq_secret_arn)) (not (empty .Values.env.pi_envs.rabbitmq_user_key)) (not (empty .Values.env.pi_envs.rabbitmq_password_key)) }} + AMAZONMQ_SECRET_ARN: {{ .Values.env.apiSecrets.amazonmq_secret_arn | quote }} + {{- end }} + {{- if and (not (empty .Values.services.pi.apiSecrets.rds_secret_arn)) (not (empty .Values.env.pi_envs.rds_db_host_key)) (not (empty .Values.env.pi_envs.rds_db_name_key)) (not (empty .Values.env.pi_envs.rds_db_password_key)) (not (empty .Values.env.pi_envs.rds_db_port_key)) (not (empty .Values.env.pi_envs.rds_db_username_key)) }} + RDS_SECRET_ARN: {{ .Values.services.pi.apiSecrets.rds_secret_arn | quote }} + {{- end }} + {{- if and (not (empty .Values.services.pi.apiSecrets.model_secret_arn)) (not (empty .Values.services.pi.ai_providers.custom_llm.api_key)) }} + MODEL_SECRET_ARN: {{ .Values.services.pi.apiSecrets.model_secret_arn | quote }} + {{- end }} {{- end }} --- @@ -91,11 +103,33 @@ data: {{- end }} PI_INTERNAL_SECRET: {{ .Values.env.pi_envs.internal_secret | default "tyfvfqvBJAgpm9bzvf3r4urJer0Ehfdubk" | quote }} + AWS_SECRET_CACHE_TTL: {{ .Values.env.aws_secret_cache_ttl | default "300" | quote }} FEATURE_FLAG_SERVER_BASE_URL: "http://{{ .Release.Name }}-monitor.{{ .Release.Namespace }}.svc.cluster.local:8080" PI_BASE_PATH: "/pi" - + {{- if and (not (empty .Values.env.apiSecrets.amazonmq_secret_arn)) (not (empty .Values.env.pi_envs.rabbitmq_password_key)) (not (empty .Values.env.pi_envs.rabbitmq_user_key)) }} + RABBITMQ_PASSWORD_KEY: {{ .Values.env.pi_envs.rabbitmq_password_key | quote }} + RABBITMQ_USER_KEY: {{ .Values.env.pi_envs.rabbitmq_user_key | quote }} + {{- end }} + + {{- if and (not (empty .Values.services.pi.apiSecrets.rds_secret_arn)) (not (empty .Values.env.pi_envs.rds_db_host_key)) (not (empty .Values.env.pi_envs.rds_db_name_key)) (not (empty .Values.env.pi_envs.rds_db_password_key)) (not (empty .Values.env.pi_envs.rds_db_port_key)) (not (empty .Values.env.pi_envs.rds_db_username_key)) }} + RDS_DB_HOST_KEY: {{ .Values.env.pi_envs.rds_db_host_key | quote }} + RDS_DB_NAME_KEY: {{ .Values.env.pi_envs.rds_db_name_key | quote }} + RDS_DB_PASSWORD_KEY: {{ .Values.env.pi_envs.rds_db_password_key | quote }} + RDS_DB_PORT_KEY: {{ .Values.env.pi_envs.rds_db_port_key | quote }} + RDS_DB_USERNAME_KEY: {{ .Values.env.pi_envs.rds_db_username_key | quote }} + {{ end }} + + {{- if and (not (empty $resolvedFollowerRdsArn)) (not (empty .Values.env.pi_envs.follower_rds_db_host_key)) (not (empty .Values.env.pi_envs.follower_rds_db_name_key)) (not (empty .Values.env.pi_envs.follower_rds_db_password_key)) (not (empty .Values.env.pi_envs.follower_rds_db_port_key)) (not (empty .Values.env.pi_envs.follower_rds_db_username_key)) }} + {{- /* Render follower DB key names only when resolved follower secret ARN exists. */ -}} + FOLLOWER_RDS_DB_HOST_KEY: {{ .Values.env.pi_envs.follower_rds_db_host_key | quote }} + FOLLOWER_RDS_DB_NAME_KEY: {{ .Values.env.pi_envs.follower_rds_db_name_key | quote }} + FOLLOWER_RDS_DB_PASSWORD_KEY: {{ .Values.env.pi_envs.follower_rds_db_password_key | quote }} + FOLLOWER_RDS_DB_PORT_KEY: {{ .Values.env.pi_envs.follower_rds_db_port_key | quote }} + FOLLOWER_RDS_DB_USERNAME_KEY: {{ .Values.env.pi_envs.follower_rds_db_username_key | quote }} + {{- end }} + {{- if eq .Values.env.pi_envs.cors_allowed_origins "*"}} CORS_ALLOWED_ORIGINS: "*" {{- else if .Values.env.pi_envs.cors_allowed_origins }} @@ -134,12 +168,14 @@ data: CUSTOM_LLM_AWS_REGION: "" {{- end }} - {{- if .Values.services.pi.ai_providers.embedding_model.enabled }} + {{- if and (not (empty .Values.services.pi.apiSecrets.model_secret_arn)) (not (empty .Values.services.pi.ai_providers.custom_llm.api_key)) }} + MODEL_CUSTOM_LLM_API_KEY: {{ .Values.services.pi.ai_providers.custom_llm.api_key | quote }} + {{- end }} + EMBEDDING_MODEL: {{ .Values.services.pi.ai_providers.embedding_model.name | default "" | quote }} OPENSEARCH_ML_MODEL_ID: {{ .Values.services.pi.ai_providers.embedding_model.model_id | default "" | quote }} BR_AWS_ACCESS_KEY_ID: {{ .Values.services.pi.ai_providers.embedding_model.aws_access_key | default "" | quote }} BR_AWS_REGION: {{ .Values.services.pi.ai_providers.embedding_model.aws_region | default "us-east-1" | quote }} - {{- end }} CELERY_VECTOR_SYNC_ENABLED: {{ .Values.env.pi_envs.celery.vector_sync_enabled | ternary "1" "0" | quote }} CELERY_VECTOR_SYNC_INTERVAL: {{ .Values.env.pi_envs.celery.vector_sync_interval | default 3 | quote }} diff --git a/charts/plane-enterprise/templates/config-secrets/silo.yaml b/charts/plane-enterprise/templates/config-secrets/silo.yaml index efc32e6a..3815568f 100644 --- a/charts/plane-enterprise/templates/config-secrets/silo.yaml +++ b/charts/plane-enterprise/templates/config-secrets/silo.yaml @@ -91,5 +91,7 @@ data: SENTRY_ENVIRONMENT: {{ .Values.env.silo_envs.sentry_environment | default "development" | quote }} SENTRY_TRACES_SAMPLE_RATE: {{ .Values.env.silo_envs.sentry_traces_sample_rate | default "0.1" | quote }} + AWS_SECRET_CACHE_TTL: {{ .Values.env.aws_secret_cache_ttl | default "300" | quote }} + {{- end }} --- \ No newline at end of file diff --git a/charts/plane-enterprise/templates/service-account.yaml b/charts/plane-enterprise/templates/service-account.yaml index 5a243e60..131a1254 100644 --- a/charts/plane-enterprise/templates/service-account.yaml +++ b/charts/plane-enterprise/templates/service-account.yaml @@ -1,9 +1,22 @@ +{{- $annotations := dict -}} +{{- with .Values.serviceAccount.annotations -}} +{{- $annotations = merge $annotations . -}} +{{- end -}} +{{- with .Values.irsa.annotations -}} +{{- $annotations = merge $annotations . -}} +{{- end -}} +{{- if .Values.irsa.roleArn -}} +{{- $annotations = merge $annotations (dict "eks.amazonaws.com/role-arn" .Values.irsa.roleArn) -}} +{{- end -}} + apiVersion: v1 automountServiceAccountToken: true kind: ServiceAccount metadata: namespace: {{ .Release.Namespace }} name: {{ .Release.Name }}-srv-account + annotations: +{{ toYaml $annotations | nindent 4 }} {{- if .Values.dockerRegistry.enabled }} imagePullSecrets: {{- if .Values.dockerRegistry.existingSecret }} diff --git a/charts/plane-enterprise/values.yaml b/charts/plane-enterprise/values.yaml index 7dc9014a..10cbb6cf 100644 --- a/charts/plane-enterprise/values.yaml +++ b/charts/plane-enterprise/values.yaml @@ -388,6 +388,14 @@ services: aws_region: 'us-east-1' aws_session_token: '' + apiSecrets: + # Used by PI API to look up external secrets/credentials. + # Values are expected to be base64-encoded strings (as provided by the user). + follower_rds_secret_arn: '' + model_secret_arn: '' + amazonmq_secret_arn: '' + rds_secret_arn: '' + pi_beat_worker: replicas: 1 memoryLimit: 1000Mi @@ -412,6 +420,32 @@ services: labels: {} annotations: {} +serviceAccount: + # ServiceAccount annotations to apply to the chart's ServiceAccount. + # Common use-case: IRSA (IAM Roles for Service Accounts). + annotations: {} + +irsa: + # When set, the chart automatically sets: + # eks.amazonaws.com/role-arn: + roleArn: '' + # Additional/override IRSA annotations to apply to the ServiceAccount. + annotations: {} + +podIdentity: + # EKS Pod Identity is configured out-of-band (for example with `eksctl`). + # This chart only renders install-time guidance; it does not create the + # PodIdentityAssociation resource. + enabled: false + # Cluster name for the `eksctl create podidentityassociation` command. + clusterName: '' + # Namespace for the ServiceAccount (defaults to the Helm release namespace). + namespace: '' + # ServiceAccount name (defaults to `-srv-account`). + serviceAccountName: '' + # IAM role ARN to associate for Pod Identity. + roleArn: '' + external_secrets: # Name of the existing Kubernetes Secret resource; see README for more details rabbitmq_existingSecret: '' @@ -426,6 +460,33 @@ external_secrets: env: storageClass: '' + # Secrets Manager credential cache TTL (seconds) + aws_secret_cache_ttl: "300" + + apiSecrets: + # Shared AWS Secrets Manager ARNs used by api/live/pi workloads. + # Values are expected to be base64-encoded strings (as provided by the user). + amazonmq_secret_arn: "arn:aws:secretsmanager:us-east-1:426043895157:secret:gp/api/mq/secrets-GMMWoG" + elasticache_secret_arn: "arn:aws:secretsmanager:us-east-1:426043895157:secret:gp/api/redis/secrets-K0zoig" + rds_secret_arn: "arn:aws:secretsmanager:us-east-1:426043895157:secret:gp/api/secrets-oFsQ2M" + + # AWS Secrets Manager key mappings used by the main app (plane-app-vars). + # The templates render: + # - `REDIS_AUTH_TOKEN_KEY` only when `env.apiSecrets.elasticache_secret_arn` is set + # - `RABBITMQ_*_KEY` only when `env.apiSecrets.amazonmq_secret_arn` is set + # - `RDS_DB_*_KEY` only when `env.apiSecrets.rds_secret_arn` is set + redis_auth_token_key: authToken + rabbitmq_user_key: username + rabbitmq_password_key: password + rds_db_host_key: host + rds_db_name_key: dbname + rds_db_password_key: password + rds_db_port_key: port + rds_db_username_key: username + + # Base URL for the automation runner (defaults via template). + runner_base_url: '' + # REDIS remote_redis_url: '' #INCASE OF REMOTE REDIS ONLY @@ -519,6 +580,25 @@ env: internal_secret: 'tyfvfqvBJAgpm9bzvf3r4urJer0Ehfdubk' log_level: 'DEBUG' + # Names/keys that PI API uses to retrieve credentials from external secret managers. + # The templates render: + # - `RABBITMQ_*_KEY` only when `env.apiSecrets.amazonmq_secret_arn` is set + # - `RDS_DB_*_KEY` only when `env.apiSecrets.rds_secret_arn` is set + # - `FOLLOWER_RDS_DB_*_KEY` only when `services.pi.apiSecrets.follower_rds_secret_arn` is set + # - `MODEL_CUSTOM_LLM_API_KEY` only when `services.pi.apiSecrets.model_secret_arn` is set + rabbitmq_password_key: password + rabbitmq_user_key: username + rds_db_host_key: host + rds_db_name_key: dbname + rds_db_password_key: password + rds_db_port_key: port + rds_db_username_key: username + follower_rds_db_host_key: host + follower_rds_db_name_key: dbname + follower_rds_db_password_key: password + follower_rds_db_port_key: port + follower_rds_db_username_key: username + celery: vector_sync_enabled: false vector_sync_interval: 3 From 77349eff6ff83f3193c6e777aaaea483973c68af Mon Sep 17 00:00:00 2001 From: Pratapa Lakshmi Date: Fri, 20 Mar 2026 14:38:35 +0530 Subject: [PATCH 2/4] chore: remove arns of secrets --- charts/plane-enterprise/values.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/plane-enterprise/values.yaml b/charts/plane-enterprise/values.yaml index 10cbb6cf..50d900e2 100644 --- a/charts/plane-enterprise/values.yaml +++ b/charts/plane-enterprise/values.yaml @@ -466,9 +466,9 @@ env: apiSecrets: # Shared AWS Secrets Manager ARNs used by api/live/pi workloads. # Values are expected to be base64-encoded strings (as provided by the user). - amazonmq_secret_arn: "arn:aws:secretsmanager:us-east-1:426043895157:secret:gp/api/mq/secrets-GMMWoG" - elasticache_secret_arn: "arn:aws:secretsmanager:us-east-1:426043895157:secret:gp/api/redis/secrets-K0zoig" - rds_secret_arn: "arn:aws:secretsmanager:us-east-1:426043895157:secret:gp/api/secrets-oFsQ2M" + amazonmq_secret_arn: "" + elasticache_secret_arn: "" + rds_secret_arn: "" # AWS Secrets Manager key mappings used by the main app (plane-app-vars). # The templates render: From c39e66f607ce056f80d64b735188610d429f610f Mon Sep 17 00:00:00 2001 From: Pratapa Lakshmi Date: Fri, 20 Mar 2026 16:05:56 +0530 Subject: [PATCH 3/4] Enhance service account and config-secrets templates to support AWS Secrets Manager ARNs and additional Redis configuration. Introduce pod identity role ARN handling in service-account.yaml and add Redis authentication token key in live-env.yaml and silo.yaml. --- charts/plane-enterprise/README.md | 14 +++++++-- .../templates/config-secrets/live-env.yaml | 1 + .../templates/config-secrets/silo.yaml | 31 +++++++++++++++++++ .../templates/service-account.yaml | 9 ++++++ charts/plane-enterprise/values.yaml | 9 ++++++ 5 files changed, 61 insertions(+), 3 deletions(-) diff --git a/charts/plane-enterprise/README.md b/charts/plane-enterprise/README.md index 257207c4..2927fb2e 100644 --- a/charts/plane-enterprise/README.md +++ b/charts/plane-enterprise/README.md @@ -137,7 +137,7 @@ airgapped: | Setting | Default | Required | Description | | ----------------------------------- | :------------------: | :------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| services.postgres.local_setup | true | | Plane uses `postgres` as the primary database to store all the transactional data. This database can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws rds or similar services). Set this to `true` when you choose to setup stateful deployment of `postgres`. Mark it as `false` when using a remotely hosted database | +| services.postgres.local_setup | true | | Plane uses `postgres` as the primary database to store all the transactional data. This database can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws rds or similar services). Set this to `true` when you choose to setup stateful deployment of `postgres`. Mark it as `false` when using a remotely hosted database. Important: if you configure PostgreSQL credentials via AWS Secrets Manager ARNs (for example `env.apiSecrets.rds_secret_arn`, and/or PI ARNs under `services.pi.apiSecrets.*_rds_secret_arn`), keep this set to `false` so the chart does not compute `DATABASE_URL` from in-cluster services. | | services.postgres.image | postgres:15.7-alpine | | Using this key, user must provide the docker image name to setup the stateful deployment of `postgres`. (must be set when `services.postgres.local_setup=true`) | | services.postgres.pullPolicy | IfNotPresent | | Using this key, user can set the pull policy for the stateful deployment of `postgres`. (must be set when `services.postgres.local_setup=true`) | | services.postgres.servicePort | 5432 | | This key sets the default port number to be used while setting up stateful deployment of `postgres`. | @@ -157,7 +157,7 @@ airgapped: | Setting | Default | Required | Description | | -------------------------------- | :-------------------------: | :------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| services.redis.local_setup | true | | Plane uses `valkey` to cache the session authentication and other static data. This database can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws rds or similar services). Set this to `true` when you choose to setup stateful deployment of `redis`. Mark it as `false` when using a remotely hosted database | +| services.redis.local_setup | true | | Plane uses `valkey` to cache the session authentication and other static data. This database can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws rds or similar services). Set this to `true` when you choose to setup stateful deployment of `redis`. Mark it as `false` when using a remotely hosted database. Important: if you configure Redis credentials via AWS Secrets Manager ARNs (for example `env.apiSecrets.elasticache_secret_arn`) and provide the Redis key mapping (`env.redis_auth_token_key`), keep this set to `false` so the chart does not compute `REDIS_URL` from in-cluster services. | | services.redis.image | valkey/valkey:7.2.11-alpine | | Using this key, user must provide the docker image name to setup the stateful deployment of `redis`. (must be set when `services.redis.local_setup=true`) | | services.redis.pullPolicy | IfNotPresent | | Using this key, user can set the pull policy for the stateful deployment of `redis`. (must be set when `services.redis.local_setup=true`) | | services.redis.servicePort | 6379 | | This key sets the default port number to be used while setting up stateful deployment of `redis`. | @@ -174,7 +174,7 @@ airgapped: | Setting | Default | Required | Description | | --------------------------------------- | :-------------------------------: | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| services.rabbitmq.local_setup | true | | Plane uses `rabbitmq` as message queuing system. This can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws mq or similar services). Set this to `true` when you choose to setup stateful deployment of `rabbitmq`. Mark it as `false` when using a remotely hosted service | +| services.rabbitmq.local_setup | true | | Plane uses `rabbitmq` as message queuing system. This can be hosted within kubernetes as part of helm chart deployment or can be used as hosted service remotely (e.g. aws mq or similar services). Set this to `true` when you choose to setup stateful deployment of `rabbitmq`. Mark it as `false` when using a remotely hosted service. Important: if you configure RabbitMQ credentials via AWS Secrets Manager ARNs (for example `env.apiSecrets.amazonmq_secret_arn`) and provide the key mappings (`env.rabbitmq_user_key` and `env.rabbitmq_password_key`), keep this set to `false` so the chart does not compute `AMQP_URL` from in-cluster services. | | services.rabbitmq.image | rabbitmq:3.13.6-management-alpine | | Using this key, user must provide the docker image name to setup the stateful deployment of `rabbitmq`. (must be set when `services.rabbitmq.local_setup=true`) | | services.rabbitmq.pullPolicy | IfNotPresent | | Using this key, user can set the pull policy for the stateful deployment of `rabbitmq`. (must be set when `services.rabbitmq.local_setup=true`) | | services.rabbitmq.servicePort | 5672 | | This key sets the default port number to be used while setting up stateful deployment of `rabbitmq`. | @@ -601,6 +601,14 @@ Note: When the email service is enabled, the cert-issuer will be automatically c | env.storageClass | <k8s-default-storage-class> | | Creating the persitant volumes for the stateful deployments needs the `storageClass` name. Set the correct value as per your kubernetes cluster configuration. | | env.secret_key | 60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5 | Yes | This must a random string which is used for hashing/encrypting the sensitive data within the application. Once set, changing this might impact the already hashed/encrypted data | +### AWS Secrets Manager (ARN) wiring + +When you configure AWS Secrets Manager credentials using Secret ARNs (via `env.apiSecrets.*_secret_arn` and/or PI secret ARNs), you must **disable local setups** for the corresponding services so the chart does not compute `REDIS_URL`, `DATABASE_URL`, and `AMQP_URL` from in-cluster deployments. + +- If `env.apiSecrets.elasticache_secret_arn` is set (and `env.redis_auth_token_key` is set), set `services.redis.local_setup=false`. +- If `env.apiSecrets.amazonmq_secret_arn` is set (and `env.rabbitmq_user_key` + `env.rabbitmq_password_key` are set), set `services.rabbitmq.local_setup=false`. +- If `env.apiSecrets.rds_secret_arn` is set (and/or PI sets `services.pi.apiSecrets.*_rds_secret_arn`), set `services.postgres.local_setup=false`. + ### Extra Environment Variables | Setting | Default | Required | Description | diff --git a/charts/plane-enterprise/templates/config-secrets/live-env.yaml b/charts/plane-enterprise/templates/config-secrets/live-env.yaml index 45b3f6a5..30d8e6ce 100644 --- a/charts/plane-enterprise/templates/config-secrets/live-env.yaml +++ b/charts/plane-enterprise/templates/config-secrets/live-env.yaml @@ -11,6 +11,7 @@ stringData: # This value is rendered into `live-secrets` (not `pi-api-vars`/`pi-api-secrets`). {{- if and (not (empty .Values.env.apiSecrets.elasticache_secret_arn)) (not (empty .Values.env.redis_auth_token_key)) }} ELASTICACHE_SECRET_ARN: {{ .Values.env.apiSecrets.elasticache_secret_arn | quote }} + REDIS_AUTH_TOKEN_KEY: {{ .Values.env.redis_auth_token_key | quote }} {{- end }} {{- if .Values.services.redis.local_setup }} REDIS_URL: "redis://{{ .Release.Name }}-redis.{{ .Release.Namespace }}.svc.cluster.local:6379/" diff --git a/charts/plane-enterprise/templates/config-secrets/silo.yaml b/charts/plane-enterprise/templates/config-secrets/silo.yaml index 3815568f..7ec7328c 100644 --- a/charts/plane-enterprise/templates/config-secrets/silo.yaml +++ b/charts/plane-enterprise/templates/config-secrets/silo.yaml @@ -10,6 +10,19 @@ stringData: SILO_HMAC_SECRET_KEY: {{ .Values.env.silo_envs.hmac_secret_key | default "gzb7MRLr0FoN129NyWARZEs84P9LzQ" | quote }} AES_SECRET_KEY: {{ .Values.env.silo_envs.aes_secret_key | default "dsOdt7YrvxsTIFJ37pOaEVvLxN8KGBCr" | quote }} + {{- /* AWS Secrets Manager ARN support (credentials-provider mode). */ -}} + {{- if and (not (empty .Values.env.apiSecrets.amazonmq_secret_arn)) (not (empty .Values.env.rabbitmq_user_key)) (not (empty .Values.env.rabbitmq_password_key)) }} + AMAZONMQ_SECRET_ARN: {{ .Values.env.apiSecrets.amazonmq_secret_arn | quote }} + {{- end }} + + {{- if and (not (empty .Values.env.apiSecrets.elasticache_secret_arn)) (not (empty .Values.env.redis_auth_token_key)) }} + ELASTICACHE_SECRET_ARN: {{ .Values.env.apiSecrets.elasticache_secret_arn | quote }} + {{- end }} + + {{- if and (not (empty .Values.env.apiSecrets.rds_secret_arn)) (not (empty .Values.env.rds_db_host_key)) (not (empty .Values.env.rds_db_name_key)) (not (empty .Values.env.rds_db_password_key)) (not (empty .Values.env.rds_db_port_key)) (not (empty .Values.env.rds_db_username_key)) }} + RDS_SECRET_ARN: {{ .Values.env.apiSecrets.rds_secret_arn | quote }} + {{- end }} + {{- if .Values.services.postgres.local_setup }} DATABASE_URL: "postgresql://{{ .Values.env.pgdb_username }}:{{ .Values.env.pgdb_password }}@{{ .Release.Name }}-pgdb.{{ .Release.Namespace }}.svc.{{ .Values.env.default_cluster_domain | default "cluster.local" }}/{{ .Values.env.pgdb_name }}" {{- else if .Values.env.pgdb_remote_url }} @@ -93,5 +106,23 @@ data: AWS_SECRET_CACHE_TTL: {{ .Values.env.aws_secret_cache_ttl | default "300" | quote }} + {{- /* Key mappings used by the credentials provider (rendered only when ARN is present). */ -}} + {{- if and (not (empty .Values.env.apiSecrets.elasticache_secret_arn)) (not (empty .Values.env.redis_auth_token_key)) }} + REDIS_AUTH_TOKEN_KEY: {{ .Values.env.redis_auth_token_key | quote }} + {{- end }} + + {{- if and (not (empty .Values.env.apiSecrets.amazonmq_secret_arn)) (not (empty .Values.env.rabbitmq_user_key)) (not (empty .Values.env.rabbitmq_password_key)) }} + RABBITMQ_USER_KEY: {{ .Values.env.rabbitmq_user_key | quote }} + RABBITMQ_PASSWORD_KEY: {{ .Values.env.rabbitmq_password_key | quote }} + {{- end }} + + {{- if and (not (empty .Values.env.apiSecrets.rds_secret_arn)) (not (empty .Values.env.rds_db_host_key)) (not (empty .Values.env.rds_db_name_key)) (not (empty .Values.env.rds_db_password_key)) (not (empty .Values.env.rds_db_port_key)) (not (empty .Values.env.rds_db_username_key)) }} + RDS_DB_HOST_KEY: {{ .Values.env.rds_db_host_key | quote }} + RDS_DB_NAME_KEY: {{ .Values.env.rds_db_name_key | quote }} + RDS_DB_PASSWORD_KEY: {{ .Values.env.rds_db_password_key | quote }} + RDS_DB_PORT_KEY: {{ .Values.env.rds_db_port_key | quote }} + RDS_DB_USERNAME_KEY: {{ .Values.env.rds_db_username_key | quote }} + {{- end }} + {{- end }} --- \ No newline at end of file diff --git a/charts/plane-enterprise/templates/service-account.yaml b/charts/plane-enterprise/templates/service-account.yaml index 131a1254..4b34a396 100644 --- a/charts/plane-enterprise/templates/service-account.yaml +++ b/charts/plane-enterprise/templates/service-account.yaml @@ -8,6 +8,13 @@ {{- if .Values.irsa.roleArn -}} {{- $annotations = merge $annotations (dict "eks.amazonaws.com/role-arn" .Values.irsa.roleArn) -}} {{- end -}} +{{- if and .Values.podIdentity.enabled .Values.podIdentity.roleArn -}} +{{- /* Pod Identity role ARN should override any IRSA role-arn when both are set. */ -}} +{{- $annotations = merge $annotations (dict "eks.amazonaws.com/role-arn" .Values.podIdentity.roleArn) -}} +{{- end -}} +{{- if .Values.podIdentity.annotations -}} +{{- $annotations = merge $annotations .Values.podIdentity.annotations -}} +{{- end -}} apiVersion: v1 automountServiceAccountToken: true @@ -15,8 +22,10 @@ kind: ServiceAccount metadata: namespace: {{ .Release.Namespace }} name: {{ .Release.Name }}-srv-account +{{- if gt (len $annotations) 0 }} annotations: {{ toYaml $annotations | nindent 4 }} +{{- end }} {{- if .Values.dockerRegistry.enabled }} imagePullSecrets: {{- if .Values.dockerRegistry.existingSecret }} diff --git a/charts/plane-enterprise/values.yaml b/charts/plane-enterprise/values.yaml index 50d900e2..d67f5ab6 100644 --- a/charts/plane-enterprise/values.yaml +++ b/charts/plane-enterprise/values.yaml @@ -53,6 +53,9 @@ ssl: services: redis: + # Set `local_setup=false` when you provide Redis credentials via AWS Secrets Manager ARNs: + # - `env.apiSecrets.elasticache_secret_arn` (ARN) + # - `env.redis_auth_token_key` (key name mapping) local_setup: true image: valkey/valkey:7.2.11-alpine servicePort: 6379 @@ -66,6 +69,9 @@ services: annotations: {} postgres: + # Set `local_setup=false` when you provide PostgreSQL credentials via AWS Secrets Manager ARNs: + # - API DB: `env.apiSecrets.rds_secret_arn` + # - PI DB / follower DB: `services.pi.apiSecrets.{rds_secret_arn,follower_rds_secret_arn}` local_setup: true image: postgres:15.7-alpine servicePort: 5432 @@ -79,6 +85,9 @@ services: annotations: {} rabbitmq: + # Set `local_setup=false` when you provide RabbitMQ credentials via AWS Secrets Manager ARNs: + # - `env.apiSecrets.amazonmq_secret_arn` (ARN) + # - `env.rabbitmq_user_key` + `env.rabbitmq_password_key` (key name mappings) local_setup: true image: rabbitmq:3.13.6-management-alpine servicePort: 5672 From 53bf94f13c6e38724ec876c17b4a81439adace81 Mon Sep 17 00:00:00 2001 From: Pratapa Lakshmi Date: Mon, 23 Mar 2026 14:47:51 +0530 Subject: [PATCH 4/4] Enhance pi-api-env.yaml to support custom LLM API key and AmazonMQ secret ARN resolution. This update introduces fallback mechanisms for compatibility with older values files, ensuring smoother transitions and improved configuration management. --- .../templates/config-secrets/pi-api-env.yaml | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/charts/plane-enterprise/templates/config-secrets/pi-api-env.yaml b/charts/plane-enterprise/templates/config-secrets/pi-api-env.yaml index 273191c9..0e30a2bb 100644 --- a/charts/plane-enterprise/templates/config-secrets/pi-api-env.yaml +++ b/charts/plane-enterprise/templates/config-secrets/pi-api-env.yaml @@ -1,4 +1,12 @@ {{ $resolvedFollowerRdsArn := default .Values.env.apiSecrets.rds_secret_arn .Values.services.pi.apiSecrets.follower_rds_secret_arn }} +{{- /* Resolve the custom-LLM API key mapping name. + Prefer `services.pi.ai_providers.custom_llm.api_key` (the documented key), + but fall back to `services.pi.apiSecrets.model_custom_llm_api_key` for compatibility + with older values files (e.g. `values_secrets.yaml`). */ -}} +{{- $resolvedModelCustomLlmApiKey := default .Values.services.pi.apiSecrets.model_custom_llm_api_key .Values.services.pi.ai_providers.custom_llm.api_key -}} +{{- /* Resolve the AmazonMQ secret ARN. + Prefer shared `env.apiSecrets.amazonmq_secret_arn`, but allow a PI-specific override. */ -}} +{{- $resolvedAmazonmqSecretArn := default .Values.env.apiSecrets.amazonmq_secret_arn .Values.services.pi.apiSecrets.amazonmq_secret_arn -}} {{- if and .Values.services.pi.enabled (empty .Values.external_secrets.pi_api_env_existingSecret)}} @@ -65,13 +73,13 @@ stringData: {{- if and (not (empty $resolvedFollowerRdsArn)) (not (empty .Values.env.pi_envs.follower_rds_db_host_key)) (not (empty .Values.env.pi_envs.follower_rds_db_name_key)) (not (empty .Values.env.pi_envs.follower_rds_db_password_key)) (not (empty .Values.env.pi_envs.follower_rds_db_port_key)) (not (empty .Values.env.pi_envs.follower_rds_db_username_key)) }} FOLLOWER_RDS_SECRET_ARN: {{ $resolvedFollowerRdsArn | quote }} {{- end }} - {{- if and (not (empty .Values.env.apiSecrets.amazonmq_secret_arn)) (not (empty .Values.env.pi_envs.rabbitmq_user_key)) (not (empty .Values.env.pi_envs.rabbitmq_password_key)) }} - AMAZONMQ_SECRET_ARN: {{ .Values.env.apiSecrets.amazonmq_secret_arn | quote }} + {{- if and (not (empty $resolvedAmazonmqSecretArn)) (not (empty .Values.env.pi_envs.rabbitmq_user_key)) (not (empty .Values.env.pi_envs.rabbitmq_password_key)) }} + AMAZONMQ_SECRET_ARN: {{ $resolvedAmazonmqSecretArn | quote }} {{- end }} {{- if and (not (empty .Values.services.pi.apiSecrets.rds_secret_arn)) (not (empty .Values.env.pi_envs.rds_db_host_key)) (not (empty .Values.env.pi_envs.rds_db_name_key)) (not (empty .Values.env.pi_envs.rds_db_password_key)) (not (empty .Values.env.pi_envs.rds_db_port_key)) (not (empty .Values.env.pi_envs.rds_db_username_key)) }} RDS_SECRET_ARN: {{ .Values.services.pi.apiSecrets.rds_secret_arn | quote }} {{- end }} - {{- if and (not (empty .Values.services.pi.apiSecrets.model_secret_arn)) (not (empty .Values.services.pi.ai_providers.custom_llm.api_key)) }} + {{- if and (not (empty .Values.services.pi.apiSecrets.model_secret_arn)) (not (empty $resolvedModelCustomLlmApiKey)) }} MODEL_SECRET_ARN: {{ .Values.services.pi.apiSecrets.model_secret_arn | quote }} {{- end }} {{- end }} @@ -168,8 +176,8 @@ data: CUSTOM_LLM_AWS_REGION: "" {{- end }} - {{- if and (not (empty .Values.services.pi.apiSecrets.model_secret_arn)) (not (empty .Values.services.pi.ai_providers.custom_llm.api_key)) }} - MODEL_CUSTOM_LLM_API_KEY: {{ .Values.services.pi.ai_providers.custom_llm.api_key | quote }} + {{- if and (not (empty .Values.services.pi.apiSecrets.model_secret_arn)) (not (empty $resolvedModelCustomLlmApiKey)) }} + MODEL_CUSTOM_LLM_API_KEY: {{ $resolvedModelCustomLlmApiKey | quote }} {{- end }} EMBEDDING_MODEL: {{ .Values.services.pi.ai_providers.embedding_model.name | default "" | quote }}