Skip to content

[linstor-gui] Add package for LINBIT linstor-gui web UI - #2382

Merged
myasnikovdaniil merged 6 commits into
mainfrom
linstor-gui-package
Apr 15, 2026
Merged

myasnikovdaniil merged 6 commits into
mainfrom
linstor-gui-package

Conversation

@myasnikovdaniil

@myasnikovdaniil myasnikovdaniil commented Apr 13, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

Adds a new Cozystack system package linstor-gui that ships LINBIT's LINSTOR web UI (GPL-3.0) so operators can manage nodes, resources, volumes and snapshots from a browser instead of the linstor CLI.

Changes:

  • packages/system/linstor-gui/ — umbrella chart with:
    • images/linstor-gui/Dockerfile builds the image from the upstream pkg.linbit.com tarball (v2.3.0) on top of nginxinc/nginx-unprivileged:1.29-alpine, mirroring the build pattern already used for piraeus-server and linstor-csi.
    • templates/configmap-nginx.yaml — chart-supplied nginx.conf that proxies /v1 and /metrics to linstor-controller.cozy-linstor.svc:3371 over mTLS, using the existing linstor-client-tls secret created by the linstor package.
    • templates/deployment.yaml — read-only rootfs, non-root (UID 101), runAsNonRoot, RuntimeDefault seccomp, no SA token mounted, reloader.stakater.com/auto on the Deployment.
    • templates/service.yaml — ClusterIP only. No Ingress is shipped because the LINSTOR controller API is a privileged cluster-wide storage surface and auth depends on the deployment's OIDC setup; operators wire up ingress + auth explicitly.
    • tests/deployment_test.yaml — helm-unittest covering Service shape, TLS secret mount, securityContext, and nginx proxy+mTLS config.
  • packages/core/platform/sources/linstor-gui.yaml — new PackageSource depending on cozystack.linstor.
  • packages/core/platform/templates/bundles/system.yaml — registers linstor-gui as an optional system package (controlled via bundles.enabledPackages), alongside velero, telepresence, etc.

Why standalone (not a sidecar in the controller pod)

LINBIT's upstream Dockerfile is designed for this standalone reverse-proxy topology, and keeping the UI as its own Deployment means it scales, upgrades, and restarts independently of linstor-controller. The price is the small amount of mTLS config in nginx.conf, all of which lives in the chart's ConfigMap.

Release note

[linstor-gui] Add an opt-in system package that deploys LINBIT's linstor-gui web UI alongside the LINSTOR controller. Enable via `bundles.enabledPackages: [cozystack.linstor-gui]`, then `kubectl -n cozy-linstor port-forward svc/linstor-gui 3373:80` to access.

Test plan

  • make unit-tests (requires helm-unittest plugin) passes on the new tests/deployment_test.yaml
  • make image in packages/system/linstor-gui/ builds and pushes the image
  • Install the package on a dev cluster with LINSTOR already deployed; confirm the pod comes up
  • kubectl -n cozy-linstor port-forward svc/linstor-gui 3373:80 → browser loads the UI at localhost:3373
  • UI lists LINSTOR nodes, storage pools and resources correctly (proves mTLS proxy to /v1 works)
  • Disable via bundles.enabledPackages removal; verify the Package resource is cleaned up

Summary by CodeRabbit

  • New Features

    • LINSTOR GUI added as an optional system package deployable to the cozy-linstor namespace.
    • Web UI serves on port 3373, proxies LINSTOR controller over mTLS, and exposes a /healthz endpoint.
    • Image build and release targets added to produce multi-arch container images.
  • Documentation

    • New README with deployment guidance, connection examples, and configurable options (endpoint, client secret, image, replicas).
  • Tests

    • Helm chart tests validating service, deployment, config and mTLS behavior.

@dosubot dosubot Bot added size/L This PR changes 100-499 lines, ignoring generated files kind/feature Categorizes issue or PR as related to a new feature labels Apr 13, 2026
@coderabbitai

coderabbitai Bot commented Apr 13, 2026 •

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a LINSTOR GUI system package: PackageSource, Helm chart (templates, values, helpers), container image build (Dockerfile, Makefile), Helm tests, README, and bundle inclusion. Deploys an nginx proxy that uses mTLS to forward GUI API requests to the LINSTOR controller.

Changes

Cohort / File(s) Summary
Package Source Definition
packages/core/platform/sources/linstor-gui.yaml
New PackageSource CR cozystack.linstor-gui referencing OCIRepository/cozystack-packages in cozy-system; variant default depends on cozystack.linstor and publishes component linstor-gui.
System Bundle Integration
packages/core/platform/templates/bundles/system.yaml
Inserted cozystack.linstor-gui into optional system packages listing.
Chart metadata, build & docs
packages/system/linstor-gui/Chart.yaml, packages/system/linstor-gui/Makefile, packages/system/linstor-gui/README.md, packages/system/linstor-gui/values.yaml
New chart metadata and values; Makefile for multi-arch image build, tagging and chart-value updates; README documents deployment, mTLS, endpoint and ingress notes.
Container image
packages/system/linstor-gui/images/linstor-gui/Dockerfile
New Dockerfile using nginx-unprivileged base; downloads/extracts linstor-gui tarball (ARG LINSTOR_GUI_VERSION), sets ownership, exposes port 3373 and runs nginx.
Helm templates (manifests)
packages/system/linstor-gui/templates/...
packages/system/linstor-gui/templates/_helpers.tpl, .../configmap-nginx.yaml, .../deployment.yaml, .../service.yaml, .../serviceaccount.yaml
New Helm helpers and templates: nginx ConfigMap with endpoint validation/parsing and mTLS proxy config, Deployment with security contexts, probes, anti-affinity and config checksum annotation, ClusterIP Service, and ServiceAccount (automount disabled).
Tests
packages/system/linstor-gui/tests/deployment_test.yaml
Helmunit tests validating Service port mapping, Deployment mounts and security constraints, nginx config proxy routes and mTLS certificate paths, and dynamic endpoint rendering.

Sequence Diagram

sequenceDiagram
    participant Client as External Client
    participant Nginx as Nginx Proxy (linstor-gui pod)
    participant Controller as LINSTOR Controller REST API
    Client->>Nginx: HTTP request (e.g. /v1/...)
    activate Nginx
    Nginx->>Controller: Establish mTLS connection and forward request
    activate Controller
    Controller-->>Nginx: HTTP response over mTLS
    deactivate Controller
    Nginx-->>Client: HTTP response
    deactivate Nginx
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hopped through charts and Docker mist,

