What the chart enforces
Pod-level hardening is rendered into every workload the chart installs. The defaults come from the chart’s templates andvalues.yaml. Chart-managed selector labels and component labels are silently dropped from per-engine podLabels and similar user surfaces so user input cannot detach selectors. Security contexts can still be overridden through the documented value surfaces. Treat the table below as the install-time baseline, not as an immutable contract.
| Pod | firebolt/component | UID / GID | runAsNonRoot | Capabilities | Seccomp | Read-only root FS |
|---|---|---|---|---|---|---|
| Engine | engine | 3473 / 3473 | true | drop ALL | not set (defer to platform default) | true on the engine container |
| Metadata Service | metadata-service | 1111 / 1111 | true | drop ALL | RuntimeDefault | true (emptyDir for tmp) |
| Bundled PostgreSQL | metadata-pg | 70 / 70 | true | drop ALL | RuntimeDefault | true (PGDATA lives on the PVC) |
| Gateway (Envoy) | gateway | 101 / unset | true | drop ALL | not set (defer to platform default) | true |
fsGroup: 3473 (with fsGroupChangePolicy: OnRootMismatch) so the data PVC’s group ownership matches the engine UID.
Memlock-setup sidecar
WhenengineSpec.memlockSetup: true and podSecurityContext.runAsNonRoot: true, the chart injects a memlock-setup sidecar container that raises the engine pod’s memlock limit. The sidecar runs as UID 0 because it issues a prlimit(2) against the engine container. The pod enables shareProcessNamespace: true so the sidecar can see the engine’s PID. It drops all capabilities except SYS_RESOURCE. When your node hosts already provide a memlock limit of at least 8 GiB, disable it with memlockSetup: false and configure the nodes yourself. See Engine node requirements.
Install-time validation
The chart shipshelm/values.schema.json. helm install and helm upgrade reject wrong types and pattern violations on selected known fields, including postgresql.host, postgresql.database, postgresql.schema, customEngineConfig.instance.id, and the engine name. The schema is intentionally permissive, so unknown value paths are not rejected. This is install-time validation, not an admission webhook. It runs only when the chart is being applied, not on subsequent edits to the rendered objects.
What the chart does not enforce
Network isolation between pods
Thefirebolt-instance Helm chart emits no NetworkPolicy objects. All pod-to-pod and pod-to-external traffic is governed by whatever the cluster’s CNI and NetworkPolicy controller already enforce. In a default Kubernetes install with no NetworkPolicy controller, every pod can reach every other pod on every port.
This is a deliberate scoping decision. NetworkPolicy semantics depend on the CNI plugin (Calico, Cilium, Antrea, etc.), the cluster’s default-allow vs. default-deny posture, and the chart-vs-platform ownership boundary for security primitives. Encoding any of those assumptions into chart-emitted NetworkPolicy objects would either be a no-op with no controller installed or actively wrong for the deployment target.
Platform teams should apply NetworkPolicy objects covering at least the allowed flows below. Recommended selectors:
| Pod kind | Selector |
|---|---|
| Engine pods | firebolt/component=engine, optionally with firebolt/engine=<name> to scope to one engine |
| Gateway pods | firebolt/component=gateway |
| Metadata Service pods | firebolt/component=metadata-service |
| Bundled PostgreSQL pods | firebolt/component=metadata-pg |
| Release scoping | app.kubernetes.io/instance=<release-name> |
Allowed flows
| From | To | Port | Purpose |
|---|---|---|---|
| External clients | Gateway | 8080 (container) / 80 (Service) | Query traffic over HTTP |
| Gateway | Engine pods | 3473 | Query forwarding |
| Engine replicas of the same engine | Each other | 5678, 16000, 1717, 3434 | Distributed query execution and internal coordination between replicas of the same engine. The chart’s engine config lists per-replica FQDNs on the headless Service. |
| Engine pods | Metadata Service | 7000 | Metadata gRPC |
| Metadata Service | PostgreSQL (bundled or external) | 5432 | Metadata catalog reads and writes |
| Engine pods | External object storage | 443 / 80 | Managed-storage reads and writes |
| Prometheus | Engine and gateway pods | 9090 | Metrics scraping |
Namespace-level resource ceilings
AResourceQuota capping the aggregate of requests.cpu, requests.memory, pod count, and PVC size across all engines in a namespace is a platform concern. The chart applies per-engine resources.requests and resources.limits from your values, but does not emit a ResourceQuota and does not validate the per-engine resources block against a Helm-side maximum. The per-namespace budget is a deployment-target decision (test cluster vs. multi-tenant production).
Image provenance and supply-chain attestation
The chart pulls whatever image you supply viaengineSpec.image.repository, metadata.image.repository, and the equivalent tag values. The chart does not validate signatures, attestations, or SBOMs. If image-policy enforcement is required, use a cluster-level admission controller such as Kyverno or Sigstore Policy Controller.
Admission-time enforcement
The chart has no controller and no admission webhook. Oncehelm install has rendered the manifests, subsequent edits to the resulting objects (engine StatefulSet pod template, metadata Deployment, gateway Deployment) are not reconciled back to the chart-rendered state. Platform teams that need drift correction should treat the firebolt-instance install as input to GitOps (Argo CD, Flux) and rely on the GitOps controller for drift.
The Firebolt Kubernetes Operator provides continuous reconciliation and admission-time validation natively. When drift correction or admission-time guardrails are a primary requirement, see the operator upgrade path.
Secrets handling
Bundled PostgreSQL
Whenpostgresql.credentials.existingSecret is empty, the chart generates a Secret named <release>-metadata-postgres-creds on first helm install:
- The Secret holds three base64-encoded keys:
username,password, anddatabase. - The password is sourced (in order): an explicit
postgresql.passwordvalue, the existing Secret’spasswordkey on ahelm upgrade, or a freshly generated 24-character alphanumeric value (randAlphaNum 24) on a clean install. - The chart never rotates the password.
- The Metadata Service Deployment loads the credentials by mounting the Secret at
postgresql.credentials.mountPath. - When
postgresql.local_enabled: true, the bundled PostgreSQL StatefulSet also reads this generated Secret.
postgresql.local_enabled: false with postgresql.credentials.existingSecret.
External PostgreSQL
For external databases, usepostgresql.credentials.existingSecret. See External PostgreSQL. Inline postgresql.password also works but leaks the password into helm get values.
External object storage
Object-storage credentials for engines are not represented as Helm values. The chart passescustomEngineConfig.storage into the engine config and relies on the engine pod’s workload identity for cloud credentials:
- AWS: IRSA or AWS Pod Identity.
- Azure: Microsoft Entra Workload ID.
- Google Cloud: Workload Identity Federation for GKE.
minio storage type, the endpoint must accept the credentials expected by the engine mode documented in Amazon S3.
TLS to PostgreSQL is not exposed
The chart does not currently exposesslmode or a CA-bundle mount for the Metadata Service’s PostgreSQL connection. Encrypt the PostgreSQL connection at the network layer (VPC peering, sidecar TLS proxy, or service-mesh mTLS) until the chart grows native support.
See also
- Monitoring: metrics surface and Prometheus Operator integration.
- Operator upgrade path: when continuous admission, drift correction, and CR-status surfaces are required.
SECURITY.md: vulnerability reporting policy for this repository.