feat(excititor): OCI OpenVEX artifact-backed configuration (SPRINT_20260423_001)
Closes SPRINT_20260423_001 — all 3 tasks DONE, sprint archived. Extends
the scalar persisted-settings model (7efa424fe) with an artifact-
reference storage layer for the complex OCI OpenVEX connector, which
carries image subscription lists + cosign keys + TUF roots that don't
fit the flat values/clearKeys shape.
OCI-CFG-001 — Storage model:
- New vex.provider_artifact_refs table via embedded migration
009_vex_provider_artifact_refs.sql (auto-applied per §2.7). Columns:
artifact_id (UUID PK), provider_id, tenant_id, mime_type, size_bytes,
sha256, payload bytea, staged_at, staged_by. RLS via
current_setting('app.tenant_id', TRUE); size_bytes CHECK >= 0.
- Upload / list / meta / delete API under /excititor/providers/{id}/
artifacts. Multipart upload returns { artifactId, sha256, ... };
meta never returns payload. Scopes: read=vex.read, write=vex.admin.
- VexProviderArtifactMaterializer: scoped session writes artifacts to
%TMP%/stella-excititor-artifacts/<guid>/<artifactId>.bin with
chmod 0700/0600 on POSIX; Dispose cleans up the scratch root.
IDisposable + IAsyncDisposable, idempotent cleanup.
- PostgresVexProviderArtifactStore uses raw Npgsql for bytea I/O
(bypasses EF compiled-model cache + change tracking); EF entity
ProviderArtifactRefRow added for future EF-consumer completeness.
OCI-CFG-002 — Config wiring:
- VexProviderConfigurationFieldDefinition.FieldShape extended:
scalar | list<string> | artifactRef | list<artifactRef>.
- list<string> encoded as newline-or-JSON text; artifactRef stored as
the artifact UUID. Snapshot exposes FieldShape for clients.
- OCI-specific blocked sub-codes (all inside PROVIDER_CONFIG_INVALID /
_REQUIRED envelope): PROVIDER_CONFIG_MISSING_IMAGE_SUBSCRIPTIONS,
_INVALID_IMAGE_REFERENCE, _MISSING_COSIGN_KEY, _MISSING_COSIGN_ISSUER,
_MISSING_COSIGN_SUBJECT, _MISSING_TUF_ROOT, _INVALID_COSIGN_MODE,
_HTTP_REGISTRY_BLOCKED.
- Readiness reuses OciOpenVexAttestationConnectorOptions validator.
OCI-CFG-003 — CLI + Web:
- CLI `stella vex providers configure` flags: --image (repeatable),
--upload-artifact <key>=@<path> (repeatable), --clear-artifact,
--list-artifacts, --host-path-compat (CLI-only compatibility mode).
- New subcommand `stella vex providers artifacts <provider>` lists
staged artifacts.
- OciOpenVexConfigurationComponent (Angular standalone): image-
subscription list editor, artifact slots with file upload + staged
meta, scalar fields for cosign mode/issuer/subject/registry auth/
offline bundle, staged-artifact table with delete. Conditionally
rendered in vex-provider-catalog when provider.id == 'excititor:
oci-openvex'.
- docs/modules/excititor/operations/provider-credentials.md §5 OCI
OpenVEX rewritten with 4 canonical setup flows: keyless, keypair,
TUF+offline, private-registry.
Tests: targeted xUnit via scripts/test-targeted-xunit.ps1:
- VexProviderConfigurationServiceTests — 8/8 (regression)
- VexProviderOciOpenVexTests — 13/13 (OCI schema + sub-codes + artifact
service size/quota/tenant-isolation + materializer tempfile lifecycle)
Worker-side wiring of VexProviderArtifactMaterializationSession into
DefaultVexProviderRunner (before cosign/TUF validation) is a natural
follow-up — the resolver + API + store exist; a future sprint ties
the runner into the session lifecycle when the first end-to-end OCI
provider scan lands.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Excititor Provider Credential Entry
|
||||
|
||||
_Last updated: 2026-04-22_
|
||||
_Last updated: 2026-04-23_
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
@@ -94,7 +94,7 @@ Current blocked codes:
|
||||
| `excititor:cisco` | Public Cisco CSAF metadata by default. Optional authenticated path depends on your Cisco API / mirror arrangement. | Usually nothing for the default public path. Optionally capture `metadataUri` override and `apiToken` if your Cisco path requires bearer auth. | Yes, for the default public Cisco CSAF metadata path. Configure it only when overriding the metadata URI or when your Cisco endpoint requires a token. | No StellaOps-side paywall for the public path. Any token requirement depends on your Cisco-side arrangement, mirror, or entitlement. |
|
||||
| `excititor:suse-rancher` | Your Rancher Hub / SUSE-auth deployment, plus the corresponding identity provider or token service. | `discoveryUri`, and when auth is required: `tokenEndpoint`, `clientId`, `clientSecret`, optional `audience`. | Sometimes. Anonymous discovery is allowed only if the hub is intentionally exposed that way. Otherwise the authenticated fields are required together. | No StellaOps-side paywall. Access depends on your Rancher Hub deployment and the identity provider that fronts it. |
|
||||
| `excititor:msrc` | `https://entra.microsoft.com` -> **App registrations** | `tenantId`, `clientId`, `clientSecret`; optionally `scope` override if you are not using the default MSRC API scope. | Not for the online MSRC client-credential path. | No separate documented MSRC paywall, but you need a Microsoft Entra tenant plus permission to register the app and grant the required consent. |
|
||||
| `excititor:oci-openvex` | Registry, identity provider, cosign/PKI authority, and any offline artifact staging path used by your deployment. | Not yet supported through the persisted UI/CLI scalar config path. | No. This remains blocked pending the artifact-backed OCI configuration design. | Depends on your registry, cosign, and offline bundle environment. |
|
||||
| `excititor:oci-openvex` | Registry, identity provider, cosign/PKI authority, and any offline artifact staging path used by your deployment. | `images` subscription list plus cosign/TUF material. Binary material (cosign keys, TUF roots, offline bundles) is uploaded as server-side artifact references via `/excititor/providers/{id}/artifacts`. Image list is a flat string map entry; artifact slots carry opaque artifact IDs. | Yes — `images` list must contain at least one valid OCI reference. Sprint 20260423_001 shipped the artifact-backed configuration path. | Depends on your registry, cosign, and offline bundle environment. |
|
||||
|
||||
## 5. What operators should actually look for
|
||||
|
||||
@@ -120,8 +120,100 @@ Current blocked codes:
|
||||
|
||||
### OCI OpenVEX
|
||||
|
||||
- No persisted UI/CLI credential path exists yet for the binary material used by OCI OpenVEX.
|
||||
- Keep using host-config compatibility mode until the artifact-backed configuration design lands.
|
||||
_Sprint 20260423_001 OCI-CFG-001/002/003 wired the artifact-backed configuration path._
|
||||
|
||||
The OCI OpenVEX provider carries configuration shapes the scalar settings store cannot cleanly absorb: a variable-length list of image subscriptions and binary cosign / TUF material that must not round-trip on reads. The implementation keeps the scalar settings JSONB column a flat string map and introduces a sibling artifact-reference store (`vex.provider_artifact_refs`) for binary material. The settings JSONB references artifact rows by opaque GUID.
|
||||
|
||||
#### 4.1 Shape of the configuration
|
||||
|
||||
| Field | Shape | Notes |
|
||||
| --- | --- | --- |
|
||||
| `images` | `list<string>` | One OCI reference per line. At least one entry is required before the provider can run. Invalid references surface as `PROVIDER_CONFIG_INVALID` with sub-code `PROVIDER_CONFIG_INVALID_IMAGE_REFERENCE`. HTTP (non-TLS) registries are rejected unless `allowHttpRegistries=true` (sub-code `PROVIDER_CONFIG_HTTP_REGISTRY_BLOCKED`). |
|
||||
| `cosignMode` | scalar (`None` \| `Keyless` \| `KeyPair`) | Controls which cosign verification material is required. Invalid modes surface as sub-code `PROVIDER_CONFIG_INVALID_COSIGN_MODE`. |
|
||||
| `cosignIssuer`, `cosignSubject` | scalar | Required when `cosignMode=Keyless`. Sub-codes: `PROVIDER_CONFIG_MISSING_COSIGN_ISSUER`, `PROVIDER_CONFIG_MISSING_COSIGN_SUBJECT`. |
|
||||
| `cosignKey` | `artifactRef` | Required when `cosignMode=KeyPair`. Staged artifact ID pointing to a PEM public/private key file. Sub-code `PROVIDER_CONFIG_MISSING_COSIGN_KEY`. |
|
||||
| `cosignCertificate` | `artifactRef` | Optional certificate paired with the cosign key. |
|
||||
| `tufRoots` | `list<artifactRef>` | One or more TUF `root.json` artifacts providing offline trust material. Sub-code `PROVIDER_CONFIG_MISSING_TUF_ROOT` when a TUF-dependent path requires one. |
|
||||
| `registryAuthority`, `registryUsername`, `registryPassword` | scalar | Optional basic-auth / token material for private registries. `registryPassword` is sensitive: retained on blank save, cleared only via `clearKeys`. |
|
||||
| `allowHttpRegistries` | scalar bool | Default `false`. Must be explicitly set to `true` to permit non-TLS registries. |
|
||||
| `offlineBundleRoot` | scalar | Server-side path to offline attestation bundles. Leave blank when bundles are uploaded via the artifact path. |
|
||||
|
||||
#### 4.2 Artifact-staging API
|
||||
|
||||
| Verb | Path | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `POST` | `/excititor/providers/{id}/artifacts` | Multipart upload (`file` field). Returns `{ artifactId, sha256, mime, sizeBytes, stagedAt }`. The SHA-256 is the only payload-derived echo. |
|
||||
| `GET` | `/excititor/providers/{id}/artifacts` | Lists staged artifacts for the provider (metadata only). |
|
||||
| `GET` | `/excititor/providers/{id}/artifacts/{artifactId}/meta` | Returns the metadata projection for a single artifact. NEVER returns the payload. |
|
||||
| `DELETE` | `/excititor/providers/{id}/artifacts/{artifactId}` | Removes a staged artifact. |
|
||||
|
||||
Size caps: **10 MiB per artifact, 50 MiB per provider total**. Tunable via `Excititor:ProviderArtifacts:MaxArtifactSizeBytes` / `MaxProviderTotalSizeBytes` environment settings. Cap violations surface as HTTP 400 with error codes `ARTIFACT_TOO_LARGE` / `ARTIFACT_PROVIDER_QUOTA_EXCEEDED`. Tenant isolation is enforced via RLS on `vex.provider_artifact_refs.tenant_id`.
|
||||
|
||||
At runtime, cosign/TUF verification libraries need on-disk paths. The effective-settings resolver materializes referenced artifacts to a per-request scratch directory (chmod 0700 on POSIX) and cleans up on session dispose — see `VexProviderArtifactMaterializer`.
|
||||
|
||||
#### 4.3 Canonical setup flows
|
||||
|
||||
**A. Keyless Sigstore, single image, default registry:**
|
||||
|
||||
```bash
|
||||
stella vex providers configure excititor:oci-openvex \
|
||||
--server https://excititor.example.internal \
|
||||
--image ghcr.io/acme/app:v1.2.3 \
|
||||
--set cosignMode=Keyless \
|
||||
--set cosignIssuer=https://token.actions.githubusercontent.com \
|
||||
--set cosignSubject='https://github.com/acme/repo/.github/workflows/release.yml@refs/tags/v1.*'
|
||||
```
|
||||
|
||||
**B. KeyPair cosign verification with operator-supplied public key:**
|
||||
|
||||
```bash
|
||||
stella vex providers configure excititor:oci-openvex \
|
||||
--server https://excititor.example.internal \
|
||||
--image ghcr.io/acme/app:v1 \
|
||||
--image ghcr.io/acme/sidecar:v1 \
|
||||
--set cosignMode=KeyPair \
|
||||
--upload-artifact cosignKey=@/secure/cosign/cosign.pub \
|
||||
--upload-artifact cosignCertificate=@/secure/cosign/cosign.crt
|
||||
```
|
||||
|
||||
**C. TUF trust roots + offline bundle:**
|
||||
|
||||
```bash
|
||||
stella vex providers configure excititor:oci-openvex \
|
||||
--server https://excititor.example.internal \
|
||||
--image registry.example.com/platform/base@sha256:... \
|
||||
--upload-artifact tufRoots=@/air-gap/tuf/root.json \
|
||||
--set offlineBundleRoot=/var/lib/stella/offline/openvex
|
||||
```
|
||||
|
||||
**D. Private registry with basic auth:**
|
||||
|
||||
```bash
|
||||
stella vex providers configure excititor:oci-openvex \
|
||||
--server https://excititor.example.internal \
|
||||
--image internal.registry.example.com/team/app:v3 \
|
||||
--set registryAuthority=internal.registry.example.com:5000 \
|
||||
--set registryUsername=stella-reader \
|
||||
--set registryPassword=<token>
|
||||
```
|
||||
|
||||
#### 4.4 Host-path compatibility (CLI only)
|
||||
|
||||
The CLI exposes `--host-path-compat` to preserve the legacy flow where cosign/TUF material is referenced by absolute path instead of uploaded as an artifact. Use it only when the Excititor server shares a filesystem with the operator workstation (typically a single-node sealed deployment). The UI deliberately never surfaces file-path inputs because the server cannot read arbitrary host paths in production-scale deployments. Host-path mode is visible in CLI `--help` as a compatibility-only flag.
|
||||
|
||||
#### 4.5 Listing and clearing staged artifacts
|
||||
|
||||
```bash
|
||||
# Show all artifacts staged against a provider
|
||||
stella vex providers artifacts excititor:oci-openvex
|
||||
|
||||
# Clear a field's artifact binding (leaves the staged blob — delete the blob separately if unused)
|
||||
stella vex providers configure excititor:oci-openvex --clear-artifact cosignCertificate
|
||||
```
|
||||
|
||||
#### 4.6 UI
|
||||
|
||||
The Web panel renders conditionally from the existing Advisory & VEX Sources detail flow when `provider.kind === 'oci-openvex'` (or the provider id matches `excititor:oci-openvex`). It provides the image-subscription list editor, artifact-reference slots with upload + staged-meta rendering, and a readiness hint surface for the OCI-specific blocked sub-codes. File-path inputs are intentionally omitted from the UI — use the CLI for host-path compat when that flow is needed.
|
||||
|
||||
## 6. References
|
||||
|
||||
|
||||
Reference in New Issue
Block a user