Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
7d88cbc
fix: eliminate race conditions in release-go job
abienkowski Sep 12, 2026
31296b6
ci: add arm64 Rust binary build with cross-compilation
abienkowski Sep 12, 2026
087e315
ci/docker: enable multi-arch builds (amd64, arm64) for all implementa…
abienkowski Sep 12, 2026
042c145
docs: update reproducible-builds.md for multi-arch support (amd64, ar…
abienkowski Sep 12, 2026
6105466
docs: document multi-arch support and fix static linking claims
abienkowski Sep 12, 2026
4273fa6
fix: handle native docker build fallback for TARGETPLATFORM/TARGETARC…
abienkowski Sep 12, 2026
35170f1
fix: properly detect architecture in native docker build for go/Docke…
abienkowski Sep 12, 2026
1a0bf79
fix: use sh -c to prevent variable expansion during dockerfile parse …
abienkowski Sep 12, 2026
0a917c4
fix: use backslash continuation instead of sh -c for proper variable …
abienkowski Sep 12, 2026
aecaa76
fix: use /bin/bash -c with proper quoting for shell conditionals in RUN
abienkowski Sep 12, 2026
02586cd
fix: use heredoc with quoted EOF to prevent variable expansion at par…
abienkowski Sep 12, 2026
09e48ff
debug: add diagnostic logging for TARGETARCH/TARGETPLATFORM and uname -m
abienkowski Sep 12, 2026
455df54
fix: docker heredoc syntax - add -c flag to /bin/bash for heredoc sup…
abienkowski Sep 12, 2026
2e65c7e
fix: use single-quoted bash -c with line continuations instead of her…
abienkowski Sep 12, 2026
bf6694b
fix: use SHELL directive to set bash as default shell for RUN commands
abienkowski Sep 12, 2026
21cd4af
fix: use /bin/sh instead of /bin/bash - stagex images use busybox
abienkowski Sep 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .cargo/Cross.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[build]
target-dir = "/target"

[target.aarch64-unknown-linux-musl]
# cross handles sysroot automatically
78 changes: 59 additions & 19 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,47 +93,61 @@ jobs:
--clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to GHCR
# Docker image build, SBOM, and Cosign steps only run in the amd64 job.
# buildx handles multi-arch in a single operation (platforms: linux/amd64,linux/arm64).
- if: matrix.goarch == 'amd64'
name: Log in to GHCR
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Go Docker image
- if: matrix.goarch == 'amd64'
name: Build and push Go Docker image (multi-arch)
uses: docker/build-push-action@v7
with:
context: go
file: go/Dockerfile
# Multi-arch: buildx creates a single image index for amd64 & arm64.
# This eliminates race conditions from matrix jobs pushing to the same tag.
# SBOM and Cosign operations then work on the completed index.
platforms: linux/amd64,linux/arm64
push: true
build-args: |
VERSION=${{ needs.version.outputs.tag }}
tags: |
ghcr.io/chainsafe/docker-socket-policy-go:${{ needs.version.outputs.tag }}
ghcr.io/chainsafe/docker-socket-policy-go:latest
- name: Install syft
- if: matrix.goarch == 'amd64'
name: Install syft
run: |
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | \
sh -s -- -b /usr/local/bin v1.42.3
- name: Generate SPDX SBOM for Go image
- if: matrix.goarch == 'amd64'
name: Generate SPDX SBOM for Go image
run: >
syft ghcr.io/chainsafe/docker-socket-policy-go:${{ needs.version.outputs.tag }}
-o spdx-json
--file docker-socket-policy-go.spdx.json
- name: Generate CycloneDX SBOM for Go image
- if: matrix.goarch == 'amd64'
name: Generate CycloneDX SBOM for Go image
run: >
syft ghcr.io/chainsafe/docker-socket-policy-go:${{ needs.version.outputs.tag }}
-o cyclonedx-json
--file docker-socket-policy-go.cyclonedx.json
- name: Upload Go SBOMs to release
- if: matrix.goarch == 'amd64'
name: Upload Go SBOMs to release
run: |
gh release upload "${{ needs.version.outputs.tag }}" \
docker-socket-policy-go.spdx.json \
docker-socket-policy-go.cyclonedx.json \
--clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: sigstore/cosign-installer@v3
- name: Sign Go Docker image with Cosign
- if: matrix.goarch == 'amd64'
uses: sigstore/cosign-installer@v3
- if: matrix.goarch == 'amd64'
name: Sign Go Docker image with Cosign
env:
COSIGN_EXPERIMENTAL: 1
run: |
Expand All @@ -143,60 +157,86 @@ jobs:
release-rust:
needs: [version]
runs-on: ubuntu-latest
strategy:
matrix:
rust_target: [x86_64-unknown-linux-musl, aarch64-unknown-linux-musl]
include:
- rust_target: x86_64-unknown-linux-musl
arch_name: amd64
- rust_target: aarch64-unknown-linux-musl
arch_name: arm64
steps:
- uses: actions/checkout@v7
- uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: "1.85"
targets: ${{ matrix.rust_target }}
- name: Install cross
run: cargo install cross
- name: Build Rust binary
run: cd rs && cargo build --release
run: cd rs && cross build --release --target ${{ matrix.rust_target }}
- name: Upload Rust binary to release
run: |
cp rs/target/release/docker-socket-policy docker-socket-policy-rs-linux-amd64
cp rs/target/${{ matrix.rust_target }}/release/docker-socket-policy docker-socket-policy-rs-linux-${{ matrix.arch_name }}
gh release upload "${{ needs.version.outputs.tag }}" \
docker-socket-policy-rs-linux-amd64 \
docker-socket-policy-rs-linux-${{ matrix.arch_name }} \
--clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to GHCR
# Docker image build, SBOM, and Cosign steps only run in the amd64 job.
- if: matrix.arch_name == 'amd64'
name: Log in to GHCR
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Rust Docker image
- if: matrix.arch_name == 'amd64'
name: Build and push Rust Docker image (multi-arch)
uses: docker/build-push-action@v7
with:
context: rs
file: rs/Dockerfile
# Multi-arch: buildx creates a single image index for amd64 & arm64.
# This eliminates race conditions from matrix jobs pushing to the same tag.
# SBOM and Cosign operations then work on the completed index.
platforms: linux/amd64,linux/arm64
push: true
build-args: |
VERSION=${{ needs.version.outputs.tag }}
tags: |
ghcr.io/chainsafe/docker-socket-policy-rs:${{ needs.version.outputs.tag }}
ghcr.io/chainsafe/docker-socket-policy-rs:latest
- name: Install syft
- if: matrix.arch_name == 'amd64'
name: Install syft
run: |
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | \
sh -s -- -b /usr/local/bin v1.42.3
- name: Generate SPDX SBOM for Rust image
- if: matrix.arch_name == 'amd64'
name: Generate SPDX SBOM for Rust image
run: >
syft ghcr.io/chainsafe/docker-socket-policy-rs:${{ needs.version.outputs.tag }}
-o spdx-json
--file docker-socket-policy-rs.spdx.json
- name: Generate CycloneDX SBOM for Rust image
- if: matrix.arch_name == 'amd64'
name: Generate CycloneDX SBOM for Rust image
run: >
syft ghcr.io/chainsafe/docker-socket-policy-rs:${{ needs.version.outputs.tag }}
-o cyclonedx-json
--file docker-socket-policy-rs.cyclonedx.json
- name: Upload Rust SBOMs to release
- if: matrix.arch_name == 'amd64'
name: Upload Rust SBOMs to release
run: |
gh release upload "${{ needs.version.outputs.tag }}" \
docker-socket-policy-rs.spdx.json \
docker-socket-policy-rs.cyclonedx.json \
--clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- uses: sigstore/cosign-installer@v3
- name: Sign Rust Docker image with Cosign
- if: matrix.arch_name == 'amd64'
uses: sigstore/cosign-installer@v3
- if: matrix.arch_name == 'amd64'
name: Sign Rust Docker image with Cosign
env:
COSIGN_EXPERIMENTAL: 1
run: |
Expand Down
62 changes: 47 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ Key features:

## Installation

### Supported Architectures

docker-socket-policy builds and runs on **amd64** (x86-64) and **arm64** (AArch64) Linux architectures:

- **Docker Images**: Multi-arch manifest indexes automatically select the correct architecture when pulling. No platform flag needed:
```bash
docker pull ghcr.io/chainsafe/docker-socket-policy-go:latest
# Pulls amd64 on x86-64, arm64 on ARM machines
```

- **Prebuilt Binaries**: Both amd64 and arm64 variants are published with each release.

See [docs/reproducible-builds.md](docs/reproducible-builds.md) for verification and per-architecture build instructions.

### Docker Images

Signed, SBOM-attested images are published to GHCR for all three implementations:
Expand All @@ -36,20 +50,37 @@ Every image is Cosign-signed and ships with SPDX + CycloneDX SBOMs attached to t

### Prebuilt Binaries

Each [release](https://github.com/ChainSafe/docker-socket-policy/releases/latest) attaches a Go binary, a Rust binary, and a TypeScript build archive (plus SBOMs for each):
Each [release](https://github.com/ChainSafe/docker-socket-policy/releases/latest) attaches binaries for amd64 and arm64 architectures, plus SBOMs for each:

**Go** (statically linked ELF binary):
```bash
# Go (statically linked binary)
curl -LO https://github.com/ChainSafe/docker-socket-policy/releases/latest/download/docker-socket-policy-go
chmod +x docker-socket-policy-go
# amd64
curl -LO https://github.com/ChainSafe/docker-socket-policy/releases/latest/download/docker-socket-policy-go-linux-amd64
chmod +x docker-socket-policy-go-linux-amd64

# TypeScript (Node 22+ required; archive includes dist/ and node_modules/)
# Replace <version> with the tag shown on the releases page, e.g. v0.2.8
curl -LO https://github.com/ChainSafe/docker-socket-policy/releases/latest/download/docker-socket-policy-ts-<version>.tar.gz
tar xzf docker-socket-policy-ts-<version>.tar.gz && node dist/index.js
# arm64
curl -LO https://github.com/ChainSafe/docker-socket-policy/releases/latest/download/docker-socket-policy-go-linux-arm64
chmod +x docker-socket-policy-go-linux-arm64
```

The Rust binary is also attached to every release; see the release page for the exact asset name.
**Rust** (statically linked ELF binary with musl):
```bash
# amd64
curl -LO https://github.com/ChainSafe/docker-socket-policy/releases/latest/download/docker-socket-policy-rs-linux-amd64
chmod +x docker-socket-policy-rs-linux-amd64

# arm64
curl -LO https://github.com/ChainSafe/docker-socket-policy/releases/latest/download/docker-socket-policy-rs-linux-arm64
chmod +x docker-socket-policy-rs-linux-arm64
```

**TypeScript** (Node 22+ required; archive includes dist/, node_modules/, and package files):
```bash
# Extract and run (platform-independent Node archive)
curl -LO https://github.com/ChainSafe/docker-socket-policy/releases/latest/download/docker-socket-policy-ts-<version>.tar.gz
tar xzf docker-socket-policy-ts-<version>.tar.gz
node dist/index.js
```

To build any implementation from source instead, see [Build All](#build-all) below.

Expand Down Expand Up @@ -188,12 +219,13 @@ docker pull attacker/malware:latest # denied: image not in allowlist
| `--log-file` | `/var/log/docker-socket-policy.log` | Audit log path |
| `--readonly` | `false` | Enable read-only mode |

> The TypeScript implementation listens on TCP only (`--listen-tcp`); it does not
> implement `--listen-socket`. All three implementations connect to the Docker
> daemon over a Unix socket only: Go and Rust treat `--docker-host` as a Unix
> socket path, and TypeScript additionally rejects `tcp://`/`http://` schemes
> outright. Connecting to the daemon over TCP would bypass the socket's
> user/group ownership, which is the security boundary.
> **Unix socket security boundary**: Go and Rust support `--listen-socket` for
> binding to a Unix socket, enabling socket-level access control via file
> permissions and Unix groups. TypeScript does not implement `--listen-socket`
> and listens on TCP only (`--listen-tcp`). All three implementations connect
> to the Docker daemon over Unix sockets exclusively; they reject `tcp://` and
> `http://` schemes for `--docker-host`. TCP connections would bypass socket
> ownership-based access control, breaking the security model.

### Systemd Socket Activation

Expand Down
54 changes: 45 additions & 9 deletions docs/reproducible-builds.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Reproducible Builds Verification

This document describes how to verify that docker-socket-policy builds are reproducible and how to verify SBOMs and signatures.
This document describes how to verify that docker-socket-policy builds are reproducible and how to verify SBOMs and signatures. docker-socket-policy is built for multiple architectures: **amd64** and **arm64**.

## Prerequisites

Expand Down Expand Up @@ -30,46 +30,79 @@ Each target builds twice with `--no-cache` and uses `cmp` to confirm bit-identic

## Verify a Single Build Step by Step

### Build and Verify amd64 Binary

```bash
# Build Go binary
# Build Go binary for amd64
docker build --no-cache --platform linux/amd64 \
--build-arg VERSION=$(git describe --tags --always --dirty) \
--output type=local,dest=/tmp/build \
--output type=local,dest=/tmp/build-amd64 \
-f go/Dockerfile go/

# Check the binary
file /tmp/build/docker-socket-policy
file /tmp/build-amd64/docker-socket-policy
# Expected: ELF 64-bit LSB executable, x86-64, statically linked

# Generate SBOM
syft scan /tmp/build/docker-socket-policy -o spdx-json > docker-socket-policy.spdx.json
syft scan /tmp/build/docker-socket-policy -o cyclonedx-json > docker-socket-policy.cyclonedx.json
syft scan /tmp/build-amd64/docker-socket-policy -o spdx-json > docker-socket-policy-amd64.spdx.json
syft scan /tmp/build-amd64/docker-socket-policy -o cyclonedx-json > docker-socket-policy-amd64.cyclonedx.json
```

### Build and Verify arm64 Binary

```bash
# Build Go binary for arm64
docker build --no-cache --platform linux/arm64 \
--build-arg VERSION=$(git describe --tags --always --dirty) \
--output type=local,dest=/tmp/build-arm64 \
-f go/Dockerfile go/

# Check the binary
file /tmp/build-arm64/docker-socket-policy
# Expected: ELF 64-bit LSB executable, ARM aarch64, statically linked

# Generate SBOM
syft scan /tmp/build-arm64/docker-socket-policy -o spdx-json > docker-socket-policy-arm64.spdx.json
syft scan /tmp/build-arm64/docker-socket-policy -o cyclonedx-json > docker-socket-policy-arm64.cyclonedx.json
```

## Verify SBOMs from a Release

Binary artifacts are available for both amd64 and arm64:

```bash
# Download SBOMs from a release
gh release download v0.1.0 --pattern "*.spdx.json"
gh release download v0.1.0 --pattern "*.cyclonedx.json"

# Inspect SBOM
# Available binaries: docker-socket-policy-{go,rs,ts}-linux-{amd64,arm64}
ls -la docker-socket-policy-*-linux-*

# Inspect SBOM (covers all architectures in the release)
cat docker-socket-policy-go.spdx.json | jq '.packages[].name'
```

## Verify Docker Image Signatures

Docker images are published as **multi-arch manifest indexes** that automatically select the correct architecture (amd64 or arm64) when pulling:

```bash
# Verify Cosign signature (keyless via OIDC)
# Verify Cosign signature on multi-arch image (verifies entire manifest index)
cosign verify \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/chainsafe/docker-socket-policy-go:<version>

# Pull for specific architecture (if you need to override auto-detection)
docker pull --platform linux/amd64 ghcr.io/chainsafe/docker-socket-policy-go:<version>
docker pull --platform linux/arm64 ghcr.io/chainsafe/docker-socket-policy-go:<version>
```

## Verify SBOMs Attached to Docker Images

SBOMs are attached to the multi-arch manifest index, covering all architectures:

```bash
# List attestations on an image
# List attestations on the multi-arch image (covers amd64 and arm64)
cosign verify-attestation \
--type spdx \
ghcr.io/chainsafe/docker-socket-policy-go:<version>
Expand All @@ -79,6 +112,9 @@ cosign download attestation \
--type spdx \
ghcr.io/chainsafe/docker-socket-policy-go:<version> \
| jq '.payload | @base64d | fromjson'

# If you need the SBOM for a specific architecture image, download from release artifacts
# Example: docker-socket-policy-go-linux-arm64.spdx.json
```

## Build from Source
Expand Down
Loading