From e5e1ff145c47e791805de701594571064991924f Mon Sep 17 00:00:00 2001 From: Kareem Hepburn Date: Tue, 21 Jan 2025 13:51:43 -0600 Subject: [PATCH] feat: Define reusable workflow for building images and signing --- .actrc | 9 + .../actions/build-container-image/action.yml | 190 ++++++++++++++ .../container-image-metadata/action.yml | 99 ++++++++ .github/workflows/cd.yml | 65 +++++ .github/workflows/ci.yml | 73 ++++++ .../reusable.build-container-image.yml | 237 ++++++++++++++++++ .../reusable.docker-content-trust.yml | 136 ++++++++++ .gitignore | 7 +- dockerfiles/bloodhound.Dockerfile | 2 +- 9 files changed, 816 insertions(+), 2 deletions(-) create mode 100644 .actrc create mode 100644 .github/actions/build-container-image/action.yml create mode 100644 .github/actions/container-image-metadata/action.yml create mode 100644 .github/workflows/cd.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/reusable.build-container-image.yml create mode 100644 .github/workflows/reusable.docker-content-trust.yml diff --git a/.actrc b/.actrc new file mode 100644 index 0000000000..2a40ed97ca --- /dev/null +++ b/.actrc @@ -0,0 +1,9 @@ +--rm +--reuse +--container-options=--privileged +--pull=false +--platform=ubuntu-latest=catthehacker/ubuntu:custom-20.04 +--container-architecture=linux/amd64 +--workflows=./.github/workflows +--secret-file=./.act.secrets +--eventpath=./.act.event.json diff --git a/.github/actions/build-container-image/action.yml b/.github/actions/build-container-image/action.yml new file mode 100644 index 0000000000..397e5a400c --- /dev/null +++ b/.github/actions/build-container-image/action.yml @@ -0,0 +1,190 @@ +# Copyright 2024 Specter Ops, Inc. +# +# Licensed under the Apache License, Version 2.0 +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +--- +name: Build and Publish Container Image + +description: |- + This composite action builds a container image based on the specified Dockerfile and metadata. + It can optionally push the built image to a container registry. + Ensure you have the necessary Docker and QEMU setup steps in your workflow for cross-platform builds. + +inputs: + dockerhub_account: + required: true + description: |- + The DockerHub username or account name used for authentication. + This account must have permissions to push images to the specified repositories. + + Example: 'organization_name' or 'username' + dockerhub_token: + required: true + description: |- + The authentication token or password for the DockerHub account. + Use a personal access token or deploy token for enhanced security. + This token must have appropriate permissions for pulling and pushing images. + ghcr_account: + required: true + description: |- + The GitHub Container Registry (GHCR) username or account name. + Usually this is your GitHub username or organization name. + Must have permissions to push containers to the specified GHCR repositories. + + Example: 'github_username' or 'organization_name' + ghcr_token: + required: true + description: |- + The GitHub personal access token (PAT) for authenticating with GitHub Container Registry. + Token must have the necessary permissions: + - read:packages + - write:packages + - delete:packages (if image overwriting is needed) + build_platforms: + required: true + default: "linux/amd64,linux/arm64" + description: |- + Comma-separated list of target platforms for the container build. + Specifies the CPU architectures and operating systems for which + the container image should be built. + + Examples: + - "linux/amd64" for x86_64 Linux only + - "linux/amd64,linux/arm64" for both x86_64 and ARM64 Linux + - "linux/amd64,linux/arm64,linux/arm/v7" for additional ARM support + image_labels: + required: true + default: "false" + description: |- + A JSON object containing image labels and metadata. + These labels help describe the image and can include information like + version, author, and licenses. + image_tags: + required: true + default: "false" + description: |- + A comma-separated list of tags to assign to the built image. + These tags help identify different versions or variants of the image. + image_provenance: + required: true + default: "false" + description: |- + Whether to include image provenance information in the image metadata. + Provenance information provides details about how the image was built and can be useful for auditing. + image_sbom: + required: true + default: "false" + description: |- + Whether to include a Software Bill of Materials (SBOM) in the image metadata. + An SBOM lists all the software components used in the image, enhancing transparency and security. + dockerfile: + default: Dockerfile + description: |- + The name of the Dockerfile used for building the container image. + If not specified, it defaults to 'Dockerfile' in the repository root. + + Example: 'Dockerfile.prod' for a production-specific Dockerfile. + build_context: + default: "{{defaultContext}}" + description: |- + Build's context is the set of files located in the specified PATH or URL (default Git context) + build_target: + description: |- + The build stage target for multi-stage Docker builds, if applicable. + Specify this if your Dockerfile has multiple stages, and you want to build a specific one. + + Example: 'production' for a multi-stage Dockerfile with a 'production' stage. + build_args: + description: |- + Additional build arguments to pass to the Docker build process. + These arguments can be used to customize the build based on your requirements. + + Example: 'MY_VARIABLE=value' to set an environment variable during the build. + build_contexts: + description: |- + Additional build contexts to pass to Docker build process. + + Define additional build context with specified contents. In Dockerfile the + context can be accessed when FROM name or --from=name is used. When + Dockerfile defines a stage with the same name it is overwritten. + + Example: 'name=path' + build_outputs: + required: true + description: |- + Set build outputs. + cache_from: + description: |- + The source image repository from which to cache layers during the build. + This can help improve build speed by reusing layers from a previously built image. + + Default: type=gha + + Example: 'docker.io/my-app:cache' to cache from a specific image. + default: |- + type=gha + cache_to: + description: |- + The destination image cache settings to optimize the caching strategy during the build. + This input specifies where to store cached layers and how they are scoped. + + Default: type=gha,mode=max + + Example: "type=gha,mode=max,scope=\$\{\{ github.workflow \}\}" + default: |- + type=gha,mode=max + +runs: + using: composite + steps: + - uses: docker/login-action@v3 + name: Authenticate with DockerHub Registry + with: + registry: docker.io + username: ${{ inputs.dockerhub_account }} + password: ${{ inputs.dockerhub_token }} + + - uses: docker/login-action@v3 + name: Authenticate with GitHub Container Registry + with: + registry: ghcr.io + username: ${{ inputs.ghcr_account }} + password: ${{ inputs.ghcr_token }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: ${{ inputs.build_platforms }} + + - name: Set up buildx + id: buildx + uses: docker/setup-buildx-action@v3 + with: + platforms: ${{ inputs.build_platforms }} + + - name: Build + uses: docker/build-push-action@v6 + with: + build-args: ${{ inputs.build_args }} + build-contexts: ${{ inputs.build_contexts }} + cache-from: ${{ inputs.cache_from }} + cache-to: ${{ inputs.cache_to }} + context: ${{ inputs.build_context }} + file: ${{ inputs.dockerfile }} + labels: ${{ inputs.image_labels }} + outputs: ${{ inputs.build_outputs }} + provenance: ${{ inputs.image_provenance }} + sbom: ${{ inputs.image_sbom }} + tags: ${{ inputs.image_tags }} + target: ${{ inputs.build_target }} diff --git a/.github/actions/container-image-metadata/action.yml b/.github/actions/container-image-metadata/action.yml new file mode 100644 index 0000000000..fe8de8ba4f --- /dev/null +++ b/.github/actions/container-image-metadata/action.yml @@ -0,0 +1,99 @@ +# Copyright 2024 Specter Ops, Inc. +# +# Licensed under the Apache License, Version 2.0 +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +--- +name: Generate Container Image Metadata + +description: |- + This composite action generates metadata for a container image and extracts useful information, + such as JSON data, tags, labels, revision, version, and the container image reference. + The metadata is essential for tracking and managing container images effectively. + +inputs: + container_image_repository_name: + required: true + description: |- + TBD ... + + image_flavor: + description: |- + See: https://github.com/docker/metadata-action#flavor-input + + image_tags: + description: |- + See: https://github.com/docker/metadata-action#tags-input + +outputs: + json: + value: ${{ steps.generate-metadata.outputs.json }} + description: |- + The JSON metadata for the container image, including details about the image and its layers. + + tags: + value: ${{ steps.generate-metadata.outputs.tags }} + description: |- + A list of tags associated with the container image, which may include version and branch information. + + labels: + value: ${{ steps.generate-metadata.outputs.labels }} + description: |- + Custom labels associated with the container image, providing additional information and metadata. + + revision: + value: ${{ fromJSON(steps.generate-metadata.outputs.json).labels['org.opencontainers.image.revision'] }} + description: |- + The revision of the container image, if available, typically extracted from metadata labels. + + version: + value: ${{ steps.generate-metadata.outputs.version }} + description: |- + The version of the container image, often derived from tags or other versioning patterns. + + image_reference: + value: ${{ steps.set-image-reference.outputs.image_reference }} + description: |- + The full image reference, including registry, repository, and tag. + This fully qualified name is used to pull or locate a specific image + from a particular registry (e.g., docker.io/myrepo/myimage:tag). + + image_name: + value: ${{ steps.set-image-reference.outputs.image_name }} + description: |- + The image name with repository and tag, typically used locally or + with the default registry. This shorthand version omits the registry + and assumes the default registry if unspecified (e.g., myimage:tag). + +runs: + using: composite + steps: + - name: Generate metadata + id: generate-metadata + uses: docker/metadata-action@v5 + with: + images: ${{ inputs.container_image_repository_name }} + flavor: ${{ inputs.image_flavor }} + tags: |- + type=raw,value=sha-{{sha}}-{{date 'YYYYMMDD-HHmmss'}},priority=1500 + type=sha,format=long,priority=1450 + ${{ inputs.image_tags }} + + - name: Set Container Image Reference + id: set-image-reference + shell: bash + run: |- + image_reference=${{ fromJSON(steps.generate-metadata.outputs.json).tags[0] }} + echo "image_reference=$image_reference" >> $GITHUB_OUTPUT + image_name=$(echo "$image_reference" | sed 's/.*\///') + echo "image_name=$image_name" >> $GITHUB_OUTPUT diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 0000000000..63568c85fe --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,65 @@ +--- +# Copyright 2024 Specter Ops, Inc. +# +# Licensed under the Apache License, Version 2.0 +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +name: Continuous Deployment (CD) + +on: + push: + branches: + - main + - develop + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + - 'v[0-9]+.[0-9]+.[0-9]+rc[0-9]+' + +jobs: + bloodhound-container-image: + name: Build and Publish BloodHound Container Image + uses: ./.github/workflows/reusable.build-container-image.yml + with: + container_image_repository_name: docker.io/specterops/bloodhound + image_sbom: true + image_provenance: mode=max + build_target: bloodhound + build_outputs: type=image,push=true + dockerfile: dockerfiles/bloodhound.Dockerfile + image_cache_from: |- + type=registry,ref=docker.io/specterops/bloodhound:buildcache + type=registry,ref=ghcr.io/specterops/bloodhound:buildcache + image_cache_to: |- + type=registry,ref=docker.io/specterops/bloodhound:buildcache,mode=max + type=registry,ref=ghcr.io/specterops/bloodhound:buildcache,mode=max + secrets: + dockerhub_account: ${{ secrets.DOCKERHUB_USERNAME }} + dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }} + ghcr_account: ${{ github.actor }} + ghcr_token: ${{ secrets.GITHUB_TOKEN }} + gh_access_token: ${{ secrets.GITHUB_TOKEN }} + + docker-content-trust-sign-image: + needs: bloodhound-container-image + name: Sign Docker Image using Docker Content Trust + uses: ./.github/workflows/reusable.docker-content-trust.yml + with: + dockerhub_image_reference: ${{ needs.bloodhound-container-image.outputs.image_reference }} + secrets: + dockerhub_account: ${{ secrets.DOCKERHUB_USERNAME }} + dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }} + gh_access_token: ${{ secrets.GITHUB_TOKEN }} + docker_content_trust_repository_key_id: ${{ secrets.DOCKER_CONTENT_TRUST_REPOSITORY_KEY_ID }} + docker_content_trust_repository_passphrase: ${{ secrets.DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE }} + docker_content_trust_repository_key: ${{ secrets.DOCKER_CONTENT_TRUST_REPOSITORY_KEY }} + docker_content_trust_repository_public_key: ${{ secrets.DOCKER_CONTENT_TRUST_REPOSITORY_PUBLIC_KEY }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..5d2e807930 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,73 @@ +--- +# Copyright 2024 Specter Ops, Inc. +# +# Licensed under the Apache License, Version 2.0 +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +name: Continuous Integration (CI) + +on: + pull_request: + branches: + - main + - develop + types: + - assigned + - opened + - synchronize + - reopened + - closed + # https://stephencharlesweiss.com/github-actions-run-on-merge-only + push: + branches: + - main + - develop + +permissions: write-all + +jobs: + cla-check: + if: ${{ !github.event.act }} + name: Process CLA + runs-on: ubuntu-latest + steps: + - name: CLA Assistant + if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' + uses: contributor-assistant/github-action@v2.2.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PERSONAL_ACCESS_TOKEN: ${{ secrets.REPO_SCOPE }} + + bloodhound-container-image: + needs: cla-check + name: Build BloodHound Container Image + uses: ./.github/workflows/reusable.build-container-image.yml + with: + container_image_repository_name: docker.io/specterops/bloodhound + build_target: bloodhound + image_sbom: false + image_provenance: false + build_outputs: type=image,push=false + dockerfile: dockerfiles/bloodhound.Dockerfile + image_cache_from: |- + type=registry,ref=docker.io/specterops/bloodhound:buildcache + type=registry,ref=ghcr.io/specterops/bloodhound:buildcache + image_cache_to: |- + type=registry,ref=docker.io/specterops/bloodhound:buildcache,mode=max + type=registry,ref=ghcr.io/specterops/bloodhound:buildcache,mode=max + secrets: + dockerhub_account: ${{ secrets.DOCKERHUB_USERNAME }} + dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }} + ghcr_account: ${{ github.actor }} + ghcr_token: ${{ secrets.GITHUB_TOKEN }} + gh_access_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/reusable.build-container-image.yml b/.github/workflows/reusable.build-container-image.yml new file mode 100644 index 0000000000..2f1eb7143b --- /dev/null +++ b/.github/workflows/reusable.build-container-image.yml @@ -0,0 +1,237 @@ +--- +# Copyright 2024 Specter Ops, Inc. +# +# Licensed under the Apache License, Version 2.0 +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +name: Container Image Builder Pipeline +run-name: Building Container Image for ${{ inputs.image_repository }} by @${{ github.actor }} on ${{ github.ref_name }} + +on: + workflow_call: + inputs: + build_context: + type: string + description: |- + Specifies the build context directory for Docker. + Choose your build context carefully to: + - Control which files are available during build + - Optimize build performance by limiting included files + - Ensure security by excluding sensitive files + - Support multi-stage builds with specific contexts + container_image_repository_name: + type: string + required: true + description: |- + The name of the repository where the container image will be stored in the container registry. + This repository name uniquely identifies the image within the registry. + + Example: 'docker.io/specterops/bloodhound'. + dockerfile: + type: string + description: |- + The name of the Dockerfile used for building the container image. + If not specified, it defaults to 'Dockerfile' in the repository root. + + Example: 'Dockerfile.prod' for a production-specific Dockerfile. + build_target: + type: string + description: |- + The build stage target for multi-stage Docker builds, if applicable. + Specify this if your Dockerfile has multiple stages, and you want to + build a specific one. + + Example: 'production' for a multi-stage Dockerfile with a 'production' + stage. + build_args: + type: string + description: |- + Build-time variables that customize the container build process. + Use build args to: + - Inject version information at build time + - Configure build-specific settings without modifying Dockerfile + - Support different configurations for dev/staging/prod + - Pass secrets safely during build (using --secret) + + Predefined values may already be present, and any inputs provided here will be appended. + + Example: 'VERSION=${GITHUB_SHA}' to embed git commit information + build_contexts: + type: string + description: |- + Additional named build contexts for multi-stage builds. + Use multiple build contexts when you need to: + - Separate build dependencies from runtime dependencies + - Include files from different locations without copying + - Optimize layer caching for different build stages + - Support complex multi-stage build patterns + + In Dockerfile, access these contexts using FROM name or --from=name. + Note: These contexts override same-named stages in the Dockerfile. + + Example: 'deps=/path/to/dependencies,assets=/path/to/static-files' + build_outputs: + type: string + required: true + description: |- + Specifies the output configuration for the Docker BuildKit build process. + Define the type and location of build outputs, such as: + - Container images to be pushed to registries + - Local tar archives for artifact storage + - Cache exports for future builds + - Custom output types for specialized needs + + Format should follow BuildKit syntax, e.g.: + - 'type=image,name=registry/image:tag,push=false' + - 'type=docker,dest=/path/to/image.tar' + - 'type=registry,ref=registry/image:tag' + + Multiple outputs can be specified using comma separation. + image_provenance: + type: string + description: |- + Controls inclusion of SLSA provenance in image metadata. + Enable provenance when you need to: + - Meet supply chain security requirements + - Provide audit trails for compliance + - Verify image build authenticity + - Support automated security policy enforcement + + Provenance data includes build source, tooling, and environment details. + image_sbom: + type: string + default: "false" + description: |- + Controls generation of Software Bill of Materials (SBOM) for the image. + Enable SBOM generation to: + - Track and audit all software dependencies + - Identify and respond to security vulnerabilities + - Meet compliance requirements for software transparency + - Support automated vulnerability scanning + - Enable dependency analysis and lifecycle management + image_flavor: + type: string + description: |- + Additional image flavor information or tags. + image_cache_from: + type: string + description: |- + The source image repository from which to cache layers during the build. + This can help improve build speed by reusing layers from a previously built image. + + Example: 'docker.io/my-app:cache' to cache from a specific image. + image_cache_to: + type: string + description: |- + The destination image cache settings to optimize the caching strategy during the build. + This input specifies where to store cached layers and how they are scoped. + Values provided here will be appended to any default cache settings. + + Predefined values may already be present, and any inputs provided here will be appended. + + Example: "type=gha,mode=max,scope=\$\{\{ github.workflow \}\}" + build_output_tar_dir: + type: string + description: |- + The directory path where the tar file of the built image will be saved, + to be used as an artifact upload location with the `actions/upload-artifact@v4` GitHub Action. + This tar archive can then be retrieved from the workflow artifacts for further use or distribution. + default: "/tmp" + timeout_minutes: + description: Job timeout configuration in minutes + type: number + default: 30 + push_image: + type: boolean + default: false + description: |- + Whether to push the built container image to the registry after building. + Set this to 'true' if you want to automatically push the image. + + Example: 'true' to push the image to the registry, 'false' to skip pushing. + secrets: + dockerhub_account: + required: true + dockerhub_token: + required: true + ghcr_account: + required: true + ghcr_token: + required: true + gh_access_token: + required: true + outputs: + image_reference: + value: ${{ jobs.build-container-image.outputs.image_reference }} + image_name: + value: ${{ jobs.build-container-image.outputs.image_name }} + +jobs: + build-container-image: + name: Build and Package ${{ inputs.image_repository }} Container + runs-on: ubuntu-latest + timeout-minutes: ${{ inputs.timeout_minutes }} + outputs: + image_reference: ${{ steps.container-image-metadata.outputs.image_reference }} + image_name: ${{ steps.container-image-metadata.outputs.image_name }} + steps: + - name: Checkout Source Code Repository + uses: actions/checkout@v4 + + - if: ${{ github.repository != 'SpecterOps/BloodHound' }} + name: Checkout Reusable Workflows and Composite Actions + uses: actions/checkout@v4 + with: + clean: false + repository: SpecterOps/BloodHound + ref: main + token: ${{ secrets.gh_access_token }} + sparse-checkout-cone-mode: false + sparse-checkout: |- + .github/actions + + - uses: ./.github/actions/container-image-metadata + id: container-image-metadata + with: + container_image_repository_name: ${{ inputs.container_image_repository_name }} + image_flavor: ${{ inputs.image_flavor }} + + - uses: ./.github/actions/build-container-image + id: build-container-image + # env: + # DOCKER_CONTENT_TRUST: 1 + with: + dockerhub_account: ${{ secrets.dockerhub_account }} + dockerhub_token: ${{ secrets.dockerhub_token }} + ghcr_account: ${{ secrets.ghcr_account }} + ghcr_token: ${{ secrets.ghcr_token }} + build_args: |- + BUILDTIME=${{ fromJSON(steps.container-image-metadata.outputs.json).labels['org.opencontainers.image.created'] }} + VERSION=${{ steps.container-image-metadata.outputs.version }} + REVISION=${{ steps.container-image-metadata.outputs.revision }} + ${{ inputs.build_args }} + build_context: ${{ inputs.build_context }} + build_contexts: ${{ inputs.build_contexts }} + build_target: ${{ inputs.build_target }} + build_outputs: ${{ inputs.build_outputs }} + cache_from: ${{ inputs.image_cache_from }} + cache_to: ${{ inputs.image_cache_to }} + dockerfile: ${{ inputs.dockerfile }} + image_labels: |- + org.opencontainers.image.authors=https://github.com/orgs/SpecterOps/teams/bloodhound-engineering + org.opencontainers.image.licenses=Apache License, Version 2.0 + org.opencontainers.image.vendor=SpecterOps + image_provenance: ${{ inputs.image_provenance }} + image_sbom: ${{ inputs.image_sbom }} + image_tags: ${{ steps.container-image-metadata.outputs.tags }} diff --git a/.github/workflows/reusable.docker-content-trust.yml b/.github/workflows/reusable.docker-content-trust.yml new file mode 100644 index 0000000000..799fe49210 --- /dev/null +++ b/.github/workflows/reusable.docker-content-trust.yml @@ -0,0 +1,136 @@ +--- +# Copyright 2024 Specter Ops, Inc. +# +# Licensed under the Apache License, Version 2.0 +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +name: Docker Content Trust +run-name: Running Docker Content Trust for ${{ inputs.dockerhub_image_reference }} by @${{ github.actor }} on ${{ github.ref_name }} + +on: + workflow_call: + inputs: + dockerhub_image_reference: + type: string + required: true + description: > + The full reference to the Docker image that needs to be signed, + including registry, repository name, and tag/digest (e.g. + 'organization/image:tag'). This image must already exist in DockerHub + and be accessible with the provided credentials. + docker_content_trust_server: + type: string + default: https://notary.docker.io + timeout_minutes: + description: Job timeout configuration in minutes + type: number + default: 30 + secrets: + gh_access_token: + required: true + dockerhub_account: + required: true + dockerhub_token: + required: true + docker_content_trust_repository_key_id: + required: true + docker_content_trust_repository_passphrase: + required: true + docker_content_trust_repository_key: + required: true + docker_content_trust_repository_public_key: + required: true + +jobs: + sign-image: + name: Sign Image + runs-on: ubuntu-latest + timeout-minutes: ${{ inputs.timeout_minutes }} + steps: + - if: ${{ github.repository != 'SpecterOps/BloodHound' }} + name: Checkout Reusable Workflows and Composite Actions + uses: actions/checkout@v4 + with: + clean: false + repository: SpecterOps/BloodHound + ref: main + token: ${{ secrets.gh_access_token }} + sparse-checkout-cone-mode: false + sparse-checkout: |- + .github/actions + .github/workflows + + - uses: docker/login-action@v3 + name: Authenticate with DockerHub Registry + with: + registry: docker.io + username: ${{ secrets.dockerhub_account }} + password: ${{ secrets.dockerhub_token }} + + - name: Prepare Docker Content Trust Credentials + env: + DOCKER_CONTENT_TRUST: "1" + DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ secrets.docker_content_trust_repository_passphrase }} + DOCKER_CONTENT_TRUST_SERVER: ${{ inputs.docker_content_trust_server }} + shell: bash + run: |- + set eu; + echo "::group::Loading Docker Content Trust Key" + echo "::notice title=Ensure .docker/trust/private/ directory exists" + mkdir -p ~/.docker/trust/private/ + echo "::notice title=Writing docker content trust key" + echo "${{ secrets.docker_content_trust_repository_key }}" >> ~/.docker/trust/private/${{ secrets.docker_content_trust_repository_key_id }}.key + echo "::notice title=Set appropriate file permissions for the docker content trust key" + chmod 600 ~/.docker/trust/private/${{ secrets.docker_content_trust_repository_key_id }}.key + echo "::notice title=Load docker content trust key" + docker trust key load ~/.docker/trust/private/${{ secrets.docker_content_trust_repository_key_id }}.key + echo "::groupend::" + + - name: Pull Image + shell: bash + run: |- + set eu; + echo "::notice title=Pulling Image::${{ inputs.dockerhub_image_reference }}" + docker pull ${{ inputs.dockerhub_image_reference }} + + - name: Signing Image + env: + DOCKER_CONTENT_TRUST: "1" + DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ secrets.docker_content_trust_repository_passphrase }} + DOCKER_CONTENT_TRUST_SERVER: ${{ inputs.docker_content_trust_server }} + shell: bash + run: |- + set eu; + docker trust sign --local ${{ inputs.dockerhub_image_reference }} + + - name: Inspect Signed Image + env: + DOCKER_CONTENT_TRUST: "1" + DOCKER_CONTENT_TRUST_SERVER: ${{ inputs.docker_content_trust_server }} + run: |- + set eu; + echo "::notice title=Inspect Docker Content Trust Signed Image" + docker trust inspect --pretty ${{ inputs.dockerhub_image_reference }} + + - name: Remove Prepared Docker Content Trust Credentials + if: always() + shell: sh + run: |- + set eu; + echo "::group::Cleaning Up Trust Keys" + echo "::notice title=Removing Trust Key" + rm -v ~/.docker/trust/private/${{ secrets.docker_content_trust_repository_key_id }}.key + echo "::notice title=Removing Trust Key Directory" + rm -rvf ~/.docker/trust/private/ + echo "::groupend::" diff --git a/.gitignore b/.gitignore index ca0085843f..714eca970f 100644 --- a/.gitignore +++ b/.gitignore @@ -449,4 +449,9 @@ local-harnesses/* !local-harnesses/*.template # folder used by antlr4 VSCode extension -.antlr \ No newline at end of file +.antlr + +# act related files +.act* + +!.actrc diff --git a/dockerfiles/bloodhound.Dockerfile b/dockerfiles/bloodhound.Dockerfile index 74cdfbca2a..b9308a4320 100644 --- a/dockerfiles/bloodhound.Dockerfile +++ b/dockerfiles/bloodhound.Dockerfile @@ -100,7 +100,7 @@ RUN sha256sum azurehound-$AZUREHOUND_VERSION.zip > azurehound-$AZUREHOUND_VERSIO ######## # Package Bloodhound ################ -FROM gcr.io/distroless/static-debian11 +FROM gcr.io/distroless/static-debian11 as bloodhound ARG SHARPHOUND_VERSION ARG AZUREHOUND_VERSION