spun an nginx bridge with a tiny twist,
mTLS snug, the GUI waves hello,
controller whispers, responses flow,
I twitch my nose — the pods all go.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title '[linstor-gui] Add package for LINBIT linstor-gui web UI' directly and clearly describes the main change: adding a new linstor-gui package. It is concise, specific, and accurately summarizes the primary objective of the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch linstor-gui-package

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the linstor-gui package, providing a web interface for managing LINSTOR nodes and resources. The implementation includes a hardened Nginx-based container that proxies the LINSTOR REST API using mTLS. Feedback focuses on resolving a group ID inconsistency between the Dockerfile and deployment, reducing duplication in the Nginx configuration by lifting common proxy settings to the server block, and reconsidering the use of the system-cluster-critical priority class for a non-essential management UI.

tar -xzf /tmp/linstor-gui.tar.gz -C /usr/share/nginx/html --strip-components=2 "linstor-gui-${LINSTOR_GUI_VERSION}/dist" && \
rm -f /tmp/linstor-gui.tar.gz && \
apk del curl tar && \
chown -R 101:0 /usr/share/nginx/html

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There is an inconsistency between the group ownership set here (group 0) and the runAsGroup setting in the deployment (group 101). Since the base image nginx-unprivileged uses GID 101 for the nginx user, it is recommended to use group 101 here as well to match the deployment configuration.

    chown -R 101:101 /usr/share/nginx/html

Comment on lines +44 to +74
# Static UI assets
location / {
try_files $uri $uri/ /index.html;
}

# Proxy LINSTOR REST API over mTLS to the controller
location /v1 {
proxy_pass {{ .Values.linstor.endpoint }};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_ssl_certificate /etc/linstor/client/tls.crt;
proxy_ssl_certificate_key /etc/linstor/client/tls.key;
proxy_ssl_trusted_certificate /etc/linstor/client/ca.crt;
proxy_ssl_verify on;
proxy_ssl_verify_depth 2;
proxy_ssl_server_name on;
proxy_ssl_name {{ $endpointHost }};
}

