docs(adr): add ADR-00020 unified importer credential resolution - #2604
docs(adr): add ADR-00020 unified importer credential resolution#2604ruromero wants to merge 1 commit into
Conversation
Reviewer's GuideAdds ADR-00020 proposing a unified auth model and credential-source abstraction for HTTP importers, with runtime resolution from inline values, environment variables, or mounted files; it also defines shared client construction, Kubernetes Secret deployment patterns, Quay migration behavior, and explicit boundaries for future mTLS and git credential support. Sequence diagram for runtime credential resolution and HTTP importsequenceDiagram
participant Importer
participant Resolver as CredentialSourceResolver
participant Runtime as EnvOrMountedFile
participant Client as SharedHttpClient
participant Source as ExternalHTTPSource
Importer->>Resolver: resolve credential sources
Resolver->>Runtime: read env variable or file
Runtime-->>Resolver: credential value
Resolver->>Client: build authenticated client
Client->>Source: authenticated HTTP request
Source-->>Client: response
Client-->>Importer: imported data
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 3 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="docs/adrs/00020-importer-credential-resolution.md" line_range="16-17" />
<code_context>
+
+* **Quay importer:** `api_token: Option<String>` — a bearer token stored as plaintext in the
+ importer's `configuration` JSONB column.
+* **Git-based importers** (OSV, CSAF, CVE, CWE, NVD, SBOM): credential callback in the git2
+ walker, no explicit auth model in the importer configuration.
+
+### Problems
</code_context>
<issue_to_address>
**issue:** The ADR incorrectly classifies NVD as a git2-based importer with a credential callback. The NVD runner downloads GitHub release data through reqwest, so this description gives an incorrect inventory of the importers covered by the shared HTTP credential model and leaves NVD's authentication path ambiguous.
**Suggested fix:** List NVD with the HTTP-based importers and document whether its GitHub API and release downloads use the shared client.
</issue_to_address>
### Comment 2
<location path="docs/adrs/00020-importer-credential-resolution.md" line_range="134-136" />
<code_context>
+
+### Shared HTTP client construction
+
+Per-importer client-building logic is replaced by a single shared function that resolves
+credentials from their configured source and builds an authenticated HTTP client. All HTTP-based
+importers delegate to this function instead of implementing their own auth handling.
+
+### Migration strategy
</code_context>
<issue_to_address>
**issue (broader_impact):** The proposed shared client construction does not cover all HTTP traffic performed by the existing Quay importer. Quay uses a reqwest client for the registry API but an independent OCI client for manifest and blob downloads, and that OCI client is currently configured with anonymous authentication; applying the shared function only to the HTTP client leaves authenticated OCI pulls unauthorized for private registries.
**Triggers:** When a Quay importer accesses a private repository or private SBOM attachment.
**Suggested fix:** Define the shared authentication integration for both the Quay API client and the OCI client, or explicitly scope the ADR to clients that support the required authentication mechanism.
```suggestion
For HTTP clients that support the required authentication mechanism, per-importer client-building
logic is replaced by a single shared function that resolves credentials from their configured source
and builds an authenticated HTTP client. This ADR does not cover OCI clients used for manifest and
blob downloads, which require separate authentication integration.
```
</issue_to_address>
### Comment 3
<location path="docs/adrs/00020-importer-credential-resolution.md" line_range="49-56" />
<code_context>
+
+### Credential source abstraction
+
+Instead of storing credential values directly, each credential is wrapped in a source descriptor
+that specifies *where* to obtain the actual secret at runtime. Three source types are supported:
+
+| Source | Description | Use case |
+|--------|-------------|----------|
+| **Inline** | Literal value in the configuration | Development and testing |
+| **Env** | Read from an environment variable | K8s `envFrom: secretRef` |
+| **File** | Read from a file path | K8s volume-mounted secrets |
+
+The file source reads a single value per file. This maps directly to Kubernetes Secret volume
</code_context>
<issue_to_address>
**🚨 issue (security):** A file credential source accepts an arbitrary path from importer configuration and resolves it at runtime, so an importer creator can point it at any readable file on the Trustify host and cause its contents to be sent as an authentication value to the configured remote endpoint. This turns the credential resolver into an unrestricted local-file disclosure primitive.
**Triggers:** When a user who can create or update an importer controls both the file path and the source URL.
**Suggested fix:** Restrict file sources to an approved secret-directory allowlist and validate source URLs and credential references before resolving them.
```suggestion
Instead of storing credential values directly, each credential is wrapped in a source descriptor
that specifies *where* to obtain the actual secret at runtime. Three source types are supported:
| Source | Description | Use case |
|--------|-------------|----------|
| **Inline** | Literal value in the configuration | Development and testing |
| **Env** | Read from an environment variable | K8s `envFrom: secretRef` |
| **File** | Read from a file path under an approved secret-directory allowlist | K8s volume-mounted secrets |
Source URLs and credential references must be validated before resolving credentials. File sources
must be restricted to the approved secret-directory allowlist.
```
</issue_to_address>Sourcery assessment
Approval pending. 3 findings to address first.
Blocking findings: docs/adrs/00020-importer-credential-resolution.md:17, docs/adrs/00020-importer-credential-resolution.md:136, docs/adrs/00020-importer-credential-resolution.md:56
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
3d56f90 to
1ebb37f
Compare
| |--------|-------------|----------| | ||
| | **Inline** | Literal value in the configuration | Development and testing | | ||
| | **Env** | Read from an environment variable | K8s `envFrom: secretRef` | | ||
| | **File** | Read from a file path | K8s volume-mounted secrets | |
There was a problem hiding this comment.
That might be dangerous, unless we somehow limit file system access.
There was a problem hiding this comment.
This is a standard practice in k8s environments. Containers are expected to limit accessible paths via volume mounts, not application-level allowlists.
Adding an allowlist would add deployment complexity without meaningful security gain in containerized environments.
| and `/run/secrets/password`). No multi-key file parsing is needed because K8s handles the | ||
| key-to-file mapping natively. | ||
|
|
||
| **Security note:** The file source accepts a path from the importer configuration and reads it |
There was a problem hiding this comment.
I'd say that's still too broad, and won't work for bare metal installations (ansible).
There was a problem hiding this comment.
As this is only related to non containerized environments. I'd just mention it in the documentation and recommend the environment variable approach instead.
Maybe the only thing I'd add to the ADR could be a base path restriction for bare metal environments. Would that be ok?
| clients that create or update importers via the REST API would break if the old field is | ||
| removed immediately. To avoid a breaking API change: | ||
|
|
||
| 1. The Quay importer continues to accept the legacy `api_token` field as a **deprecated** |
There was a problem hiding this comment.
I'm not sure we have to do this. Not sure if we ever agreed on this being stable. Not pushing back on this though.
There was a problem hiding this comment.
I wanted to keep backward compatibility. I prefer the reviewers to decide.
|
|
||
| ### Git-based importer credentials | ||
|
|
||
| Git-based importers (OSV, CSAF, CVE, CWE, SBOM) use git2's credential callback, which operates |
There was a problem hiding this comment.
I think it would be worth exploring this, as we are designing something should unify things already.
There was a problem hiding this comment.
Sounds good. I'll add a proposal for git as well
ctron
left a comment
There was a problem hiding this comment.
This looks great! Some small comments. And I'd like to explore the git2 integration, I think that's something we might tackle at the same time. At least we should understand what that would mean.
4115184 to
cb57cfa
Compare
Proposes a shared auth model with credential source abstraction (inline/env/file) for all HTTP-based importers, enabling production deployments to use K8s Secrets populated by Vault or ESO instead of storing credentials as plaintext in the database. Quay's legacy api_token field is deprecated in the API with backward compatibility during a transition period. Implements TC-5738 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cb57cfa to
7e9253f
Compare
Done @ctron , let me know what you think about the suggested approach. |
Summary
Refs TC-5738
🤖 Generated with Claude Code
Summary by Sourcery
Adopt a shared, reference-based credential resolution model for importer authentication without storing production secrets directly in the database.
Enhancements:
Documentation: