-
Notifications
You must be signed in to change notification settings - Fork 30
feat(plane-enterprise): support OpenShift's restricted-v2 SCC and Route ingress #289
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
pratapalakshmi
wants to merge
5
commits into
master
Choose a base branch
from
feat/openshift-support
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
2321bbb
feat(plane-enterprise): support OpenShift's restricted-v2 SCC and Rou…
pratapalakshmi 743a25b
fix(plane-enterprise): address CodeRabbit review on the OpenShift sup…
pratapalakshmi 1777720
revert(plane-enterprise): restore the nginx-only gate on templates/in…
pratapalakshmi 3e5f702
docs(plane-enterprise): make securityContext self-documenting in valu…
pratapalakshmi cb4c72a
fix(plane-enterprise): address the remaining CodeRabbit review on Ope…
pratapalakshmi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| # ============================================================================= | ||
| # OpenShift (restricted-v2 SCC) | ||
| # ============================================================================= | ||
| # Overlay these on top of your own values: | ||
| # | ||
| # helm upgrade --install plane-ee plane/plane-enterprise \ | ||
| # -f my-values.yaml -f examples/values-openshift.yaml | ||
| # | ||
| # WHY THIS FILE EXISTS | ||
| # | ||
| # OpenShift's default `restricted-v2` SCC ignores the image's USER and runs every | ||
| # container as an arbitrary UID from the namespace's range, always in group 0. It | ||
| # also validates the pod's own request with MustRunAsRange: a manifest that asks | ||
| # for a *specific* runAsUser or fsGroup outside that range is REJECTED at | ||
| # admission, so `securityContext.enabled: true` with the chart's defaults means no | ||
| # pod ever schedules. | ||
| # | ||
| # The fix is not to disable hardening — it is to keep everything except the IDs | ||
| # and let the platform assign those. Setting a key to `null` in a values file | ||
| # removes it during Helm's coalescing, so the rendered securityContext keeps | ||
| # runAsNonRoot, seccompProfile and the dropped capabilities but carries no UID. | ||
| # | ||
| # REQUIRES the images that grant group 0 write access to their runtime paths | ||
| # (plane-ee #9018). Older images crash under an arbitrary UID — nginx exits with | ||
| # `mkdir() "/var/cache/nginx/client_temp" failed (13: Permission denied)`. | ||
| # ============================================================================= | ||
|
|
||
| securityContext: | ||
| enabled: true | ||
| podSecurityContext: | ||
| runAsUser: null # assigned by the SCC from the namespace's UID range | ||
| runAsGroup: null # the SCC always places the process in group 0 | ||
| fsGroup: null # assigned by the SCC; still applied to PVCs on mount | ||
| containerSecurityContext: | ||
| runAsUser: null | ||
|
|
||
| # ----------------------------------------------------------------------------- | ||
| # Services that pin their own UID outside the block above | ||
| # ----------------------------------------------------------------------------- | ||
| services: | ||
| email_service: | ||
| # The image's app-user is uid 100; let the SCC choose instead. | ||
| runAsUser: null | ||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Bundled datastores MUST be external on OpenShift. | ||
| # | ||
| # postgres / redis / rabbitmq / minio / opensearch are third-party images with | ||
| # baked-in UID and data-directory ownership assumptions. They cannot run under | ||
| # an arbitrary UID, and the chart deliberately does not apply the hardened | ||
| # securityContext to them. Point the chart at managed services (RDS, | ||
| # ElastiCache, Amazon MQ, S3, OpenSearch Service) and leave local_setup off. | ||
| # | ||
| # If you must run them in-cluster, they need a relaxed SCC (e.g. `oc adm | ||
| # policy add-scc-to-user anyuid -z <serviceaccount>`), which defeats the point. | ||
| # --------------------------------------------------------------------------- | ||
| postgres: | ||
| local_setup: false | ||
| redis: | ||
| local_setup: false | ||
| rabbitmq: | ||
| local_setup: false | ||
| minio: | ||
| local_setup: false | ||
| opensearch: | ||
| local_setup: false | ||
|
|
||
| # ----------------------------------------------------------------------------- | ||
| # Ingress | ||
| # ----------------------------------------------------------------------------- | ||
| # 'openshift' renders one route.openshift.io/v1 Route per path, with the HAProxy | ||
| # timeout set explicitly. This is the only OpenShift ingress path the chart offers | ||
| # — 'openshift-default' (letting the ingress-to-route controller convert a plain | ||
| # Ingress) renders nothing, because templates/ingress.yaml is gated on 'nginx'. | ||
| ingress: | ||
| enabled: true | ||
| ingressClass: 'openshift' | ||
| openshift: | ||
| # The router default is 30s, which severs /live/'s collaborative-editing | ||
| # WebSockets and /pi/'s streaming responses. Do not lower this. | ||
| timeout: '300s' | ||
| termination: 'edge' | ||
| insecureEdgeTerminationPolicy: 'Redirect' | ||
| # Leave empty to serve the Ingress Operator's wildcard certificate. | ||
| externalCertificate: '' | ||
|
|
||
| # The router terminates TLS, so the chart's own cert-manager wiring is not used. | ||
| ssl: | ||
| createIssuer: false | ||
| generateCerts: false | ||
|
|
||
| # ----------------------------------------------------------------------------- | ||
| # NOT handled by this file | ||
| # ----------------------------------------------------------------------------- | ||
| # - Traefik's body-size Middleware (ingress.traefik.maxRequestBodyBytes) has NO | ||
| # OpenShift Route equivalent. Enforce upload limits in the app or at a WAF. | ||
| # - A path that needs prefix stripping requires | ||
| # `haproxy.router.openshift.io/rewrite-target` via ingress.openshift.route_annotations. |
117 changes: 117 additions & 0 deletions
117
charts/plane-enterprise/templates/ingress-openshift.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| {{/* | ||
| ================================================================================ | ||
| OpenShift ingress: one route.openshift.io/v1 Route per path. | ||
| ================================================================================ | ||
| Rendered when ingress.ingressClass == "openshift". | ||
|
|
||
| Why explicit Routes rather than a plain Ingress: | ||
|
|
||
| OpenShift's ingress-to-route controller can convert a networking.k8s.io/v1 | ||
| Ingress into Routes. This chart does not offer that path: templates/ingress.yaml | ||
| is gated on ingressClass == "nginx", so setting "openshift-default" renders | ||
| nothing. Declaring the Routes here is also the more predictable option — the | ||
| conversion only picks up an Ingress whose class maps to the | ||
| openshift.io/ingress-to-route controller, and whether per-path HAProxy | ||
| annotations survive it varies by OCP version. Plane needs the timeout below, so | ||
| there is no guesswork this way. | ||
|
|
||
| Differences from the Traefik IngressRoute this mirrors: | ||
|
|
||
| - Ordering is irrelevant. HAProxy matches the most specific path prefix, so | ||
| the "/" catch-all cannot shadow the others the way it would in Traefik. | ||
| - There is NO equivalent of the `<release>-body-limit` Middleware. HAProxy | ||
| Routes have no per-route request-body cap, so the upload limit enforced by | ||
| ingress.traefik.maxRequestBodyBytes is NOT applied here. Enforce it in the | ||
| application or at a WAF/CDN in front of the router. | ||
| - Path-based Routes require edge or reencrypt TLS termination; they are not | ||
| supported with passthrough. | ||
| */}} | ||
| {{- if and .Values.ingress.enabled (eq .Values.ingress.ingressClass "openshift") .Values.license.licenseDomain }} | ||
| {{- $host := .Values.license.licenseDomain }} | ||
| {{- $name := .Release.Name }} | ||
| {{- $oc := .Values.ingress.openshift | default dict }} | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| {{/* Every Route below carries a spec.path, and OpenShift rejects a Route that | ||
| combines a path with passthrough termination -- the router cannot inspect | ||
| the URL of a connection it never decrypts. Caught here rather than at | ||
| admission, so a bad value fails the render instead of half-applying. */}} | ||
| {{- $termination := $oc.termination | default "edge" }} | ||
| {{- if not (has $termination (list "edge" "reencrypt")) }} | ||
| {{- fail (printf "ingress.openshift.termination must be \"edge\" or \"reencrypt\", got %q. Path-based Routes cannot use passthrough termination; see charts/plane-enterprise/README.md." $termination) }} | ||
| {{- end }} | ||
|
|
||
| {{/* Same path -> service mapping as templates/ingress.yaml, most specific first | ||
| (ordering is cosmetic here, kept aligned so the two are easy to diff). */}} | ||
| {{- $routes := list | ||
| (dict "slug" "spaces" "path" "/spaces/" "svc" (printf "%s-space" $name) "port" 3000) | ||
| (dict "slug" "god-mode" "path" "/god-mode/" "svc" (printf "%s-admin" $name) "port" 3000) | ||
| (dict "slug" "api" "path" "/api/" "svc" (printf "%s-api" $name) "port" 8000) | ||
| (dict "slug" "auth" "path" "/auth/" "svc" (printf "%s-api" $name) "port" 8000) | ||
| (dict "slug" "graphql" "path" "/graphql/" "svc" (printf "%s-api" $name) "port" 8000) | ||
| (dict "slug" "marketplace" "path" "/marketplace/" "svc" (printf "%s-api" $name) "port" 8000) | ||
| (dict "slug" "live" "path" "/live/" "svc" (printf "%s-live" $name) "port" 3000) | ||
| }} | ||
| {{- if .Values.services.silo.enabled }} | ||
| {{- $routes = append $routes (dict "slug" "silo" "path" "/silo/" "svc" (printf "%s-silo" $name) "port" 3000) }} | ||
| {{- end }} | ||
| {{- if .Values.services.pi.enabled }} | ||
| {{- $routes = append $routes (dict "slug" "pi" "path" "/pi/" "svc" (printf "%s-pi-api" $name) "port" 8000) }} | ||
| {{- end }} | ||
| {{- if and (eq (include "plane.minioEnabled" .) "true") .Values.env.docstore_bucket }} | ||
| {{- $routes = append $routes (dict "slug" "docstore" "path" (printf "/%s" .Values.env.docstore_bucket) "svc" (printf "%s-minio" $name) "port" 9000) }} | ||
| {{- end }} | ||
| {{/* Catch-all LAST, purely for readability. */}} | ||
| {{- $routes = append $routes (dict "slug" "web" "path" "/" "svc" (printf "%s-web" $name) "port" 3000) }} | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| {{/* The bundled MinIO console and RabbitMQ management UI live on their own hosts, | ||
| matching templates/ingress.yaml. Both are gated on the corresponding | ||
| local_setup, so neither renders in the recommended OpenShift configuration | ||
| (where the bundled datastores are off because they cannot run under an | ||
| arbitrary UID) -- they are here for a cluster that grants those workloads a | ||
| relaxed SCC. */}} | ||
| {{- if and (eq (include "plane.minioEnabled" .) "true") .Values.ingress.minioHost }} | ||
| {{- $routes = append $routes (dict "slug" "minio-console" "host" .Values.ingress.minioHost "path" "/" "svc" (printf "%s-minio" $name) "port" 9090) }} | ||
| {{- end }} | ||
| {{- if and .Values.services.rabbitmq.local_setup .Values.ingress.rabbitmqHost }} | ||
| {{- $routes = append $routes (dict "slug" "rabbitmq-console" "host" .Values.ingress.rabbitmqHost "path" "/" "svc" (printf "%s-rabbitmq" $name) "port" 15672) }} | ||
| {{- end }} | ||
|
|
||
| {{- range $r := $routes }} | ||
| --- | ||
| apiVersion: route.openshift.io/v1 | ||
| kind: Route | ||
| metadata: | ||
| namespace: {{ $.Release.Namespace }} | ||
| name: {{ $name }}-{{ $r.slug }} | ||
| labels: | ||
| {{- include "plane.commonLabels" $ | nindent 4 }} | ||
| annotations: | ||
| {{/* The router's default timeout is 30s, which silently severs /live/'s | ||
| collaborative-editing WebSockets and /pi/'s streaming responses. */}} | ||
| haproxy.router.openshift.io/timeout: {{ $oc.timeout | default "300s" | quote }} | ||
| {{- with $oc.route_annotations }} | ||
| {{- range $key, $value := . }} | ||
| {{ $key }}: {{ $value | quote }} | ||
| {{- end }} | ||
| {{- end }} | ||
| spec: | ||
| host: {{ $r.host | default $host }} | ||
| path: {{ $r.path }} | ||
| to: | ||
| kind: Service | ||
| name: {{ $r.svc }} | ||
| weight: 100 | ||
| port: | ||
| targetPort: {{ $r.port }} | ||
| tls: | ||
| termination: {{ $termination }} | ||
| insecureEdgeTerminationPolicy: {{ $oc.insecureEdgeTerminationPolicy | default "Redirect" }} | ||
| {{- if $oc.externalCertificate }} | ||
| {{/* Serve a cert from a namespace Secret instead of the router's wildcard. | ||
| Requires OpenShift 4.16+ and a RoleBinding granting the router | ||
| permission to read the Secret; leave unset to use the wildcard. */}} | ||
| externalCertificate: | ||
| name: {{ $oc.externalCertificate }} | ||
| {{- end }} | ||
| {{- end }} | ||
| {{- end }} | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.