Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
178 changes: 178 additions & 0 deletions .github/workflows/publish-quovy-images.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
name: Publish Quovy OpenSandbox images

on:
push:
branches: [main]
paths:
- .github/workflows/publish-quovy-images.yml
- kubernetes/Dockerfile.image-committer
- kubernetes/cmd/image-committer/**
- kubernetes/go.mod
- kubernetes/go.sum
- server/Dockerfile
- server/egress/**
- server/pyproject.toml
- server/uv.lock
workflow_dispatch:

permissions:
contents: read
id-token: write
packages: write

concurrency:
group: publish-quovy-opensandbox-${{ github.ref }}
cancel-in-progress: false

jobs:
publish:
name: Build, scan, and sign ${{ matrix.name }}
if: github.repository == 'Quovy/OpenSandbox'
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
include:
- name: server
context: server
dockerfile: server/Dockerfile
repository: opensandbox-server
- name: egress
context: server/egress
dockerfile: server/egress/Dockerfile
repository: opensandbox-egress
- name: image-committer
context: kubernetes
dockerfile: kubernetes/Dockerfile.image-committer
repository: opensandbox-image-committer
env:
IMAGE_REF: ghcr.io/quovy/${{ matrix.repository }}
COSIGN_CERTIFICATE_IDENTITY: https://github.com/Quovy/OpenSandbox/.github/workflows/publish-quovy-images.yml@refs/heads/main
COSIGN_VERIFICATION_OIDC_ISSUER: https://token.actions.githubusercontent.com
steps:
- name: Check out maintained source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Set up QEMU
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4
with:
cache-image: false

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4

- name: Log in to GitHub Container Registry
env:
GHCR_TOKEN: ${{ github.token }}
run: echo "$GHCR_TOKEN" | docker login ghcr.io --username "${GITHUB_ACTOR}" --password-stdin

- name: Build and publish immutable candidate
id: build
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7
with:
context: ${{ matrix.context }}
file: ${{ matrix.dockerfile }}
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ env.IMAGE_REF }}:sha-${{ github.sha }}
cache-from: type=gha,scope=${{ matrix.name }}
cache-to: type=gha,scope=${{ matrix.name }},mode=max
provenance: mode=max
sbom: true

- name: Scan amd64 image and record High/Critical findings
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
env:
TRIVY_PLATFORM: linux/amd64
with:
version: v0.66.0
scan-type: image
image-ref: ${{ env.IMAGE_REF }}@${{ steps.build.outputs.digest }}
scanners: vuln
severity: HIGH,CRITICAL
format: json
output: trivy-${{ matrix.name }}-amd64.json
exit-code: '0'

- name: Block Critical findings on amd64
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
env:
TRIVY_PLATFORM: linux/amd64
with:
version: v0.66.0
scan-type: image
image-ref: ${{ env.IMAGE_REF }}@${{ steps.build.outputs.digest }}
scanners: vuln
severity: CRITICAL
format: table
exit-code: '1'

- name: Scan arm64 image and record High/Critical findings
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
env:
TRIVY_PLATFORM: linux/arm64
with:
version: v0.66.0
scan-type: image
image-ref: ${{ env.IMAGE_REF }}@${{ steps.build.outputs.digest }}
scanners: vuln
severity: HIGH,CRITICAL
format: json
output: trivy-${{ matrix.name }}-arm64.json
exit-code: '0'

- name: Block Critical findings on arm64
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
env:
TRIVY_PLATFORM: linux/arm64
with:
version: v0.66.0
scan-type: image
image-ref: ${{ env.IMAGE_REF }}@${{ steps.build.outputs.digest }}
scanners: vuln
severity: CRITICAL
format: table
exit-code: '1'

- name: Install Cosign
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
with:
cosign-release: v3.0.6

- name: Sign and verify the scanned digest
env:
IMAGE_DIGEST: ${{ steps.build.outputs.digest }}
run: |
set -euo pipefail
digest_ref="${IMAGE_REF}@${IMAGE_DIGEST}"
cosign sign --yes "$digest_ref"
cosign verify \
--certificate-identity "$COSIGN_CERTIFICATE_IDENTITY" \
--certificate-oidc-issuer "$COSIGN_VERIFICATION_OIDC_ISSUER" \
"$digest_ref" > "cosign-${{ matrix.name }}.json"

- name: Upload publication evidence
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: opensandbox-${{ matrix.name }}-${{ github.sha }}
path: |
trivy-${{ matrix.name }}-amd64.json
trivy-${{ matrix.name }}-arm64.json
cosign-${{ matrix.name }}.json
if-no-files-found: error
retention-days: 90

- name: Summarize immutable publication
env:
IMAGE_DIGEST: ${{ steps.build.outputs.digest }}
run: |
{
echo "### ${{ matrix.name }}"
echo
echo "- Source commit: \`${GITHUB_SHA}\`"
echo "- Image: \`${IMAGE_REF}@${IMAGE_DIGEST}\`"
echo "- Platforms: \`linux/amd64\`, \`linux/arm64\`"
echo "- Critical gate: passed on both platforms"
echo "- Signature: verified against the exact main-branch workflow identity"
echo "- Full High/Critical reports: attached as workflow artifacts"
} >> "$GITHUB_STEP_SUMMARY"
41 changes: 15 additions & 26 deletions components/egress/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

FROM golang:1.25.9-bookworm AS builder
FROM golang:1.26.5-alpine3.23 AS builder

WORKDIR /workspace

Expand Down Expand Up @@ -75,41 +75,30 @@ RUN cd /workspace/components/internal && \
-X 'github.com/alibaba/opensandbox/internal/version.GitCommit=${GIT_COMMIT}'" \
-o /out/opensandbox-supervisor ./cmd/supervisor

FROM debian:bookworm-slim
FROM cgr.dev/chainguard/wolfi-base@sha256:07e60ff6586b56f03c625e27b604f9f7d29498fef32f099f6560f0d207b4a056

# iptables is needed for DNS REDIRECT; ca-certificates for TLS to upstream resolvers
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
iptables \
# Keep only the packages required by the egress runtime. The prior diagnostic
# toolbelt materially expanded the image's vulnerability surface and is not
# used by the entrypoint or enforcement code.
RUN apk add --no-cache \
ca-certificates \
iproute2 \
iptables \
nftables \
ca-certificates \
sudo \
curl \
wget \
net-tools \
dnsutils \
netcat-openbsd \
iputils-ping \
traceroute \
telnet \
tcpdump \
nmap \
htop \
procps \
strace \
lsof \
python3 \
python3-pip \
&& rm -rf /var/lib/apt/lists/*
py3.12-pip \
python-3.12 \
sudo

# Python mitmproxy (transparent mode): mitmdump runs as user mitmproxy; iptables skips this uid.
# /var/lib/mitmproxy is mitm's home, used as the confdir (CA + config.yaml live under .mitmproxy/).
RUN useradd -r -u 10042 -d /var/lib/mitmproxy -s /usr/sbin/nologin mitmproxy \
RUN addgroup -S -g 10042 mitmproxy \
&& adduser -S -D -H -u 10042 -G mitmproxy -h /var/lib/mitmproxy -s /sbin/nologin mitmproxy \
&& mkdir -p /var/lib/mitmproxy/.mitmproxy \
&& chown -R mitmproxy:mitmproxy /var/lib/mitmproxy \
&& pip3 install --no-cache-dir --break-system-packages 'mitmproxy==11.0.2' \
&& pip3.12 install --no-cache-dir 'mitmproxy==12.2.3' \
&& (command -v mitmdump && mitmdump --version) \
&& rm -rf /root/.cache \
&& mkdir -p /var/egress/mitmscripts

# Static mitmproxy options (mode, listen_host, connection_strategy, stream_large_bodies,
Expand Down
12 changes: 6 additions & 6 deletions components/egress/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ require (
go.opentelemetry.io/otel/metric v1.43.0
go.opentelemetry.io/otel/sdk/metric v1.43.0
go.uber.org/automaxprocs v1.6.0
golang.org/x/sys v0.45.0
golang.org/x/sys v0.46.0
k8s.io/apimachinery v0.34.2
)

Expand All @@ -35,11 +35,11 @@ require (
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.1 // indirect
golang.org/x/mod v0.35.0 // indirect
golang.org/x/net v0.55.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/text v0.37.0 // indirect
golang.org/x/tools v0.44.0 // indirect
golang.org/x/mod v0.37.0 // indirect
golang.org/x/net v0.56.0 // indirect
golang.org/x/sync v0.21.0 // indirect
golang.org/x/text v0.39.0 // indirect
golang.org/x/tools v0.47.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect
google.golang.org/grpc v1.82.1 // indirect
Expand Down
24 changes: 12 additions & 12 deletions components/egress/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,19 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus=
golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM=
golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec=
Expand Down
28 changes: 19 additions & 9 deletions kubernetes/Dockerfile.image-committer
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@
# limitations under the License.

# Build stage
FROM golang:1.25-alpine AS builder

# Use Aliyun mirror for faster downloads in China
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
FROM golang:1.26.5-alpine3.23@sha256:622e56dbc11a8cfe87cafa2331e9a201877271cbff918af53d3be315f3da88cc AS builder

WORKDIR /workspace

Expand All @@ -31,18 +28,31 @@ COPY cmd/image-committer/ cmd/image-committer/
RUN CGO_ENABLED=0 GOOS=linux go build -o /usr/local/bin/image-committer ./cmd/image-committer/

# Runtime stage
FROM alpine:3.19
FROM alpine:3.23@sha256:fd791d74b68913cbb027c6546007b3f0d3bc45125f797758156952bc2d6daf40

# Use Aliyun mirror for faster downloads in China
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
ARG TARGETARCH

# Install nerdctl for container operations
# nerdctl is used to find containers, commit rootfs, and push images.
# We use nerdctl directly (not crictl or ctr) to avoid CRI API version issues.
RUN apk add --no-cache \
curl \
jq \
nerdctl
jq

# Install the current upstream minimal client, verify the release checksum, and
# preserve multi-platform builds. The distro package lags the nerdctl behavior
# used by the snapshot path.
RUN case "${TARGETARCH}" in \
amd64) NERDCTL_SHA256="de3206aeb7cbd5f20f5fb1f55c1e3bf2db1be567812a8a3f5e65eba2488347ee" ;; \
arm64) NERDCTL_SHA256="76ced9bd0d03f6140f9cf7b927958b654cb8d5ecd3c58af585d096c8bdf9d6c2" ;; \
*) printf 'unsupported TARGETARCH: %s\n' "${TARGETARCH}" >&2; exit 1 ;; \
esac \
&& curl -fsSLo /tmp/nerdctl.tgz \
"https://github.com/containerd/nerdctl/releases/download/v2.3.5/nerdctl-2.3.5-linux-${TARGETARCH}.tar.gz" \
&& printf '%s %s\n' "${NERDCTL_SHA256}" /tmp/nerdctl.tgz | sha256sum -c - \
&& tar -xzf /tmp/nerdctl.tgz -C /usr/local/bin nerdctl \
&& rm /tmp/nerdctl.tgz \
&& nerdctl --version

# Create directory for containerd socket mount
RUN mkdir -p /var/run/containerd
Expand Down
Loading
Loading