name: 'SDK: Container Build and Push' on: push: branches: - 'master' paths-ignore: - '.github/**' - '!.github/workflows/sdk-container-build-push.yml' - 'README.md' - 'docs/**' - 'ui/**' - 'api/**' release: types: - 'published' workflow_dispatch: inputs: release_tag: description: 'Release tag (e.g., 5.14.0)' required: true type: string concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false env: # Container configuration IMAGE_NAME: prowler DOCKERFILE_PATH: ./Dockerfile # Python configuration PYTHON_VERSION: '3.12' # Tags (dynamically set based on version) LATEST_TAG: latest STABLE_TAG: stable # Container registries PROWLERCLOUD_DOCKERHUB_REPOSITORY: prowlercloud PROWLERCLOUD_DOCKERHUB_IMAGE: prowler TONIBLYX_DOCKERHUB_REPOSITORY: toniblyx # AWS configuration (for ECR) AWS_REGION: us-east-1 permissions: {} jobs: setup: if: github.repository == 'prowler-cloud/prowler' runs-on: ubuntu-latest timeout-minutes: 5 outputs: prowler_version: ${{ steps.get-prowler-version.outputs.prowler_version }} latest_tag: ${{ steps.get-prowler-version.outputs.latest_tag }} stable_tag: ${{ steps.get-prowler-version.outputs.stable_tag }} permissions: contents: read steps: - name: Harden Runner uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: egress-policy: block allowed-endpoints: > github.com:443 pypi.org:443 files.pythonhosted.org:443 - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Get Prowler version and set tags id: get-prowler-version run: | PROWLER_VERSION="$(grep -E '^version = ' pyproject.toml | sed -E 's/version = "([^"]+)"/\1/' | tr -d '[:space:]')" echo "prowler_version=${PROWLER_VERSION}" >> "${GITHUB_OUTPUT}" PROWLER_VERSION_MAJOR="${PROWLER_VERSION%%.*}" if [[ "${PROWLER_VERSION_MAJOR}" != "5" ]]; then echo "::error::Unsupported Prowler major version: ${PROWLER_VERSION_MAJOR}" exit 1 fi echo "latest_tag=latest" >> "${GITHUB_OUTPUT}" echo "stable_tag=stable" >> "${GITHUB_OUTPUT}" notify-release-started: if: github.repository == 'prowler-cloud/prowler' && (github.event_name == 'release' || github.event_name == 'workflow_dispatch') needs: setup runs-on: ubuntu-latest timeout-minutes: 5 outputs: message-ts: ${{ steps.slack-notification.outputs.ts }} permissions: contents: read steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: egress-policy: audit - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Notify container push started id: slack-notification uses: ./.github/actions/slack-notification env: SLACK_CHANNEL_ID: ${{ secrets.SLACK_PLATFORM_DEPLOYMENTS }} COMPONENT: SDK RELEASE_TAG: ${{ needs.setup.outputs.prowler_version }} GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_REPOSITORY: ${{ github.repository }} GITHUB_RUN_ID: ${{ github.run_id }} with: slack-bot-token: ${{ secrets.SLACK_BOT_TOKEN }} payload-file-path: "./.github/scripts/slack-messages/container-release-started.json" container-build-push: needs: [setup, notify-release-started] if: always() && needs.setup.result == 'success' && (needs.notify-release-started.result == 'success' || needs.notify-release-started.result == 'skipped') runs-on: ${{ matrix.runner }} strategy: matrix: include: - platform: linux/amd64 runner: ubuntu-latest arch: amd64 - platform: linux/arm64 runner: ubuntu-24.04-arm arch: arm64 timeout-minutes: 45 permissions: contents: read packages: write steps: - name: Harden Runner uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: egress-policy: block allowed-endpoints: > api.ecr-public.us-east-1.amazonaws.com:443 public.ecr.aws:443 registry-1.docker.io:443 production.cloudflare.docker.com:443 auth.docker.io:443 debian.map.fastlydns.net:80 github.com:443 release-assets.githubusercontent.com:443 pypi.org:443 files.pythonhosted.org:443 www.powershellgallery.com:443 aka.ms:443 cdn.powershellgallery.com:443 _http._tcp.deb.debian.org:443 powershellinfraartifacts-gkhedzdeaghdezhr.z01.azurefd.net:443 - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Login to DockerHub uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to Public ECR uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: registry: public.ecr.aws username: ${{ secrets.PUBLIC_ECR_AWS_ACCESS_KEY_ID }} password: ${{ secrets.PUBLIC_ECR_AWS_SECRET_ACCESS_KEY }} env: AWS_REGION: ${{ env.AWS_REGION }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - name: Build and push SDK container for ${{ matrix.arch }} id: container-push if: github.event_name == 'push' || github.event_name == 'release' || github.event_name == 'workflow_dispatch' uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 with: context: . file: ${{ env.DOCKERFILE_PATH }} push: true platforms: ${{ matrix.platform }} tags: | ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${{ needs.setup.outputs.latest_tag }}-${{ matrix.arch }} cache-from: type=gha,scope=${{ matrix.arch }} cache-to: type=gha,mode=${{ github.event_name == 'pull_request' && 'min' || 'max' }},scope=${{ matrix.arch }} # Create and push multi-architecture manifest create-manifest: needs: [setup, container-build-push] if: always() && needs.setup.result == 'success' && needs.container-build-push.result == 'success' runs-on: ubuntu-latest permissions: contents: read steps: - name: Harden Runner uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: egress-policy: block allowed-endpoints: > registry-1.docker.io:443 auth.docker.io:443 public.ecr.aws:443 production.cloudflare.docker.com:443 github.com:443 release-assets.githubusercontent.com:443 api.ecr-public.us-east-1.amazonaws.com:443 - name: Login to DockerHub uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to Public ECR uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: registry: public.ecr.aws username: ${{ secrets.PUBLIC_ECR_AWS_ACCESS_KEY_ID }} password: ${{ secrets.PUBLIC_ECR_AWS_SECRET_ACCESS_KEY }} env: AWS_REGION: ${{ env.AWS_REGION }} - name: Create and push manifests for push event if: github.event_name == 'push' run: | docker buildx imagetools create \ -t ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${NEEDS_SETUP_OUTPUTS_LATEST_TAG} \ -t ${{ secrets.PUBLIC_ECR_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${NEEDS_SETUP_OUTPUTS_LATEST_TAG} \ ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${NEEDS_SETUP_OUTPUTS_LATEST_TAG}-amd64 \ ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${NEEDS_SETUP_OUTPUTS_LATEST_TAG}-arm64 env: NEEDS_SETUP_OUTPUTS_LATEST_TAG: ${{ needs.setup.outputs.latest_tag }} - name: Create and push manifests for release event if: github.event_name == 'release' || github.event_name == 'workflow_dispatch' run: | docker buildx imagetools create \ -t ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${NEEDS_SETUP_OUTPUTS_PROWLER_VERSION} \ -t ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${NEEDS_SETUP_OUTPUTS_STABLE_TAG} \ -t ${{ secrets.PUBLIC_ECR_REPOSITORY }}/${{ env.IMAGE_NAME }}:${NEEDS_SETUP_OUTPUTS_PROWLER_VERSION} \ -t ${{ secrets.PUBLIC_ECR_REPOSITORY }}/${{ env.IMAGE_NAME }}:${NEEDS_SETUP_OUTPUTS_STABLE_TAG} \ ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${NEEDS_SETUP_OUTPUTS_LATEST_TAG}-amd64 \ ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${NEEDS_SETUP_OUTPUTS_LATEST_TAG}-arm64 env: NEEDS_SETUP_OUTPUTS_PROWLER_VERSION: ${{ needs.setup.outputs.prowler_version }} NEEDS_SETUP_OUTPUTS_STABLE_TAG: ${{ needs.setup.outputs.stable_tag }} NEEDS_SETUP_OUTPUTS_LATEST_TAG: ${{ needs.setup.outputs.latest_tag }} # Push to toniblyx/prowler only for current version (latest/stable/release tags) - name: Login to DockerHub (toniblyx) if: needs.setup.outputs.latest_tag == 'latest' uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: username: ${{ secrets.TONIBLYX_DOCKERHUB_USERNAME }} password: ${{ secrets.TONIBLYX_DOCKERHUB_PASSWORD }} - name: Push manifests to toniblyx for push event if: needs.setup.outputs.latest_tag == 'latest' && github.event_name == 'push' run: | docker buildx imagetools create \ -t ${{ env.TONIBLYX_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:latest \ ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:latest - name: Push manifests to toniblyx for release event if: needs.setup.outputs.latest_tag == 'latest' && (github.event_name == 'release' || github.event_name == 'workflow_dispatch') run: | docker buildx imagetools create \ -t ${{ env.TONIBLYX_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${NEEDS_SETUP_OUTPUTS_PROWLER_VERSION} \ -t ${{ env.TONIBLYX_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:stable \ ${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:stable env: NEEDS_SETUP_OUTPUTS_PROWLER_VERSION: ${{ needs.setup.outputs.prowler_version }} # Re-login as prowlercloud for cleanup of intermediate tags - name: Login to DockerHub (prowlercloud) if: always() uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Install regctl if: always() uses: regclient/actions/regctl-installer@da9319db8e44e8b062b3a147e1dfb2f574d41a03 # main - name: Cleanup intermediate architecture tags if: always() run: | echo "Cleaning up intermediate tags..." regctl tag delete "${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${NEEDS_SETUP_OUTPUTS_LATEST_TAG}-amd64" || true regctl tag delete "${{ env.PROWLERCLOUD_DOCKERHUB_REPOSITORY }}/${{ env.PROWLERCLOUD_DOCKERHUB_IMAGE }}:${NEEDS_SETUP_OUTPUTS_LATEST_TAG}-arm64" || true echo "Cleanup completed" env: NEEDS_SETUP_OUTPUTS_LATEST_TAG: ${{ needs.setup.outputs.latest_tag }} notify-release-completed: if: always() && needs.notify-release-started.result == 'success' && (github.event_name == 'release' || github.event_name == 'workflow_dispatch') needs: [setup, notify-release-started, container-build-push, create-manifest] runs-on: ubuntu-latest timeout-minutes: 5 permissions: contents: read steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 with: egress-policy: audit - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Determine overall outcome id: outcome run: | if [[ "${NEEDS_CONTAINER_BUILD_PUSH_RESULT}" == "success" && "${NEEDS_CREATE_MANIFEST_RESULT}" == "success" ]]; then echo "outcome=success" >> $GITHUB_OUTPUT else echo "outcome=failure" >> $GITHUB_OUTPUT fi env: NEEDS_CONTAINER_BUILD_PUSH_RESULT: ${{ needs.container-build-push.result }} NEEDS_CREATE_MANIFEST_RESULT: ${{ needs.create-manifest.result }} - name: Notify container push completed uses: ./.github/actions/slack-notification env: SLACK_CHANNEL_ID: ${{ secrets.SLACK_PLATFORM_DEPLOYMENTS }} MESSAGE_TS: ${{ needs.notify-release-started.outputs.message-ts }} COMPONENT: SDK RELEASE_TAG: ${{ needs.setup.outputs.prowler_version }} GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_REPOSITORY: ${{ github.repository }} GITHUB_RUN_ID: ${{ github.run_id }} with: slack-bot-token: ${{ secrets.SLACK_BOT_TOKEN }} payload-file-path: "./.github/scripts/slack-messages/container-release-completed.json" step-outcome: ${{ steps.outcome.outputs.outcome }} update-ts: ${{ needs.notify-release-started.outputs.message-ts }}