Skip to content

docs(adr): add ADR-00020 unified importer credential resolution - #2604

Open
ruromero wants to merge 1 commit into
guacsec:mainfrom
ruromero:docs/adr-00020-importer-credential-resolution
Open

docs(adr): add ADR-00020 unified importer credential resolution#2604
ruromero wants to merge 1 commit into
guacsec:mainfrom
ruromero:docs/adr-00020-importer-credential-resolution

Conversation

@ruromero

@ruromero ruromero commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Proposes a shared auth model (Basic, Bearer, API key) with a credential source abstraction (inline/env/file) for all HTTP-based importers
  • Enables production deployments to reference secrets via env vars or mounted files instead of storing credentials as plaintext in the database
  • Integrates with Kubernetes Secrets populated by Vault or External Secrets Operator

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:

  • Define a unified authentication and credential-source model for HTTP and Git-based importers, supporting inline, environment-variable, and file-backed secrets.
  • Standardize authenticated client construction and provide a Kubernetes-compatible pattern for external secret injection and runtime credential rotation.
  • Specify migration and backward-compatibility handling for existing Quay API tokens while preserving unauthenticated and legacy Git behavior.

Documentation:

  • Add ADR-00020 documenting unified importer credential resolution, supported auth methods, deployment patterns, migration strategy, alternatives, and future scope.

@sourcery-ai

sourcery-ai Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds 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 import

sequenceDiagram
    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
Loading

File-Level Changes

Change Details Files
Defines a shared authentication and runtime credential-resolution design for HTTP-based importers.
  • Adds Basic, Bearer, and API-key authentication types.
  • Introduces inline, environment-variable, and file-based credential sources resolved at import time.
  • Specifies a shared authenticated HTTP client builder for importer implementations.
  • Documents Kubernetes Secret integration patterns, including env and mounted-file usage.
docs/adrs/00020-importer-credential-resolution.md
Defines migration and compatibility handling for existing importer credentials.
  • Rewrites Quay api_token configuration into the new auth structure using inline sources.
  • Relies on startup migration timing rather than serde compatibility shims.
  • Documents adoption for new importers and preservation of existing tokens.
docs/adrs/00020-importer-credential-resolution.md
Establishes scope boundaries, alternatives, and future extension points for credential management.
  • Rejects direct Vault integration, database encryption, and per-importer auth traits in favor of deployment-level secret projection and a shared model.
  • Keeps mTLS and git-based importer credentials out of scope while designing the source abstraction for future reuse.
  • Documents operational consequences, including file-based secret rotation without pod restart.
docs/adrs/00020-importer-credential-resolution.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai 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.

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


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread docs/adrs/00020-importer-credential-resolution.md Outdated
Comment thread docs/adrs/00020-importer-credential-resolution.md
Comment thread docs/adrs/00020-importer-credential-resolution.md
@ruromero
ruromero force-pushed the docs/adr-00020-importer-credential-resolution branch 2 times, most recently from 3d56f90 to 1ebb37f Compare August 26, 2026 13:39
|--------|-------------|----------|
| **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 |

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.

That might be dangerous, unless we somehow limit file system access.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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

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.

I'd say that's still too broad, and won't work for bare metal installations (ansible).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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**

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.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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

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.

I think it would be worth exploring this, as we are designing something should unify things already.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sounds good. I'll add a proposal for git as well

@ctron ctron 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.

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.

@ruromero
ruromero force-pushed the docs/adr-00020-importer-credential-resolution branch 2 times, most recently from 4115184 to cb57cfa Compare August 27, 2026 14:42
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>
@ruromero
ruromero force-pushed the docs/adr-00020-importer-credential-resolution branch from cb57cfa to 7e9253f Compare August 27, 2026 14:43
@ruromero

Copy link
Copy Markdown
Contributor Author

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.

Done @ctron , let me know what you think about the suggested approach.
Thanks for your comments and review!

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

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants