From 880756ed6ab2fa695ca7e1440ece4868e71b235a Mon Sep 17 00:00:00 2001 From: Pratapa Lakshmi Date: Thu, 6 Aug 2026 13:58:57 +0530 Subject: [PATCH 01/12] docs(self-hosting): add FIPS-enabled deployment guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the FIPS deployment guide for the makeplane/*-commercial-fips image set under the Commercial Edition install methods, matching the sibling pages' hidden/noindex convention (search:false, sidebar:false, robots noindex, added to the LLM ignoreFiles list). Covers the host fips=1 prerequisite (the single most-missed step), the full -commercial-fips image list (and why there is no FIPS AIO image), the docker-compose-fips.yml deploy + verify-fips.sh flow, the FIPS-only config defaults (LDAP_TLS_REQUIRE_CERT, SAML_REJECT_DEPRECATED_ALGORITHM, SECRET_ENCRYPTION_V2, USAGE_ID_DIGEST) with overrides, non-root/OpenShift arbitrary-UID guidance, and an honest scope-of-coverage section (bundled data plane is not FIPS; modules are certified, not Plane as a product). Supersedes makeplane/docs#484 — self-hosting content lives on developers.plane.so. Co-Authored-By: Claude Opus 4.8 --- docs/.vitepress/config.mts | 1 + .../fips-deployment.md | 189 ++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 docs/self-hosting/methods/install-methods-commercial/fips-deployment.md diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 7d2ce701..50ccb2f0 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -89,6 +89,7 @@ export default extendConfig( ignoreFiles: [ "self-hosting/methods/install-methods-commercial/docker-compose.md", "self-hosting/methods/install-methods-commercial/kubernetes.md", + "self-hosting/methods/install-methods-commercial/fips-deployment.md", ], }), ], diff --git a/docs/self-hosting/methods/install-methods-commercial/fips-deployment.md b/docs/self-hosting/methods/install-methods-commercial/fips-deployment.md new file mode 100644 index 00000000..73c9c70b --- /dev/null +++ b/docs/self-hosting/methods/install-methods-commercial/fips-deployment.md @@ -0,0 +1,189 @@ +--- +title: FIPS-enabled deployment +description: Deploy the FIPS variant of Plane Enterprise on a FIPS-enforcing host, including prerequisites, image list, verification, and scope of coverage. +keywords: plane fips, fips 140-3 deployment, plane commercial fips, govcloud plane, federal self-hosting, fips enabled containers +search: false +sidebar: false +head: + - - meta + - name: robots + content: noindex, nofollow +--- + +# FIPS-enabled deployment + +Plane Enterprise publishes a FIPS variant of every application image alongside the standard set. +These images are built on Red Hat UBI 10, apply the system-wide FIPS cryptographic policy, and run +their cryptography against FIPS-validated modules (Red Hat's OpenSSL FIPS provider for the Python +and static services; the Go FIPS 140-3 module for the Go services). They are intended for +deployments that must meet FIPS 140-3 expectations, such as US Federal or GovCloud environments. + +> **The single most important prerequisite:** FIPS mode is a property of the **host**, not of the +> image. A FIPS image on a non-FIPS host starts cleanly and looks identical from the inside while +> providing none of the guarantees. Read [Host prerequisite](#host-prerequisite) first. + +## Images + +The FIPS images use the same names as the standard `-commercial` images with a `-fips` suffix, in +the `makeplane` Docker Hub organization: + +| Service | Image | +| ------------- | --------------------------------------- | +| Backend / API | `makeplane/backend-commercial-fips` | +| Web | `makeplane/web-commercial-fips` | +| Admin | `makeplane/admin-commercial-fips` | +| Space | `makeplane/space-commercial-fips` | +| Live | `makeplane/live-commercial-fips` | +| Silo | `makeplane/silo-commercial-fips` | +| Monitor | `makeplane/monitor-commercial-fips` | +| Email | `makeplane/email-commercial-fips` | +| Plane AI (Pi) | `makeplane/plane-pi-commercial-fips` | +| Proxy | `makeplane/proxy-commercial-fips` | +| Flux | `makeplane/flux-commercial-fips` | +| Node runner | `makeplane/node-runner-commercial-fips` | + +Pin a specific release tag for any accredited deployment rather than tracking `latest` — a known, +fixed image version is part of the audit trail. + +> **Note:** there is no FIPS All-in-One (AIO) image. The AIO image is built on an Alpine base, which +> has no FIPS-validated cryptography, so a FIPS deployment uses the multi-container Compose stack +> below, not the AIO image. + +## Host prerequisite + +The host kernel must be booted in FIPS mode. The container inherits this through +`/proc/sys/crypto/fips_enabled` and **cannot set it itself**. Verify before deploying: + +```bash +cat /proc/sys/crypto/fips_enabled # must print 1 +``` + +To enable FIPS mode on a RHEL-family host (RHEL, Rocky, Alma, Amazon Linux 2023): + +```bash +sudo dnf install -y crypto-policies-scripts +sudo fips-mode-setup --enable +sudo reboot +``` + +`/boot` must be its own filesystem for this to work — it is on the stock cloud images. +Alternatively, boot a vendor FIPS image (a RHEL FIPS AMI, Ubuntu Pro FIPS) or use OpenShift with +FIPS enabled at install time. + +As a safeguard, the shipped Compose file sets `PLANE_REQUIRE_FIPS=1`, so the containers **refuse to +start** if the host is not in FIPS mode. Set it to `0` to downgrade that to a startup warning. + +## Deploy + +The Compose file and its supporting files live in the plane-ee repository under +`deployments/cli/commercial/`: + +- `docker-compose-fips.yml` — the FIPS stack +- `variables.env` — environment template +- `README-FIPS.md` — the authoritative operations reference +- `verify-fips.sh` — the verification script (see [Verify](#verify)) + +```bash +# 1. Confirm the host is in FIPS mode (above). +# 2. Prepare the environment file. +cp variables.env .env +# Edit at least: DOMAIN_NAME, WEB_URL, SECRET_KEY, MACHINE_SIGNATURE. + +# 3. Bring the stack up. +docker compose -f docker-compose-fips.yml up -d +``` + +Each container logs its posture on startup: + +``` +plane: FIPS mode ACTIVE (host kernel reports fips_enabled=1) +``` + +The Go services (monitor, email, proxy) log a corresponding line, for example +`Go FIPS 140-3 module ACTIVE`. + +## Verify + +`verify-fips.sh` asserts the posture across the running stack — the kernel flag inside each +container, that the validated OpenSSL provider is loaded and active, that a non-approved digest is +refused, that Node's `crypto.getFips()` returns 1, and that the Go services report the module. It +exits non-zero if any assertion fails, so it can gate a deployment pipeline: + +```bash +./verify-fips.sh +``` + +## Configuration defaults specific to FIPS images + +The FIPS images default to a stricter security posture than the standard images. Each default is +overridable with an environment variable, in either direction. These matter mainly if you are +moving an existing standard deployment onto the FIPS images; a fresh FIPS install needs none of +them changed. + +| Setting | FIPS default | Standard default | Notes | +| ---------------------------------- | ------------ | ---------------- | ----------------------------------------------------------------------------------------------------- | +| `LDAP_TLS_REQUIRE_CERT` | `demand` | `never` | Validates the directory server's TLS certificate. See [LDAP](#ldap-certificate-validation). | +| `SAML_REJECT_DEPRECATED_ALGORITHM` | on | off | Rejects assertions signed with RSA-SHA1. The IdP must sign with SHA-256. | +| `SECRET_ENCRYPTION_V2` | on | off | Writes at-rest secrets as AES-256-GCM instead of the legacy format. Both formats are always readable. | +| `USAGE_ID_DIGEST` | `sha256` | `md5` | Digest for Plane AI usage-ledger keys. A FIPS-mode Postgres refuses `md5()`. | + +### LDAP certificate validation + +On the FIPS images, LDAP TLS certificate validation is on by default. For it to succeed, **both** of +the following must hold: + +1. The directory certificate chains to a trusted CA. For a private or self-signed CA, point + `LDAP_TLS_CA_CERTFILE` at your CA bundle (PEM). +2. The certificate's CN/SAN matches the host in `LDAP_SERVER_URI`. An IP address or short hostname + that is not in the certificate's SAN fails hostname verification **even with the correct CA + bundle** — use the fully qualified name the certificate was issued for. + +Setting `LDAP_TLS_REQUIRE_CERT=never` restores the previous behaviour and logs a warning on every +connection. + +## Running under a non-root or arbitrary UID (OpenShift) + +The FIPS application images run non-root. On plain Kubernetes, set a `securityContext` that pins the +image's built-in user (uid `1000`); FIPS mode itself requires no privilege. On OpenShift, the +`restricted-v2` SCC runs each container as an arbitrary high UID that is always a member of group +`0` — the images' writable directories are group-`0` writable to support exactly this, so no image +change is needed. Run the pods with `runAsGroup: 0` / `fsGroup: 0` so that arbitrary UID keeps write +access. The bundled proxy is the one exception: Caddy binds `:80`/`:443`, which `restricted-v2` +forbids for non-root — front it with an OpenShift Route on high ports, or use a custom SCC that +grants `NET_BIND_SERVICE`. Ingress-based deployments do not use the bundled proxy. + +## Scope of coverage + +**Covered.** The Plane application images run their cryptography against FIPS-validated modules on a +FIPS-enforcing host. Non-approved algorithms are refused. + +**The bundled data plane is not FIPS.** The `postgres`, `valkey`, `rabbitmq`, `minio`, and +`iframely` services in the Compose file are upstream Alpine/musl images with no FIPS-validated +cryptography — there are no FIPS variants of them. They are suitable for evaluation only. For an +accreditable deployment, replace them with externally managed datastores on FIPS endpoints and +repoint the connection variables: + +| Service | Replace with | Variables | +| ------------- | --------------------------------- | --------------------------------------------- | +| `plane-db` | RDS / Aurora PostgreSQL | `DATABASE_URL`, `PGHOST`, `POSTGRES_*` | +| `plane-redis` | ElastiCache (Valkey/Redis) | `REDIS_URL`, `REDIS_HOST`, `REDIS_PORT` | +| `plane-mq` | Amazon MQ (RabbitMQ) | `AMQP_URL`, `RABBITMQ_*` | +| `plane-minio` | S3 on a FIPS endpoint, or similar | `AWS_S3_ENDPOINT_URL`, `AWS_*`, `USE_MINIO=0` | + +Then set the corresponding `*_REPLICAS` to `0`, or remove those services, so the bundled ones do +not start. + +**TLS termination.** The bundled proxy (Caddy) is built against a FIPS-validated module, but for an +accredited topology the recommended pattern is to terminate TLS at a validated endpoint in front of +the deployment — such as a FIPS-enabled load balancer — and have the proxy serve HTTP internally. + +**FIPS validation applies to the cryptographic modules, not to Plane as a product.** FIPS 140-3 +certificates are held by the module vendors (Red Hat and the Go project). This deployment ensures +Plane's cryptography _uses_ those validated modules on a compliant host; it does not make Plane +itself a FIPS-certified product. + +## Reference + +The authoritative operations reference — including every environment variable and the migration +notes for moving a standard deployment onto FIPS images — is `README-FIPS.md`, shipped alongside +the Compose file in `deployments/cli/commercial/`. From bb0a9a71632497c9bb9089ba1fb10de6f7db9a30 Mon Sep 17 00:00:00 2001 From: Pratapa Lakshmi Date: Thu, 6 Aug 2026 17:30:10 +0530 Subject: [PATCH 02/12] docs(self-hosting): make the FIPS guide discoverable in the sidebar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a "FIPS deployment" entry to the "Self-host Plane" sidebar group in both the root and /self-hosting/ sidebars, and drop `search: false` / `sidebar: false` from the page frontmatter so it renders with the nav and is found by in-site (⌘K) search. Keep the robots noindex tag — discoverable within the docs site, not indexed by public search engines (matching its commercial-feature nature). Co-Authored-By: Claude Opus 4.8 --- docs/.vitepress/config.mts | 2 ++ .../methods/install-methods-commercial/fips-deployment.md | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 50ccb2f0..ab488b1f 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -300,6 +300,7 @@ export default extendConfig( { text: "Self-hosting 101", link: "/self-hosting/self-hosting-101" }, { text: "Docker Compose", link: "/self-hosting/methods/docker-compose" }, { text: "Kubernetes", link: "/self-hosting/methods/kubernetes" }, + { text: "FIPS deployment", link: "/self-hosting/methods/install-methods-commercial/fips-deployment" }, ], }, { @@ -328,6 +329,7 @@ export default extendConfig( { text: "Self-hosting 101", link: "/self-hosting/self-hosting-101" }, { text: "Plane Editions", link: "/self-hosting/editions-and-versions" }, { text: "Plane Architecture", link: "/self-hosting/plane-architecture" }, + { text: "FIPS deployment", link: "/self-hosting/methods/install-methods-commercial/fips-deployment" }, ], }, { diff --git a/docs/self-hosting/methods/install-methods-commercial/fips-deployment.md b/docs/self-hosting/methods/install-methods-commercial/fips-deployment.md index 73c9c70b..2bdea501 100644 --- a/docs/self-hosting/methods/install-methods-commercial/fips-deployment.md +++ b/docs/self-hosting/methods/install-methods-commercial/fips-deployment.md @@ -2,8 +2,6 @@ title: FIPS-enabled deployment description: Deploy the FIPS variant of Plane Enterprise on a FIPS-enforcing host, including prerequisites, image list, verification, and scope of coverage. keywords: plane fips, fips 140-3 deployment, plane commercial fips, govcloud plane, federal self-hosting, fips enabled containers -search: false -sidebar: false head: - - meta - name: robots From ffa4807eb55a7d7d634c88be4e9200a55b5011d9 Mon Sep 17 00:00:00 2001 From: Pratapa Lakshmi Date: Thu, 6 Aug 2026 17:35:46 +0530 Subject: [PATCH 03/12] docs(fips): address review feedback on the FIPS deployment guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Host setup: split by distro/version. AL2023 + RHEL 8/9 use fips-mode-setup (AL2023 keeps /boot on root; RHEL needs a mounted separate /boot if present). RHEL 10 removed fips-mode-setup — FIPS must be enabled at install with fips=1; post-install switching is unsupported. - OpenShift: don't set runAsUser/runAsGroup/fsGroup under restricted-v2 — the SCC assigns the UID and allocates fsGroup from the namespace supplemental-group range, so an explicit fsGroup: 0 is rejected unless that range includes 0. The runAsGroup/fsGroup: 0 guidance now applies only to plain Kubernetes. Proxy note reworded to "an SCC that permits NET_BIND_SERVICE". - USAGE_ID_DIGEST: clarify md5 is incompatible under FIPS (sha256 required), not an either-direction override. - verify-fips.sh: attribute to the plane-ee repo and soften wording (it is not committed to this docs repo). - Add the `text` language tag to the startup-log fenced block. Co-Authored-By: Claude Opus 4.8 --- .../fips-deployment.md | 61 ++++++++++++------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/docs/self-hosting/methods/install-methods-commercial/fips-deployment.md b/docs/self-hosting/methods/install-methods-commercial/fips-deployment.md index 2bdea501..10a90859 100644 --- a/docs/self-hosting/methods/install-methods-commercial/fips-deployment.md +++ b/docs/self-hosting/methods/install-methods-commercial/fips-deployment.md @@ -56,7 +56,9 @@ The host kernel must be booted in FIPS mode. The container inherits this through cat /proc/sys/crypto/fips_enabled # must print 1 ``` -To enable FIPS mode on a RHEL-family host (RHEL, Rocky, Alma, Amazon Linux 2023): +How you put the host into FIPS mode depends on the distribution and version: + +**Amazon Linux 2023, RHEL 8/9 (and Rocky, Alma)** — enable in place, then reboot: ```bash sudo dnf install -y crypto-policies-scripts @@ -64,9 +66,13 @@ sudo fips-mode-setup --enable sudo reboot ``` -`/boot` must be its own filesystem for this to work — it is on the stock cloud images. -Alternatively, boot a vendor FIPS image (a RHEL FIPS AMI, Ubuntu Pro FIPS) or use OpenShift with -FIPS enabled at install time. +On AL2023 `/boot` lives on the root filesystem, so no separate partition is required. On RHEL/Rocky/Alma with a **separate** `/boot` (or `/boot/efi`) partition, that partition must be mounted so `fips-mode-setup` can update the bootloader. + +**RHEL 10** — `fips-mode-setup` has been **removed**, and switching an already-installed system to FIPS mode is **not supported**. FIPS mode must be enabled **at install time** by adding `fips=1` to the kernel command line (or `fips = true` in a RHEL image-builder blueprint). A post-install `update-crypto-policies --set FIPS` is **not** sufficient for FIPS 140 compliance — the only supported path on a non-FIPS install is reinstalling. + +**Other** — boot a vendor FIPS image (a RHEL FIPS AMI, Ubuntu Pro FIPS), or install OpenShift with FIPS enabled. + +In all cases, the definitive check is the kernel flag above (`/proc/sys/crypto/fips_enabled` = `1`). As a safeguard, the shipped Compose file sets `PLANE_REQUIRE_FIPS=1`, so the containers **refuse to start** if the host is not in FIPS mode. Set it to `0` to downgrade that to a startup warning. @@ -93,7 +99,7 @@ docker compose -f docker-compose-fips.yml up -d Each container logs its posture on startup: -``` +```text plane: FIPS mode ACTIVE (host kernel reports fips_enabled=1) ``` @@ -102,10 +108,11 @@ The Go services (monitor, email, proxy) log a corresponding line, for example ## Verify -`verify-fips.sh` asserts the posture across the running stack — the kernel flag inside each -container, that the validated OpenSSL provider is loaded and active, that a non-approved digest is -refused, that Node's `crypto.getFips()` returns 1, and that the Go services report the module. It -exits non-zero if any assertion fails, so it can gate a deployment pipeline: +`verify-fips.sh` (shipped in plane-ee under `deployments/cli/commercial/`, alongside the Compose +file) checks the posture across the running stack — the kernel flag inside each container, that the +validated OpenSSL provider is loaded and active, that a non-approved digest is refused, that Node's +`crypto.getFips()` returns 1, and that the Go services report the module. It is designed to exit +non-zero when a check does not hold, so it can gate a deployment pipeline: ```bash ./verify-fips.sh @@ -118,12 +125,12 @@ overridable with an environment variable, in either direction. These matter main moving an existing standard deployment onto the FIPS images; a fresh FIPS install needs none of them changed. -| Setting | FIPS default | Standard default | Notes | -| ---------------------------------- | ------------ | ---------------- | ----------------------------------------------------------------------------------------------------- | -| `LDAP_TLS_REQUIRE_CERT` | `demand` | `never` | Validates the directory server's TLS certificate. See [LDAP](#ldap-certificate-validation). | -| `SAML_REJECT_DEPRECATED_ALGORITHM` | on | off | Rejects assertions signed with RSA-SHA1. The IdP must sign with SHA-256. | -| `SECRET_ENCRYPTION_V2` | on | off | Writes at-rest secrets as AES-256-GCM instead of the legacy format. Both formats are always readable. | -| `USAGE_ID_DIGEST` | `sha256` | `md5` | Digest for Plane AI usage-ledger keys. A FIPS-mode Postgres refuses `md5()`. | +| Setting | FIPS default | Standard default | Notes | +| ---------------------------------- | ------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `LDAP_TLS_REQUIRE_CERT` | `demand` | `never` | Validates the directory server's TLS certificate. See [LDAP](#ldap-certificate-validation). | +| `SAML_REJECT_DEPRECATED_ALGORITHM` | on | off | Rejects assertions signed with RSA-SHA1. The IdP must sign with SHA-256. | +| `SECRET_ENCRYPTION_V2` | on | off | Writes at-rest secrets as AES-256-GCM instead of the legacy format. Both formats are always readable. | +| `USAGE_ID_DIGEST` | `sha256` (required) | `md5` | Digest for Plane AI usage-ledger keys. Under FIPS this is **not** an "either direction" override: a FIPS-mode Postgres refuses `md5()`, so `sha256` is required and `md5` is incompatible. | ### LDAP certificate validation @@ -141,14 +148,22 @@ connection. ## Running under a non-root or arbitrary UID (OpenShift) -The FIPS application images run non-root. On plain Kubernetes, set a `securityContext` that pins the -image's built-in user (uid `1000`); FIPS mode itself requires no privilege. On OpenShift, the -`restricted-v2` SCC runs each container as an arbitrary high UID that is always a member of group -`0` — the images' writable directories are group-`0` writable to support exactly this, so no image -change is needed. Run the pods with `runAsGroup: 0` / `fsGroup: 0` so that arbitrary UID keeps write -access. The bundled proxy is the one exception: Caddy binds `:80`/`:443`, which `restricted-v2` -forbids for non-root — front it with an OpenShift Route on high ports, or use a custom SCC that -grants `NET_BIND_SERVICE`. Ingress-based deployments do not use the bundled proxy. +The FIPS application images run non-root, and FIPS mode itself requires no privilege. How you set +the pod security context depends on the platform: + +**Plain Kubernetes.** Pin the image's built-in user with `runAsUser: 1000`. If you run under a +different UID, also set `runAsGroup: 0` and `fsGroup: 0` so that UID keeps write access through the +images' group-`0`-writable directories. + +**OpenShift (`restricted-v2`).** Do **not** set `runAsUser`, `runAsGroup`, or `fsGroup` yourself. The +SCC assigns an arbitrary high UID that is a member of group `0`, and it allocates `fsGroup` from the +namespace's `openshift.io/sa.scc.supplemental-groups` range — an explicit `fsGroup: 0` is rejected +unless that range includes `0`. No image change or group override is needed: the images' writable +directories are already group-`0` writable, which is exactly what the assigned UID needs. + +The bundled proxy is the one exception: Caddy binds `:80`/`:443`, which `restricted-v2` forbids for a +non-root process. Front it with an OpenShift Route (running Caddy on high ports), or grant it an SCC +that permits `NET_BIND_SERVICE`. Ingress-based deployments do not use the bundled proxy. ## Scope of coverage From fec3329221dc14f32bb4af63cea533588f7e4f04 Mon Sep 17 00:00:00 2001 From: danciaclara Date: Wed, 12 Aug 2026 10:54:17 +0530 Subject: [PATCH 04/12] minor fixes --- docs/.vitepress/config.mts | 5 +- .../fips-deployment.md | 56 ++++++++----------- 2 files changed, 26 insertions(+), 35 deletions(-) rename docs/self-hosting/methods/{install-methods-commercial => }/fips-deployment.md (78%) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index ab488b1f..95efd02b 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -299,8 +299,7 @@ export default extendConfig( { text: "Overview", link: "/self-hosting/overview" }, { text: "Self-hosting 101", link: "/self-hosting/self-hosting-101" }, { text: "Docker Compose", link: "/self-hosting/methods/docker-compose" }, - { text: "Kubernetes", link: "/self-hosting/methods/kubernetes" }, - { text: "FIPS deployment", link: "/self-hosting/methods/install-methods-commercial/fips-deployment" }, + { text: "Kubernetes", link: "/self-hosting/methods/kubernetes" } ], }, { @@ -329,7 +328,6 @@ export default extendConfig( { text: "Self-hosting 101", link: "/self-hosting/self-hosting-101" }, { text: "Plane Editions", link: "/self-hosting/editions-and-versions" }, { text: "Plane Architecture", link: "/self-hosting/plane-architecture" }, - { text: "FIPS deployment", link: "/self-hosting/methods/install-methods-commercial/fips-deployment" }, ], }, { @@ -352,6 +350,7 @@ export default extendConfig( collapsed: true, items: [{ text: "High availability", link: "/self-hosting/govern/high-availability" }], }, + { text: "FIPS deployment", link: "/self-hosting/methods/fips-deployment" }, { text: "Podman Quadlets", link: "/self-hosting/methods/podman-quadlets" }, { text: "Airgapped Edition", diff --git a/docs/self-hosting/methods/install-methods-commercial/fips-deployment.md b/docs/self-hosting/methods/fips-deployment.md similarity index 78% rename from docs/self-hosting/methods/install-methods-commercial/fips-deployment.md rename to docs/self-hosting/methods/fips-deployment.md index 10a90859..e4d439d6 100644 --- a/docs/self-hosting/methods/install-methods-commercial/fips-deployment.md +++ b/docs/self-hosting/methods/fips-deployment.md @@ -1,5 +1,5 @@ --- -title: FIPS-enabled deployment +title: FIPS deployment description: Deploy the FIPS variant of Plane Enterprise on a FIPS-enforcing host, including prerequisites, image list, verification, and scope of coverage. keywords: plane fips, fips 140-3 deployment, plane commercial fips, govcloud plane, federal self-hosting, fips enabled containers head: @@ -8,7 +8,7 @@ head: content: noindex, nofollow --- -# FIPS-enabled deployment +# FIPS deployment Plane Enterprise publishes a FIPS variant of every application image alongside the standard set. These images are built on Red Hat UBI 10, apply the system-wide FIPS cryptographic policy, and run @@ -16,14 +16,13 @@ their cryptography against FIPS-validated modules (Red Hat's OpenSSL FIPS provid and static services; the Go FIPS 140-3 module for the Go services). They are intended for deployments that must meet FIPS 140-3 expectations, such as US Federal or GovCloud environments. -> **The single most important prerequisite:** FIPS mode is a property of the **host**, not of the -> image. A FIPS image on a non-FIPS host starts cleanly and looks identical from the inside while -> providing none of the guarantees. Read [Host prerequisite](#host-prerequisite) first. +::: warning **The single most important prerequisite** +FIPS mode is a property of the **host**, not of the image. A FIPS image on a non-FIPS host starts cleanly and looks identical from the inside while providing none of the guarantees. Read [Host prerequisite](#host-prerequisite) first. +::: ## Images -The FIPS images use the same names as the standard `-commercial` images with a `-fips` suffix, in -the `makeplane` Docker Hub organization: +The FIPS images use the same names as the standard `-commercial` images with a `-fips` suffix, in the `makeplane` Docker Hub organization: | Service | Image | | ------------- | --------------------------------------- | @@ -35,17 +34,17 @@ the `makeplane` Docker Hub organization: | Silo | `makeplane/silo-commercial-fips` | | Monitor | `makeplane/monitor-commercial-fips` | | Email | `makeplane/email-commercial-fips` | -| Plane AI (Pi) | `makeplane/plane-pi-commercial-fips` | +| Plane AI | `makeplane/plane-pi-commercial-fips` | | Proxy | `makeplane/proxy-commercial-fips` | | Flux | `makeplane/flux-commercial-fips` | | Node runner | `makeplane/node-runner-commercial-fips` | -Pin a specific release tag for any accredited deployment rather than tracking `latest` — a known, +Pin a specific release tag for any accredited deployment rather than tracking `latest` - a known, fixed image version is part of the audit trail. -> **Note:** there is no FIPS All-in-One (AIO) image. The AIO image is built on an Alpine base, which -> has no FIPS-validated cryptography, so a FIPS deployment uses the multi-container Compose stack -> below, not the AIO image. +:::info +There is no FIPS All-in-One (AIO) image. The AIO image is built on an Alpine base, which has no FIPS-validated cryptography, so a FIPS deployment uses the multi-container Compose stack below, not the AIO image. +::: ## Host prerequisite @@ -58,7 +57,7 @@ cat /proc/sys/crypto/fips_enabled # must print 1 How you put the host into FIPS mode depends on the distribution and version: -**Amazon Linux 2023, RHEL 8/9 (and Rocky, Alma)** — enable in place, then reboot: +**Amazon Linux 2023, RHEL 8/9 (and Rocky, Alma)** - enable in place, then reboot: ```bash sudo dnf install -y crypto-policies-scripts @@ -68,9 +67,9 @@ sudo reboot On AL2023 `/boot` lives on the root filesystem, so no separate partition is required. On RHEL/Rocky/Alma with a **separate** `/boot` (or `/boot/efi`) partition, that partition must be mounted so `fips-mode-setup` can update the bootloader. -**RHEL 10** — `fips-mode-setup` has been **removed**, and switching an already-installed system to FIPS mode is **not supported**. FIPS mode must be enabled **at install time** by adding `fips=1` to the kernel command line (or `fips = true` in a RHEL image-builder blueprint). A post-install `update-crypto-policies --set FIPS` is **not** sufficient for FIPS 140 compliance — the only supported path on a non-FIPS install is reinstalling. +**RHEL 10** - `fips-mode-setup` has been **removed**, and switching an already-installed system to FIPS mode is **not supported**. FIPS mode must be enabled **at install time** by adding `fips=1` to the kernel command line (or `fips = true` in a RHEL image-builder blueprint). A post-install `update-crypto-policies --set FIPS` is **not** sufficient for FIPS 140 compliance - the only supported path on a non-FIPS install is reinstalling. -**Other** — boot a vendor FIPS image (a RHEL FIPS AMI, Ubuntu Pro FIPS), or install OpenShift with FIPS enabled. +**Other** - boot a vendor FIPS image (a RHEL FIPS AMI, Ubuntu Pro FIPS), or install OpenShift with FIPS enabled. In all cases, the definitive check is the kernel flag above (`/proc/sys/crypto/fips_enabled` = `1`). @@ -82,10 +81,10 @@ start** if the host is not in FIPS mode. Set it to `0` to downgrade that to a st The Compose file and its supporting files live in the plane-ee repository under `deployments/cli/commercial/`: -- `docker-compose-fips.yml` — the FIPS stack -- `variables.env` — environment template -- `README-FIPS.md` — the authoritative operations reference -- `verify-fips.sh` — the verification script (see [Verify](#verify)) +- `docker-compose-fips.yml` - the FIPS stack +- `variables.env` - environment template +- `README-FIPS.md` - the authoritative operations reference +- `verify-fips.sh` - the verification script (see [Verify](#verify)) ```bash # 1. Confirm the host is in FIPS mode (above). @@ -108,8 +107,7 @@ The Go services (monitor, email, proxy) log a corresponding line, for example ## Verify -`verify-fips.sh` (shipped in plane-ee under `deployments/cli/commercial/`, alongside the Compose -file) checks the posture across the running stack — the kernel flag inside each container, that the +`verify-fips.sh` checks the posture across the running stack - the kernel flag inside each container, that the validated OpenSSL provider is loaded and active, that a non-approved digest is refused, that Node's `crypto.getFips()` returns 1, and that the Go services report the module. It is designed to exit non-zero when a check does not hold, so it can gate a deployment pipeline: @@ -141,7 +139,7 @@ the following must hold: `LDAP_TLS_CA_CERTFILE` at your CA bundle (PEM). 2. The certificate's CN/SAN matches the host in `LDAP_SERVER_URI`. An IP address or short hostname that is not in the certificate's SAN fails hostname verification **even with the correct CA - bundle** — use the fully qualified name the certificate was issued for. + bundle** - use the fully qualified name the certificate was issued for. Setting `LDAP_TLS_REQUIRE_CERT=never` restores the previous behaviour and logs a warning on every connection. @@ -157,7 +155,7 @@ images' group-`0`-writable directories. **OpenShift (`restricted-v2`).** Do **not** set `runAsUser`, `runAsGroup`, or `fsGroup` yourself. The SCC assigns an arbitrary high UID that is a member of group `0`, and it allocates `fsGroup` from the -namespace's `openshift.io/sa.scc.supplemental-groups` range — an explicit `fsGroup: 0` is rejected +namespace's `openshift.io/sa.scc.supplemental-groups` range - an explicit `fsGroup: 0` is rejected unless that range includes `0`. No image change or group override is needed: the images' writable directories are already group-`0` writable, which is exactly what the assigned UID needs. @@ -172,7 +170,7 @@ FIPS-enforcing host. Non-approved algorithms are refused. **The bundled data plane is not FIPS.** The `postgres`, `valkey`, `rabbitmq`, `minio`, and `iframely` services in the Compose file are upstream Alpine/musl images with no FIPS-validated -cryptography — there are no FIPS variants of them. They are suitable for evaluation only. For an +cryptography - there are no FIPS variants of them. They are suitable for evaluation only. For an accreditable deployment, replace them with externally managed datastores on FIPS endpoints and repoint the connection variables: @@ -188,15 +186,9 @@ not start. **TLS termination.** The bundled proxy (Caddy) is built against a FIPS-validated module, but for an accredited topology the recommended pattern is to terminate TLS at a validated endpoint in front of -the deployment — such as a FIPS-enabled load balancer — and have the proxy serve HTTP internally. +the deployment - such as a FIPS-enabled load balancer - and have the proxy serve HTTP internally. **FIPS validation applies to the cryptographic modules, not to Plane as a product.** FIPS 140-3 certificates are held by the module vendors (Red Hat and the Go project). This deployment ensures Plane's cryptography _uses_ those validated modules on a compliant host; it does not make Plane -itself a FIPS-certified product. - -## Reference - -The authoritative operations reference — including every environment variable and the migration -notes for moving a standard deployment onto FIPS images — is `README-FIPS.md`, shipped alongside -the Compose file in `deployments/cli/commercial/`. +itself a FIPS-certified product. \ No newline at end of file From 2156389e341d549f1c18226fe08dd32d41dbf716 Mon Sep 17 00:00:00 2001 From: Pratapa Lakshmi Date: Wed, 12 Aug 2026 11:06:36 +0530 Subject: [PATCH 05/12] docs(fips): stop pointing readers at the private plane-ee repository plane-ee is a private repository, so the deployment files the Deploy section referenced (deployments/cli/commercial/...) are not browsable by the customers and prospects this page is for. Describe them as the FIPS deployment bundle shipped with each release instead, with a pointer to the account team / support for obtaining it. Co-Authored-By: Claude Opus 4.8 --- docs/self-hosting/methods/fips-deployment.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/self-hosting/methods/fips-deployment.md b/docs/self-hosting/methods/fips-deployment.md index e4d439d6..c176712d 100644 --- a/docs/self-hosting/methods/fips-deployment.md +++ b/docs/self-hosting/methods/fips-deployment.md @@ -16,7 +16,7 @@ their cryptography against FIPS-validated modules (Red Hat's OpenSSL FIPS provid and static services; the Go FIPS 140-3 module for the Go services). They are intended for deployments that must meet FIPS 140-3 expectations, such as US Federal or GovCloud environments. -::: warning **The single most important prerequisite** +::: warning **The single most important prerequisite** FIPS mode is a property of the **host**, not of the image. A FIPS image on a non-FIPS host starts cleanly and looks identical from the inside while providing none of the guarantees. Read [Host prerequisite](#host-prerequisite) first. ::: @@ -42,9 +42,9 @@ The FIPS images use the same names as the standard `-commercial` images with a ` Pin a specific release tag for any accredited deployment rather than tracking `latest` - a known, fixed image version is part of the audit trail. -:::info +:::info There is no FIPS All-in-One (AIO) image. The AIO image is built on an Alpine base, which has no FIPS-validated cryptography, so a FIPS deployment uses the multi-container Compose stack below, not the AIO image. -::: +::: ## Host prerequisite @@ -78,8 +78,10 @@ start** if the host is not in FIPS mode. Set it to `0` to downgrade that to a st ## Deploy -The Compose file and its supporting files live in the plane-ee repository under -`deployments/cli/commercial/`: +Each Plane Enterprise FIPS release ships a deployment bundle containing the files below. The +Plane Enterprise source repository is private, so these files are not publicly browsable - they +are distributed with the release. If you don't have the bundle for your release, request it from +your Plane account team or [contact support](https://plane.so/contact). - `docker-compose-fips.yml` - the FIPS stack - `variables.env` - environment template @@ -191,4 +193,4 @@ the deployment - such as a FIPS-enabled load balancer - and have the proxy serve **FIPS validation applies to the cryptographic modules, not to Plane as a product.** FIPS 140-3 certificates are held by the module vendors (Red Hat and the Go project). This deployment ensures Plane's cryptography _uses_ those validated modules on a compliant host; it does not make Plane -itself a FIPS-certified product. \ No newline at end of file +itself a FIPS-certified product. From cc68b6ced0f7e4765b97c7bc896bf750a06e435c Mon Sep 17 00:00:00 2001 From: Pratapa Lakshmi Date: Wed, 12 Aug 2026 11:44:16 +0530 Subject: [PATCH 06/12] style: fix prettier formatting in config.mts A trailing comma went missing in the sidebar edit, failing the CI `pnpm check:format` gate. Co-Authored-By: Claude Opus 4.8 --- docs/.vitepress/config.mts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 95efd02b..5d8f21c3 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -299,7 +299,7 @@ export default extendConfig( { text: "Overview", link: "/self-hosting/overview" }, { text: "Self-hosting 101", link: "/self-hosting/self-hosting-101" }, { text: "Docker Compose", link: "/self-hosting/methods/docker-compose" }, - { text: "Kubernetes", link: "/self-hosting/methods/kubernetes" } + { text: "Kubernetes", link: "/self-hosting/methods/kubernetes" }, ], }, { From adfc0154fe13c99f11bb41a603292ea6c306907a Mon Sep 17 00:00:00 2001 From: Pratapa Lakshmi Date: Wed, 12 Aug 2026 12:40:57 +0530 Subject: [PATCH 07/12] =?UTF-8?q?docs(fips):=20simplify=20=E2=80=94=20self?= =?UTF-8?q?-contained=20Deploy,=20drop=20LDAP=20deep-dive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Deploy: the bundle ships with every FIPS release; drop the "request it from your account team / contact support" routing and the README-FIPS mention — this page is the reference for readers. - Remove the LDAP certificate-validation subsection; the config table row now carries the one line that matters (validate cert, `never` restores old behavior). - Tighten host-prerequisite (fold the /boot and crypto-policies nuances), non-root/OpenShift (half the length, same guidance), and the config-defaults intro. Co-Authored-By: Claude Opus 4.8 --- docs/self-hosting/methods/fips-deployment.md | 67 ++++++-------------- 1 file changed, 19 insertions(+), 48 deletions(-) diff --git a/docs/self-hosting/methods/fips-deployment.md b/docs/self-hosting/methods/fips-deployment.md index c176712d..776e48bd 100644 --- a/docs/self-hosting/methods/fips-deployment.md +++ b/docs/self-hosting/methods/fips-deployment.md @@ -65,27 +65,19 @@ sudo fips-mode-setup --enable sudo reboot ``` -On AL2023 `/boot` lives on the root filesystem, so no separate partition is required. On RHEL/Rocky/Alma with a **separate** `/boot` (or `/boot/efi`) partition, that partition must be mounted so `fips-mode-setup` can update the bootloader. - -**RHEL 10** - `fips-mode-setup` has been **removed**, and switching an already-installed system to FIPS mode is **not supported**. FIPS mode must be enabled **at install time** by adding `fips=1` to the kernel command line (or `fips = true` in a RHEL image-builder blueprint). A post-install `update-crypto-policies --set FIPS` is **not** sufficient for FIPS 140 compliance - the only supported path on a non-FIPS install is reinstalling. +**RHEL 10** - `fips-mode-setup` has been removed and post-install switching is not supported: enable FIPS **at install time** with `fips=1` on the kernel command line. **Other** - boot a vendor FIPS image (a RHEL FIPS AMI, Ubuntu Pro FIPS), or install OpenShift with FIPS enabled. -In all cases, the definitive check is the kernel flag above (`/proc/sys/crypto/fips_enabled` = `1`). - As a safeguard, the shipped Compose file sets `PLANE_REQUIRE_FIPS=1`, so the containers **refuse to start** if the host is not in FIPS mode. Set it to `0` to downgrade that to a startup warning. ## Deploy -Each Plane Enterprise FIPS release ships a deployment bundle containing the files below. The -Plane Enterprise source repository is private, so these files are not publicly browsable - they -are distributed with the release. If you don't have the bundle for your release, request it from -your Plane account team or [contact support](https://plane.so/contact). +The FIPS deployment bundle ships with every Plane Enterprise FIPS release: - `docker-compose-fips.yml` - the FIPS stack - `variables.env` - environment template -- `README-FIPS.md` - the authoritative operations reference - `verify-fips.sh` - the verification script (see [Verify](#verify)) ```bash @@ -120,50 +112,29 @@ non-zero when a check does not hold, so it can gate a deployment pipeline: ## Configuration defaults specific to FIPS images -The FIPS images default to a stricter security posture than the standard images. Each default is -overridable with an environment variable, in either direction. These matter mainly if you are -moving an existing standard deployment onto the FIPS images; a fresh FIPS install needs none of -them changed. - -| Setting | FIPS default | Standard default | Notes | -| ---------------------------------- | ------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `LDAP_TLS_REQUIRE_CERT` | `demand` | `never` | Validates the directory server's TLS certificate. See [LDAP](#ldap-certificate-validation). | -| `SAML_REJECT_DEPRECATED_ALGORITHM` | on | off | Rejects assertions signed with RSA-SHA1. The IdP must sign with SHA-256. | -| `SECRET_ENCRYPTION_V2` | on | off | Writes at-rest secrets as AES-256-GCM instead of the legacy format. Both formats are always readable. | -| `USAGE_ID_DIGEST` | `sha256` (required) | `md5` | Digest for Plane AI usage-ledger keys. Under FIPS this is **not** an "either direction" override: a FIPS-mode Postgres refuses `md5()`, so `sha256` is required and `md5` is incompatible. | - -### LDAP certificate validation +The FIPS images default to a stricter security posture than the standard images. A fresh FIPS +install needs none of these changed; they matter mainly when moving an existing standard +deployment onto the FIPS images. -On the FIPS images, LDAP TLS certificate validation is on by default. For it to succeed, **both** of -the following must hold: - -1. The directory certificate chains to a trusted CA. For a private or self-signed CA, point - `LDAP_TLS_CA_CERTFILE` at your CA bundle (PEM). -2. The certificate's CN/SAN matches the host in `LDAP_SERVER_URI`. An IP address or short hostname - that is not in the certificate's SAN fails hostname verification **even with the correct CA - bundle** - use the fully qualified name the certificate was issued for. - -Setting `LDAP_TLS_REQUIRE_CERT=never` restores the previous behaviour and logs a warning on every -connection. +| Setting | FIPS default | Standard default | Notes | +| ---------------------------------- | ------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------- | +| `LDAP_TLS_REQUIRE_CERT` | `demand` | `never` | Validates the LDAP server's TLS certificate. Set to `never` to restore the previous behavior. | +| `SAML_REJECT_DEPRECATED_ALGORITHM` | on | off | Rejects assertions signed with RSA-SHA1. The IdP must sign with SHA-256. | +| `SECRET_ENCRYPTION_V2` | on | off | Writes at-rest secrets as AES-256-GCM instead of the legacy format. Both formats are always readable. | +| `USAGE_ID_DIGEST` | `sha256` (required) | `md5` | Digest for Plane AI usage-ledger keys. `md5` is incompatible with a FIPS-mode Postgres, so `sha256` is required. | ## Running under a non-root or arbitrary UID (OpenShift) -The FIPS application images run non-root, and FIPS mode itself requires no privilege. How you set -the pod security context depends on the platform: - -**Plain Kubernetes.** Pin the image's built-in user with `runAsUser: 1000`. If you run under a -different UID, also set `runAsGroup: 0` and `fsGroup: 0` so that UID keeps write access through the -images' group-`0`-writable directories. +The FIPS application images run non-root, and FIPS mode itself requires no privilege. -**OpenShift (`restricted-v2`).** Do **not** set `runAsUser`, `runAsGroup`, or `fsGroup` yourself. The -SCC assigns an arbitrary high UID that is a member of group `0`, and it allocates `fsGroup` from the -namespace's `openshift.io/sa.scc.supplemental-groups` range - an explicit `fsGroup: 0` is rejected -unless that range includes `0`. No image change or group override is needed: the images' writable -directories are already group-`0` writable, which is exactly what the assigned UID needs. +**Plain Kubernetes** - set `runAsUser: 1000` (the images' built-in user). For any other UID, add +`runAsGroup: 0` and `fsGroup: 0`. -The bundled proxy is the one exception: Caddy binds `:80`/`:443`, which `restricted-v2` forbids for a -non-root process. Front it with an OpenShift Route (running Caddy on high ports), or grant it an SCC -that permits `NET_BIND_SERVICE`. Ingress-based deployments do not use the bundled proxy. +**OpenShift (`restricted-v2`)** - works out of the box. Don't set `runAsUser`/`runAsGroup`/`fsGroup` +yourself; the SCC assigns an arbitrary UID in group `0`, and the images' writable directories are +group-`0` writable by design. One exception: the bundled proxy binds ports 80/443, which +`restricted-v2` forbids - front it with an OpenShift Route instead. Ingress-based deployments don't +use the bundled proxy. ## Scope of coverage From 6fb3c64be19c75e3ed4782e2e1d3e32d8de116c9 Mon Sep 17 00:00:00 2001 From: Pratapa Lakshmi Date: Wed, 12 Aug 2026 12:47:52 +0530 Subject: [PATCH 08/12] docs(fips): drop the deployment-bundle section until the files are public The bundle files (docker-compose-fips.yml, variables.env, verify-fips.sh) are not yet published anywhere customers can reach, so remove the Deploy section and the verify-fips.sh instructions rather than document files readers can't obtain. The Deploy section returns when the compose file is publicly hosted. Verify is now self-contained docker exec checks (kernel flag, provider active, MD5 refused, Node getFips), and the PLANE_REQUIRE_FIPS safeguard is described as an env var on the images rather than "the shipped Compose file". Co-Authored-By: Claude Opus 4.8 --- docs/self-hosting/methods/fips-deployment.md | 46 ++++++++------------ 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/docs/self-hosting/methods/fips-deployment.md b/docs/self-hosting/methods/fips-deployment.md index 776e48bd..f1b7701c 100644 --- a/docs/self-hosting/methods/fips-deployment.md +++ b/docs/self-hosting/methods/fips-deployment.md @@ -43,7 +43,7 @@ Pin a specific release tag for any accredited deployment rather than tracking `l fixed image version is part of the audit trail. :::info -There is no FIPS All-in-One (AIO) image. The AIO image is built on an Alpine base, which has no FIPS-validated cryptography, so a FIPS deployment uses the multi-container Compose stack below, not the AIO image. +There is no FIPS All-in-One (AIO) image. The AIO image is built on an Alpine base, which has no FIPS-validated cryptography, so a FIPS deployment uses the multi-container stack, not the AIO image. ::: ## Host prerequisite @@ -69,26 +69,11 @@ sudo reboot **Other** - boot a vendor FIPS image (a RHEL FIPS AMI, Ubuntu Pro FIPS), or install OpenShift with FIPS enabled. -As a safeguard, the shipped Compose file sets `PLANE_REQUIRE_FIPS=1`, so the containers **refuse to -start** if the host is not in FIPS mode. Set it to `0` to downgrade that to a startup warning. +As a safeguard, run the FIPS images with `PLANE_REQUIRE_FIPS=1`: the containers then **refuse to +start** if the host is not in FIPS mode. Without it, a FIPS image on a non-FIPS host logs a startup +warning but runs. -## Deploy - -The FIPS deployment bundle ships with every Plane Enterprise FIPS release: - -- `docker-compose-fips.yml` - the FIPS stack -- `variables.env` - environment template -- `verify-fips.sh` - the verification script (see [Verify](#verify)) - -```bash -# 1. Confirm the host is in FIPS mode (above). -# 2. Prepare the environment file. -cp variables.env .env -# Edit at least: DOMAIN_NAME, WEB_URL, SECRET_KEY, MACHINE_SIGNATURE. - -# 3. Bring the stack up. -docker compose -f docker-compose-fips.yml up -d -``` +## Verify Each container logs its posture on startup: @@ -99,15 +84,20 @@ plane: FIPS mode ACTIVE (host kernel reports fips_enabled=1) The Go services (monitor, email, proxy) log a corresponding line, for example `Go FIPS 140-3 module ACTIVE`. -## Verify - -`verify-fips.sh` checks the posture across the running stack - the kernel flag inside each container, that the -validated OpenSSL provider is loaded and active, that a non-approved digest is refused, that Node's -`crypto.getFips()` returns 1, and that the Go services report the module. It is designed to exit -non-zero when a check does not hold, so it can gate a deployment pipeline: +To check a running container directly: ```bash -./verify-fips.sh +# Kernel flag inherited from the host - must print 1 +docker exec plane-api cat /proc/sys/crypto/fips_enabled + +# The FIPS-validated OpenSSL provider must be loaded and "active" +docker exec plane-api openssl list -providers + +# A non-approved digest must be refused - this must FAIL +docker exec plane-api sh -c 'echo x | openssl md5' + +# Node services must report FIPS - must print 1 +docker exec plane-live node -p "require('crypto').getFips()" ``` ## Configuration defaults specific to FIPS images @@ -142,7 +132,7 @@ use the bundled proxy. FIPS-enforcing host. Non-approved algorithms are refused. **The bundled data plane is not FIPS.** The `postgres`, `valkey`, `rabbitmq`, `minio`, and -`iframely` services in the Compose file are upstream Alpine/musl images with no FIPS-validated +`iframely` services in the stack are upstream Alpine/musl images with no FIPS-validated cryptography - there are no FIPS variants of them. They are suitable for evaluation only. For an accreditable deployment, replace them with externally managed datastores on FIPS endpoints and repoint the connection variables: From 1b8b5ce2d47c1d503d7071b1b369fcddcedd52a8 Mon Sep 17 00:00:00 2001 From: Pratapa Lakshmi Date: Wed, 12 Aug 2026 13:38:06 +0530 Subject: [PATCH 09/12] docs(fips): add a Kubernetes deployment section After dropping the Compose bundle section, the page had no deployment path at all - and Kubernetes is the one that is fully self-serve today: the plane-enterprise chart on helm.plane.so and the -fips images on Docker Hub are both public. Add a "Deploy on Kubernetes" section with the three FIPS deltas over a standard install (FIPS node pool with label+taint, -fips image overrides, per-service nodeSelector/toleration - a pod that misses them silently loses FIPS), the proven values overlay, and the helm command matching the standard Kubernetes guide. Verify gains the kubectl exec equivalent of its docker exec checks. Co-Authored-By: Claude Opus 4.8 --- docs/self-hosting/methods/fips-deployment.md | 84 ++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/docs/self-hosting/methods/fips-deployment.md b/docs/self-hosting/methods/fips-deployment.md index f1b7701c..ed9ec90c 100644 --- a/docs/self-hosting/methods/fips-deployment.md +++ b/docs/self-hosting/methods/fips-deployment.md @@ -73,6 +73,87 @@ As a safeguard, run the FIPS images with `PLANE_REQUIRE_FIPS=1`: the containers start** if the host is not in FIPS mode. Without it, a FIPS image on a non-FIPS host logs a startup warning but runs. +## Deploy on Kubernetes + +Use the same `plane-enterprise` Helm chart as a [standard Kubernetes install](/self-hosting/methods/kubernetes) - +FIPS is a values overlay, not a different chart. Three things change: + +1. **Nodes** - provision a node pool whose machine image boots in FIPS mode (see + [Host prerequisite](#host-prerequisite)). Label it (e.g. `fips: enabled`) and taint it (e.g. + `fips=true:NoSchedule`) so only FIPS workloads land there. +2. **Images** - override every service image to its `-fips` variant. +3. **Scheduling** - every service must carry the matching `nodeSelector` and `toleration`. A pod + that misses them schedules onto a stock node and **silently loses FIPS**. + +```yaml +# values-fips.yaml +planeVersion: + +# Non-root with group 0, matching the FIPS images' group-0-writable directories +# (see the non-root section below). +securityContext: + enabled: true + podSecurityContext: + runAsGroup: 0 + fsGroup: 0 + +_fips_sched: &fips + nodeSelector: + fips: enabled + tolerations: + - key: fips + operator: Equal + value: "true" + effect: NoSchedule + +services: + api: + image: makeplane/backend-commercial-fips + <<: *fips + web: + image: makeplane/web-commercial-fips + <<: *fips + space: + image: makeplane/space-commercial-fips + <<: *fips + admin: + image: makeplane/admin-commercial-fips + <<: *fips + live: + image: makeplane/live-commercial-fips + <<: *fips + silo: + image: makeplane/silo-commercial-fips + <<: *fips + monitor: + image: makeplane/monitor-commercial-fips + <<: *fips + worker: + <<: *fips + beatworker: + <<: *fips + # Every additional service you enable (pi, opensearch, the bundled + # datastores, ...) needs the same <<: *fips block. + postgres: + <<: *fips + redis: + <<: *fips + rabbitmq: + <<: *fips + minio: + <<: *fips +``` + +```bash +helm repo add plane https://helm.plane.so/ +helm upgrade --install plane-app plane/plane-enterprise \ + --namespace plane --create-namespace \ + -f values-fips.yaml +``` + +On OpenShift, drop the `securityContext` override and see +[Running under a non-root or arbitrary UID](#running-under-a-non-root-or-arbitrary-uid-openshift). + ## Verify Each container logs its posture on startup: @@ -100,6 +181,9 @@ docker exec plane-api sh -c 'echo x | openssl md5' docker exec plane-live node -p "require('crypto').getFips()" ``` +On Kubernetes, run the same checks with `kubectl exec` against any application pod, e.g. +`kubectl -n plane exec deploy/plane-app-api-wl -- cat /proc/sys/crypto/fips_enabled`. + ## Configuration defaults specific to FIPS images The FIPS images default to a stricter security posture than the standard images. A fresh FIPS From 78f34240adfc346c71515ed421271a2fc1788f71 Mon Sep 17 00:00:00 2001 From: Pratapa Lakshmi Date: Wed, 12 Aug 2026 13:43:03 +0530 Subject: [PATCH 10/12] docs(fips): opensearch is part of the not-FIPS data plane; sharpen pi note Cross-checked against the one live Kubernetes FIPS deployment: the chart's bundled OpenSearch (services.opensearch.local_setup) is a third-party image with no FIPS-validated crypto, but the Scope section's data-plane list and replacement table omitted it. Add it, with Amazon OpenSearch Service as the managed replacement. Also sharpen the values-snippet comment: Plane AI takes its own -fips image override, and its workers need the scheduling block too. Co-Authored-By: Claude Opus 4.8 --- docs/self-hosting/methods/fips-deployment.md | 30 +++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/docs/self-hosting/methods/fips-deployment.md b/docs/self-hosting/methods/fips-deployment.md index ed9ec90c..f9c08d82 100644 --- a/docs/self-hosting/methods/fips-deployment.md +++ b/docs/self-hosting/methods/fips-deployment.md @@ -132,8 +132,9 @@ services: <<: *fips beatworker: <<: *fips - # Every additional service you enable (pi, opensearch, the bundled - # datastores, ...) needs the same <<: *fips block. + # Every additional service you enable needs the same <<: *fips block - + # e.g. Plane AI also takes image: makeplane/plane-pi-commercial-fips, and + # its pi_worker / pi_beat_worker need the block too. postgres: <<: *fips redis: @@ -215,18 +216,19 @@ use the bundled proxy. **Covered.** The Plane application images run their cryptography against FIPS-validated modules on a FIPS-enforcing host. Non-approved algorithms are refused. -**The bundled data plane is not FIPS.** The `postgres`, `valkey`, `rabbitmq`, `minio`, and -`iframely` services in the stack are upstream Alpine/musl images with no FIPS-validated -cryptography - there are no FIPS variants of them. They are suitable for evaluation only. For an -accreditable deployment, replace them with externally managed datastores on FIPS endpoints and -repoint the connection variables: - -| Service | Replace with | Variables | -| ------------- | --------------------------------- | --------------------------------------------- | -| `plane-db` | RDS / Aurora PostgreSQL | `DATABASE_URL`, `PGHOST`, `POSTGRES_*` | -| `plane-redis` | ElastiCache (Valkey/Redis) | `REDIS_URL`, `REDIS_HOST`, `REDIS_PORT` | -| `plane-mq` | Amazon MQ (RabbitMQ) | `AMQP_URL`, `RABBITMQ_*` | -| `plane-minio` | S3 on a FIPS endpoint, or similar | `AWS_S3_ENDPOINT_URL`, `AWS_*`, `USE_MINIO=0` | +**The bundled data plane is not FIPS.** The `postgres`, `valkey`, `rabbitmq`, `minio`, +`opensearch`, and `iframely` services in the stack are upstream third-party images with no +FIPS-validated cryptography - there are no FIPS variants of them. They are suitable for evaluation +only. For an accreditable deployment, replace them with externally managed datastores on FIPS +endpoints and repoint the connection variables: + +| Service | Replace with | Variables | +| ------------- | ------------------------------------- | --------------------------------------------- | +| `plane-db` | RDS / Aurora PostgreSQL | `DATABASE_URL`, `PGHOST`, `POSTGRES_*` | +| `plane-redis` | ElastiCache (Valkey/Redis) | `REDIS_URL`, `REDIS_HOST`, `REDIS_PORT` | +| `plane-mq` | Amazon MQ (RabbitMQ) | `AMQP_URL`, `RABBITMQ_*` | +| `plane-minio` | S3 on a FIPS endpoint, or similar | `AWS_S3_ENDPOINT_URL`, `AWS_*`, `USE_MINIO=0` | +| `opensearch` | Amazon OpenSearch Service, or similar | `OPENSEARCH_URL`, `OPENSEARCH_*` | Then set the corresponding `*_REPLICAS` to `0`, or remove those services, so the bundled ones do not start. From ca799be2153c7baf5d9ed9032ba55302008a0b36 Mon Sep 17 00:00:00 2001 From: Pratapa Lakshmi Date: Wed, 12 Aug 2026 14:31:51 +0530 Subject: [PATCH 11/12] docs(fips): remove the Scope of coverage section Drop the public scope/limitations section (bundled data plane, TLS topology, modules-vs-product disclaimer) per review - the compliance boundary detail moves to sales/assessment conversations rather than the public page. Co-Authored-By: Claude Opus 4.8 --- docs/self-hosting/methods/fips-deployment.md | 33 +------------------- 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/docs/self-hosting/methods/fips-deployment.md b/docs/self-hosting/methods/fips-deployment.md index f9c08d82..d3965d6b 100644 --- a/docs/self-hosting/methods/fips-deployment.md +++ b/docs/self-hosting/methods/fips-deployment.md @@ -1,6 +1,6 @@ --- title: FIPS deployment -description: Deploy the FIPS variant of Plane Enterprise on a FIPS-enforcing host, including prerequisites, image list, verification, and scope of coverage. +description: Deploy the FIPS variant of Plane Enterprise on a FIPS-enforcing host, including prerequisites, image list, and verification. keywords: plane fips, fips 140-3 deployment, plane commercial fips, govcloud plane, federal self-hosting, fips enabled containers head: - - meta @@ -210,34 +210,3 @@ yourself; the SCC assigns an arbitrary UID in group `0`, and the images' writabl group-`0` writable by design. One exception: the bundled proxy binds ports 80/443, which `restricted-v2` forbids - front it with an OpenShift Route instead. Ingress-based deployments don't use the bundled proxy. - -## Scope of coverage - -**Covered.** The Plane application images run their cryptography against FIPS-validated modules on a -FIPS-enforcing host. Non-approved algorithms are refused. - -**The bundled data plane is not FIPS.** The `postgres`, `valkey`, `rabbitmq`, `minio`, -`opensearch`, and `iframely` services in the stack are upstream third-party images with no -FIPS-validated cryptography - there are no FIPS variants of them. They are suitable for evaluation -only. For an accreditable deployment, replace them with externally managed datastores on FIPS -endpoints and repoint the connection variables: - -| Service | Replace with | Variables | -| ------------- | ------------------------------------- | --------------------------------------------- | -| `plane-db` | RDS / Aurora PostgreSQL | `DATABASE_URL`, `PGHOST`, `POSTGRES_*` | -| `plane-redis` | ElastiCache (Valkey/Redis) | `REDIS_URL`, `REDIS_HOST`, `REDIS_PORT` | -| `plane-mq` | Amazon MQ (RabbitMQ) | `AMQP_URL`, `RABBITMQ_*` | -| `plane-minio` | S3 on a FIPS endpoint, or similar | `AWS_S3_ENDPOINT_URL`, `AWS_*`, `USE_MINIO=0` | -| `opensearch` | Amazon OpenSearch Service, or similar | `OPENSEARCH_URL`, `OPENSEARCH_*` | - -Then set the corresponding `*_REPLICAS` to `0`, or remove those services, so the bundled ones do -not start. - -**TLS termination.** The bundled proxy (Caddy) is built against a FIPS-validated module, but for an -accredited topology the recommended pattern is to terminate TLS at a validated endpoint in front of -the deployment - such as a FIPS-enabled load balancer - and have the proxy serve HTTP internally. - -**FIPS validation applies to the cryptographic modules, not to Plane as a product.** FIPS 140-3 -certificates are held by the module vendors (Red Hat and the Go project). This deployment ensures -Plane's cryptography _uses_ those validated modules on a compliant host; it does not make Plane -itself a FIPS-certified product. From 4d3632c272ede13c776460d8c218300e8c5c8619 Mon Sep 17 00:00:00 2001 From: danciaclara Date: Wed, 12 Aug 2026 14:42:45 +0530 Subject: [PATCH 12/12] minor fixes --- docs/self-hosting/methods/fips-deployment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/self-hosting/methods/fips-deployment.md b/docs/self-hosting/methods/fips-deployment.md index d3965d6b..f374714d 100644 --- a/docs/self-hosting/methods/fips-deployment.md +++ b/docs/self-hosting/methods/fips-deployment.md @@ -10,7 +10,7 @@ head: # FIPS deployment -Plane Enterprise publishes a FIPS variant of every application image alongside the standard set. +Plane publishes a FIPS variant of every application image alongside the standard set. These images are built on Red Hat UBI 10, apply the system-wide FIPS cryptographic policy, and run their cryptography against FIPS-validated modules (Red Hat's OpenSSL FIPS provider for the Python and static services; the Go FIPS 140-3 module for the Go services). They are intended for