Skip to content

PULP-2257: Add EnvVarHeaderContentGuard for env-backed header validation - #1420

Open
decko wants to merge 2 commits into
pulp:mainfrom
decko:pulp-2257-envvar-header-guard
Open

PULP-2257: Add EnvVarHeaderContentGuard for env-backed header validation#1420
decko wants to merge 2 commits into
pulp:mainfrom
decko:pulp-2257-envvar-header-guard

Conversation

@decko

@decko decko commented Aug 24, 2026

Copy link
Copy Markdown
Member

Summary

  • Add EnvVarHeaderContentGuard (envvar_header type) that compares a configurable request header to a secret read from os.environ[env_var] at request time using hmac.compare_digest.
  • Expose CRUD via /pulp/api/v3/contentguards/service/envvar_header/ with RBAC matching the existing FeatureContentGuard pattern.
  • Add migration, unit tests, functional tests (live API content access), and RBAC access-policy coverage.

Secrets can be rotated by updating the deployment environment and redeploying pods — no need to update guard records in the database.

Test plan

  • Unit tests: pulp_service/tests/unit/test_envvar_header_content_guard.py (7 passed)
  • Functional tests: pulp_service/tests/functional/test_envvar_header_content_guard.py (4 passed, requires ENVVAR_HEADER_GUARD_TEST_SECRET in container env)
  • RBAC: test_envvar_header_content_guard_access_policy_exists
  • CI unit/functional suite on PR

Follow-up

  • app-interface MR to inject production/stage secrets on pulp pods
  • Operators configure header name and env var when creating guards (not fixed in the guard type)

Made with Cursor

Summary by Sourcery

Add an environment-backed header content guard for securely validating guarded content requests.

New Features:

  • Add an environment-backed header content guard that validates Base64-encoded request secrets at access time and supports secret rotation without database updates.
  • Expose CRUD management for the new content guard through the service API with matching role-based access policies.

Documentation:

  • Document the new environment-backed header content guard and its rotation behavior.

Tests:

  • Add unit, functional content-access, and RBAC coverage for the new content guard.

Introduce a content guard that compares a request header to a secret read
from os.environ at request time, enabling rotation via deployment config
without updating guard records in the database.

Assisted-by: Cursor
@sourcery-ai

sourcery-ai Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds an envvar_header content guard whose secret is resolved from the pod environment for every request, exposes it through a dedicated CRUD/RBAC endpoint, and validates behavior with unit, live API, and access-policy tests.

Sequence diagram for environment-backed header validation

sequenceDiagram
    participant Client
    participant ContentGuard as EnvVarHeaderContentGuard
    participant Environment as os.environ

    Client->>ContentGuard: permit(request)
    ContentGuard->>ContentGuard: request.headers.get(header_name)
    ContentGuard->>Environment: get(env_var)
    Environment-->>ContentGuard: expected secret
    ContentGuard->>ContentGuard: hmac.compare_digest(header_content, expected.strip())
    alt header matches
        ContentGuard-->>Client: allow request
    else header missing, secret unavailable, or mismatch
        ContentGuard-->>Client: PermissionError Access denied
    end
Loading

File-Level Changes

Change Details Files
Implemented request-time validation of a configurable header against an environment-backed secret.
  • Added a polymorphic content guard with configurable header and environment-variable names.
  • Rejects missing, empty, or mismatched values and uses constant-time comparison after trimming the configured secret.
  • Keeps the secret out of the database and API representation, enabling rotation through pod environment updates.
pulp_service/pulp_service/app/models.py
pulp_service/pulp_service/app/serializers.py
CHANGES/2257.feature
Exposed the new guard through a dedicated API endpoint with object-level RBAC.
  • Added the envvar_header serializer and viewset endpoint.
  • Mirrored FeatureContentGuard CRUD, role-management, creation-hook, and queryset-scoping policy patterns.
  • Added the model and role-management permission via migration.
pulp_service/pulp_service/app/viewsets.py
pulp_service/pulp_service/app/serializers.py
pulp_service/pulp_service/app/migrations/0019_envvarheadercontentguard.py
pulp_service/pulp_service/app/models.py
Added coverage for guard behavior, live content access, and access-policy registration.
  • Unit-tested successful validation and denial paths, including unset, blank, and newline-terminated environment values.
  • Added functional API creation/retrieval and guarded RPM distribution access tests.
  • Added an RBAC access-policy existence and action coverage test.
pulp_service/pulp_service/tests/unit/test_envvar_header_content_guard.py
pulp_service/pulp_service/tests/functional/test_envvar_header_content_guard.py
pulp_service/pulp_service/tests/functional/test_rbac.py
pulp_service/pulp_service/tests/functional/conftest.py
pulp_service/pulp_service/tests/functional/constants.py

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 reviewed your changes and they look great!

Sourcery assessment

Needs a human reviewer. If the header comparison is wrong, a protected distribution could expose its content to callers who do not possess the intended secret, or deny legitimate callers. Reverting restores the prior guard implementation but cannot retract any content that was already exposed.


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.

Decode the request header from Base64 to UTF-8 before comparing against
the plaintext environment variable. Compare UTF-8 bytes with hmac.compare_digest
so non-ASCII secrets work. Update tests and API help text accordingly.

Assisted-by: Cursor
@decko

decko commented Aug 26, 2026

Copy link
Copy Markdown
Member Author

Updated implementation to Base64-decode the request header before comparing to the plaintext env var (UTF-8 byte compare via hmac.compare_digest). Supports non-ASCII secrets on the wire.

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.

1 participant