Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion charts/plane-enterprise/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: Meet Plane. An Enterprise software development tool to manage issue

type: application

version: 2.6.1
version: 2.7.0
appVersion: "2.6.3"

home: https://plane.so/
Expand Down
47 changes: 36 additions & 11 deletions charts/plane-enterprise/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,26 +189,51 @@ The default value is `"traefik"`. If you are switching to a standard ingress con
| Setting | Default | Required | Description |
| ----------------------- | :-----: | :------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| airgapped.enabled | false | No | Specifies the airgapped mode the Plane API runs in. |
| airgapped.s3Secrets | [] | No | List of Kubernetes Secrets containing CA certificates to install. Each item must have `name` (Secret name) and `key` (file key in the Secret). Example: `kubectl -n plane create secret generic plane-s3-ca --from-file=s3-custom-ca.crt=/path/to/ca.crt`. Supports multiple certs (e.g. S3 + internal CA). |
| airgapped.s3SecretName | "" | No | **(Deprecated, backward compatibility)** Name of a single Kubernetes Secret containing the S3 CA cert. Used only when `s3Secrets` is empty. Prefer migrating to `s3Secrets`. |
| airgapped.s3SecretKey | "" | No | **(Deprecated, backward compatibility)** Key (filename) of the cert file inside the Secret. Used only when `s3Secrets` is empty. Set together with `airgapped.s3SecretName`. |
| airgapped.s3Secrets | [] | No | **(Deprecated, backward compatibility)** Custom S3 CA configuration moved to the top-level `customCA` section and no longer requires airgapped mode. Still honored as a fallback when `customCA.*` is unset. Prefer `customCA.s3Secrets`. |
| airgapped.s3SecretName | "" | No | **(Deprecated, backward compatibility)** Single-secret fallback. Used only when no `customCA.*` and no `airgapped.s3Secrets` are set. Prefer `customCA.s3Secrets`. |
| airgapped.s3SecretKey | "" | No | **(Deprecated, backward compatibility)** Key (filename) of the cert file for the deprecated single-secret fallback. Set together with `airgapped.s3SecretName`. |

#### Backward compatibility: custom S3 CA (upgrading from older charts)
### Custom CA Certificates

If you previously used the single-secret custom CA configuration (`airgapped.s3SecretName` and `airgapped.s3SecretKey`), it continues to work. No change is required when upgrading.
Use this when your object storage / S3-compatible endpoint presents a certificate signed by a private or internal CA. It is **independent of `airgapped.enabled`** — set it in any deployment (including non-airgapped). The chart mounts the cert(s) into the relevant pods, runs `update-ca-certificates`, and points the runtime (including Node.js via `NODE_EXTRA_CA_CERTS` and boto via `AWS_CA_BUNDLE`) at the resulting bundle.

- **Old configuration (still supported):** Set `airgapped.s3SecretName` to your Secret name and `airgapped.s3SecretKey` to the key (e.g. `s3-custom-ca.crt`). The chart mounts that single cert, runs `update-ca-certificates`, and sets `AWS_CA_BUNDLE` to the system bundle path.
- **New configuration (recommended):** Use `airgapped.s3Secrets` with a list of `{ name, key }` entries. This allows multiple CA certificates (e.g. S3 endpoint CA and internal PKI) and matches the same runtime behavior.
| Setting | Default | Required | Description |
| --------------------- | :-----: | :------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| customCA.s3Secrets | [] | No | List of Kubernetes Secrets containing CA certificates to install. Each item must have `name` (Secret name) and `key` (file key in the Secret). Example: `kubectl -n plane create secret generic plane-s3-ca --from-file=s3-custom-ca.crt=/path/to/ca.crt`. Supports multiple certs (e.g. S3 + internal CA). |
| customCA.s3SecretName | "" | No | Single-secret form. Name of a Kubernetes Secret containing the CA cert. Used only when `customCA.s3Secrets` is empty. Prefer `customCA.s3Secrets`. |
| customCA.s3SecretKey | "" | No | Key (filename) of the cert file inside the Secret. Used only when `customCA.s3Secrets` is empty. Set together with `customCA.s3SecretName`. |

**Migration (optional):** To move from the deprecated keys to `s3Secrets`, set for example:
Resolution precedence is: `customCA.s3Secrets` → `customCA.s3SecretName`/`s3SecretKey` → `airgapped.s3Secrets` → `airgapped.s3SecretName`/`s3SecretKey`.

Example:

```yaml
customCA:
s3Secrets:
- name: plane-s3-ca
key: s3-custom-ca.crt
- name: plane-internal-ca
key: internal-ca.crt
```

#### Backward compatibility (upgrading from older charts)

If you previously configured custom CA certs under `airgapped.*` (`airgapped.s3Secrets`, or `airgapped.s3SecretName`/`s3SecretKey`), they continue to work as a fallback — **no change is required when upgrading**. To migrate, move the same values to the `customCA` section:

```yaml
# Before
airgapped:
enabled: true
s3Secrets:
- name: plane-s3-ca # same as your previous s3SecretName
key: s3-custom-ca.crt # same as your previous s3SecretKey
# s3SecretName and s3SecretKey can be removed after migration
- name: plane-s3-ca
key: s3-custom-ca.crt

# After
customCA:
s3Secrets:
- name: plane-s3-ca
key: s3-custom-ca.crt
# airgapped.s3* can be removed; airgapped.enabled is no longer required for custom CA
```

### Pod Security (PSA `restricted`)
Expand Down
66 changes: 33 additions & 33 deletions charts/plane-enterprise/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,43 @@ of the local_setup flag's value.
{{- end -}}

{{/*
Normalize the deprecated s3SecretName/s3SecretKey into the s3Secrets list format.
Returns "true" when airgapped is enabled and at least one CA secret is configured.
Returns "true" when at least one custom CA secret is configured, in either the
top-level `customCA` section or the legacy `airgapped` keys. This is decoupled
from `airgapped.enabled` so custom CA certs can be mounted in non-airgapped
deployments (e.g. an S3-compatible endpoint that uses a private CA).
*/}}
{{- define "plane.s3CAEnabled" -}}
{{- if and .Values.airgapped.enabled (or (gt (len .Values.airgapped.s3Secrets) 0) (and .Values.airgapped.s3SecretName .Values.airgapped.s3SecretKey)) -}}
{{- if or (gt (len .Values.customCA.s3Secrets) 0) (and .Values.customCA.s3SecretName .Values.customCA.s3SecretKey) (gt (len .Values.airgapped.s3Secrets) 0) (and .Values.airgapped.s3SecretName .Values.airgapped.s3SecretKey) -}}
true
{{- end -}}
{{- end -}}

{{/*
Resolve the effective list of CA secrets and render them as projected-volume sources.
Precedence: customCA.s3Secrets > customCA single secret > airgapped.s3Secrets > airgapped single secret.
Single-secret (legacy) configs are normalized into the same { name, key } shape.
Output starts at column 0; caller controls indentation (e.g. nindent).
*/}}
{{- define "plane.s3CAProjectedSources" -}}
{{- $secrets := list -}}
{{- if gt (len .Values.customCA.s3Secrets) 0 -}}
{{- $secrets = .Values.customCA.s3Secrets -}}
{{- else if and .Values.customCA.s3SecretName .Values.customCA.s3SecretKey -}}
{{- $secrets = list (dict "name" .Values.customCA.s3SecretName "key" .Values.customCA.s3SecretKey) -}}
{{- else if gt (len .Values.airgapped.s3Secrets) 0 -}}
{{- $secrets = .Values.airgapped.s3Secrets -}}
{{- else if and .Values.airgapped.s3SecretName .Values.airgapped.s3SecretKey -}}
{{- $secrets = list (dict "name" .Values.airgapped.s3SecretName "key" .Values.airgapped.s3SecretKey) -}}
{{- end -}}
{{- range $secrets }}
- secret:
name: {{ .name }}
items:
- key: {{ .key }}
path: {{ .key }}
{{- end }}
{{- end -}}

{{/*
Render the volumes block for custom S3 CA certificates.
Always uses a projected volume so both single-secret (legacy) and multi-secret configs
Expand All @@ -86,21 +114,7 @@ volumes:
- name: s3-custom-ca
projected:
sources:
{{- if gt (len .Values.airgapped.s3Secrets) 0 }}
{{- range .Values.airgapped.s3Secrets }}
- secret:
name: {{ .name }}
items:
- key: {{ .key }}
path: {{ .key }}
{{- end }}
{{- else }}
- secret:
name: {{ .Values.airgapped.s3SecretName }}
items:
- key: {{ .Values.airgapped.s3SecretKey }}
path: {{ .Values.airgapped.s3SecretKey }}
{{- end }}
{{- include "plane.s3CAProjectedSources" . | trim | nindent 8 }}
{{- end }}
{{- end -}}

Expand Down Expand Up @@ -164,21 +178,7 @@ volumes:
- name: s3-custom-ca
projected:
sources:
{{- if gt (len .Values.airgapped.s3Secrets) 0 }}
{{- range .Values.airgapped.s3Secrets }}
- secret:
name: {{ .name }}
items:
- key: {{ .key }}
path: {{ .key }}
{{- end }}
{{- else }}
- secret:
name: {{ .Values.airgapped.s3SecretName }}
items:
- key: {{ .Values.airgapped.s3SecretKey }}
path: {{ .Values.airgapped.s3SecretKey }}
{{- end }}
{{- include "plane.s3CAProjectedSources" . | trim | nindent 8 }}
- name: ca-bundle
emptyDir: {}
{{- end }}
Expand Down
21 changes: 16 additions & 5 deletions charts/plane-enterprise/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,20 @@ license:

airgapped:
enabled: false
# The boto module used by API does not use the CA bundles in the container, so we need to
# mount the CA bundle into the API pod by passing it in as an environment
# variable.
# DEPRECATED: custom S3 CA configuration has moved to the top-level `customCA`
# section and no longer requires airgapped mode. These keys are still honored
# as a fallback for backward compatibility - prefer `customCA.*` going forward.
s3Secrets: []
s3SecretName: ""
s3SecretKey: ""

# Custom CA certificates for object storage / S3-compatible endpoints whose
# certificates are signed by a private or internal CA. This is independent of
# `airgapped.enabled` - set it whenever your storage endpoint presents a custom
# CA, including non-airgapped deployments.
# The boto module used by the API does not use the OS CA bundle, so the chart
# mounts these certs into the relevant pods and points the runtime at them.
customCA:
# Preferred: list of Kubernetes Secrets containing CA certificates (supports multiple certs).
# Example:
# s3Secrets:
Expand All @@ -26,8 +37,8 @@ airgapped:
# key: s3-custom-ca-2.crt
s3Secrets: []

# Deprecated (backward compatibility): use a single secret for custom S3 CA.
# If set, used when s3Secrets is empty. Prefer migrating to s3Secrets.
# Single-secret form (backward compatible). Used only when s3Secrets is empty.
# Prefer migrating to s3Secrets.
# Example:
# s3SecretName: plane-s3-ca
# s3SecretKey: s3-custom-ca.crt
Expand Down