├── .gitattributes ├── .vscode └── settings.json ├── .dockerignore ├── SECURITY.md ├── .github ├── dependabot.yml ├── CODEOWNERS └── workflows │ ├── oob-update.yaml │ └── build.yml ├── src ├── dotnet-framework │ ├── manifest.json │ └── Dockerfile ├── dummy │ ├── manifest.json │ └── Dockerfile ├── java │ ├── manifest.json │ └── Dockerfile ├── php │ ├── manifest.json │ └── Dockerfile ├── python │ ├── manifest.json │ ├── Dockerfile │ └── fetch-python.sh ├── shared │ └── entrypoint.sh ├── nodejs │ ├── manifest.json │ └── Dockerfile ├── dotnet-core │ ├── manifest.json │ └── Dockerfile └── flex │ ├── manifest.json │ └── Dockerfile ├── .editorconfig ├── CONTRIBUTING.md ├── LICENSE ├── validate-image.ps1 ├── README.md └── .gitignore /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.sh text eol=lf 3 | *.json text diff 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "yaml.schemas": { 3 | "https://json.schemastore.org/github-workflow.json": "./.github/workflows/build.yml" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/Dockerfile 2 | **/Dockerfile-* 3 | **/docker-compose.yaml 4 | **/docker-compose.yml 5 | 6 | **/.git/ 7 | **/.vs/ 8 | **/.vscode/ 9 | **/.github 10 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | If you believe you have found a security vulnerability in any Contrast Security repository, please report it to us at [security@contrastsecurity.com](mailto:security@contrastsecurity.com). 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "docker" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "daily" 11 | -------------------------------------------------------------------------------- /src/dotnet-framework/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "51.5.0", 3 | "imageNameSuffix": "dotnet-framework", 4 | "dockerFile": "src/dotnet-framework/Dockerfile", 5 | "context": ".", 6 | "platforms": "windows/amd64", 7 | "validation": { 8 | "enabled": false, 9 | "expects": [] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/dummy/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.1", 3 | "imageNameSuffix": "dummy", 4 | "dockerFile": "src/dummy/Dockerfile", 5 | "context": ".", 6 | "platforms": "linux/amd64,linux/arm64", 7 | "validation": { 8 | "enabled": true, 9 | "expects": [ 10 | "image-manifest.json" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @Contrast-Security-OSS/dotnet 2 | 3 | /src/dotnet-core/ @Contrast-Security-OSS/dotnet 4 | /src/dotnet-framework/ @Contrast-Security-OSS/dotnet 5 | /src/java/ @Contrast-Security-OSS/java-team 6 | /src/php/ @Contrast-Security-OSS/php-agent-team 7 | /src/nodejs/ @Contrast-Security-OSS/node-team 8 | /src/python/ @Contrast-Security-OSS/python-team 9 | -------------------------------------------------------------------------------- /src/java/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "6.24.0", 3 | "imageNameSuffix": "java", 4 | "dockerFile": "src/java/Dockerfile", 5 | "context": ".", 6 | "platforms": "linux/amd64,linux/arm64", 7 | "validation": { 8 | "enabled": true, 9 | "expects": [ 10 | "contrast-agent.jar", 11 | "image-manifest.json" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/php/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.47.0", 3 | "imageNameSuffix": "php", 4 | "dockerFile": "src/php/Dockerfile", 5 | "context": ".", 6 | "platforms": "linux/amd64,linux/arm64", 7 | "validation": { 8 | "enabled": true, 9 | "expects": [ 10 | "contrast.so", 11 | "image-manifest.json", 12 | "ini/php.ini" 13 | ] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/python/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "10.22.2", 3 | "imageNameSuffix": "python", 4 | "dockerFile": "src/python/Dockerfile", 5 | "context": ".", 6 | "platforms": "linux/amd64,linux/arm64", 7 | "validation": { 8 | "enabled": true, 9 | "expects": [ 10 | "contrast/loader/__init__.py", 11 | "image-manifest.json" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/shared/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Contrast Security, Inc licenses this file to you under the Apache 2.0 License. 3 | # See the LICENSE file in the project root for more information. 4 | 5 | set -x 6 | mkdir -p $CONTRAST_MOUNT_PATH 7 | cp --force --recursive --verbose /contrast/* $CONTRAST_MOUNT_PATH/ 8 | 9 | echo "Completed setup of $CONTRAST_AGENT_TYPE $CONTRAST_VERSION. Have fun!" 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | trim_trailing_whitespace = true 6 | insert_final_newline = true 7 | file_header_template = Contrast Security, Inc licenses this file to you under the Apache 2.0 License.\nSee the LICENSE file in the project root for more information. 8 | 9 | [*.yaml,*.json,Dockerfile] 10 | indent_style = space 11 | indent_size = 2 12 | charset = utf-8 13 | -------------------------------------------------------------------------------- /src/nodejs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "5.49.0", 3 | "imageNameSuffix": "nodejs", 4 | "dockerFile": "src/nodejs/Dockerfile", 5 | "context": ".", 6 | "platforms": "linux/amd64,linux/arm64", 7 | "validation": { 8 | "enabled": true, 9 | "expects": [ 10 | "node_modules/@contrast/agent/lib/index.js", 11 | "node_modules/@contrast/agent/lib/esm-loader.mjs", 12 | "image-manifest.json" 13 | ] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Hi there! We welcome any contribution to the Contrast Agent Operator Images, be it raising issues, adding improvements, or just asking questions. 4 | 5 | Have feedback or having problems, open a [GitHub issue](https://github.com/Contrast-Security-OSS/agent-operator-images/issues). If your issue contains sensitive information and cannot be public, contact us through your standard [support channel](https://support.contrastsecurity.com/hc/en-us). 6 | 7 | Have fun out there! 8 | -------------------------------------------------------------------------------- /src/dotnet-core/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "5.0.0", 3 | "imageNameSuffix": "dotnet-core", 4 | "dockerFile": "src/dotnet-core/Dockerfile", 5 | "context": ".", 6 | "platforms": "linux/amd64,linux/arm64,windows/amd64", 7 | "validation": { 8 | "enabled": true, 9 | "expects": [ 10 | "image-manifest.json", 11 | "runtimes/linux-x64/native/ContrastProfiler.so", 12 | "runtimes/linux-x64/native/ContrastChainLoader.so", 13 | "runtimes/linux-arm64/native/ContrastProfiler.so", 14 | "runtimes/linux-arm64/native/ContrastChainLoader.so" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 Contrast Security, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /src/flex/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.10.0", 3 | "imageNameSuffix": "flex", 4 | "dockerFile": "src/flex/Dockerfile", 5 | "context": ".", 6 | "platforms": "linux/amd64,linux/arm64", 7 | "validation": { 8 | "enabled": true, 9 | "expects": [ 10 | "image-manifest.json", 11 | "injector/agent_injector.so", 12 | "flex-comms/flex_agent.auto_attach", 13 | "flex-comms/dotnet-core-arm64-default", 14 | "flex-comms/dotnet-core-x64-default", 15 | "flex-comms/java-default", 16 | "flex-comms/node-default", 17 | "flex-comms/python-default" 18 | ] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/dummy/Dockerfile: -------------------------------------------------------------------------------- 1 | # Contrast Security, Inc licenses this file to you under the Apache 2.0 License. 2 | # See the LICENSE file in the project root for more information. 3 | 4 | FROM --platform=$BUILDPLATFORM ubuntu:noble AS builder 5 | 6 | ARG VERSION=0.0.1 7 | 8 | RUN set -xe \ 9 | && mkdir /contrast \ 10 | && echo "{ \"version\": \"${VERSION}\" }" > /contrast/image-manifest.json 11 | 12 | # Dummy agent for testing non-root containers with the agent-operator 13 | FROM busybox:stable AS final 14 | 15 | RUN set -xe \ 16 | && addgroup -g 1001 custom-group \ 17 | && adduser -u 1001 -G custom-group -D -H custom-user 18 | 19 | COPY ./src/shared/entrypoint.sh /entrypoint.sh 20 | COPY --from=builder /contrast /contrast 21 | 22 | ARG VERSION=0.0.1 23 | ENV CONTRAST_MOUNT_PATH=/contrast-init \ 24 | CONTRAST_VERSION=${VERSION} \ 25 | CONTRAST_AGENT_TYPE=dummy 26 | 27 | USER 1001 28 | 29 | ENTRYPOINT [ "/bin/sh", "/entrypoint.sh" ] 30 | -------------------------------------------------------------------------------- /src/nodejs/Dockerfile: -------------------------------------------------------------------------------- 1 | # Contrast Security, Inc licenses this file to you under the Apache 2.0 License. 2 | # See the LICENSE file in the project root for more information. 3 | 4 | FROM node:lts-slim AS builder 5 | 6 | ARG VERSION=4.18.0 7 | 8 | RUN set -xe \ 9 | && mkdir -p /contrast \ 10 | && npm install --prefix /contrast @contrast/agent@${VERSION} \ 11 | && npm install --prefix /contrast @swc/core --libc=glibc \ 12 | && npm install --prefix /contrast @swc/core --libc=musl \ 13 | && echo "{ \"version\": \"${VERSION}\" }" > /contrast/image-manifest.json 14 | 15 | FROM busybox:stable AS final 16 | 17 | RUN set -xe \ 18 | && addgroup -g 1001 custom-group \ 19 | && adduser -u 1001 -G custom-group -D -H custom-user 20 | 21 | COPY ./src/shared/entrypoint.sh /entrypoint.sh 22 | COPY --from=builder /contrast /contrast 23 | 24 | ARG VERSION=4.18.0 25 | ENV CONTRAST_MOUNT_PATH=/contrast-init \ 26 | CONTRAST_VERSION=${VERSION} \ 27 | CONTRAST_AGENT_TYPE=nodejs 28 | 29 | USER 1001 30 | 31 | ENTRYPOINT [ "/bin/sh", "/entrypoint.sh" ] 32 | -------------------------------------------------------------------------------- /src/python/Dockerfile: -------------------------------------------------------------------------------- 1 | # Contrast Security, Inc licenses this file to you under the Apache 2.0 License. 2 | # See the LICENSE file in the project root for more information. 3 | 4 | FROM python:3.9-slim-bookworm AS builder 5 | ARG VERSION=10.0.2 6 | RUN set -xe \ 7 | && apt-get update \ 8 | && apt-get install -y jq unzip 9 | COPY ./src/python/fetch-python.sh . 10 | RUN set -xe \ 11 | && mkdir -p /contrast \ 12 | && echo ${VERSION} \ 13 | && ./fetch-python.sh ${VERSION} /contrast \ 14 | && echo "{ \"version\": \"${VERSION}\" }" > /contrast/image-manifest.json 15 | 16 | 17 | FROM busybox:stable AS final 18 | 19 | RUN set -xe \ 20 | && addgroup -g 1001 custom-group \ 21 | && adduser -u 1001 -G custom-group -D -H custom-user 22 | 23 | COPY ./src/shared/entrypoint.sh /entrypoint.sh 24 | COPY --from=builder /contrast /contrast 25 | 26 | ARG VERSION=10.0.2 27 | ENV CONTRAST_MOUNT_PATH=/contrast-init \ 28 | CONTRAST_VERSION=${VERSION} \ 29 | CONTRAST_AGENT_TYPE=python 30 | 31 | USER 1001 32 | 33 | ENTRYPOINT [ "/bin/sh", "/entrypoint.sh" ] 34 | -------------------------------------------------------------------------------- /src/dotnet-framework/Dockerfile: -------------------------------------------------------------------------------- 1 | # Contrast Security, Inc licenses this file to you under the Apache 2.0 License. 2 | # See the LICENSE file in the project root for more information. 3 | 4 | FROM --platform=$BUILDPLATFORM ubuntu:noble AS builder 5 | 6 | RUN set -xe \ 7 | && apt-get update \ 8 | && apt-get install -y curl unzip rsync 9 | 10 | ARG VERSION=50.0.18 11 | 12 | RUN set -xe \ 13 | && curl --retry 5 --retry-all-errors --retry-delay 120 --fail --location https://www.nuget.org/api/v2/package/Contrast.NET.Azure.AppService/${VERSION} --output /tmp/contrast.zip \ 14 | && unzip /tmp/contrast.zip -d /tmp/contrast \ 15 | && rsync -aP /tmp/contrast/content/contrastsecurity/* /contrast/ \ 16 | && echo "{ \"version\": \"${VERSION}\" }" > /contrast/image-manifest.json 17 | 18 | # We don't currently support Windows containers in the Agent Operator. 19 | # This image exists purely to make it easier to get the .NET Framework agent files into windows container image. 20 | FROM mcr.microsoft.com/windows/nanoserver:2004 AS final 21 | COPY --from=builder /contrast /contrast 22 | -------------------------------------------------------------------------------- /src/java/Dockerfile: -------------------------------------------------------------------------------- 1 | # Contrast Security, Inc licenses this file to you under the Apache 2.0 License. 2 | # See the LICENSE file in the project root for more information. 3 | 4 | FROM --platform=$BUILDPLATFORM ubuntu:noble AS builder 5 | 6 | RUN set -xe \ 7 | && apt-get update \ 8 | && apt-get install -y curl 9 | 10 | ARG VERSION=3.13.0.26525 11 | 12 | RUN set -xe \ 13 | && mkdir -p /contrast \ 14 | && curl --location \ 15 | https://repo1.maven.org/maven2/com/contrastsecurity/contrast-agent/${VERSION}/contrast-agent-${VERSION}.jar \ 16 | --output /contrast/contrast-agent.jar \ 17 | && echo "{ \"version\": \"${VERSION}\" }" > /contrast/image-manifest.json 18 | 19 | FROM busybox:stable AS final 20 | 21 | RUN set -xe \ 22 | && addgroup -g 1001 custom-group \ 23 | && adduser -u 1001 -G custom-group -D -H custom-user 24 | 25 | COPY ./src/shared/entrypoint.sh /entrypoint.sh 26 | COPY --from=builder /contrast /contrast 27 | 28 | ARG VERSION=3.13.0.26525 29 | ENV CONTRAST_MOUNT_PATH=/contrast-init \ 30 | CONTRAST_VERSION=${VERSION} \ 31 | CONTRAST_AGENT_TYPE=java 32 | 33 | USER 1001 34 | 35 | ENTRYPOINT [ "/bin/sh", "/entrypoint.sh" ] 36 | -------------------------------------------------------------------------------- /src/php/Dockerfile: -------------------------------------------------------------------------------- 1 | # Contrast Security, Inc licenses this file to you under the Apache 2.0 License. 2 | # See the LICENSE file in the project root for more information. 3 | 4 | FROM ubuntu:noble AS builder 5 | 6 | RUN set -xe \ 7 | && apt-get update \ 8 | && apt-get install -y curl gnupg2 rsync 9 | 10 | ARG VERSION=1.2.0 11 | 12 | RUN set -xe \ 13 | && curl https://pkg.contrastsecurity.com/api/gpg/key/public | apt-key add - \ 14 | && echo "deb https://pkg.contrastsecurity.com/debian-public/ noble contrast" > /etc/apt/sources.list.d/contrast.list \ 15 | && echo "deb https://pkg.contrastsecurity.com/debian-public/ all contrast" >> /etc/apt/sources.list.d/contrast.list \ 16 | && apt-get update \ 17 | && apt-get install -y contrast-php-agent=${VERSION} \ 18 | && mkdir -p /contrast \ 19 | && mkdir -p /contrast/ini \ 20 | && rsync -aP /usr/local/lib/contrast/php/ /contrast/ \ 21 | && echo 'extension=${CONTRAST_MOUNT_PATH}/contrast.so' > /contrast/ini/php.ini \ 22 | && echo '{ "version": "${VERSION}" }' > /contrast/image-manifest.json 23 | 24 | FROM busybox:stable AS final 25 | 26 | RUN set -xe \ 27 | && addgroup -g 1001 custom-group \ 28 | && adduser -u 1001 -G custom-group -D -H custom-user 29 | 30 | COPY ./src/shared/entrypoint.sh /entrypoint.sh 31 | COPY --from=builder /contrast /contrast 32 | 33 | ARG VERSION=1.2.0 34 | ENV CONTRAST_MOUNT_PATH=/contrast-init \ 35 | CONTRAST_VERSION=${VERSION} \ 36 | CONTRAST_AGENT_TYPE=php 37 | 38 | USER 1001 39 | 40 | ENTRYPOINT [ "/bin/sh", "/entrypoint.sh" ] 41 | -------------------------------------------------------------------------------- /src/python/fetch-python.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o nounset 4 | set -o pipefail 5 | set -o errexit 6 | 7 | if [ "${1-}" == "--help" ] || [ "${1-}" == "-h" ]; then 8 | echo "Usage: $0 [version] [destination]" 9 | echo " [version] : Optional. The version of the contrast-agent to download (e.g., 'latest' or '1.2.3'). Defaults to latest." 10 | echo " [destination] : Optional. The directory to save the downloaded files. Defaults to /agents/python/{version}/." 11 | exit 1 12 | fi 13 | 14 | VERSION=${1:-"latest"} 15 | if [ "$VERSION" == "latest" ]; then 16 | VERSION=$( 17 | python3 -m pip index versions --json "contrast-agent" --only-binary :all: \ 18 | | jq .latest 19 | ) 20 | fi 21 | 22 | SCRIPT_DIR=$(dirname "$(realpath "$0")") 23 | REPO_ROOT="$SCRIPT_DIR/../.." 24 | DESTINATION=${2:-"$REPO_ROOT/agents/python/$VERSION/"} 25 | 26 | PLATFORMS="manylinux_2_17_x86_64 manylinux_2_28_aarch64 musllinux_1_2_x86_64 musllinux_1_2_aarch64" 27 | PYTHON_VERSIONS="39 310 311 312 313" 28 | 29 | TEMP_DIR=$(mktemp -d) 30 | 31 | for platform in $PLATFORMS; do 32 | for py_version in $PYTHON_VERSIONS; do 33 | if [[ "$platform" == "musl"* && ("$py_version" == "39" || "$py_version" == "310") ]]; then 34 | # In python 3.10 and earlier, musl builds and glibc C extension builds have 35 | # the same filename. This causes some binaries to be overwritten when 36 | # combined into a single directory. 37 | # See https://github.com/python/cpython/issues/87278 38 | echo "Skipping download for $platform $py_version"; 39 | continue 40 | fi 41 | echo "Downloading wheels for $platform $py_version"; 42 | python3 -m pip download \ 43 | --dest "$TEMP_DIR" \ 44 | --only-binary :all: \ 45 | --platform "$platform" \ 46 | --python-version "$py_version" \ 47 | "contrast-agent==$VERSION"; 48 | done 49 | done 50 | 51 | mkdir -p "$DESTINATION" 52 | for file in "$TEMP_DIR"/*.whl; do 53 | unzip -o "$file" -d "$DESTINATION"; 54 | done 55 | rm -rf "$TEMP_DIR" 56 | 57 | echo "Download complete" 58 | -------------------------------------------------------------------------------- /validate-image.ps1: -------------------------------------------------------------------------------- 1 | # Contrast Security, Inc licenses this file to you under the Apache 2.0 License. 2 | # See the LICENSE file in the project root for more information. 3 | 4 | param ( 5 | [Parameter(Mandatory = $true)] 6 | [string] $Type, 7 | [string] $Image 8 | ) 9 | 10 | $runId = [System.Guid]::NewGuid().ToString() 11 | $workspace = Join-Path ([System.IO.Path]::GetTempPath()) $runId 12 | New-Item -ItemType Directory $workspace | Out-Null 13 | 14 | $agentPath = "./src/$Type" 15 | 16 | if (-not (Test-Path $agentPath)) 17 | { 18 | throw "Agent path $agentPath does not exist." 19 | } 20 | 21 | if ($Image) 22 | { 23 | docker tag $Image $runId 24 | } 25 | else 26 | { 27 | docker build --file "$agentPath/Dockerfile" --tag $runId . 28 | } 29 | 30 | try 31 | { 32 | $validation = (Get-Content -Path "$agentPath/manifest.json" | ConvertFrom-Json).validation 33 | $enabled = $validation.enabled 34 | 35 | if (-not $enabled) 36 | { 37 | Write-Host "Validation disabled, assuming success." 38 | exit 0 39 | } 40 | 41 | Write-Host "Running image entrypoint." 42 | docker run ` 43 | -i ` 44 | --rm ` 45 | --env "CONTRAST_MOUNT_PATH=/contrast-tmp" ` 46 | --volume "${workspace}/:/contrast-tmp/" ` 47 | $runId 48 | 49 | if (-not $?) 50 | { 51 | throw "Entrypoint failed." 52 | } 53 | 54 | $actualFiles = Get-ChildItem -Recurse -File $workspace 55 | $actualFiles | ForEach-Object { 56 | Write-Host "Found file `"$($_.FullName)`"" 57 | } 58 | 59 | $expectedFiles = $validation.expects | ForEach-Object { 60 | Join-Path $workspace $_ 61 | } 62 | 63 | Write-Host "Validating expected files." 64 | $missingFile = $expectedFiles | Where-Object { 65 | return -not (Test-Path $_) 66 | } 67 | 68 | $missingFile | ForEach-Object { 69 | Write-Host "Expected file `"$($_)`", but the file was not found." 70 | } 71 | 72 | if (@($missingFile).Length -gt 0) 73 | { 74 | throw "Expected files failed." 75 | } 76 | else 77 | { 78 | Write-Host "All expected files exist." 79 | } 80 | } 81 | finally 82 | { 83 | Remove-Item -Recurse $workspace -ErrorAction Continue 84 | docker rmi $runId | Out-Null 85 | } 86 | -------------------------------------------------------------------------------- /src/dotnet-core/Dockerfile: -------------------------------------------------------------------------------- 1 | # Contrast Security, Inc licenses this file to you under the Apache 2.0 License. 2 | # See the LICENSE file in the project root for more information. 3 | 4 | FROM --platform=$BUILDPLATFORM ubuntu:noble AS builder 5 | 6 | RUN set -xe \ 7 | && apt-get update \ 8 | && apt-get install -y curl unzip rsync 9 | 10 | ARG VERSION=4.2.8 11 | 12 | RUN set -xe \ 13 | && curl --retry 5 --retry-all-errors --retry-delay 120 --fail --location https://www.nuget.org/api/v2/package/Contrast.SensorsNetCore/${VERSION} --output /tmp/contrast.zip \ 14 | && unzip /tmp/contrast.zip -d /tmp/contrast \ 15 | && rsync -aP /tmp/contrast/contentFiles/any/netstandard2.0/contrast/* /contrast/ \ 16 | && chmod +x /contrast/diagnostics/linux-x64/contrast-dotnet-diagnostics \ 17 | && chmod +x /contrast/diagnostics/linux-arm64/contrast-dotnet-diagnostics \ 18 | && echo "{ \"version\": \"${VERSION}\" }" > /contrast/image-manifest.json 19 | 20 | FROM --platform=linux/amd64 busybox:stable AS final-linux-amd64 21 | RUN set -xe \ 22 | && addgroup -g 1001 custom-group \ 23 | && adduser -u 1001 -G custom-group -D -H custom-user 24 | COPY ./src/shared/entrypoint.sh /entrypoint.sh 25 | COPY --from=builder /contrast /contrast 26 | RUN set -xe \ 27 | && mkdir -p /contrast/runtimes/linux/native \ 28 | && cp /contrast/runtimes/linux-x64/native/ContrastChainLoader.so /contrast/runtimes/linux/native 29 | ARG VERSION=4.2.8 30 | ENV CONTRAST_MOUNT_PATH=/contrast-init \ 31 | CONTRAST_VERSION=${VERSION} \ 32 | CONTRAST_AGENT_TYPE=dotnet-core 33 | USER 1001 34 | ENTRYPOINT [ "/bin/sh", "/entrypoint.sh" ] 35 | 36 | FROM --platform=linux/arm64 busybox:stable AS final-linux-arm64 37 | RUN set -xe \ 38 | && addgroup -g 1001 custom-group \ 39 | && adduser -u 1001 -G custom-group -D -H custom-user 40 | COPY ./src/shared/entrypoint.sh /entrypoint.sh 41 | COPY --from=builder /contrast /contrast 42 | RUN set -xe \ 43 | && mkdir -p /contrast/runtimes/linux/native \ 44 | && cp /contrast/runtimes/linux-arm64/native/ContrastChainLoader.so /contrast/runtimes/linux/native 45 | ARG VERSION=4.2.8 46 | ENV CONTRAST_MOUNT_PATH=/contrast-init \ 47 | CONTRAST_VERSION=${VERSION} \ 48 | CONTRAST_AGENT_TYPE=dotnet-core 49 | USER 1001 50 | ENTRYPOINT [ "/bin/sh", "/entrypoint.sh" ] 51 | 52 | # We don't currently support Windows containers in the Agent Operator 53 | # This image exists purely get .NET Core agent files into windows container images. 54 | FROM --platform=windows/amd64 mcr.microsoft.com/windows/nanoserver:2004 AS final-windows-amd64 55 | COPY --from=builder /contrast /contrast 56 | 57 | # Use the correct final image based on TARGETPLATFORM 58 | FROM final-$TARGETOS-$TARGETARCH 59 | -------------------------------------------------------------------------------- /src/flex/Dockerfile: -------------------------------------------------------------------------------- 1 | # Contrast Security, Inc licenses this file to you under the Apache 2.0 License. 2 | # See the LICENSE file in the project root for more information. 3 | 4 | FROM --platform=$BUILDPLATFORM ubuntu:noble AS builder 5 | 6 | RUN set -xe \ 7 | && apt-get update \ 8 | && apt-get install -y curl 9 | 10 | ARG VERSION=1.6.0 11 | 12 | # Download the flex-agent package, untar 13 | RUN set -xe \ 14 | && curl --retry 5 --retry-all-errors --retry-delay 120 --fail --location https://pkg.contrastsecurity.com/artifactory/flex-agent-release/${VERSION}/contrast-flex-agent.tar.gz --output /tmp/contrast-flex-agent.tar.gz \ 15 | && mkdir /contrast \ 16 | && tar -xpzf /tmp/contrast-flex-agent.tar.gz -C /contrast \ 17 | && echo "{ \"version\": \"${VERSION}\" }" > /contrast/image-manifest.json 18 | 19 | # Create user (required for the agents command to work), generate comms files, enable auto-attach 20 | RUN set -xe \ 21 | && /contrast/service/x86_64/contrast-flex install-user \ 22 | && /contrast/service/x86_64/contrast-flex --comms-parent-dir "/contrast" --agents-parent-dir "/contrast" agents \ 23 | && /contrast/service/x86_64/contrast-flex --comms-parent-dir "/contrast" --agents-parent-dir "/contrast" auto-attach set true 24 | 25 | # Fix the path for the php agent, '-exec echo "{}" \;' + 'grep .' gives a non-zero exit code if no files were found 26 | RUN set -xe \ 27 | && find /contrast/flex-agents/php/ -name contrast.ini -type f -exec echo "{}" \; -exec sed -i 's|/usr/bin/contrast-flex-agent/|${CONTRAST_MOUNT_PATH}/|g' {} \; | grep . 28 | 29 | FROM --platform=linux/amd64 busybox:stable AS final-amd64 30 | RUN set -xe \ 31 | && addgroup -g 1001 custom-group \ 32 | && adduser -u 1001 -G custom-group -D -H custom-user 33 | COPY ./src/shared/entrypoint.sh /entrypoint.sh 34 | COPY --from=builder /contrast /contrast 35 | RUN set -xe \ 36 | && mkdir -p /contrast/injector \ 37 | && cp /contrast/service/x86_64/agent_injector.so /contrast/injector 38 | ARG VERSION=1.6.0 39 | ENV CONTRAST_MOUNT_PATH=/contrast-init \ 40 | CONTRAST_VERSION=${VERSION} \ 41 | CONTRAST_AGENT_TYPE=flex 42 | USER 1001 43 | ENTRYPOINT [ "/bin/sh", "/entrypoint.sh" ] 44 | 45 | FROM --platform=linux/arm64 busybox:stable AS final-arm64 46 | RUN set -xe \ 47 | && addgroup -g 1001 custom-group \ 48 | && adduser -u 1001 -G custom-group -D -H custom-user 49 | COPY ./src/shared/entrypoint.sh /entrypoint.sh 50 | COPY --from=builder /contrast /contrast 51 | RUN set -xe \ 52 | && mkdir -p /contrast/injector \ 53 | && cp /contrast/service/aarch64/agent_injector.so /contrast/injector 54 | ARG VERSION=1.6.0 55 | ENV CONTRAST_MOUNT_PATH=/contrast-init \ 56 | CONTRAST_VERSION=${VERSION} \ 57 | CONTRAST_AGENT_TYPE=flex 58 | USER 1001 59 | ENTRYPOINT [ "/bin/sh", "/entrypoint.sh" ] 60 | 61 | # Use the correct final image based on TARGETPLATFORM 62 | FROM final-$TARGETARCH 63 | -------------------------------------------------------------------------------- /.github/workflows/oob-update.yaml: -------------------------------------------------------------------------------- 1 | name: "OOB Update" 2 | on: 3 | repository_dispatch: 4 | types: 5 | - oob-update 6 | 7 | jobs: 8 | oob-update: 9 | runs-on: ubuntu-latest 10 | outputs: 11 | agent-type: ${{ steps.validate-event.outputs.agent-type }} 12 | agent-version: ${{ steps.validate-event.outputs.agent-version }} 13 | steps: 14 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 15 | - name: Validate Event 16 | id: validate-event 17 | run: | 18 | $clientPayload = '${{ toJSON(github.event.client_payload) }}' | ConvertFrom-Json 19 | [string] $agentType = $clientPayload.type 20 | [string] $agentVersion = $clientPayload.version 21 | [string] $manifestPath = "./src/$agentType/manifest.json" 22 | 23 | if ($agentType -notmatch '^(nodejs|java|dotnet-core|dotnet-framework|flex|php|python)$') 24 | { 25 | Write-Error "Failed to validate agent type." 26 | exit 1 27 | } 28 | if (-not (Test-Path -Path $manifestPath)) 29 | { 30 | Write-Error "$manifestPath does not exist." 31 | exit 1 32 | } 33 | if ($agentVersion -notmatch '^\d+(\.\d+){2,3}$') 34 | { 35 | Write-Error "Failed to validate agent version." 36 | exit 1 37 | } 38 | 39 | Write-Host "::set-output name=agent-type::$agentType" 40 | Write-Host "::set-output name=agent-version::$agentVersion" 41 | Write-Host "::set-output name=manifest-path::$manifestPath" 42 | shell: pwsh 43 | 44 | - name: Stage Changes 45 | id: stage-changes 46 | run: | 47 | $agentVersion = '${{ steps.validate-event.outputs.agent-version }}' 48 | $manifestPath = '${{ steps.validate-event.outputs.manifest-path }}' 49 | 50 | $manifest = Get-Content -Path $manifestPath | ConvertFrom-Json 51 | Write-Host "::set-output name=old-version::$($manifest.version)" 52 | 53 | $manifest.version = $agentVersion 54 | $manifest | ConvertTo-Json | Set-Content -Path $manifestPath -Encoding UTF8 55 | shell: pwsh 56 | 57 | - name: Create Pull Request 58 | uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6 59 | id: create-pr 60 | with: 61 | add-paths: | 62 | ${{ steps.validate-event.outputs.manifest-path }} 63 | commit-message: | 64 | Upgraded '${{ steps.validate-event.outputs.agent-type }}' to '${{ steps.validate-event.outputs.agent-version }}' 65 | branch: actions/oob-${{ steps.validate-event.outputs.agent-type }}-${{ steps.validate-event.outputs.agent-version }} 66 | title: | 67 | [OOB] Upgrades '${{ steps.validate-event.outputs.agent-type }}' to '${{ steps.validate-event.outputs.agent-version }}' 68 | body: | 69 | Automated OOB update requested by ${{ github.actor }}. 70 | 71 | Agent: `${{ steps.validate-event.outputs.agent-type }}` 72 | Version: `${{ steps.stage-changes.outputs.old-version }}` -> `${{ steps.validate-event.outputs.agent-version }}` 73 | labels: | 74 | oob-update 75 | token: ${{ secrets.GH_PR_WRITE_PAT }} 76 | 77 | - name: Enable Pull Request Automerge 78 | if: steps.create-pr.outputs.pull-request-operation == 'created' 79 | run: gh pr merge --rebase --auto "${{ steps.create-pr.outputs.pull-request-number }}" 80 | env: 81 | GH_TOKEN: ${{ secrets.GH_PR_WRITE_PAT }} 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # agent-operator-images 2 | 3 | Images for the [agent-operator](https://github.com/Contrast-Security-OSS/agent-operator) project. 4 | 5 | Managed by the Contrast .NET agent team. 6 | 7 | ## Images 8 | 9 | Public images are deployed to DockerHub. Currently, this repo publishes: 10 | 11 | 12 | - [![contrast/agent-dotnet-core](https://img.shields.io/docker/v/contrast/agent-dotnet-core?label=contrast%2Fagent-dotnet-core&logo=docker&logoColor=white&style=flat-square&cacheSeconds=10800)](https://hub.docker.com/r/contrast/agent-dotnet-core) 13 | - [![contrast/agent-dotnet-framework](https://img.shields.io/docker/v/contrast/agent-dotnet-framework?label=contrast%2Fagent-dotnet-framework&logo=docker&logoColor=white&style=flat-square&cacheSeconds=10800)](https://hub.docker.com/r/contrast/agent-dotnet-framework) 14 | - [![contrast/agent-java](https://img.shields.io/docker/v/contrast/agent-java?label=contrast%2Fagent-java&logo=docker&logoColor=white&style=flat-square&cacheSeconds=10800)](https://hub.docker.com/r/contrast/agent-java) 15 | - [![contrast/agent-nodejs](https://img.shields.io/docker/v/contrast/agent-nodejs?label=contrast%2Fagent-nodejs&logo=docker&logoColor=white&style=flat-square&cacheSeconds=10800)](https://hub.docker.com/r/contrast/agent-nodejs) 16 | - [![contrast/agent-php](https://img.shields.io/docker/v/contrast/agent-php?label=contrast%2Fagent-php&logo=docker&logoColor=white&style=flat-square&cacheSeconds=10800)](https://hub.docker.com/r/contrast/agent-php) 17 | - [![contrast/agent-python](https://img.shields.io/docker/v/contrast/agent-python?label=contrast%2Fagent-python&logo=docker&logoColor=white&style=flat-square&cacheSeconds=10800)](https://hub.docker.com/r/contrast/agent-python) 18 | - - [![contrast/agent-flex](https://img.shields.io/docker/v/contrast/agent-flex?label=contrast%2Fagent-flex&logo=docker&logoColor=white&style=flat-square&cacheSeconds=10800)](https://hub.docker.com/r/contrast/agent-flex) 19 | 20 | 21 | Tags are generated in the following format: 22 | 23 | ``` 24 | :2 25 | :2.1 26 | :2.1.10 27 | :latest 28 | ``` 29 | 30 | ## Image layout 31 | 32 | All images contain a directory of `/contrast` containing all the agent files. This directory is stable and may be publicly documented. 33 | 34 | Inside this directory is a json file `image-manifest.json` with the layout of: 35 | 36 | ```json 37 | { 38 | "version": "${VERSION}" 39 | } 40 | ``` 41 | 42 | This file may be used by agents or for debugging containerized deployments in production. Additional information may be added in the future. 43 | 44 | Upon starting, the default entrypoint of these images will copy all files from `/contrast` to `$CONTRAST_MOUNT_PATH` (defaults to `/contrast-init`) and exit. Some agents may require a specific `CONTRAST_MOUNT_PATH` to function correctly. 45 | 46 | > The .NET Framework agent image does not contain an entrypoint and should only be used to aid in creating base images for Windows Containers. .NET Framework agent files are located in `C:\Contrast`. 47 | 48 | ## Updating Images 49 | 50 | Images are updated by executing a repository dispatch with a provided PAT. 51 | 52 | ```bash 53 | curl -H "Authorization: token ${GH_PAT}" \ 54 | -H 'Accept: application/vnd.github.everest-preview+json' \ 55 | "https://api.github.com/repos/Contrast-Security-OSS/agent-operator-images/dispatches" \ 56 | -d '{"event_type": "oob-update", "client_payload": {"type": "dotnet-core", "version": "2.1.12"}}' 57 | ``` 58 | 59 | Once the dispatch request is received, the following events execute automatically: 60 | 61 | - A PR with the requested version is created on a new branch. 62 | - Basic checks are executed to ensure the version can be built. 63 | - Upon successful validation, the PR is automatically merged into trunk. 64 | 65 | Merging into trunk starts the following events: 66 | 67 | - Create and publish all images in this repository. 68 | - When all images have been built successfully, start a deployment to `internal`. This copies the artifact images from the first step, with final image tags. 69 | - When the internal environment deployment has succeeded, start a deployment to `public`. 70 | 71 | ## Creating Backports 72 | 73 | Backports may be created by pushing a branch in the format of `backport/-v` with the version of the agent being backported. Backports will not update the `latest` tag and will not update the Major/Minor tags. 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc 262 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: "Build" 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - master 7 | - backport/** 8 | jobs: 9 | # 10 | # Building Stage 11 | # 12 | build: 13 | strategy: 14 | matrix: 15 | variant: 16 | - dotnet-core 17 | - dotnet-framework 18 | - java 19 | - nodejs 20 | - php 21 | - python 22 | - flex 23 | - dummy 24 | runs-on: ubuntu-latest 25 | outputs: 26 | artifact-image-dotnet-core: ${{ steps.detect-artifact.outputs.artifact-image-dotnet-core }} 27 | artifact-version-dotnet-core: ${{ steps.detect-artifact.outputs.artifact-version-dotnet-core }} 28 | artifact-image-dotnet-framework: ${{ steps.detect-artifact.outputs.artifact-image-dotnet-framework }} 29 | artifact-version-dotnet-framework: ${{ steps.detect-artifact.outputs.artifact-version-dotnet-framework }} 30 | artifact-image-java: ${{ steps.detect-artifact.outputs.artifact-image-java }} 31 | artifact-version-java: ${{ steps.detect-artifact.outputs.artifact-version-java }} 32 | artifact-image-nodejs: ${{ steps.detect-artifact.outputs.artifact-image-nodejs }} 33 | artifact-version-nodejs: ${{ steps.detect-artifact.outputs.artifact-version-nodejs }} 34 | artifact-image-php: ${{ steps.detect-artifact.outputs.artifact-image-php }} 35 | artifact-version-php: ${{ steps.detect-artifact.outputs.artifact-version-php }} 36 | artifact-image-python: ${{ steps.detect-artifact.outputs.artifact-image-python }} 37 | artifact-version-python: ${{ steps.detect-artifact.outputs.artifact-version-python }} 38 | artifact-image-flex: ${{ steps.detect-artifact.outputs.artifact-image-flex }} 39 | artifact-version-flex: ${{ steps.detect-artifact.outputs.artifact-version-flex }} 40 | artifact-image-dummy: ${{ steps.detect-artifact.outputs.artifact-image-dummy }} 41 | artifact-version-dummy: ${{ steps.detect-artifact.outputs.artifact-version-dummy }} 42 | env: 43 | AGENT_TYPE: ${{ matrix.variant }} 44 | steps: 45 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 46 | - name: Load Manifest 47 | id: load-manifest 48 | run: | 49 | $manifest = Get-Content './src/${{ env.AGENT_TYPE }}/manifest.json' | ConvertFrom-Json 50 | 51 | "version=$($manifest.version)" >> $env:GITHUB_OUTPUT 52 | "image-name-suffix=$($manifest.imageNameSuffix)" >> $env:GITHUB_OUTPUT 53 | "docker-file=$($manifest.dockerFile)" >> $env:GITHUB_OUTPUT 54 | "context=$($manifest.context)" >> $env:GITHUB_OUTPUT 55 | "platforms=$($manifest.platforms)" >> $env:GITHUB_OUTPUT 56 | 57 | "image-name=ghcr.io/contrast-security-oss/agent-operator-images/agent-$($manifest.imageNameSuffix)" >> $env:GITHUB_OUTPUT 58 | shell: pwsh 59 | 60 | - name: Setup QEMU 61 | uses: docker/setup-qemu-action@53851d14592bedcffcf25ea515637cff71ef929a # v3.3.0 62 | with: 63 | platforms: arm64 64 | 65 | - name: Setup Buildx 66 | uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0 67 | with: 68 | install: true 69 | version: latest 70 | 71 | - name: Docker Meta 72 | id: meta 73 | uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 # v5.6.1 74 | with: 75 | images: ${{ steps.load-manifest.outputs.image-name }} 76 | tags: | 77 | type=raw,value=trunk-artifact,enable=${{ github.ref == 'refs/heads/master' }} 78 | type=raw,value=backport-artifact,enable=${{ startsWith(github.ref, 'refs/heads/backport/') }} 79 | type=raw,value=pr-artifact,enable=${{ github.event_name == 'pull_request' }} 80 | 81 | - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 82 | with: 83 | registry: ghcr.io 84 | username: ${{ github.actor }} 85 | password: ${{ secrets.GITHUB_TOKEN }} 86 | 87 | - uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0 88 | id: build 89 | with: 90 | file: ${{ steps.load-manifest.outputs.docker-file }} 91 | context: ${{ steps.load-manifest.outputs.context }} 92 | platforms: ${{ steps.load-manifest.outputs.platforms }} 93 | push: ${{ !(github.event_name == 'pull_request' && github.actor == 'dependabot[bot]') }} # dependabot cant push on pr's 94 | cache-from: ${{ github.actor != 'dependabot[bot]' && format('type=registry,ref={0}:cache', steps.load-manifest.outputs.image-name) || ''}} 95 | cache-to: ${{ github.actor != 'dependabot[bot]' && format('type=registry,ref={0}:cache,mode=max', steps.load-manifest.outputs.image-name) || ''}} 96 | build-args: | 97 | VERSION=${{ steps.load-manifest.outputs.version }} 98 | tags: ${{ steps.meta.outputs.tags }} 99 | labels: | 100 | org.opencontainers.image.title=agent-operator-images 101 | org.opencontainers.image.description=Images of agents for the agent-operator. 102 | org.opencontainers.image.url=https://github.com/Contrast-Security-OSS/agent-operator-images 103 | org.opencontainers.image.source=https://github.com/Contrast-Security-OSS/agent-operator-images 104 | org.opencontainers.image.version=${{ steps.load-manifest.outputs.version }} 105 | 106 | - name: Detect Artifact 107 | id: detect-artifact 108 | if: ${{ !(github.event_name == 'pull_request' && github.actor == 'dependabot[bot]') }} 109 | run: | 110 | $artifactImage = '${{ steps.load-manifest.outputs.image-name }}@${{ steps.build.outputs.digest }}' 111 | "artifact-image-${{ env.AGENT_TYPE }}=$artifactImage" >> $env:GITHUB_OUTPUT 112 | "artifact-version-${{ env.AGENT_TYPE }}=${{ steps.load-manifest.outputs.version }}" >> $env:GITHUB_OUTPUT 113 | shell: pwsh 114 | test: 115 | runs-on: ubuntu-latest 116 | needs: 117 | - build 118 | strategy: 119 | matrix: 120 | variants: 121 | - type: dotnet-core 122 | digest: ${{ needs.build.outputs.artifact-image-dotnet-core }} 123 | version: ${{ needs.build.outputs.artifact-version-dotnet-core }} 124 | - type: dotnet-framework 125 | digest: ${{ needs.build.outputs.artifact-image-dotnet-framework }} 126 | version: ${{ needs.build.outputs.artifact-version-dotnet-framework }} 127 | - type: java 128 | digest: ${{ needs.build.outputs.artifact-image-java }} 129 | version: ${{ needs.build.outputs.artifact-version-java }} 130 | - type: nodejs 131 | digest: ${{ needs.build.outputs.artifact-image-nodejs }} 132 | version: ${{ needs.build.outputs.artifact-version-nodejs }} 133 | - type: php 134 | digest: ${{ needs.build.outputs.artifact-image-php }} 135 | version: ${{ needs.build.outputs.artifact-version-php }} 136 | - type: python 137 | digest: ${{ needs.build.outputs.artifact-image-python }} 138 | version: ${{ needs.build.outputs.artifact-version-python }} 139 | - type: flex 140 | digest: ${{ needs.build.outputs.artifact-image-flex }} 141 | version: ${{ needs.build.outputs.artifact-version-flex }} 142 | - type: dummy 143 | digest: ${{ needs.build.outputs.artifact-image-dummy }} 144 | version: ${{ needs.build.outputs.artifact-version-dummy }} 145 | if: ${{ !(github.event_name == 'pull_request' && github.actor == 'dependabot[bot]') }} 146 | steps: 147 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 148 | - name: Login (Github) 149 | uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 150 | with: 151 | registry: ghcr.io 152 | username: ${{ github.actor }} 153 | password: ${{ secrets.GITHUB_TOKEN }} 154 | - name: Test 155 | run: | 156 | docker pull '${{ matrix.variants.digest }}' 157 | ./validate-image.ps1 -Type '${{ matrix.variants.type }}' -Image '${{ matrix.variants.digest }}' 158 | shell: pwsh 159 | 160 | # Currently just used for pr's. 161 | build-succeeded: 162 | needs: 163 | - build 164 | - test 165 | runs-on: ubuntu-latest 166 | if: ${{ always() && github.event_name == 'pull_request' && github.actor != 'dependabot[bot]' }} 167 | steps: 168 | - name: Ensure Build Success 169 | if: ${{ needs.test.result != 'success' }} 170 | run: exit 1 171 | - name: Create Digest Comment 172 | uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 173 | if: ${{ github.actor != 'dependabot[bot]' }} 174 | with: 175 | issue-number: ${{ github.event.pull_request.number }} 176 | body: | 177 | Images built: 178 | ``` 179 | dotnet-core: ${{ needs.build.outputs.artifact-image-dotnet-core }} 180 | dotnet-framework: ${{ needs.build.outputs.artifact-image-dotnet-framework }} 181 | java: ${{ needs.build.outputs.artifact-image-java }} 182 | nodejs: ${{ needs.build.outputs.artifact-image-nodejs }} 183 | php: ${{ needs.build.outputs.artifact-image-php }} 184 | python: ${{ needs.build.outputs.artifact-image-python }} 185 | flex: ${{ needs.build.outputs.artifact-image-flex }} 186 | ``` 187 | # 188 | # Release Internal Stage 189 | # 190 | release-internal: 191 | runs-on: ubuntu-latest 192 | environment: internal 193 | needs: 194 | - build 195 | - test 196 | strategy: 197 | matrix: 198 | variants: 199 | - type: dotnet-core 200 | digest: ${{ needs.build.outputs.artifact-image-dotnet-core }} 201 | version: ${{ needs.build.outputs.artifact-version-dotnet-core }} 202 | - type: dotnet-framework 203 | digest: ${{ needs.build.outputs.artifact-image-dotnet-framework }} 204 | version: ${{ needs.build.outputs.artifact-version-dotnet-framework }} 205 | - type: java 206 | digest: ${{ needs.build.outputs.artifact-image-java }} 207 | version: ${{ needs.build.outputs.artifact-version-java }} 208 | - type: nodejs 209 | digest: ${{ needs.build.outputs.artifact-image-nodejs }} 210 | version: ${{ needs.build.outputs.artifact-version-nodejs }} 211 | - type: php 212 | digest: ${{ needs.build.outputs.artifact-image-php }} 213 | version: ${{ needs.build.outputs.artifact-version-php }} 214 | - type: python 215 | digest: ${{ needs.build.outputs.artifact-image-python }} 216 | version: ${{ needs.build.outputs.artifact-version-python }} 217 | - type: flex 218 | digest: ${{ needs.build.outputs.artifact-image-flex }} 219 | version: ${{ needs.build.outputs.artifact-version-flex }} 220 | - type: dummy 221 | digest: ${{ needs.build.outputs.artifact-image-dummy }} 222 | version: ${{ needs.build.outputs.artifact-version-dummy }} 223 | concurrency: 224 | group: internal-${{ matrix.variants.type }} 225 | if: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/backport/') }} 226 | steps: 227 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 228 | - name: Login (Github) 229 | uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 230 | with: 231 | registry: ghcr.io 232 | username: ${{ github.actor }} 233 | password: ${{ secrets.GITHUB_TOKEN }} 234 | - name: Docker Meta 235 | id: meta 236 | uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 # v5.6.1 237 | with: 238 | images: ghcr.io/contrast-security-oss/agent-operator-images/agent-${{ matrix.variants.type }} 239 | tags: | 240 | type=semver,pattern={{version}},value=${{ matrix.variants.version }},enable=${{ github.ref == 'refs/heads/master' }} 241 | type=semver,pattern={{major}}.{{minor}},value=${{ matrix.variants.version }},enable=${{ github.ref == 'refs/heads/master' }} 242 | type=semver,pattern={{major}},value=${{ matrix.variants.version }},enable=${{ github.ref == 'refs/heads/master' }} 243 | type=raw,value=${{ matrix.variants.version }} 244 | type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }} 245 | - name: Tag for Release 246 | uses: akhilerm/tag-push-action@f35ff2cb99d407368b5c727adbcc14a2ed81d509 # v2.2.0 247 | with: 248 | src: ${{ matrix.variants.digest }} 249 | dst: | 250 | ${{ steps.meta.outputs.tags }} 251 | # 252 | # Release Public Stage 253 | # 254 | release-public: 255 | runs-on: ubuntu-latest 256 | environment: public 257 | needs: 258 | - build 259 | - release-internal 260 | strategy: 261 | matrix: 262 | variants: 263 | - type: dotnet-core 264 | digest: ${{ needs.build.outputs.artifact-image-dotnet-core }} 265 | version: ${{ needs.build.outputs.artifact-version-dotnet-core }} 266 | - type: dotnet-framework 267 | digest: ${{ needs.build.outputs.artifact-image-dotnet-framework }} 268 | version: ${{ needs.build.outputs.artifact-version-dotnet-framework }} 269 | - type: java 270 | digest: ${{ needs.build.outputs.artifact-image-java }} 271 | version: ${{ needs.build.outputs.artifact-version-java }} 272 | - type: nodejs 273 | digest: ${{ needs.build.outputs.artifact-image-nodejs }} 274 | version: ${{ needs.build.outputs.artifact-version-nodejs }} 275 | - type: php 276 | digest: ${{ needs.build.outputs.artifact-image-php }} 277 | version: ${{ needs.build.outputs.artifact-version-php }} 278 | - type: python 279 | digest: ${{ needs.build.outputs.artifact-image-python }} 280 | version: ${{ needs.build.outputs.artifact-version-python }} 281 | - type: flex 282 | digest: ${{ needs.build.outputs.artifact-image-flex }} 283 | version: ${{ needs.build.outputs.artifact-version-flex }} 284 | - type: dummy 285 | digest: ${{ needs.build.outputs.artifact-image-dummy }} 286 | version: ${{ needs.build.outputs.artifact-version-dummy }} 287 | concurrency: 288 | group: public-${{ matrix.variants.type }} 289 | if: ${{ github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/backport/') }} 290 | steps: 291 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 292 | - name: Login (Github) 293 | uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 294 | with: 295 | registry: ghcr.io 296 | username: ${{ github.actor }} 297 | password: ${{ secrets.GITHUB_TOKEN }} 298 | - name: Login (Dockerhub) 299 | uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 300 | with: 301 | username: ${{ secrets.DOCKERHUB_USERNAME }} 302 | password: ${{ secrets.DOCKERHUB_PAT }} 303 | - name: Login (Quay) 304 | uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 305 | with: 306 | registry: quay.io 307 | username: ${{ secrets.QUAY_USERNAME }} 308 | password: ${{ secrets.QUAY_PASSWORD }} 309 | - name: Docker Meta 310 | id: meta 311 | uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 # v5.6.1 312 | with: 313 | images: | 314 | docker.io/contrast/agent-${{ matrix.variants.type }} 315 | quay.io/contrast/agent-${{ matrix.variants.type }} 316 | tags: | 317 | type=semver,pattern={{version}},value=${{ matrix.variants.version }},enable=${{ github.ref == 'refs/heads/master' }} 318 | type=semver,pattern={{major}}.{{minor}},value=${{ matrix.variants.version }},enable=${{ github.ref == 'refs/heads/master' }} 319 | type=semver,pattern={{major}},value=${{ matrix.variants.version }},enable=${{ github.ref == 'refs/heads/master' }} 320 | type=raw,value=${{ matrix.variants.version }} 321 | type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }} 322 | - name: Tag for Release 323 | uses: akhilerm/tag-push-action@f35ff2cb99d407368b5c727adbcc14a2ed81d509 # v2.2.0 324 | with: 325 | src: ${{ matrix.variants.digest }} 326 | dst: | 327 | ${{ steps.meta.outputs.tags }} 328 | --------------------------------------------------------------------------------