Skip to content

feat(nico): add AUTH-XX-03 specified-key access validation#469

Open
abegnoche wants to merge 10 commits into
mainfrom
cursor/auth-xx-03-specified-key-access-42e9
Open

feat(nico): add AUTH-XX-03 specified-key access validation#469
abegnoche wants to merge 10 commits into
mainfrom
cursor/auth-xx-03-specified-key-access-42e9

Conversation

@abegnoche

@abegnoche abegnoche commented Jun 12, 2026

Copy link
Copy Markdown
Member

Summary

Adds SpecifiedKeyAccessCheck (AUTH-XX-03), a provider-neutral "a tenant-specified key can access out-of-band components" check, wired for NICo via the bare_metal suite. Following the recent NICo-validation pattern (#457/#458/#466). Ships unreleased (released_tests.json untouched); run with ISVTEST_INCLUDE_UNRELEASED=1.

NICo models a specified key as an SSH Key Group synced to a Site; the Site's serial console (SOL) is key-accessible when the provider enables it (isSerialConsoleEnabled), SSH-key auth is on (isSerialConsoleSSHKeysEnabled), and a key group is synced to the site. The serial console is the verifiable target; network-device access is provider-managed and reported as null (unverified) rather than passing vacuously. Key material is never emitted.

Two ways to run it:

  • Read-only (bare_metal.yaml): reports against whatever keys exist. When nothing is synced to the site it emits a structured skip whose reason distinguishes "no key groups exist at all" from "key groups exist but none synced to this site" (with an org_key_groups count), so the operator knows what to configure.
  • Auto-provisioning (key_access.yaml): a setup → test → teardown config that provisions an ephemeral SSH key + key group synced to the site, runs the check, then deletes them — so AUTH-XX-03 goes green in CI with no manual key setup. The throwaway private key is generated and discarded immediately; only the unusable public key is registered.

Changes

  • Adds SpecifiedKeyAccessCheck: passes when a specified key can reach ≥1 component, fails when a key-access path is disabled/broken, skips (with a precise reason) when access can't be evidenced.
  • Adds query_key_access.py (read-only) + setup_key_access.py / teardown_key_access.py (provisioning), via the existing /carbide/ helpers; adds forge_post/forge_patch/forge_delete to nico_client.
  • Wires the read-only step into suites/bare_metal.yaml + providers/nico/config/bare_metal.yaml; adds the dedicated providers/nico/config/key_access.yaml.
  • Unit coverage in isvtest/tests/test_key_access.py and the query/setup/teardown scripts in isvctl/tests/providers/nico/test_nico_provider.py.

Testing

  • make test (full suite, incl. new validation + script tests)
  • make lint
  • make demo-test
  • uvx pre-commit run -a
  • ✅ End-to-end via the orchestrator against a local /carbide/ stub — auto-provisioning key_access.yaml runs setup → check PASS → teardown, and the throwaway key group is removed afterward; read-only skip diagnostics and the disabled-key fail path also verified.

auth_xx_03_key_access_provision_e2e.log
auth_xx_03_key_access_e2e.log

Closes #248

To show artifacts inline, enable in settings.

Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features

    • Added specified-key access validation for bare-metal environments, including serial-console access checks.
    • Added an optional workflow that provisions temporary access credentials, validates access, and cleans them up automatically.
    • Validation results now distinguish successful, failed, skipped, and unverifiable access scenarios.
    • Expanded test-plan labels for bare-metal, identity, security, and virtual-machine coverage.
  • Bug Fixes

    • Improved handling of empty or non-JSON successful API responses.
    • Cleanup now safely treats already-removed resources as successful.

@copy-pr-bot

copy-pr-bot Bot commented Jun 12, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds AUTH03-01 specified-key access validation for serial-console and network-device targets, with NICo query, ephemeral setup/teardown workflows, configuration wiring, HTTP client extensions, labels, and comprehensive unit and integration tests.

Changes

Specified-key access

Layer / File(s) Summary
Validation contract and suite wiring
isvtest/src/isvtest/validations/key_access.py, isvtest/tests/test_key_access.py, isvctl/configs/suites/*.yaml, docs/test-plan.*
Adds SpecifiedKeyAccessCheck, pass/fail/skip handling, AUTH03-01 suite registration, expanded labels, and validation tests.
NICo query and HTTP client support
isvctl/configs/providers/nico/scripts/common/nico_client.py, isvctl/configs/providers/nico/scripts/auth/query_key_access.py
Adds generalized authenticated HTTP methods, synced key-group detection, and structured access reports for serial-console and network-device targets.
Ephemeral key workflow and configuration
isvctl/configs/providers/nico/scripts/auth/{setup,teardown}_key_access.py, isvctl/configs/providers/nico/config/*.yaml
Adds temporary SSH key provisioning, synchronization polling, access querying, cleanup, site-flag restoration, and NICo workflow configuration.
Provider integration coverage
isvctl/tests/providers/nico/test_nico_provider.py
Adds script loaders and tests for query, setup, teardown, cleanup idempotency, configuration wiring, and non-JSON DELETE responses.

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

Sequence Diagram(s)

sequenceDiagram
  participant SetupKeyAccess
  participant NICoAPI
  participant QueryKeyAccess
  participant SpecifiedKeyAccessCheck
  participant TeardownKeyAccess
  SetupKeyAccess->>NICoAPI: create temporary SSH key and key group
  SetupKeyAccess->>NICoAPI: wait for synchronization and enable site access
  QueryKeyAccess->>NICoAPI: query synced groups and site configuration
  QueryKeyAccess-->>SpecifiedKeyAccessCheck: emit access evidence JSON
  SpecifiedKeyAccessCheck-->>TeardownKeyAccess: complete validation workflow
  TeardownKeyAccess->>NICoAPI: delete temporary resources and restore site flag
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding AUTH-XX-03 specified-key access validation.
Linked Issues check ✅ Passed The PR implements AUTH-XX-03 by adding specified-key access validation, provider scripts, suite wiring, and tests for SOL/network-device access.
Out of Scope Changes check ✅ Passed The changes stay focused on AUTH-XX-03 validation, provider plumbing, docs, and tests without unrelated functional additions.
Docstring Coverage ✅ Passed Docstring coverage is 87.10% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/auth-xx-03-specified-key-access-42e9

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

cursoragent and others added 5 commits July 14, 2026 14:31
Verify a tenant-specified key (SSH key) can access out-of-band
components, with the serial console (SOL) as the verifiable example.

- nico_client: add NICo tenant REST (/nico/) path helpers alongside the
  Forge control-plane (/carbide/) helpers; SSH key groups and per-site
  serial-console config live only on the tenant REST API.
- query_key_access.py: read the site's SSH key groups and serial-console
  configuration and emit the provider-neutral access contract; network
  devices are provider-managed and reported as unverified.
- SpecifiedKeyAccessCheck: pass when a specified key can reach at least
  one component, fail when a key-access path is disabled/broken, skip
  when access can only be left unverified.
- Wire query_key_access into the bare_metal suite + NICo config.

Ships unreleased (not added to released_tests.json).

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Alexandre Begnoche <abegnoche@users.noreply.github.com>
The deployed NICo clusters serve the API under the /carbide/ segment
(/nico/ is the newer name). Use the existing forge_get/forge_get_all
helpers like every other NICo script instead of introducing /nico/
helpers, which 404 on current clusters.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Alexandre Begnoche <abegnoche@users.noreply.github.com>
When no SSH key group with a key is synced to the site, emit a
structured skip that distinguishes 'no key groups exist at all' from
'key groups exist but none are synced to this site' (carrying an
org_key_groups count), so the operator knows what to configure.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Alexandre Begnoche <abegnoche@users.noreply.github.com>
Add a key_access.yaml config that provisions an ephemeral SSH key + key
group synced to the site (setup), runs SpecifiedKeyAccessCheck (test),
then deletes them (teardown), so AUTH-XX-03 can go green in CI without
manual key setup. The throwaway private key is generated and discarded
immediately; only the unusable public key is registered.

- nico_client: add forge_post/forge_patch/forge_delete write helpers.
- setup_key_access.py / teardown_key_access.py scripts (best-effort,
  emit created IDs even on failure so teardown stays reliable).
- Unit coverage for the query/setup/teardown scripts in
  test_nico_provider.py.

Read-only bare_metal.yaml flow is unchanged.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Alexandre Begnoche <abegnoche@users.noreply.github.com>
Signed-off-by: Alexandre Begnoche <abegnoche@nvidia.com>
@abegnoche abegnoche force-pushed the cursor/auth-xx-03-specified-key-access-42e9 branch from 766776b to 6fcfa6d Compare July 14, 2026 18:55
@copy-pr-bot

copy-pr-bot Bot commented Jul 14, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

abegnoche and others added 3 commits July 14, 2026 15:04
Make query_key_access a read-only test-phase step (like the other NICo
checks) so a key-access failure no longer skips the whole bare_metal run.
Restore the dedicated key_access.yaml for the opt-in setup -> test ->
teardown auto-provisioning path.

Signed-off-by: Alexandre Begnoche <abegnoche@nvidia.com>
Cleanup pass over the AUTH-XX-03 key-access change; no new behavior
except teardown now failing when a deletion fails:

- fold forge_get into forge_request (params + GET default) so URL
  building and HTTPError body rewrap live in one place
- hoist the shared sshkeygroup_synced_to_site predicate and
  SYNCED_STATUS into common/nico_client.py (was duplicated in the
  setup and query scripts)
- query_key_access: fetch the site only on the non-skip path, drop the
  always-true keys_synced parameter and its unreachable branch, single
  print/return exit, and drop the unread key_groups /
  keys_synced_to_site output fields per JSON contract discipline
- setup_key_access: short-circuit _wait_for_sync on an empty group id
  instead of polling the collection URL for 3 minutes
- teardown_key_access: report success = no cleanup_errors (matches
  teardown_nim.py) so leaked resources fail the step visibly; shrink
  _as_bool to the spellings the pipeline can produce
- drop the dead labels ClassVar on SpecifiedKeyAccessCheck (labels are
  declared only in suite YAML) and the min_accessible_targets: 1 suite
  param that restated the code default
- collapse three copy-pasted test script loaders into _load_nico_script

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Alexandre Begnoche <abegnoche@nvidia.com>
Legacy carbide DELETE returns a bare OK body, which broke forge_request with
JSONDecodeError and failed teardown even when the API accepted cleanup.
Treat non-JSON 2xx bodies as success, make DELETE 404 idempotent, and surface
cleanup_errors in the step error field for easier diagnosis.

Signed-off-by: Alexandre Begnoche <abegnoche@nvidia.com>
@abegnoche abegnoche marked this pull request as ready for review July 14, 2026 21:03
@abegnoche abegnoche requested a review from a team as a code owner July 14, 2026 21:03
@github-actions

Copy link
Copy Markdown

🔐 TruffleHog Secret Scan

No secrets or credentials found!

Your code has been scanned for 700+ types of secrets and credentials. All clear! 🎉

🔗 View scan details

🕐 Last updated: 2026-07-14 21:04:21 UTC | Commit: 26d19e3

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
isvctl/tests/providers/nico/test_nico_provider.py (1)

377-385: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Align the new test helpers with the repository’s Python standards.

Add a PEP 257 docstring to _RawResponse, parameterize the bare dict/list annotations, and move HTTPError into the top-level import block.

Representative fixes
+from urllib.error import HTTPError
+
 class _RawResponse:
+    """Provide a context-managed byte response for mocked HTTP calls."""
+
-    def fake_all(..., params: dict | None = None, ...) -> list:
+    def fake_all(..., params: dict[str, str] | None = None, ...) -> list[dict[str, Any]]:

-    def fake_post(..., body: dict, ...) -> dict:
+    def fake_post(..., body: dict[str, Any], ...) -> dict[str, Any]:

-    from urllib.error import HTTPError

As per coding guidelines, all classes require PEP 257 docstrings, collection annotations require generic parameters, and imports belong at module scope.

Also applies to: 3182-3182, 3211-3221, 3253-3253, 3318-3318

🤖 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 `@isvctl/tests/providers/nico/test_nico_provider.py` around lines 377 - 385,
Update the test helpers in test_nico_provider.py to follow repository standards:
add a PEP 257 class docstring to _RawResponse, replace every bare dict and list
annotation in the affected helpers with parameterized generics, and move the
HTTPError import into the module’s top-level import block.

Source: Coding guidelines

🤖 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 `@isvctl/configs/providers/nico/config/key_access.yaml`:
- Around line 65-75: Increase the setup_key_access command timeout from 240
seconds to allow the 180-second polling period plus authentication and all
subsequent HTTP operations to complete and print the resource IDs.

In `@isvctl/configs/providers/nico/scripts/auth/setup_key_access.py`:
- Around line 152-175: Update the setup flow around _wait_for_sync so a false
synchronization result stops provisioning before setting result["success"] =
True or proceeding with site-flag handling. Mark the operation as failed using
the existing failure/error path, while preserving the successful flow only when
the key group synchronizes.

In `@isvctl/configs/providers/nico/scripts/common/nico_client.py`:
- Around line 211-238: Update the response parsing in the client request method
around Request and urlopen so empty or malformed successful bodies return {}
only for DELETE requests. For GET, POST, and other methods requiring structured
data, propagate a clear parsing error instead of silently returning {}, while
preserving the existing HTTPError behavior and legacy DELETE handling.

In `@isvtest/src/isvtest/validations/key_access.py`:
- Around line 120-125: Update the reachable handling in the key-access
validation flow to require an exact boolean True instead of coercing
target.get("reachable"), so string values such as "false" do not increment
accessible. Add a regression test covering non-boolean reachable output while
preserving the existing enabled and detail behavior.

---

Nitpick comments:
In `@isvctl/tests/providers/nico/test_nico_provider.py`:
- Around line 377-385: Update the test helpers in test_nico_provider.py to
follow repository standards: add a PEP 257 class docstring to _RawResponse,
replace every bare dict and list annotation in the affected helpers with
parameterized generics, and move the HTTPError import into the module’s
top-level import block.
🪄 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: 0fbc35ea-e36c-4918-ad63-bf1289ddc036

📥 Commits

Reviewing files that changed from the base of the PR and between d21c1b1 and 26d19e3.

📒 Files selected for processing (13)
  • docs/test-plan.adoc
  • docs/test-plan.yaml
  • isvctl/configs/providers/nico/config/bare_metal.yaml
  • isvctl/configs/providers/nico/config/key_access.yaml
  • isvctl/configs/providers/nico/scripts/auth/query_key_access.py
  • isvctl/configs/providers/nico/scripts/auth/setup_key_access.py
  • isvctl/configs/providers/nico/scripts/auth/teardown_key_access.py
  • isvctl/configs/providers/nico/scripts/common/nico_client.py
  • isvctl/configs/suites/bare_metal.yaml
  • isvctl/configs/suites/vm.yaml
  • isvctl/tests/providers/nico/test_nico_provider.py
  • isvtest/src/isvtest/validations/key_access.py
  • isvtest/tests/test_key_access.py

Comment thread isvctl/configs/providers/nico/config/key_access.yaml Outdated
Comment thread isvctl/configs/providers/nico/scripts/auth/setup_key_access.py Outdated
Comment thread isvctl/configs/providers/nico/scripts/common/nico_client.py Outdated
Comment thread isvtest/src/isvtest/validations/key_access.py
Scope non-JSON DELETE tolerance to DELETE only, fail setup when the key
group never syncs, require boolean True for reachable, and budget more
setup timeout for the 180s sync poll.

Signed-off-by: Alexandre Begnoche <abegnoche@nvidia.com>
Signed-off-by: Alexandre Begnoche <abegnoche@nvidia.com>
@abegnoche

Copy link
Copy Markdown
Member Author

Addressed all four CodeRabbit actionable comments in 090d5bc / 3d9b1e6:

  1. key_access.yaml — setup timeout 240 → 360s (180s sync poll + HTTP headroom)
  2. setup_key_access.py — fail setup when _wait_for_sync returns false (IDs still emitted for teardown)
  3. nico_client.py — non-JSON 2xx tolerance scoped to DELETE only; POST/GET still raise JSONDecodeError
  4. key_access.pyreachable requires literal True (string "false" no longer counts as accessible)

Also fixed ruff import order (090d5bc). E2E-verified teardown fix from 26d19e3 unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AUTH-XX-03: Access other components via a specified key as possible (SOL, Network devices)

2 participants