Skip to content

Conversation

akash2237778
Copy link
Collaborator

@akash2237778 akash2237778 commented Jun 3, 2025

Description

This GitHub Actions workflow automates the process of pulling, tagging, pushing, and signing a container image. Triggered by a repository_dispatch event (sign-image) or manual workflow_dispatch, it runs on an ubuntu-latest environment with the following steps:

  1. Checkout Code: Clones the repository using actions/checkout@v4.

  2. Docker Hub Login: Authenticates with Docker Hub using provided credentials from repository_dispatch API call.

  3. Pull Image: Pulls the specified container image using the pull_tag input.

  4. Extract Image Name: Derives the image name from the push_tag input and stores it in the environment.

  5. Get Image Digest: Retrieves the image digest for verification.

  6. Install Cosign: Installs Cosign (v2.2.1) for signing the image.

  7. Tag and Push Image: Tags the pulled image with push_tag and pushes it to Docker Hub.

  8. Sign Image: Signs the pushed image using Cosign with experimental features enabled.

  9. Output Digest: Logs the signed image's digest for verification.

The workflow uses OIDC token permissions for secure operations and outputs the digest for post-signing verification. It ensures a streamlined process for pulling, tagging, pushing, and cryptographically signing container images.

Fixes #25

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • I have not reduced the existing code coverage
  • I have added docstrings following the Python style guidelines of this project to all new modules, classes, methods and functions are documented with docstrings following; I have updated any previously existing docstrings, if applicable
  • I have updated any sections of the app's documentation that are affected by the proposed changes, if applicable

Summary by Sourcery

Add a GitHub Actions workflow to automate pulling, tagging, pushing, and signing Docker images with Cosign

New Features:

  • Create image-signer workflow triggered by repository_dispatch and manual dispatch
  • Automate Docker Hub login, image pull, tag, push, and Cosign-based signing

Enhancements:

  • Extract and output the image digest for post-signing verification

CI:

  • Grant OIDC id-token permission and configure workflow triggers and jobs

Copy link

sourcery-ai bot commented Jun 3, 2025

Reviewer's Guide

Adds a GitHub Actions workflow that automates pulling, re-tagging, pushing, and cryptographically signing Docker images using repository_dispatch or manual triggers, OIDC-backed Docker Hub authentication, digest extraction, Cosign installation, and logging for post-sign verification.

Sequence Diagram for the Image Pull, Tag, Push, and Sign Workflow

sequenceDiagram
    actor User/System
    participant GHA as GitHub Actions Workflow
    participant DockerHub as Docker Hub
    participant Cosign

    User/System->>GHA: Trigger (repository_dispatch: sign-image / workflow_dispatch with payload)
    GHA->>GHA: Checkout code
    GHA->>DockerHub: Login (credentials from client_payload)
    GHA->>DockerHub: Pull Image (client_payload.pull_tag)
    GHA->>GHA: Extract image_name (from client_payload.push_tag)
    GHA->>GHA: Get Image Digest (from client_payload.pull_tag)
    GHA->>GHA: Install Cosign (v2.2.1)
    GHA->>GHA: Tag Image (client_payload.pull_tag to client_payload.push_tag)
    GHA->>DockerHub: Push Image (client_payload.push_tag)
    GHA->>Cosign: Sign Image (client_payload.push_tag)
    Cosign-->>GHA: Confirmation
    GHA->>GHA: Output Digest for Verification
Loading

File-Level Changes

Change Details Files
Define workflow triggers and permissions
  • Trigger on repository_dispatch (sign-image) and workflow_dispatch
  • Grant contents read and id-token write for OIDC
.github/workflows/image-signer.yaml
Authenticate and manage image lifecycle
  • Checkout repository with actions/checkout@v4
  • Log in to Docker Hub via docker/login-action@v3 using client_payload credentials
  • Pull source image and tag it for push
.github/workflows/image-signer.yaml
Extract metadata and retrieve digest
  • Derive IMAGE_NAME from push_tag into GITHUB_ENV
  • Inspect pulled image to capture its digest as a workflow output
.github/workflows/image-signer.yaml
Install Cosign and sign the image
  • Install Cosign v2.2.1 via sigstore/cosign-installer@v3
  • Sign pushed image with COSIGN_EXPERIMENTAL enabled
.github/workflows/image-signer.yaml
Log final digest for verification
  • Output signed image digest and sample cosign verify command
.github/workflows/image-signer.yaml

