diff --git a/.github/actions/build-container-image/action.yml b/.github/actions/build-container-image/action.yml new file mode 100644 index 0000000000..7b8035cbb0 --- /dev/null +++ b/.github/actions/build-container-image/action.yml @@ -0,0 +1,171 @@ +# 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: |- + TBD ... + dockerhub_token: + required: true + description: |- + TBD ... + ghcr_account: + required: true + description: |- + TBD ... + ghcr_token: + required: true + description: |- + TBD ... + build_platforms: + required: true + default: "linux/amd64,linux/arm64" + description: |- + TBD ... + 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..18cca4f5df --- /dev/null +++ b/.github/actions/container-image-metadata/action.yml @@ -0,0 +1,100 @@ +# 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/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..5a9640af74 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,100 @@ +--- +# 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: true + image_provenance: mode=max + 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 + docker_content_trust_server: https://notary.docker.io + 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_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 }} + + # static-code-analysis: + # name: Static Code Analysis + # needs: cla-check + # uses: ./.github/workflows/static-code-analysis.yml + # with: + # bloodhound_image_tar_artifact_name: ${{ needs.build-container-images.outputs.bloodhound_image_tar_artifact_name }} + # bloodhound_image_tar_path: ${{ needs.build-container-images.outputs.bloodhound_image_tar_path }} + # 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 }} + # + # test-suite: + # name: Test Suite + # needs: [cla-check, build-container-images] + # uses: ./.github/workflows/test-suite.yml + # with: + # bloodhound_image_tar_artifact_name: ${{ needs.build-container-images.outputs.bloodhound_image_tar_artifact_name }} + # bloodhound_image_tar_path: ${{ needs.build-container-images.outputs.bloodhound_image_tar_path }} diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml deleted file mode 100644 index 262e05591b..0000000000 --- a/.github/workflows/cla.yml +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright 2023 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: "CLA Assistant" -on: - issue_comment: - types: [created] - pull_request_target: - types: [opened, closed, synchronize] - -jobs: - CLAssistant: - 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 }} - with: - path-to-signatures: "signatures.json" - path-to-document: "https://github.com/BloodHoundAD/CLA/blob/main/ICLA.md" - branch: "main" - remote-organization-name: BloodHoundAD - remote-repository-name: CLA diff --git a/.github/workflows/reusable.build-container-image-artifact.yml b/.github/workflows/reusable.build-container-image-artifact.yml new file mode 100644 index 0000000000..932018770b --- /dev/null +++ b/.github/workflows/reusable.build-container-image-artifact.yml @@ -0,0 +1,249 @@ +# 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 + image_repository: + 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. + Combine this with the 'container_registry' input to form the full URL for the image. + + Example: 'my-app' for a repository named 'my-app' in the container registry. + 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: |- + ... + 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. + build_automation_ref: + type: string + description: |- + When the workflow is reused in another repo, we have to import the + .github/actions directory for repository. + 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 }} + image_tar_path: + value: ${{ jobs.build-container-image.outputs.image_tar_path }} + image_tar_artifact_name: + value: ${{ jobs.build-container-image.outputs.image_tar_artifact_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 }} + image_tar_path: ${{ inputs.build_output_tar_dir }}/${{ steps.container-image-metadata.outputs.version }}.tar + image_tar_artifact_name: ${{ steps.container-image-metadata.outputs.version }} + steps: + - name: Checkout Source Code Repository + uses: actions/checkout@v4 + + - if: ${{ github.repository != 'SpecterOps/BloodHound' }} + name: Checkout Build Automation Workflows + 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/workflows + .github/actions + + - uses: docker/login-action@v3 + name: Authenticate with DockerHub Registry + with: + registry: docker.io + username: ${{ secrets.dockerhub_account }} + password: ${{ secrets.dockerhub_token }} + + - uses: docker/login-action@v3 + name: Authenticate with GitHub Container Registry + with: + registry: docker.io + username: ${{ secrets.ghcr_account }} + password: ${{ secrets.ghcr_token }} + + - uses: ./.github/actions/container-image-metadata + id: container-image-metadata + with: + image_repository: ${{ inputs.image_repository }} + image_flavor: ${{ inputs.image_flavor }} + + - uses: ./.github/actions/build-container-image + id: build-container-image + with: + build_args: ${{ 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: ${{ steps.container-image-metadata.outputs.labels }} + image_metadata_json: ${{ steps.container-image-metadata.outputs.json }} + image_provenance: ${{ inputs.image_provenance }} + image_sbom: ${{ inputs.image_sbom }} + image_tags: ${{ steps.container-image-metadata.outputs.tags }} + push_image: ${{ inputs.push_image }} + + # - if: inputs.build_output_tar_dir != '' + # name: Archive Container Image for Downstream Jobs + # uses: actions/upload-artifact@v4 + # with: + # if-no-files-found: error + # retention-days: 1 + # name: ${{ steps.container-image-metadata.outputs.version }} + # path: ${{ inputs.build_output_tar_dir }}/${{ steps.container-image-metadata.outputs.version }}.tar diff --git a/.github/workflows/reusable.build-container-image.yml b/.github/workflows/reusable.build-container-image.yml new file mode 100644 index 0000000000..519dde9916 --- /dev/null +++ b/.github/workflows/reusable.build-container-image.yml @@ -0,0 +1,277 @@ +--- +# 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: |- + ... + 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. + docker_content_trust_server: + type: string + required: true + secrets: + dockerhub_account: + required: true + dockerhub_token: + required: true + ghcr_account: + required: true + ghcr_token: + required: true + gh_access_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 + 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 }} + + - name: Pull Image + shell: sh + run: |- + docker pull ${{ steps.container-image-metadata.outputs.image_reference }} + + - name: Sign 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: |- + echo "::group::Loading Key" + mkdir -p ~/.docker/trust/private/ + echo "${{ secrets.docker_content_trust_repository_key }}" >> ~/.docker/trust/private/${{ secrets.docker_content_trust_repository_key_id }}.key + chmod 600 ~/.docker/trust/private/${{ secrets.docker_content_trust_repository_key_id }}.key + docker trust key load ~/.docker/trust/private/${{ secrets.docker_content_trust_repository_key_id }}.key + echo "::groupend::" + echo "::group::Signing Image" + docker trust sign --local ${{ steps.container-image-metadata.outputs.image_reference }} + echo "::groupend::" + + - name: Inspect Signed Image + env: + DOCKER_CONTENT_TRUST: "1" + DOCKER_CONTENT_TRUST_SERVER: ${{ inputs.docker_content_trust_server }} + run: |- + docker trust inspect --pretty "${{ steps.container-image-metadata.outputs.image_reference }}" + + - name: Remove Trust Key + shell: sh + run: |- + echo "::group::Cleaning Up Trust Keys" + echo "::notice::Removing Trust Key" + rm -v ~/.docker/trust/private/${{ secrets.docker_content_trust_repository_key_id }}.key + echo "::notice::Trust Key Removed" + echo "::notice::Removing Trust Key Directory" + rm -rvf ~/.docker/trust/private/ + echo "::notice::Trust Key Directory Removed" + echo "::groupend::" diff --git a/.github/workflows/reusable.cla.yml b/.github/workflows/reusable.cla.yml new file mode 100644 index 0000000000..b59de06bc1 --- /dev/null +++ b/.github/workflows/reusable.cla.yml @@ -0,0 +1,76 @@ +# 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: CLA Assistant Reusable Workflow +on: + workflow_call: + inputs: + signatures_path: + type: string + default: "signatures.json" + description: |- + Location where CLA signatures are stored and tracked. + This file maintains the record of all contributors who have signed the agreement. + Customize this path when: + - Using a different storage location for signatures + - Managing multiple CLAs in the same repository + - Integrating with existing signature tracking systems + document_path: + type: string + default: "https://github.com/BloodHoundAD/CLA/blob/main/ICLA.md" + description: |- + URL to the Contributor License Agreement document that contributors must sign. + This document defines the terms under which contributions are accepted. + Customize this when: + - Using a different CLA version + - Implementing organization-specific agreements + - Supporting multiple languages or jurisdictions + branch: + type: string + default: "main" + description: "Branch name for the CLA repository" + remote_organization: + type: string + default: "BloodHoundAD" + description: "Organization name containing the CLA repository" + remote_repository: + type: string + default: "CLA" + description: "Name of the CLA repository" + secrets: + gh_access_token: + required: true + gh_repo_scope: + required: true + +jobs: + process-cla: + 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.gh_access_token }} + PERSONAL_ACCESS_TOKEN: ${{ secrets.gh_repo_scope }} + with: + path-to-signatures: ${{ inputs.signatures_path }} + path-to-document: ${{ inputs.document_path }} + branch: ${{ inputs.branch }} + remote-organization-name: ${{ inputs.remote_organization }} + remote-repository-name: ${{ inputs.remote_repository }} diff --git a/.github/workflows/reusable.lint-container-image.yml b/.github/workflows/reusable.lint-container-image.yml new file mode 100644 index 0000000000..88a8dff49e --- /dev/null +++ b/.github/workflows/reusable.lint-container-image.yml @@ -0,0 +1,91 @@ +# 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 Image Linting +run-name: Docker Image Linting by @${{ github.actor }} for ${{ github.ref_name }} + +on: + workflow_call: + inputs: + build_automation_ref: + type: string + description: |- + When the workflow is reused in another repo, we have to import the .github/actions directory + for the cloudops-tools repository. + secrets: + dockerhub_account: + required: true + dockerhub_token: + required: true + ghcr_account: + required: true + ghcr_token: + required: true + gh_access_token: + required: true + +jobs: + find-dockerfiles: + name: Discover Dockerfile Locations + runs-on: ubuntu-latest + timeout-minutes: 30 + outputs: + file_paths: ${{ steps.find-files.outputs.file_paths }} + steps: + - name: Checkout Source Code Repository + uses: actions/checkout@v4 + + - uses: docker/login-action@v3 + name: Authenticate with DockerHub Registry + with: + registry: docker.io + username: ${{ secrets.dockerhub_account }} + password: ${{ secrets.dockerhub_token }} + + - name: Pull Build Automation Image + shell: bash + run: | + docker pull specterops/build-automation:berner-pre0.0 + + - name: Locate Dockerfile Paths + id: find-files + shell: bash + run: | + docker run --rm \ + --volume $GITHUB_WORKSPACE:/app \ + specterops/build-automation:berner-pre0.0 \ + find-files --format=json-array --output=file:///app/file_paths.txt . Dockerfile + + echo "$(cat $GITHUB_WORKSPACE/file_paths.txt)" >> $GITHUB_OUTPUT + + hadolint: + needs: find-dockerfiles + name: Run Hadolint Analysis + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + matrix: + paths: ${{ fromJson(needs.find-dockerfiles.outputs.file_paths) }} + steps: + - name: Checkout Source Code Repository + uses: actions/checkout@v4 + + - name: Lint Dockerfile + uses: hadolint/hadolint-action@v3.1.0 + with: + dockerfile: ${{ matrix.paths }} + recursive: false diff --git a/.github/workflows/reusable.vulnerability-scan.yml b/.github/workflows/reusable.vulnerability-scan.yml new file mode 100644 index 0000000000..a1449087d2 --- /dev/null +++ b/.github/workflows/reusable.vulnerability-scan.yml @@ -0,0 +1,116 @@ +# 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: Security Vulnerability Analysis +run-name: Security Vulnerability Analysis started by @${{ github.actor }} for ${{ github.ref_name }} + +on: + workflow_call: + inputs: + scan_type: + type: string + description: |- + Controls the scope and target of the vulnerability scan to match your security needs. + Different scan types serve different security assessment purposes: + - 'repo': Comprehensive scan of the entire repository to catch all potential vulnerabilities + - 'fs': Targeted scan of specific directories when you need to check particular components + - 'image': Container-specific vulnerabilities scan for Docker images + - 'config': Focus on misconfigurations in infrastructure-as-code files + + Defaults to 'repo' for maximum coverage + default: repo + scan_ref: + type: string + description: |- + The target directory or path to scan for vulnerabilities. + For image scans, specify the image reference. + For filesystem scans, specify the directory path. + + Default: './' (current directory) + default: ./ + severity: + type: string + description: |- + Determines which security findings to report based on their impact level. + Filter vulnerabilities to focus on the most important issues first: + - CRITICAL: Severe vulnerabilities requiring immediate attention + - HIGH: Significant security risks that should be addressed soon + - MEDIUM: Moderate risks that should be planned for remediation + - LOW: Minor issues that pose minimal risk + - UNKNOWN: Issues with undetermined impact + + Default: 'CRITICAL,HIGH' to prioritize the most severe security issues + default: CRITICAL,HIGH + exit_code: + type: string + description: |- + Exit code to return when vulnerabilities matching the specified severity criteria are found. + Use '0' to always pass the workflow regardless of findings. + + Default: '1' (fail on findings) + default: '1' + ignore_unfixed: + type: boolean + description: |- + Controls whether to include vulnerabilities that currently have no available fixes. + Enable this when you want to: + - Focus on actionable items that can be immediately addressed + - Reduce noise in security reports + - Prioritize fixable vulnerabilities first + + Set to false to maintain awareness of all vulnerabilities, even those without current patches. + Default: false + default: false + trivy_db_repository: + type: string + default: ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db + trivy_java_db_repository: + type: string + default: ghcr.io/aquasecurity/trivy-java-db,public.ecr.aws/aquasecurity/trivy-java-db + +jobs: + trivy-scan: + name: Trivy Vulnerability Analysis + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - if: ${{ !github.event.act }} + 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: ${{ inputs.build_automation_ref }} + token: ${{ secrets.gh_access_token }} + sparse-checkout-cone-mode: false + sparse-checkout: |- + .github/actions + + - name: Execute Trivy Security Scan + uses: aquasecurity/trivy-action@0.29.0 + with: + scan-type: ${{ inputs.scan_type }} + scan-ref: ${{ inputs.scan_ref }} + severity: ${{ inputs.severity }} + exit-code: ${{ inputs.exit_code }} + ignore-unfixed: ${{ inputs.ignore_unfixed }} + env: + TRIVY_DB_REPOSITORY: ${{ inputs.trivy_db_repository }} + TRIVY_JAVA_DB_REPOSITORY: ${{ inputs.trivy_java_db_repository }} diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml deleted file mode 100644 index 2600142943..0000000000 --- a/.github/workflows/run-tests.yml +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright 2023 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: Run Tests - -on: - push: - branches: [main] - pull_request: - types: [opened, synchronize] - -jobs: - run-tests: - runs-on: ubuntu-latest - - steps: - - name: Checkout source code for this repository - uses: actions/checkout@v3 - - - name: Set up Docker Compose - run: | - sudo apt-get update -y - sudo apt-get install -y docker-compose - docker-compose version - - - name: Start PostgreSQL and Neo4j Containers - run: | - docker-compose -f docker-compose.testing.yml up -d - - - name: Install Go - uses: actions/setup-go@v4 - with: - go-version: '^1.23.0' - - - name: Install Python - uses: actions/setup-python@v4 - with: - python-version: '3.10' - - - name: Run Tests - run: | - export INTEGRATION_CONFIG_PATH=$(pwd)/.github/config/integration.config.json - python packages/python/beagle/main.py test -avi - - - name: Neo4j Debug Logs - if: failure() - run: | - docker-compose -f docker-compose.testing.yml exec -T testgraph cat /logs/debug.log - - - name: Postgres Debug Logs - if: failure() - run: | - docker-compose -f docker-compose.testing.yml logs testdb - - - name: Stop and Remove Containers - if: ${{ ! cancelled() }} - run: | - docker-compose -f docker-compose.testing.yml down diff --git a/.github/workflows/vuln-scan.yml b/.github/workflows/vuln-scan.yml deleted file mode 100644 index fc540eaf90..0000000000 --- a/.github/workflows/vuln-scan.yml +++ /dev/null @@ -1,44 +0,0 @@ -# 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: Vulnerability Scan - -on: - push: - branches: [main] - pull_request: - types: [opened, synchronize] - -jobs: - run-analysis: - runs-on: ubuntu-latest - - steps: - - name: Checkout source code for this repository - uses: actions/checkout@v3 - - - name: Run vulnerability scanner - uses: aquasecurity/trivy-action@0.28.0 - with: - scan-type: 'repo' - scan-ref: './' - severity: 'CRITICAL,HIGH' - exit-code: '1' - ignore-unfixed: true - env: - TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db - TRIVY_JAVA_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-java-db,public.ecr.aws/aquasecurity/trivy-java-db - diff --git a/dockerfiles/bloodhound.Dockerfile b/dockerfiles/bloodhound.Dockerfile index 74cdfbca2a..7631ac83d9 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