Skip to content

Re-introduce basic auth, generate password if not set#3492

Merged
kensimon merged 9 commits into
NVIDIA:mainfrom
kensimon:initial-admin-pw
Jul 20, 2026
Merged

Re-introduce basic auth, generate password if not set#3492
kensimon merged 9 commits into
NVIDIA:mainfrom
kensimon:initial-admin-pw

Conversation

@kensimon

@kensimon kensimon commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

A recent security finding pointed out that the default configuration of nico-core, with the helm chart at its defaults, is very insecure, since there is no authentication on the web frontend. We could easily say this behaves correctly, since "auth=none" should mean no auth, but I think there's a point to be made that the defaults should be secure.

Defaulting to oauth2 is untenable, because it requires too many other parameters (client ID, client secret, endpoint, etc), so we can't just have a working "default" oauth2 setup.

Instead, this follows prior art from projects like Grafana and ArgoCD, by defaulting to basic auth, and requiring a password to be set, with systems for generating a default one. The helm chart will generate an initial randomly-generated admin password via kubernetes secrets, or allow the user to configure a pre-existing secret. If oauth2 is specified (like in other gitops deployments that aren't using the helm chart), nothing needs to be done: using oauth does not require an admin password to be set.

If the helm chart is not used, and no auth is configured, nico-api will generate one at startup and use it for the lifetime of the process. The password is logged as a warning message, and the basic auth prompt explains to look in the logs for the generated password. This is not expected to be seen in production: the helm chart will generate a secret and configure it. Logging a password would only happen if somebody is running nico-api without the helm chart, without a password env var, and via a deployment that isn't already configured for auth=none or auth=oauth2.

The devspace deployment is configured to use webAuth.mode=none, which is an acceptable default for convenience for local development.

While we're at it, make auth a little easier to specify from values.yaml by creating a toplevel values.yaml config for webAuth.mode in nico-api, rather than having to use extraEnv and configuring it via environment variables.

Related issues

#3485

Type of Change

  • Add - New feature or capability
  • Change - Changes in existing functionality
  • Fix - Bug fixes
  • Remove - Removed features or deprecated functionality
  • Internal - Internal changes (refactoring, tests, docs, etc.)

Breaking Changes

  • This PR contains breaking changes

It's debatable whether this is "breaking" or not... what breaks is the setup where you leave nothing configured and expect to use the web UI without a password. In such cases you will be guided on how to (a) find the password in the logs, if you didn't use the helm chart, or (b) get the secret from kubectl, if you used the helm chart. I'm going to call this "not breaking" but it's a judgement call.

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing performed
  • No testing required (docs, internal refactor, etc.)

Additional Notes

A recent security finding pointed out that the default configuration of
nico-core, with the helm chart at its defaults, is very insecure, since
there is no authentication on the web frontend. We could easily say this
behaves correctly, since "auth=none" should mean no auth, but I think
there's a point to be made that the defaults should be secure.

Defaulting to oauth2 is untenable, because it requires too many other
parameters (client ID, client secret, endpoint, etc), so we can't just
have a working "default" oauth2 setup.

Instead, this follows prior art from projects like Grafana and ArgoCD,
by defaulting to basic auth, and requiring a password to be set. If no
password is set in the values.yaml of the helm chart, the chart will
create an initial randomly-generated admin password via kubernetes
secrets. If oauth2 is specified (like in other gitops deployments that
aren't using the helm chart), nothing needs to be done: using oauth does
not require an admin password to be set.

If the helm chart is not used, and no auth is configured, nico-api will
generate one at startup and use it for the lifetime of the process. The
password is logged as a warning message, and the basic auth prompt
explains to look in the logs for the generated password. This is not
expected to be seen in production: the helm chart will generate a secret
and configure it. Logging a password would only happen if somebody is
running nico-api without the helm chart, without a password env var, and
via a deployment that isn't already configured for auth=none or
auth=oauth2.

While we're at it, make auth a little easier to specify from values.yaml
by creating a toplevel values.yaml config for `webAuth.mode` in nico-api,
rather than having to use `extraEnv` and configuring it via environment
variables.
@kensimon
kensimon requested review from a team and polarweasel as code owners July 14, 2026 17:29
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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

WebUI authentication now supports Basic, OAuth2, and none modes. The API adds typed mode handling and middleware, while Helm manages mode precedence, Basic-auth Secrets, deployment variables, tests, examples, and operational documentation.

Changes

WebUI authentication

Layer / File(s) Summary
Typed authentication runtime
crates/api-web/..., crates/api-web/Cargo.toml
Authentication modes are parsed as basic, oauth2, or none; Basic credentials and temporary passwords are supported; OAuth2 routing uses shared layers; middleware, ownership handling, and tests are updated.
Helm mode resolution and credentials
helm/charts/nico-api/templates/..., helm/charts/nico-api/tests/..., helm/charts/nico-api/values.yaml
Helm resolves configured and legacy modes, injects authentication variables, creates or references Basic-auth Secrets, exposes retrieval notes, and validates supported configurations.
Deployment examples and operational guidance
book/..., deploy/..., dev/..., docs/..., helm/README.md, helm/examples/...
Documentation and development configurations describe Basic defaults, OAuth2 and none modes, Secret handling, temporary passwords, and legacy override precedence.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Helm
  participant KubernetesSecret
  participant NicoApi
  participant WebUIClient
  Helm->>KubernetesSecret: Render or reference Basic-auth password
  Helm->>NicoApi: Inject authentication mode and password
  WebUIClient->>NicoApi: Request /admin
  NicoApi->>NicoApi: Apply web_auth_middleware_fn
  NicoApi-->>WebUIClient: Return protected response or authentication challenge
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately summarizes the main change: restoring basic auth and generating a password when one is not provided.
Description check ✅ Passed The description aligns with the PR goals and clearly explains the basic-auth default, generated password, and webAuth.mode changes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@github-actions

Copy link
Copy Markdown

@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: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
helm/README.md (1)

171-195: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Align the WebUI OAuth2 docs with the Entra/Graph flow.
oauth2 mode is Entra-specific in the runtime: it relies on PKCE, User.Read, and MS Graph group lookup. Both docs should stop implying generic Keycloak/Okta support unless a provider-specific integration is added and exercised.

  • helm/README.md: replace the Okta/Azure AD wording with Entra-specific guidance.
  • book/src/configuration/configurability.md: replace the Keycloak example with the Entra/WebUI configuration.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@helm/README.md` around lines 171 - 195, Update the OAuth2 documentation to
describe the Entra-specific WebUI flow, including PKCE, User.Read, and Microsoft
Graph group lookup; in helm/README.md replace generic Azure AD/Okta wording and
retain the Entra configuration example, and in
book/src/configuration/configurability.md replace the Keycloak example with
matching Entra/WebUI configuration. Do not imply generic provider support.

Source: Path instructions

🧹 Nitpick comments (3)
crates/api-web/src/lib.rs (2)

484-488: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Record the authentication variable as a structured tracing field.

Proposed change
 tracing::warn!(
-    "{}: admin web UI has no in-process authentication; restrict access with network policy, a private network, or an authenticating reverse proxy (for example OAuth2 Proxy)",
-    AUTH_TYPE_ENV
+    auth_type_env = AUTH_TYPE_ENV,
+    "admin web UI has no in-process authentication; restrict access with network policy, a private network, or an authenticating reverse proxy (for example OAuth2 Proxy)"
 );

As per coding guidelines, common values must be structured fields rather than interpolated message content.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/api-web/src/lib.rs` around lines 484 - 488, Update the
WebAuthMode::None tracing::warn call to record AUTH_TYPE_ENV as a structured
tracing field instead of interpolating it into the message string. Keep the
existing warning text and authentication guidance unchanged.

Sources: Coding guidelines, Path instructions


908-923: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid cloning the complete authentication configuration on every request.

WebAuth::Basic copies the password, while WebAuth::OAuth2 deep-clones its boxed layer and maps. Borrow the Basic variant and store the OAuth2 layer in an Arc.

Proposed direction
-    OAuth2(Box<Oauth2Layer>),
+    OAuth2(Arc<Oauth2Layer>),

-    let oauth_extension_layer = match req.extensions().get::<WebAuth>().cloned() {
+    let oauth_extension_layer = match req.extensions().get::<WebAuth>() {
...
-        Some(WebAuth::OAuth2(layer)) => *layer,
+        Some(WebAuth::OAuth2(layer)) => Arc::clone(layer),
     };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/api-web/src/lib.rs` around lines 908 - 923, Update the authentication
handling around WebAuth and the oauth_extension_layer match to avoid cloning the
full configuration per request: borrow WebAuth::Basic fields when validating
credentials, and change WebAuth::OAuth2 to retain its layer through an Arc
rather than deep-cloning the boxed layer and maps. Preserve the existing None,
Basic authorization, and OAuth2 request behavior while adjusting ownership and
dereferencing as needed.
helm/charts/nico-api/templates/web-basic-auth-secret.yaml (1)

3-6: 🩺 Stability & Availability | 🔵 Trivial

Password rotation risk if lookup can't read Secrets.

Reusing the password via lookup on upgrade only works if the Helm-installing ServiceAccount has get/list RBAC on Secrets in the target namespace; otherwise lookup silently returns empty and a brand-new password is generated on every helm upgrade, invalidating credentials already handed to operators without any warning. Please confirm the chart's RBAC (or the cluster-operator's role) grants Secret read access wherever this chart is installed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@helm/charts/nico-api/templates/web-basic-auth-secret.yaml` around lines 3 -
6, Ensure the chart’s installing ServiceAccount or cluster-operator role has
get/list RBAC permissions for Secrets in the target namespace, so the lookup in
the password initialization flow can reuse the existing password across
upgrades. Update the chart’s RBAC configuration associated with the
web-basic-auth secret without changing the existing password reuse logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/api-web/src/lib.rs`:
- Around line 914-921: Update the Basic-auth branch in the WebAuth middleware to
apply the existing CSRF protection check to unsafe methods before calling
next.run(req).await. Require an accepted Origin/Fetch Metadata signal or CSRF
token, while preserving the current credential validation and unauthorized
response behavior.

In `@dev/mac-local-dev/README.md`:
- Line 240: Update the README environment-variable example by removing the
inline “# local development only” text from the quoted echo value, so the
emitted line remains exactly CARBIDE_WEB_AUTH_TYPE=none. Move the explanatory
note outside the command, such as a Markdown comment above the code block, while
preserving the other echo commands.

In `@helm/charts/nico-api/templates/_helpers.tpl`:
- Around line 87-101: Defer evaluating configuredMode in
nico-api.webAuth.effectiveMode until the final branch that uses it, after
literal and non-literal override checks. Preserve validation and precedence for
literal CARBIDE_WEB_AUTH_TYPE overrides so a valid override does not trigger
configuredMode failure, while still returning configuredMode when no override
applies.

In `@helm/charts/nico-api/templates/deployment.yaml`:
- Around line 143-153: Update the password environment block in the deployment
template to use the same basic-auth Secret rendering condition as
web-basic-auth-secret.yaml, including the unknown mode with configuredMode set
to basic. Keep CARBIDE_WEB_BASIC_AUTH_PASSWORD sourced from
webAuth.basicSecretName and webAuth.basicSecretKey, and preserve the existing
effective basic-mode behavior.

In `@helm/charts/nico-api/templates/web-basic-auth-secret.yaml`:
- Around line 1-18: Replace the hardcoded secret name and password key in
web-basic-auth-secret.yaml with the nico-api.webAuth.basicSecretName and
nico-api.webAuth.basicSecretKey helpers, including the lookup and data access.
In helm/charts/nico-api/templates/NOTES.txt lines 1-7, replace the hardcoded
secret name in the kubectl command with the basicSecretName helper.
- Around line 1-18: Update the generated basic-auth Secret template to use
nico-api.webAuth.basicSecretName for the Secret name and
nico-api.webAuth.basicSecretKey for both lookup and data-key access. Replace the
hardcoded name and password key references while preserving the existing
generation and reuse behavior.

In `@helm/examples/values-full.yaml`:
- Around line 81-94: The extraEnv example is missing the required
CARBIDE_WEB_ALLOWED_ACCESS_GROUPS_ID_LIST variable. Add it alongside
CARBIDE_WEB_ALLOWED_ACCESS_GROUPS, using a realistic deployment-safe
comma-separated group ID value rather than a placeholder that could be deployed
unchanged.

---

Outside diff comments:
In `@helm/README.md`:
- Around line 171-195: Update the OAuth2 documentation to describe the
Entra-specific WebUI flow, including PKCE, User.Read, and Microsoft Graph group
lookup; in helm/README.md replace generic Azure AD/Okta wording and retain the
Entra configuration example, and in book/src/configuration/configurability.md
replace the Keycloak example with matching Entra/WebUI configuration. Do not
imply generic provider support.

---

Nitpick comments:
In `@crates/api-web/src/lib.rs`:
- Around line 484-488: Update the WebAuthMode::None tracing::warn call to record
AUTH_TYPE_ENV as a structured tracing field instead of interpolating it into the
message string. Keep the existing warning text and authentication guidance
unchanged.
- Around line 908-923: Update the authentication handling around WebAuth and the
oauth_extension_layer match to avoid cloning the full configuration per request:
borrow WebAuth::Basic fields when validating credentials, and change
WebAuth::OAuth2 to retain its layer through an Arc rather than deep-cloning the
boxed layer and maps. Preserve the existing None, Basic authorization, and
OAuth2 request behavior while adjusting ownership and dereferencing as needed.

In `@helm/charts/nico-api/templates/web-basic-auth-secret.yaml`:
- Around line 3-6: Ensure the chart’s installing ServiceAccount or
cluster-operator role has get/list RBAC permissions for Secrets in the target
namespace, so the lookup in the password initialization flow can reuse the
existing password across upgrades. Update the chart’s RBAC configuration
associated with the web-basic-auth secret without changing the existing password
reuse logic.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f0f5678b-7424-408b-87e5-bf9427ecd978

📥 Commits

Reviewing files that changed from the base of the PR and between 8b5fd50 and d6e9896.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (19)
  • book/src/configuration/configurability.md
  • crates/api-web/Cargo.toml
  • crates/api-web/src/lib.rs
  • crates/api-web/src/tests/mod.rs
  • deploy/nico-base/api/deployment.yaml
  • dev/deployment/devspace/values.base.yaml
  • dev/mac-local-dev/README.md
  • dev/mac-local-dev/run-nico-api.sh
  • dev/webdev-env/run-env.sh
  • docs/operations/debug_webui.md
  • helm/README.md
  • helm/charts/nico-api/templates/NOTES.txt
  • helm/charts/nico-api/templates/_helpers.tpl
  • helm/charts/nico-api/templates/deployment.yaml
  • helm/charts/nico-api/templates/web-basic-auth-secret.yaml
  • helm/charts/nico-api/tests/web_auth_deployment_test.yaml
  • helm/charts/nico-api/tests/web_auth_secret_test.yaml
  • helm/charts/nico-api/values.yaml
  • helm/examples/values-full.yaml

Comment thread crates/api-web/src/lib.rs Outdated
Comment thread dev/mac-local-dev/README.md
Comment thread helm/charts/nico-api/templates/_helpers.tpl
Comment thread helm/charts/nico-api/templates/deployment.yaml
Comment thread helm/charts/nico-api/templates/web-basic-auth-secret.yaml
Comment thread helm/examples/values-full.yaml
@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown

🔍 Container Scan Summary

Service Total Critical High Medium Low Other
boot-artifacts-aarch64 3 0 0 3 0 0
boot-artifacts-x86_64 3 0 0 3 0 0
forge-admin-cli-x86_64 259 16 29 79 7 128
machine-a-tron 181 7 17 52 6 99
machine-validation-runner 532 39 113 184 15 181
machine_validation 532 39 113 184 15 181
machine_validation-aarch64 532 39 113 184 15 181
nvmetal-carbide 532 39 113 184 15 181
TOTAL 2574 179 498 873 73 951

Per-CVE detail lives in the per-service grype-* artifacts (JSON + SARIF). Severity counts only — no CVE IDs published here.

@kensimon
kensimon enabled auto-merge (squash) July 14, 2026 20:33

@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 (1)
helm/charts/nico-api/tests/web_auth_deployment_test.yaml (1)

92-107: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert that the built-in mode is absent when the legacy override wins.

This test only checks that an oauth2 entry exists. It would still pass if the chart also rendered the configured/default mode as a duplicate CARBIDE_WEB_AUTH_TYPE. Add notContains assertions for the suppressed mode (at least basic, and the invalid saml value) so precedence and duplicate suppression are actually verified.

As per path instructions, Helm changes should be tested for rendered-template correctness and configuration compatibility.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@helm/charts/nico-api/tests/web_auth_deployment_test.yaml` around lines 92 -
107, The test for legacy CARBIDE_WEB_AUTH_TYPE precedence should also verify
suppressed built-in values are not rendered. In the “literal legacy mode takes
precedence…” case, add notContains assertions for CARBIDE_WEB_AUTH_TYPE entries
with values basic and saml, while preserving the existing oauth2 presence and
basic-auth-password absence checks.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@helm/charts/nico-api/templates/NOTES.txt`:
- Line 6: Update the kubectl jsonpath command in the NOTES template to access
the Secret data key using bracket notation, incorporating the value from
nico-api.webAuth.basicSecretKey so keys containing dots such as admin.password
resolve correctly.

---

Nitpick comments:
In `@helm/charts/nico-api/tests/web_auth_deployment_test.yaml`:
- Around line 92-107: The test for legacy CARBIDE_WEB_AUTH_TYPE precedence
should also verify suppressed built-in values are not rendered. In the “literal
legacy mode takes precedence…” case, add notContains assertions for
CARBIDE_WEB_AUTH_TYPE entries with values basic and saml, while preserving the
existing oauth2 presence and basic-auth-password absence checks.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 9a28b1e2-f09a-477a-b0f3-49f2f775ec41

📥 Commits

Reviewing files that changed from the base of the PR and between d6e9896 and e718662.

📒 Files selected for processing (11)
  • crates/api-web/src/auth.rs
  • crates/api-web/src/lib.rs
  • crates/api-web/src/redfish_actions.rs
  • crates/api-web/src/redfish_browser.rs
  • helm/charts/nico-api/templates/NOTES.txt
  • helm/charts/nico-api/templates/_helpers.tpl
  • helm/charts/nico-api/templates/deployment.yaml
  • helm/charts/nico-api/templates/web-basic-auth-secret.yaml
  • helm/charts/nico-api/tests/web_auth_deployment_test.yaml
  • helm/charts/nico-api/tests/web_auth_secret_test.yaml
  • helm/examples/values-full.yaml
🚧 Files skipped from review as they are similar to previous changes (6)
  • helm/charts/nico-api/templates/web-basic-auth-secret.yaml
  • helm/charts/nico-api/templates/deployment.yaml
  • helm/charts/nico-api/tests/web_auth_secret_test.yaml
  • helm/examples/values-full.yaml
  • helm/charts/nico-api/templates/_helpers.tpl
  • crates/api-web/src/lib.rs

Comment thread helm/charts/nico-api/templates/NOTES.txt Outdated
kensimon and others added 2 commits July 15, 2026 09:10
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Ken Simon <ken@kensimon.io>
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
putComment timed out

@kensimon
kensimon merged commit d3c53bb into NVIDIA:main Jul 20, 2026
63 of 64 checks passed
@kensimon kensimon added this to the v2.1 milestone Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Verify

Development

Successfully merging this pull request may close these issues.

5 participants