Assessment against linked issues

Issue Objective Addressed Explanation
#25 Implement a centralized and secure approach to store and access DCT keys for signing images. The PR implements a GitHub Actions workflow to pull, tag, push, and sign container images. However, it does not address the central storage and secure access of DCT keys. It uses Cosign for signing, but the keys are not managed in a centralized or secure manner as described in the issue.
#25 Reduce the need to upload separate DCT keys for each repository. The PR doesn't describe how it reduces the need to upload separate DCT keys for each repository. The workflow uses Cosign, but the key management aspect is not addressed.
#25 Simplify the process of updating or rotating DCT keys. The PR doesn't describe how it simplifies updating or rotating DCT keys. The workflow automates the signing process, but the key management aspect remains unaddressed.

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

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @akash2237778 - I've reviewed your changes and found some issues that need to be addressed.

Blocking issues:

  • Potential security risk in using repository_dispatch payload for credentials (link)

General comments:

  • After pushing the image you should retrieve the new digest rather than using the digest from the original pulled image, so your verification output remains accurate.
  • Make the cosign signing step explicit by specifying whether you’re using keyless OIDC or passing a private key (e.g. with --key), and ensure any required environment variables (COSIGN_PASSWORD or key) are defined so the workflow doesn’t hang.
Here's what I looked at during the review
  • 🟡 General issues: 3 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟢 Documentation: all looks good

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.


on:
repository_dispatch:
types: [sign-image]
Copy link

Choose a reason for hiding this comment

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

🚨 issue (security): Potential security risk in using repository_dispatch payload for credentials

Store credentials as GitHub secrets instead of passing them via client_payload, and restrict workflow dispatch permissions to trusted users.

@akash2237778 akash2237778 requested review from lvarin and uniqueg June 3, 2025 09:10
uses: docker/login-action@v3
with:
registry: docker.io
username: ${{ github.event.client_payload.docker_username }}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@uniqueg do you think Container Registry will remain same for one pubgrade instance? If yes, then we can move these registry credentials to Github Secrets.

Copy link
Member

Choose a reason for hiding this comment

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

Instance: yes. Implementation: no. That is, one organization deploying Pubgrade will almost certainly use a different set of credentials than another organization deploying their own Pubgrade instance.

Would one code repository use different instances/deployments of Pubgrade, connected with different registries? Possibly. But it's not a feature we absolutely have to support. At least not now.

Does that answer your question?

Copy link
Member

@uniqueg uniqueg left a comment

Choose a reason for hiding this comment

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

Thanks a lot @akash2237778! Looks pretty good, but I have a couple of questions. Most importantly, I think this really needs comprehensive documentation.


- name: Sign container image
env:
COSIGN_EXPERIMENTAL: "1"
Copy link
Member

Choose a reason for hiding this comment

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

What is the significance of that? Why is it required? What are potential risks?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This flag is needed to use Keyless signing in Cosign. I'll check if we can remove it with some latest version of cosign

run: docker tag ${{ github.event.client_payload.pull_tag }} ${{ github.event.client_payload.push_tag }}

- name: Push Tagged Image
run: docker push ${{ github.event.client_payload.push_tag }}
Copy link
Member

Choose a reason for hiding this comment

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

Why are we pushing before we are signing? Is this the usual flow, i.e., are only pushed images signed (on the registry)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Correct this is the usual flow, only pushed images are signed. But image integrity is maintained.

- name: Output Digest for Verification
run: |
echo "Image pushed and signed as ${{ github.event.client_payload.push_tag }} with digest $IMAGE_NAME@${{ steps.get-digest.outputs.digest }}"
echo "Verify with: cosign verify $IMAGE_NAME@${{ steps.get-digest.outputs.digest }}"
Copy link
Member

Choose a reason for hiding this comment

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

Don't we want to explicitly verify the signed image against the digest ourselves in an additional step so that we are sure everything worked as expected?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yes, we can do that. I'll push this change.

Copy link
Member

Choose a reason for hiding this comment

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

It is not immediately obvious why we are pulling an existing image, retag it, push it and sign it. Where does the original image come from? And what event would typically trigger this workflow?

Can you explain in the documentation, together with a concrete example, how this whole process works? The use case has to be crystal clear. Ideally include a diagram.

Copy link
Member

Choose a reason for hiding this comment

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

One more question I have is in the linked issue: Do you think other container registries (Docker Hub) would work as well?

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.

Management of DCT Keys for Image Signing

2 participants