location /metrics {
proxy_pass {{ .Values.linstor.endpoint }};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_ssl_certificate /etc/linstor/client/tls.crt;
proxy_ssl_certificate_key /etc/linstor/client/tls.key;
proxy_ssl_trusted_certificate /etc/linstor/client/ca.crt;
proxy_ssl_verify on;
proxy_ssl_verify_depth 2;
proxy_ssl_server_name on;
proxy_ssl_name {{ $endpointHost }};
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The proxy configuration for /v1 and /metrics contains significant duplication of mTLS and SSL settings. These directives can be moved to the server block level to improve maintainability and reduce the risk of configuration drift between endpoints.

        # Common proxy settings
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_ssl_certificate     /etc/linstor/client/tls.crt;
        proxy_ssl_certificate_key /etc/linstor/client/tls.key;
        proxy_ssl_trusted_certificate /etc/linstor/client/ca.crt;
        proxy_ssl_verify       on;
        proxy_ssl_verify_depth 2;
        proxy_ssl_server_name  on;
        proxy_ssl_name         {{ $endpointHost }};

        # Static UI assets
        location / {
          try_files $uri $uri/ /index.html;
        }

        # Proxy LINSTOR REST API over mTLS to the controller
        location /v1 {
          proxy_pass {{ .Values.linstor.endpoint }};
        }

        location /metrics {
          proxy_pass {{ .Values.linstor.endpoint }};
        }

spec:
serviceAccountName: linstor-gui
automountServiceAccountToken: false
priorityClassName: system-cluster-critical

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The system-cluster-critical priority class is typically reserved for essential cluster components like the LINSTOR controller or CNI plugins. Since linstor-gui is an optional management interface, using such a high priority might lead to the eviction of more critical components during resource pressure. Consider using a lower priority class or omitting it if it's not strictly required for cluster stability.

Ships LINBIT's LINSTOR web UI (GPL-3.0) as an opt-in system package under
packages/system/linstor-gui so operators can manage LINSTOR nodes, resources,
and volumes from a browser instead of the linstor CLI.

The UI is built from the upstream pkg.linbit.com tarball onto an
nginx-unprivileged base image. A chart-supplied nginx.conf proxies /v1 and
/metrics to the LINSTOR controller REST API over mTLS using the existing
linstor-client-tls secret created by the linstor package.

Only a ClusterIP Service is created; no Ingress is shipped, because LINSTOR's
controller API is a privileged cluster-wide storage surface and auth depends
on the deployment's OIDC setup. Operators opt in via bundles.enabledPackages
and wire up ingress + auth themselves (port-forward works out of the box).

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
packages/system/linstor-gui/Makefile (1)

9-32: Add .PHONY declarations for targets.

The image, image-linstor-gui, and test targets are not declared as phony. While this works, declaring them prevents issues if files with these names are ever created.

♻️ Proposed fix to add PHONY declarations
 include ../../../hack/package.mk

 LINSTOR_GUI_VERSION ?= 2.3.0

+.PHONY: image image-linstor-gui test
+
 image: image-linstor-gui

 image-linstor-gui:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/system/linstor-gui/Makefile` around lines 9 - 32, The Makefile is
missing .PHONY declarations for the targets which can cause unexpected behavior
if files named "image", "image-linstor-gui", or "test" appear; add a .PHONY line
listing image, image-linstor-gui, and test (e.g. .PHONY: image image-linstor-gui
test) near the top of the Makefile before the rules so the Makefile treats these
as phony targets rather than files.
packages/system/linstor-gui/images/linstor-gui/Dockerfile (1)

9-13: Consider adding checksum verification for supply chain security.

The tarball is downloaded from an external source without integrity verification. Adding a checksum validation step would strengthen the supply chain security of the build.

🛡️ Proposed approach for checksum verification
 ARG LINSTOR_GUI_VERSION
+ARG LINSTOR_GUI_SHA256
 USER root
 RUN apk add --no-cache curl tar && \
     curl -fsSL "https://pkg.linbit.com/downloads/linstor/linstor-gui-${LINSTOR_GUI_VERSION}.tar.gz" -o /tmp/linstor-gui.tar.gz && \
+    echo "${LINSTOR_GUI_SHA256}  /tmp/linstor-gui.tar.gz" | sha256sum -c - && \
     mkdir -p /usr/share/nginx/html && \

The checksum should be maintained alongside LINSTOR_GUI_VERSION in the Makefile and passed as a build arg.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/system/linstor-gui/images/linstor-gui/Dockerfile` around lines 9 -
13, Add SHA256 checksum verification for the downloaded tarball: introduce a
build-arg (e.g., LINSTOR_GUI_SHA256) alongside LINSTOR_GUI_VERSION (maintained
in the Makefile), pass it into the Docker build, and in the Dockerfile's RUN
sequence (the lines that curl the tarball and extract it) compute the tarball's
sha256 and compare it to LINSTOR_GUI_SHA256 (fail/exit non-zero on mismatch)
before extracting; ensure the comparison uses a reliable tool (sha256sum or
sha256sum -c via an echo "checksum  filename" | sha256sum -c -) and remove the
tarball on failure to avoid continuing with an unverified artifact.
packages/system/linstor-gui/templates/deployment.yaml (1)

28-28: Consider lowering priorityClassName for a non-critical UI component.

system-cluster-critical is typically reserved for core cluster components whose failure would impact cluster operation. The LINSTOR GUI is an optional management interface - operators can still use the CLI if the GUI is unavailable. Consider using a lower priority or removing this setting to allow proper pod eviction during resource pressure.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/system/linstor-gui/templates/deployment.yaml` at line 28, The
deployment template sets priorityClassName to the heavyweight
"system-cluster-critical" for the LINSTOR GUI; remove that setting or replace
"priorityClassName: system-cluster-critical" with a lower/non-critical class (or
omit the priorityClassName key entirely) in the Deployment template so the GUI
pods can be evicted under pressure; update the templates/deployment.yaml
manifest where the priorityClassName key appears in the LINSTOR GUI Deployment
spec.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/system/linstor-gui/Chart.yaml`:
- Around line 1-3: Convert this standalone Helm chart into the repository
umbrella-chart pattern: rename or update the Chart.yaml for the umbrella chart
(keeping name "cozy-linstor-gui") and add a charts/ directory containing the
vendored upstream chart(s) for linstor; add a dependencies section in Chart.yaml
(or Chart.lock) pointing to the vendored chart names and ensure values.yaml
merges/forwards any required subchart values to the vendored chart; remove
standalone upstream layout files from the root and ensure templates in the
umbrella chart only include wrapper templates or hooks that reference the
vendored charts under charts/ so the package conforms to the packages/system/**
umbrella pattern.

In `@packages/system/linstor-gui/templates/configmap-nginx.yaml`:
- Around line 6-7: Add a pre-check in the Helm template before the existing
trimming logic to ensure .Values.linstor.endpoint starts with "https://"; if it
does not, call the Helm fail function with a clear message so rendering aborts
(e.g. use if not (hasPrefix "https://" .Values.linstor.endpoint) then fail
"linstor.endpoint must start with 'https://'"). Update the same check for the
other occurrences referenced (lines 51-74) so all places that compute
$endpoint/$endpointHost validate .Values.linstor.endpoint first; keep the
existing trimPrefix/splitList logic unchanged after the validation.

---

Nitpick comments:
In `@packages/system/linstor-gui/images/linstor-gui/Dockerfile`:
- Around line 9-13: Add SHA256 checksum verification for the downloaded tarball:
introduce a build-arg (e.g., LINSTOR_GUI_SHA256) alongside LINSTOR_GUI_VERSION
(maintained in the Makefile), pass it into the Docker build, and in the
Dockerfile's RUN sequence (the lines that curl the tarball and extract it)
compute the tarball's sha256 and compare it to LINSTOR_GUI_SHA256 (fail/exit
non-zero on mismatch) before extracting; ensure the comparison uses a reliable
tool (sha256sum or sha256sum -c via an echo "checksum  filename" | sha256sum -c
-) and remove the tarball on failure to avoid continuing with an unverified
artifact.

In `@packages/system/linstor-gui/Makefile`:
- Around line 9-32: The Makefile is missing .PHONY declarations for the targets
which can cause unexpected behavior if files named "image", "image-linstor-gui",
or "test" appear; add a .PHONY line listing image, image-linstor-gui, and test
(e.g. .PHONY: image image-linstor-gui test) near the top of the Makefile before
the rules so the Makefile treats these as phony targets rather than files.

In `@packages/system/linstor-gui/templates/deployment.yaml`:
- Line 28: The deployment template sets priorityClassName to the heavyweight
"system-cluster-critical" for the LINSTOR GUI; remove that setting or replace
"priorityClassName: system-cluster-critical" with a lower/non-critical class (or
omit the priorityClassName key entirely) in the Deployment template so the GUI
pods can be evicted under pressure; update the templates/deployment.yaml
manifest where the priorityClassName key appears in the LINSTOR GUI Deployment
spec.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c9e0a0ba-0817-4a30-bdba-a9468065466f

📥 Commits

Reviewing files that changed from the base of the PR and between 1436fee and a82dd25.

📒 Files selected for processing (13)
  • packages/core/platform/sources/linstor-gui.yaml
  • packages/core/platform/templates/bundles/system.yaml
  • packages/system/linstor-gui/Chart.yaml
  • packages/system/linstor-gui/Makefile
  • packages/system/linstor-gui/README.md
  • packages/system/linstor-gui/images/linstor-gui/Dockerfile
  • packages/system/linstor-gui/templates/_helpers.tpl
  • packages/system/linstor-gui/templates/configmap-nginx.yaml
  • packages/system/linstor-gui/templates/deployment.yaml
  • packages/system/linstor-gui/templates/service.yaml
  • packages/system/linstor-gui/templates/serviceaccount.yaml
  • packages/system/linstor-gui/tests/deployment_test.yaml
  • packages/system/linstor-gui/values.yaml

Comment on lines +1 to +3
apiVersion: v2
name: cozy-linstor-gui
version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Align this package to the repository umbrella-chart pattern.

This new system package is introduced as a standalone chart layout; repository rules require the umbrella pattern with vendored upstream charts under charts/. Please restructure before merge to avoid drifting from the platform packaging standard.
As per coding guidelines: "Applies to packages/system/**: Use Helm Chart umbrella pattern with vendored upstream charts in charts/ directory".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/system/linstor-gui/Chart.yaml` around lines 1 - 3, Convert this
standalone Helm chart into the repository umbrella-chart pattern: rename or
update the Chart.yaml for the umbrella chart (keeping name "cozy-linstor-gui")
and add a charts/ directory containing the vendored upstream chart(s) for
linstor; add a dependencies section in Chart.yaml (or Chart.lock) pointing to
the vendored chart names and ensure values.yaml merges/forwards any required
subchart values to the vendored chart; remove standalone upstream layout files
from the root and ensure templates in the umbrella chart only include wrapper
templates or hooks that reference the vendored charts under charts/ so the
package conforms to the packages/system/** umbrella pattern.

Comment thread packages/system/linstor-gui/templates/configmap-nginx.yaml Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
packages/system/linstor-gui/Makefile (1)

9-32: Consider adding .PHONY declarations for non-file targets.

The image, image-linstor-gui, and test targets don't produce files with those names, so declaring them as .PHONY prevents issues if files with those names are ever created and ensures they always run.

🔧 Proposed fix
+.PHONY: image image-linstor-gui test
+
 image: image-linstor-gui

 image-linstor-gui:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/system/linstor-gui/Makefile` around lines 9 - 32, Add a .PHONY
declaration for the non-file Makefile targets to ensure they always run: declare
.PHONY for the targets image, image-linstor-gui, and test in the Makefile (e.g.,
add a line like ".PHONY: image image-linstor-gui test" near the top or before
the target definitions) so these names are treated as phony targets rather than
real files.
packages/system/linstor-gui/templates/configmap-nginx.yaml (1)

50-61: Consider extracting repeated mTLS proxy directives into a shared snippet.

The /v1 and /metrics locations duplicate the same 8 proxy_ssl_* directives. While functional, this increases maintenance burden if TLS settings need adjustment.

Also applies to: 63-74

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/system/linstor-gui/templates/configmap-nginx.yaml` around lines 50 -
61, The nginx config duplicates the same mTLS proxy directives in the location
blocks (e.g., location /v1 and location /metrics) — extract the repeated
proxy_ssl_* lines (proxy_ssl_certificate, proxy_ssl_certificate_key,
proxy_ssl_trusted_certificate, proxy_ssl_verify, proxy_ssl_verify_depth,
proxy_ssl_server_name, proxy_ssl_name, etc.) into a reusable template/snippet
(e.g., define a Helm template or nginx include named something like
"linstor-mtls-proxy") and replace the repeated blocks with a single call to that
snippet, preserving interpolation of {{ $endpointHost }} and any existing
proxy_pass/headers in the location blocks so TLS settings are managed in one
place.
packages/system/linstor-gui/templates/deployment.yaml (1)

7-8: Redundant config reload mechanisms.

Both reloader.stakater.com/auto: "true" (line 8) and checksum/nginx-config (line 22) trigger pod restarts on ConfigMap changes. The reloader annotation automatically watches mounted ConfigMaps, making the manual checksum annotation unnecessary when Stakater Reloader is deployed.

Consider keeping only one approach for clarity. The Reloader annotation is simpler but requires the operator to be installed; the checksum is self-contained.

Also applies to: 21-22

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/system/linstor-gui/templates/deployment.yaml` around lines 7 - 8,
The deployment currently uses two restart mechanisms: the annotation
reloader.stakater.com/auto: "true" on the Deployment and the manual
checksum/nginx-config Pod annotation; remove the redundant approach by choosing
one strategy—prefer keeping reloader.stakater.com/auto and delete the
checksum/nginx-config annotation (or vice versa if you require self-contained
behavior) so only a single ConfigMap-watching restart mechanism remains; update
the templates/deployment.yaml to remove the unused checksum/nginx-config entry
(or remove the reloader annotation) and ensure any references to checksum-based
rolling restarts (e.g., label or annotation generation code that sets
checksum/nginx-config) are also cleaned up.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/system/linstor-gui/templates/configmap-nginx.yaml`:
- Around line 76-80: The location block for health checks uses "return 200
'ok';" but "add_header Content-Type" placed after return will never be sent;
update the location = /healthz block to set the response content type via
nginx's default_type (or otherwise emit the body with the desired Content-Type)
instead of relying on add_header after return—modify the location = /healthz
block (look for the add_header and return directives) to use default_type
text/plain (or an equivalent approach that sets Content-Type before returning
the body).

---

Nitpick comments:
In `@packages/system/linstor-gui/Makefile`:
- Around line 9-32: Add a .PHONY declaration for the non-file Makefile targets
to ensure they always run: declare .PHONY for the targets image,
image-linstor-gui, and test in the Makefile (e.g., add a line like ".PHONY:
image image-linstor-gui test" near the top or before the target definitions) so
these names are treated as phony targets rather than real files.

In `@packages/system/linstor-gui/templates/configmap-nginx.yaml`:
- Around line 50-61: The nginx config duplicates the same mTLS proxy directives
in the location blocks (e.g., location /v1 and location /metrics) — extract the
repeated proxy_ssl_* lines (proxy_ssl_certificate, proxy_ssl_certificate_key,
proxy_ssl_trusted_certificate, proxy_ssl_verify, proxy_ssl_verify_depth,
proxy_ssl_server_name, proxy_ssl_name, etc.) into a reusable template/snippet
(e.g., define a Helm template or nginx include named something like
"linstor-mtls-proxy") and replace the repeated blocks with a single call to that
snippet, preserving interpolation of {{ $endpointHost }} and any existing
proxy_pass/headers in the location blocks so TLS settings are managed in one
place.

In `@packages/system/linstor-gui/templates/deployment.yaml`:
- Around line 7-8: The deployment currently uses two restart mechanisms: the
annotation reloader.stakater.com/auto: "true" on the Deployment and the manual
checksum/nginx-config Pod annotation; remove the redundant approach by choosing
one strategy—prefer keeping reloader.stakater.com/auto and delete the
checksum/nginx-config annotation (or vice versa if you require self-contained
behavior) so only a single ConfigMap-watching restart mechanism remains; update
the templates/deployment.yaml to remove the unused checksum/nginx-config entry
(or remove the reloader annotation) and ensure any references to checksum-based
rolling restarts (e.g., label or annotation generation code that sets
checksum/nginx-config) are also cleaned up.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fdd0e81c-9e78-44b4-abd9-ae8a09e22f4f

📥 Commits

Reviewing files that changed from the base of the PR and between a82dd25 and ae88fe3.

📒 Files selected for processing (13)
  • packages/core/platform/sources/linstor-gui.yaml
  • packages/core/platform/templates/bundles/system.yaml
  • packages/system/linstor-gui/Chart.yaml
  • packages/system/linstor-gui/Makefile
  • packages/system/linstor-gui/README.md
  • packages/system/linstor-gui/images/linstor-gui/Dockerfile
  • packages/system/linstor-gui/templates/_helpers.tpl
  • packages/system/linstor-gui/templates/configmap-nginx.yaml
  • packages/system/linstor-gui/templates/deployment.yaml
  • packages/system/linstor-gui/templates/service.yaml
  • packages/system/linstor-gui/templates/serviceaccount.yaml
  • packages/system/linstor-gui/tests/deployment_test.yaml
  • packages/system/linstor-gui/values.yaml
✅ Files skipped from review due to trivial changes (8)
  • packages/system/linstor-gui/Chart.yaml
  • packages/core/platform/templates/bundles/system.yaml
  • packages/system/linstor-gui/values.yaml
  • packages/system/linstor-gui/README.md
  • packages/system/linstor-gui/templates/_helpers.tpl
  • packages/core/platform/sources/linstor-gui.yaml
  • packages/system/linstor-gui/images/linstor-gui/Dockerfile
  • packages/system/linstor-gui/tests/deployment_test.yaml

Comment thread packages/system/linstor-gui/templates/configmap-nginx.yaml
- Dockerfile: chown html root to 101:101 (match runAsGroup, not 101:0)
- Makefile: declare .PHONY targets (image, image-linstor-gui, test)
- deployment: drop priorityClassName system-cluster-critical (GUI is
  not control-plane critical; chart does not need to reserve the slot)
- configmap-nginx:
  - fail the render if linstor.endpoint is not https:// so a misconfig
    cannot silently downgrade the mTLS-protected controller path
  - hoist proxy_set_header and proxy_ssl_* directives to the server
    block instead of duplicating across /v1 and /metrics locations

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
@myasnikovdaniil

Copy link
Copy Markdown
Contributor Author

Pushed ba9f9e9 addressing review feedback.

Applied:

  • Dockerfile: chown -R 101:101 (was 101:0) — matches runAsGroup: 101 in the deployment securityContext
  • Makefile: added .PHONY: image image-linstor-gui test
  • templates/deployment.yaml: dropped priorityClassName: system-cluster-critical — this is a UI, not control-plane critical, and should not reserve a high-priority slot
  • templates/configmap-nginx.yaml:
    • fail the render if linstor.endpoint is not https:// so a misconfig cannot silently downgrade the mTLS-protected controller path
    • hoisted proxy_set_header and proxy_ssl_* directives to the server block instead of duplicating them across /v1 and /metrics

Skipped (with reasoning):

  • "Switch to umbrella chart with vendored upstream chart" — LINBIT does not publish a Helm chart for linstor-gui; only a static tarball at pkg.linbit.com. The neighboring packages/system/linstor package follows the same plain-templates/ approach for its own self-built images (piraeus-server, linstor-csi). Nothing to vendor here.
  • "Add SHA256 checksum verification for the downloaded tarball" — consistent with the rest of the repo: the existing packages/system/linstor/images/piraeus-server/Dockerfile and images/linstor-csi/Dockerfile also fetch from pkg.linbit.com without checksum pinning. Happy to add it here if we want to adopt the pattern repo-wide as a follow-up.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
packages/system/linstor-gui/templates/configmap-nginx.yaml (1)

72-76: ⚠️ Potential issue | 🟡 Minor

Content-Type header is not applied in /healthz due to return ordering.

On Line 74, return finalizes the response; Line 75 add_header is ineffective in this block.

🔧 Proposed fix
         location = /healthz {
           access_log off;
+          default_type text/plain;
           return 200 'ok';
-          add_header Content-Type text/plain;
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/system/linstor-gui/templates/configmap-nginx.yaml` around lines 72 -
76, The Content-Type header in the location = /healthz block is not applied
because the return directive finalizes the response before add_header runs; move
the add_header directive (use add_header Content-Type text/plain always;) above
the return in the location = /healthz block (refer to the location = /healthz
block to find and update the lines), keeping access_log off and the same return
body.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/system/linstor-gui/templates/deployment.yaml`:
- Around line 1-100: The deployment manifest in templates/deployment.yaml
violates the repo rule to use an umbrella Helm chart with a vendored upstream
chart; instead of owning the primary workload here, vendor the upstream
linstor-gui chart under charts/ and convert this package into an umbrella that
declares that chart as a dependency. Specifically: remove the full Deployment
from templates/, add the upstream chart tarball or folder to charts/ (vendored),
update Chart.yaml to list the vendored linstor-gui as a dependency and move only
package-specific overrides into values.yaml (and any small wrapper templates
like configmap checksums if needed), and ensure templates/ contains only
umbrella-specific resources (values overrides, NOTES, RBAC/serviceaccount if
truly required). Update references to .Values.image,
.Values.linstor.clientSecret, and the checksum template usage to be provided via
values.yaml so the vendored chart uses them.
- Around line 72-73: The TLS file permission blocks are incompatible with the
pod running as runAsUser/runAsGroup 101 because secret files are mounted with
defaultMode: 0400 and owned by root:root, so add an fsGroup (e.g., fsGroup: 101)
to the pod/securityContext so the non-root process can read the certs used by
nginx proxy_ssl_certificate directives; additionally refactor this package to
follow the repository Helm umbrella pattern by moving the chart into a charts/
subdirectory and vendoring the upstream linstor-gui chart there (matching other
packages), updating Chart.yaml/values and any chart references to reflect the
umbrella layout.

---

Duplicate comments:
In `@packages/system/linstor-gui/templates/configmap-nginx.yaml`:
- Around line 72-76: The Content-Type header in the location = /healthz block is
not applied because the return directive finalizes the response before
add_header runs; move the add_header directive (use add_header Content-Type
text/plain always;) above the return in the location = /healthz block (refer to
the location = /healthz block to find and update the lines), keeping access_log
off and the same return body.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3da63e12-011e-4def-9643-ec389f46e324

📥 Commits

Reviewing files that changed from the base of the PR and between ae88fe3 and ba9f9e9.

📒 Files selected for processing (4)
  • packages/system/linstor-gui/Makefile
  • packages/system/linstor-gui/images/linstor-gui/Dockerfile
  • packages/system/linstor-gui/templates/configmap-nginx.yaml
  • packages/system/linstor-gui/templates/deployment.yaml
✅ Files skipped from review due to trivial changes (1)
  • packages/system/linstor-gui/images/linstor-gui/Dockerfile

Comment on lines +1 to +100
apiVersion: apps/v1
kind: Deployment
metadata:
name: linstor-gui
labels:
{{- include "linstor-gui.labels" . | nindent 4 }}
annotations:
reloader.stakater.com/auto: "true"
spec:
replicas: {{ .Values.replicas }}
selector:
matchLabels:
{{- include "linstor-gui.selectorLabels" . | nindent 6 }}
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
template:
metadata:
annotations:
checksum/nginx-config: {{ include (print $.Template.BasePath "/configmap-nginx.yaml") . | sha256sum }}
labels:
{{- include "linstor-gui.labels" . | nindent 8 }}
spec:
serviceAccountName: linstor-gui
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
{{- include "linstor-gui.selectorLabels" . | nindent 20 }}
topologyKey: kubernetes.io/hostname
containers:
- name: linstor-gui
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 3373
protocol: TCP
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 5
periodSeconds: 20
readinessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 2
periodSeconds: 10
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 101
runAsGroup: 101
capabilities:
drop:
- ALL
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
readOnly: true
- name: linstor-client-tls
mountPath: /etc/linstor/client
readOnly: true
- name: tmp
mountPath: /tmp
- name: nginx-cache
mountPath: /var/cache/nginx
volumes:
- name: nginx-config
configMap:
name: linstor-gui-nginx
- name: linstor-client-tls
secret:
secretName: {{ .Values.linstor.clientSecret }}
defaultMode: 0400
- name: tmp
emptyDir: {}
- name: nginx-cache
emptyDir: {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Package structure should follow the repository’s system-package umbrella chart rule.

This file implements the workload directly in templates/; this package should be structured as an umbrella chart consuming a vendored upstream chart in charts/ instead of owning primary workload manifests here.

As per coding guidelines: packages/system/**: "Use Helm Chart umbrella pattern with vendored upstream charts in charts/ directory".

🧰 Tools
🪛 YAMLlint (1.38.0)

[error] 6-6: syntax error: expected the node content, but found '-'

(syntax)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/system/linstor-gui/templates/deployment.yaml` around lines 1 - 100,
The deployment manifest in templates/deployment.yaml violates the repo rule to
use an umbrella Helm chart with a vendored upstream chart; instead of owning the
primary workload here, vendor the upstream linstor-gui chart under charts/ and
convert this package into an umbrella that declares that chart as a dependency.
Specifically: remove the full Deployment from templates/, add the upstream chart
tarball or folder to charts/ (vendored), update Chart.yaml to list the vendored
linstor-gui as a dependency and move only package-specific overrides into
values.yaml (and any small wrapper templates like configmap checksums if
needed), and ensure templates/ contains only umbrella-specific resources (values
overrides, NOTES, RBAC/serviceaccount if truly required). Update references to
.Values.image, .Values.linstor.clientSecret, and the checksum template usage to
be provided via values.yaml so the vendored chart uses them.

Comment thread packages/system/linstor-gui/templates/deployment.yaml
The linstor-client-tls secret was mounted with defaultMode 0400 and
owned by root, so the nginx process (UID 101) got EACCES on tls.crt
and crash-looped with:

  [emerg] cannot load certificate "/etc/linstor/client/tls.crt":
  BIO_new_file() failed ... Permission denied

Set defaultMode to 0444. The secret volume is pod-local and
readOnlyRootFilesystem is on, so making it world-readable inside the
pod is not a broader disclosure.

Verified on dev10: pod Ready, /healthz 200, /v1/nodes proxied through
mTLS to linstor-controller:3371 returns real node data.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
packages/system/linstor-gui/templates/deployment.yaml (1)

1-102: 🛠️ Refactor suggestion | 🟠 Major

Use umbrella + vendored-chart structure for this system package.

This package still owns the primary workload manifest directly in templates/, which conflicts with the repository rule for packages/system/**.

As per coding guidelines: packages/system/**: "Use Helm Chart umbrella pattern with vendored upstream charts in charts/ directory".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/system/linstor-gui/templates/deployment.yaml` around lines 1 - 102,
The deployment manifest (Deployment named linstor-gui, container name
linstor-gui, etc.) lives directly under templates/ which violates the
packages/system umbrella+vendored-chart rule; fix by converting this package
into an umbrella chart that vendors the workload as a subchart: create a
charts/linstor-gui subchart (Chart.yaml, values.yaml, templates/) and move the
existing deployment.yaml contents into that subchart's templates, update the
umbrella Chart.yaml to declare the linstor-gui chart as a dependency and mirror
any values used (.Values.image, .Values.replicas, .Values.linstor.clientSecret)
into the umbrella values.yaml, then remove the original
templates/deployment.yaml so the vendored subchart owns the Deployment manifest.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@packages/system/linstor-gui/templates/deployment.yaml`:
- Around line 1-102: The deployment manifest (Deployment named linstor-gui,
container name linstor-gui, etc.) lives directly under templates/ which violates
the packages/system umbrella+vendored-chart rule; fix by converting this package
into an umbrella chart that vendors the workload as a subchart: create a
charts/linstor-gui subchart (Chart.yaml, values.yaml, templates/) and move the
existing deployment.yaml contents into that subchart's templates, update the
umbrella Chart.yaml to declare the linstor-gui chart as a dependency and mirror
any values used (.Values.image, .Values.replicas, .Values.linstor.clientSecret)
into the umbrella values.yaml, then remove the original
templates/deployment.yaml so the vendored subchart owns the Deployment manifest.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a8fff507-171b-4f1d-8023-3b119aa27c9f

📥 Commits

Reviewing files that changed from the base of the PR and between ba9f9e9 and 4b76a93.

📒 Files selected for processing (1)
  • packages/system/linstor-gui/templates/deployment.yaml

Follow-up to 4b76a93: the assertion still expected defaultMode 0400
and would fail in CI.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
myasnikovdaniil added a commit that referenced this pull request Apr 13, 2026
Follow-up to the initial linstor-gui package (#2382). Publishes the UI on
https://linstor-gui.<root-host> behind the cluster Keycloak realm using
the same oauth2-proxy gatekeeper pattern that dashboard uses, so the
LINSTOR REST API is never exposed unauthenticated through Ingress.

Rendered only when both gates are satisfied:
  * _cluster.oidc-enabled == "true"
  * "linstor-gui" is listed in _cluster.expose-services
    (i.e. in publishing.exposedServices in the core cozystack values).

If OIDC is disabled cluster-wide we intentionally do not ship an
unauthenticated token-proxy fallback — unlike dashboard, there is no
reason to front a raw storage-management API with k8s bearer tokens.
Operators can still reach the UI via `kubectl port-forward` to the
ClusterIP service.

New resources:
  * templates/gatekeeper.yaml       — oauth2-proxy Deployment (OIDC mode)
  * templates/gatekeeper-svc.yaml   — ClusterIP :8000 in front of it
  * templates/gatekeeper-sa.yaml    — dedicated SA, no autotoken
  * templates/keycloakclient.yaml   — persistent client + cookie
                                      Secrets, KeycloakClient CRD with
                                      redirectUris for /oauth2/callback
  * templates/ingress.yaml          — Ingress to the gatekeeper Service,
                                      cert-manager ClusterIssuer, gated
                                      on expose-services + oidc-enabled
  * tests/ingress_auth_test.yaml    — unit tests for each conditional
                                      branch and the client CRD

README.md updated with both Option 1 (Keycloak-protected Ingress) and
the existing Option 2 (port-forward).

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
…tation

- Fix /healthz Content-Type header: nginx silently drops `add_header`
  after `return` because the response is already finalized. Switch to
  `default_type text/plain;` placed BEFORE the `return 200` so the
  probe response is actually labeled as text/plain.
- Drop the redundant `checksum/nginx-config` pod-template annotation:
  the deployment already carries `reloader.stakater.com/auto: "true"`,
  so Stakater Reloader handles ConfigMap rollouts. Matches the sister
  `linstor` package convention.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
@myasnikovdaniil

Copy link
Copy Markdown
Contributor Author

Reviewed all bot comments on this PR. Status:

Addressed in 727401c (this commit):

  • ✅ /healthz add_header after return is a no-op — fixed via default_type text/plain; placed before return 200.
  • ✅ Redundant checksum/nginx-config annotation dropped — Stakater Reloader (reloader.stakater.com/auto: "true") already triggers rollouts when the ConfigMap changes, matching the sister linstor package convention.

Already addressed in earlier commits on this branch:

  • HTTPS enforcement on linstor.endpoint (ba9f9e9c) — fails at template render if not https://.
  • TLS secret readability for non-root nginx (bbeaaf3) — defaultMode: 0444 makes the mounted client keypair world-readable inside the pod, so UID 101 nginx can load it without needing fsGroup.
  • Dockerfile chown -R 101:101 /usr/share/nginx/html already matches the deployment's runAsUser/runAsGroup: 101.
  • Common proxy/mTLS settings already hoisted to the server block (no per-location duplication).
  • No priorityClassName is set, so the system-cluster-critical concern is moot.

Deliberately not addressed:

  • Umbrella-chart restructuring (coderabbit): there is no upstream Helm chart for linstor-gui — LINBIT only publishes a pre-built tarball at pkg.linbit.com/downloads/linstor/linstor-gui-*.tar.gz. The package builds a hardened nginx image around that tarball (see images/linstor-gui/Dockerfile) and supplies first-party nginx config + Deployment to expose it. There is nothing meaningful to vendor under charts/. Forcing an empty umbrella here would be cargo-cult; the chart is intentionally first-party.

The matchRegex assertion used 5 literal spaces but the template
renders 9 (aligned with proxy_ssl_trusted_certificate). Use \s+
so the test is resilient to alignment changes.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
myasnikovdaniil added a commit that referenced this pull request Apr 14, 2026
Follow-up to the initial linstor-gui package (#2382). Publishes the UI on
https://linstor-gui.<root-host> behind the cluster Keycloak realm using
the same oauth2-proxy gatekeeper pattern that dashboard uses, so the
LINSTOR REST API is never exposed unauthenticated through Ingress.

Rendered only when both gates are satisfied:
  * _cluster.oidc-enabled == "true"
  * "linstor-gui" is listed in _cluster.expose-services
    (i.e. in publishing.exposedServices in the core cozystack values).

If OIDC is disabled cluster-wide we intentionally do not ship an
unauthenticated token-proxy fallback — unlike dashboard, there is no
reason to front a raw storage-management API with k8s bearer tokens.
Operators can still reach the UI via `kubectl port-forward` to the
ClusterIP service.

New resources:
  * templates/gatekeeper.yaml       — oauth2-proxy Deployment (OIDC mode)
  * templates/gatekeeper-svc.yaml   — ClusterIP :8000 in front of it
  * templates/gatekeeper-sa.yaml    — dedicated SA, no autotoken
  * templates/keycloakclient.yaml   — persistent client + cookie
                                      Secrets, KeycloakClient CRD with
                                      redirectUris for /oauth2/callback
  * templates/ingress.yaml          — Ingress to the gatekeeper Service,
                                      cert-manager ClusterIssuer, gated
                                      on expose-services + oidc-enabled
  * tests/ingress_auth_test.yaml    — unit tests for each conditional
                                      branch and the client CRD

README.md updated with both Option 1 (Keycloak-protected Ingress) and
the existing Option 2 (port-forward).

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Well-structured chart with proper security hardening (read-only rootfs, non-root UID 101, dropped capabilities, RuntimeDefault seccomp, no SA token), correct mTLS proxy to the LINSTOR controller, HTTPS-only validation via fail, and comprehensive helm-unittest coverage. The PackageSource correctly depends on cozystack.linstor and the bundle registration follows the optional package pattern. Bot comments have been addressed by the author.

Non-blocking action items for follow-up

A1: Missing /api/v2 proxy route for optional LINSTOR Gateway

The upstream nginx.conf.template includes a location /api/v2 block for the optional LINSTOR Gateway service (iSCSI/NFS/NVMe management). The GUI gracefully degrades when Gateway is unavailable, so this is not a bug — but if LINSTOR Gateway is later deployed, this chart would need updating. Consider adding a commented-out or conditionally-enabled /api/v2 location block.

A2: Makefile does not use shared $(BUILDX_ARGS) macro

Most system packages use $(BUILDX_ARGS) from hack/common-envs.mk for centralized build flags. This Makefile specifies them manually (same as dashboard). Not a bug, but diverges from the common pattern and makes future build system changes harder to propagate.

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Apr 15, 2026
@myasnikovdaniil
myasnikovdaniil merged commit c6739cf into main Apr 15, 2026
9 checks passed
@myasnikovdaniil
myasnikovdaniil deleted the linstor-gui-package branch April 15, 2026 12:32
myasnikovdaniil added a commit that referenced this pull request Apr 15, 2026
## What this PR does

Stacked on top of #2382. Publishes the linstor-gui UI on
`https://linstor-gui.<root-host>` behind the cluster Keycloak realm
using the same oauth2-proxy gatekeeper pattern that the dashboard uses,
so the LINSTOR REST API is never exposed unauthenticated through
Ingress.

The Ingress is rendered only when **both** gates are satisfied:
- `_cluster.oidc-enabled == "true"`
- `linstor-gui` is listed in `_cluster.expose-services` (i.e. in
`publishing.exposedServices` in the core cozystack values)

If OIDC is disabled cluster-wide we intentionally do **not** ship an
unauthenticated ``token-proxy`` fallback — unlike dashboard, there is no
reason to front a raw storage-management API with k8s bearer tokens.
Operators can still reach the UI via \`kubectl port-forward\` to the
ClusterIP service.

## What's new

- \`templates/gatekeeper.yaml\` — oauth2-proxy Deployment (OIDC mode),
upstream \`linstor-gui.<ns>.svc:80\`
- \`templates/gatekeeper-svc.yaml\` — ClusterIP :8000 in front of it
- \`templates/gatekeeper-sa.yaml\` — dedicated SA, no auto-token
- \`templates/keycloakclient.yaml\` — persistent \`linstor-gui-client\`
+ \`linstor-gui-auth-config\` Secrets, \`KeycloakClient\` CRD that
auto-provisions the OIDC client with \`redirectUris:
[/oauth2/callback/*]\`
- \`templates/ingress.yaml\` — Ingress to the gatekeeper Service with
cert-manager ClusterIssuer, gated on expose-services + oidc-enabled
- \`tests/ingress_auth_test.yaml\` — unit tests for each conditional
branch, KeycloakClient rendering, oauth2-proxy args

README updated with Option 1 (Keycloak-protected Ingress) and Option 2
(port-forward).

### Release note

\`\`\`release-note
Added an opt-in Keycloak-protected Ingress for the linstor-gui package.
Add \`linstor-gui\` to \`publishing.exposedServices\` to publish
\`https://linstor-gui.<root-host>\` behind the cluster OIDC realm.
\`\`\`

## Test plan

- [ ] Deploy to dev10 with \`linstor-gui\` added to
\`publishing.exposedServices\`
- [ ] Verify cert-manager issues the TLS certificate
- [ ] Open \`https://linstor-gui.<root-host>\` in a browser, confirm
Keycloak login challenge
- [ ] After login, confirm LINSTOR node list loads via the nginx → mTLS
proxy
- [ ] Verify helm-unittest cases pass in CI
- [ ] Sanity-check that when \`oidc-enabled=false\` or the service is
not in \`expose-services\`, the Ingress + gatekeeper resources are
skipped
myasnikovdaniil added a commit that referenced this pull request Apr 27, 2026
The linstor-gui package was added in #2382 with its own per-package
Makefile and Dockerfile, but the root Makefile's `build:` target was
not updated to invoke it. As a result `ghcr.io/cozystack/cozystack/
linstor-gui` has never been published (registry returns NAME_UNKNOWN)
and the chart's `image.tag` was never digest-pinned. Any cluster
deploying the chart hits ImagePullBackOff.

Wire the package into the root build alongside the other system
images. The next CI build will publish the image and the per-package
Makefile will rewrite values.yaml `image.repository`/`image.tag` to a
digest-pinned reference automatically.

Assisted-By: Claude <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
myasnikovdaniil added a commit that referenced this pull request Apr 27, 2026
The per-package Makefile added in #2382 hardcoded buildx flags
(--provenance, --builder, --platform=linux/amd64,linux/arm64,
--push, --load, --label) instead of using the shared $(BUILDX_ARGS)
macro from hack/common-envs.mk.

This broke CI: the runner's default docker driver does not support
multi-platform builds, and the hardcoded multi-arch platform list
crashed `make build` with "Multi-platform build is not supported for
the docker driver."

Replace the hardcoded flags with $(BUILDX_ARGS) to match every other
package (e.g. linstor, dashboard, cilium). $(BUILDX_ARGS) injects
--push, --load, --label, --provenance=false, and only sets --builder
or --platform when the operator explicitly exports BUILDER/PLATFORM.

Assisted-By: Claude <[email protected]>
Signed-off-by: Myasnikov Daniil <[email protected]>
Aleksei Sviridkin (lexfrei) added a commit that referenced this pull request Apr 28, 2026
## What this PR does

The `linstor-gui` package (added in #2382) was never wired into the root
`Makefile`'s `build:` target, so the image is not built or published by
CI. `ghcr.io/cozystack/cozystack/linstor-gui` returns `NAME_UNKNOWN`
from the GHCR API, and `values.yaml` still pins `tag: 2.3.0` without a
digest because the per-package Makefile that rewrites the tag after a
successful push has never run in CI.

This PR adds the missing line so the next build publishes the image and
digest-pins `values.yaml` automatically.

### Release note

```release-note
build(linstor): include linstor-gui in root image build target so the image is built and published by CI (the chart previously referenced an image that did not exist in the registry)
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Integrated the linstor-gui container image build into the main image
build workflow so the GUI image is produced as part of standard builds.
* Streamlined the GUI image build configuration to use consolidated
build arguments, improving consistency and maintainability of automated
image builds and metadata updates.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/feature Categorizes issue or PR as related to a new feature lgtm This PR has been approved by a maintainer size/L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants