Skip to content

Commit 54e4856

Browse files
committed
Modernize PastureStack Compose compatibility
1 parent 5cefb38 commit 54e4856

797 files changed

Lines changed: 196982 additions & 51897 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.drone.yml

Lines changed: 0 additions & 9 deletions
This file was deleted.

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* text=auto eol=lf
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
name: Security release gate
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- 'verification/compose-cli-*'
8+
9+
permissions:
10+
contents: read
11+
12+
concurrency:
13+
group: compose-cli-security-${{ github.ref }}
14+
cancel-in-progress: false
15+
16+
jobs:
17+
build-test-scan:
18+
runs-on: ubuntu-24.04
19+
timeout-minutes: 75
20+
env:
21+
DAPPER_IMAGE: pasturestack/compose-cli-dapper:${{ github.sha }}
22+
TRIVY_IMAGE: aquasec/trivy:0.73.0@sha256:7cced7cae583819fc7806d4cbc0dbbc7cad18b99f7d3e235192e6da8c091045c
23+
VERSION_OVERRIDE: v0.14.32
24+
PLATFORM_COMPAT_JAR_URL: https://github.com/PastureStack/server/releases/download/v1.6.346/orchestration-engine-0.183.276.jar
25+
PLATFORM_COMPAT_JAR_SHA256: 64bc18a1654b73116dce89f29ede7b4c629a8c5af236b482ef95f50a78ed6376
26+
27+
steps:
28+
- name: Check out candidate
29+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
30+
with:
31+
fetch-depth: 0
32+
persist-credentials: false
33+
34+
- name: Record candidate identity
35+
shell: bash
36+
run: |
37+
set -euo pipefail
38+
test -z "$(git status --porcelain)"
39+
mkdir -p evidence
40+
git rev-parse HEAD > evidence/source-revision.txt
41+
sha256sum Dockerfile.dapper ubuntu-apt.lock vendor.conf > evidence/source-locks.sha256
42+
43+
- name: Build, test, validate, and package twice
44+
shell: bash
45+
run: |
46+
set -euo pipefail
47+
docker build \
48+
--build-arg DAPPER_HOST_ARCH=amd64 \
49+
--tag "$DAPPER_IMAGE" \
50+
--file Dockerfile.dapper \
51+
.
52+
53+
source_path="$GITHUB_WORKSPACE"
54+
run_ci() {
55+
docker run --rm \
56+
--volume "${source_path}:/go/src/github.com/PastureStack/compose-cli" \
57+
--volume /var/run/docker.sock:/var/run/docker.sock \
58+
--env "DAPPER_UID=$(id -u)" \
59+
--env "DAPPER_GID=$(id -g)" \
60+
--env "GOCACHE=/tmp/go-build-cache-${GITHUB_RUN_ID}" \
61+
--env "XDG_CONFIG_HOME=/tmp/go-config-${GITHUB_RUN_ID}" \
62+
--env "GIT_CONFIG_GLOBAL=/tmp/gitconfig-${GITHUB_RUN_ID}" \
63+
--env "VERSION_OVERRIDE=$VERSION_OVERRIDE" \
64+
--env "SKIP_INTEGRATION=true" \
65+
--env "PLATFORM_COMPAT_JAR_URL=$PLATFORM_COMPAT_JAR_URL" \
66+
--env "PLATFORM_COMPAT_JAR_SHA256=$PLATFORM_COMPAT_JAR_SHA256" \
67+
"$DAPPER_IMAGE" ci
68+
}
69+
70+
run_ci
71+
artifact="dist/artifacts/compose-executor-0.14.32-linux-amd64.gz"
72+
test -s "$artifact"
73+
cp "$artifact" /tmp/compose-executor-first.gz
74+
75+
rm -rf bin build dist
76+
run_ci
77+
cmp /tmp/compose-executor-first.gz "$artifact"
78+
./scripts/check-pasturestack-source
79+
80+
mkdir -p evidence/product
81+
gzip -cd "$artifact" > evidence/product/compose-executor
82+
chmod +x evidence/product/compose-executor
83+
evidence/product/compose-executor --version | grep -F '0.14.32' >/dev/null
84+
sha256sum "$artifact" > evidence/compose-executor.gz.sha256
85+
docker run --rm --entrypoint go \
86+
--volume "$PWD:/work:ro" \
87+
"$DAPPER_IMAGE" \
88+
version -m /work/evidence/product/compose-executor \
89+
> evidence/product-go-version.txt
90+
91+
- name: Record resolved build inputs
92+
shell: bash
93+
run: |
94+
set -euo pipefail
95+
docker inspect "$DAPPER_IMAGE" > evidence/dapper-image-inspect.json
96+
docker run --rm --entrypoint sh "$DAPPER_IMAGE" -lc \
97+
"printf 'package\\tversion\\n'; dpkg-query -W -f='\${binary:Package}\\t\${Version}\\n' | LC_ALL=C sort" \
98+
> evidence/dapper-dpkg.tsv
99+
cp vendor.conf evidence/vendor.conf
100+
101+
- name: Scan source, product, and build image
102+
shell: bash
103+
run: |
104+
set -euo pipefail
105+
docker pull "$TRIVY_IMAGE"
106+
trivy_cache="$RUNNER_TEMP/trivy-cache"
107+
source_tree="$(mktemp -d)"
108+
trap 'rm -rf "$source_tree"' EXIT
109+
mkdir -p "$trivy_cache"
110+
git archive --format=tar HEAD | tar -xf - -C "$source_tree"
111+
112+
docker run --rm \
113+
-v "$source_tree:/scan:ro" \
114+
-v "$PWD/evidence:/evidence" \
115+
-v "$trivy_cache:/root/.cache/trivy" \
116+
"$TRIVY_IMAGE" fs \
117+
--scanners vuln,secret --format json \
118+
--output /evidence/source-security.json /scan
119+
docker run --rm \
120+
-v "$PWD/evidence:/evidence" \
121+
-v "$trivy_cache:/root/.cache/trivy" \
122+
"$TRIVY_IMAGE" rootfs \
123+
--scanners vuln,secret --format json \
124+
--output /evidence/product-security.json /evidence/product
125+
docker run --rm \
126+
-v /var/run/docker.sock:/var/run/docker.sock \
127+
-v "$PWD:/work" -w /work \
128+
-v "$trivy_cache:/root/.cache/trivy" \
129+
"$TRIVY_IMAGE" image \
130+
--scanners vuln,secret --format json \
131+
--output /work/evidence/dapper-image-raw.json "$DAPPER_IMAGE"
132+
133+
jq -r '
134+
.Results[]?.Vulnerabilities[]?
135+
| select(.Severity == "CRITICAL" or .Severity == "HIGH")
136+
| [.VulnerabilityID, .PkgIdentifier.PURL]
137+
| @tsv
138+
' evidence/dapper-image-raw.json | LC_ALL=C sort -u \
139+
> /tmp/dapper-critical-high.tsv
140+
jq -r '
141+
.statements[]?
142+
| select(
143+
.status == "not_affected"
144+
and .justification == "vulnerable_code_not_present"
145+
)
146+
| .vulnerability.name as $id
147+
| .products[]?
148+
| [$id, .["@id"]]
149+
| @tsv
150+
' security/dapper.openvex.json | LC_ALL=C sort -u \
151+
> /tmp/dapper-vex.tsv
152+
test -s /tmp/dapper-critical-high.tsv
153+
diff -u /tmp/dapper-critical-high.tsv /tmp/dapper-vex.tsv
154+
test "$(jq '.statements | length' security/dapper.openvex.json)" \
155+
-eq "$(wc -l < /tmp/dapper-vex.tsv)"
156+
jq -e '
157+
all(
158+
.statements[];
159+
.status == "not_affected"
160+
and .justification == "vulnerable_code_not_present"
161+
and ((.impact_statement | type) == "string")
162+
and ((.impact_statement | length) > 0)
163+
)
164+
' security/dapper.openvex.json >/dev/null
165+
grep -F $'build\tCGO_ENABLED=0' evidence/product-go-version.txt >/dev/null
166+
if grep -Eq '^linux-(image|modules)([-:]|[[:space:]])' evidence/dapper-dpkg.tsv; then
167+
printf 'Unexpected Linux kernel runtime package in Dapper image\n' >&2
168+
exit 1
169+
fi
170+
docker run --rm \
171+
-v /var/run/docker.sock:/var/run/docker.sock \
172+
-v "$PWD:/work" -w /work \
173+
-v "$trivy_cache:/root/.cache/trivy" \
174+
"$TRIVY_IMAGE" image \
175+
--scanners vuln,secret \
176+
--vex /work/security/dapper.openvex.json --show-suppressed \
177+
--format json \
178+
--output /work/evidence/dapper-image-applicable.json "$DAPPER_IMAGE"
179+
docker run --rm \
180+
-v "$source_tree:/scan:ro" \
181+
-v "$PWD/evidence:/evidence" \
182+
-v "$trivy_cache:/root/.cache/trivy" \
183+
"$TRIVY_IMAGE" fs \
184+
--format cyclonedx --output /evidence/source.cdx.json /scan
185+
docker run --rm \
186+
-v "$PWD/evidence:/evidence" \
187+
-v "$trivy_cache:/root/.cache/trivy" \
188+
"$TRIVY_IMAGE" rootfs \
189+
--format cyclonedx --output /evidence/product.cdx.json /evidence/product
190+
191+
- name: Summarize and enforce candidate security
192+
shell: bash
193+
run: |
194+
set -euo pipefail
195+
count() {
196+
jq "$2" "$1"
197+
}
198+
source_secrets=$(count evidence/source-security.json '[.Results[]?.Secrets[]?] | length')
199+
source_critical=$(count evidence/source-security.json '[.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL")] | length')
200+
source_high=$(count evidence/source-security.json '[.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH")] | length')
201+
product_secrets=$(count evidence/product-security.json '[.Results[]?.Secrets[]?] | length')
202+
product_critical=$(count evidence/product-security.json '[.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL")] | length')
203+
product_high=$(count evidence/product-security.json '[.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH")] | length')
204+
dapper_raw_critical=$(count evidence/dapper-image-raw.json '[.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL")] | length')
205+
dapper_raw_high=$(count evidence/dapper-image-raw.json '[.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH")] | length')
206+
dapper_applicable_critical=$(count evidence/dapper-image-applicable.json '[.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL" and .Status != "not_affected")] | length')
207+
dapper_applicable_high=$(count evidence/dapper-image-applicable.json '[.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH" and .Status != "not_affected")] | length')
208+
dapper_secrets=$(count evidence/dapper-image-applicable.json '[.Results[]?.Secrets[]?] | length')
209+
{
210+
printf 'source_secrets=%s\n' "$source_secrets"
211+
printf 'source_critical=%s\n' "$source_critical"
212+
printf 'source_high=%s\n' "$source_high"
213+
printf 'product_secrets=%s\n' "$product_secrets"
214+
printf 'product_critical=%s\n' "$product_critical"
215+
printf 'product_high=%s\n' "$product_high"
216+
printf 'dapper_raw_critical=%s\n' "$dapper_raw_critical"
217+
printf 'dapper_raw_high=%s\n' "$dapper_raw_high"
218+
printf 'dapper_applicable_critical=%s\n' "$dapper_applicable_critical"
219+
printf 'dapper_applicable_high=%s\n' "$dapper_applicable_high"
220+
printf 'dapper_secrets=%s\n' "$dapper_secrets"
221+
} | tee evidence/security-summary.txt
222+
223+
test "$source_secrets" -eq 0
224+
test "$product_secrets" -eq 0
225+
test "$product_critical" -eq 0
226+
test "$product_high" -eq 0
227+
test "$dapper_applicable_critical" -eq 0
228+
test "$dapper_applicable_high" -eq 0
229+
test "$dapper_secrets" -eq 0
230+
231+
- name: Upload review evidence
232+
if: always()
233+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
234+
with:
235+
name: compose-cli-security-${{ github.sha }}
236+
path: evidence/
237+
if-no-files-found: error
238+
retention-days: 30
239+
240+
- name: Clean runner resources
241+
if: always()
242+
shell: bash
243+
run: |
244+
set +e
245+
docker rm -f $(docker ps -aq --filter ancestor="$DAPPER_IMAGE") 2>/dev/null
246+
docker image rm -f "$DAPPER_IMAGE" "$TRIVY_IMAGE" 2>/dev/null
247+
docker builder prune --all --force >/dev/null 2>&1
248+
rm -rf bin build dist evidence/product

COMPATIBILITY.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Compatibility Contract
2+
3+
The migration preserves Docker Compose parsing, stack event names, generated stack fields, service labels, catalog question data, upgrade and rollback payloads, and compatibility API behavior.
4+
5+
The archived external Compose implementation is no longer a build dependency. The conversion, collection-copy, logger, and Compose configuration-hash contracts still required by this compatibility client are implemented locally and covered by behavior tests. This does not expand or silently reinterpret the accepted Compose document format.
6+
7+
`platform-compose.yml`, `PLATFORM_URL`, `PLATFORM_ACCESS_KEY`, and `PLATFORM_SECRET_KEY` are preferred. The historical companion filename, legacy CLI alias, `RANCHER_*` and `CATTLE_*` variables, `io.rancher.*` labels, generated `RancherCompose` and `RancherClient` types, legacy service-image identifiers, and vendored `github.com/rancher/*` paths remain only as wire, data, or dependency contracts.
8+
9+
At startup the maintained executor also removes an active `rancher-compose-executor` event-handler registration before installing the canonical `compose-executor` handler. This exact historical identifier is retained only as a one-way upgrade cleanup key. Without that cleanup, an upgraded database can dispatch each stack action to both executors and leave upgrades waiting on the stale handler.
10+
11+
Operator messages support `en-US` and `zh-TW`. Compose documents, catalog questions, API payloads, stack names, labels, and remote errors are not translated.
12+
13+
Before release, validate create, up, render, upgrade, finish-upgrade, rollback, service hashing, sidekicks, load balancers, secrets, volumes, legacy companion-file fallback, and real event execution.

Dockerfile.dapper

Lines changed: 91 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,94 @@
1-
FROM golang:1.9.7-stretch
2-
RUN go get github.com/rancher/trash
3-
RUN go get golang.org/x/lint/golint
4-
RUN curl -sL https://get.docker.com/builds/Linux/x86_64/docker-17.03.1-ce.tgz | tar xzf - -C /usr/bin --strip-components=1 && \
5-
chmod +x /usr/bin/docker
6-
RUN apt-get update && \
7-
apt-get install -y --no-install-recommends default-jre python-pip python-setuptools zip xz-utils rsync
8-
RUN pip install --upgrade pip==6.0.3 tox==1.8.1 virtualenv==12.0.4
9-
ENV PATH /go/bin:$PATH
10-
ENV DAPPER_SOURCE /go/src/github.com/rancher/rancher-compose-executor
11-
ENV DAPPER_OUTPUT bin dist
12-
ENV DAPPER_DOCKER_SOCKET true
13-
ENV DAPPER_ENV TAG REPO CROSS
14-
ENV TRASH_CACHE ${DAPPER_SOURCE}/.trash-cache
1+
FROM ubuntu:26.04@sha256:678c6550cc43645e08669028bc177f50be4e7c5b8cca677067b1914d4afc7a03
2+
3+
COPY ubuntu-apt.lock /licenses/ubuntu-apt.lock
4+
ADD --checksum=sha256:6077d27c6b6f8b23590cb01ff877ed8c804a67a5442cc32b5a33da10d2bd0e90 https://archive.ubuntu.com/ubuntu/pool/main/c/ca-certificates/ca-certificates_20260601~26.04.1_all.deb /tmp/ca-certificates.deb
5+
6+
ARG GO_VERSION=1.26.5
7+
ARG GO_SHA256_amd64=5c2c3b16caefa1d968a94c1daca04a7ca301a496d9b086e17ad77bb81393f053
8+
ARG GO_SHA256_arm64=fe4789e92b1f33358680864bbe8704289e7bb5fc207d80623c308935bd696d49
9+
ARG DAPPER_HOST_ARCH=amd64
10+
ARG DOCKER_VERSION=29.6.2
11+
ARG DOCKER_SHA256_amd64=d6204aea92238e2453d5445c885b9d2e5eb8f82915568ec50edf9dbe12a3ac74
12+
ARG DOCKER_SHA256_arm64=8d16d8b3b158c132a9fb9963d4b4345746f925e287e154c9ed880ac257baf292
13+
14+
ENV DEBIAN_FRONTEND=noninteractive \
15+
GOPATH=/go \
16+
GO111MODULE=off \
17+
GOCACHE=/tmp/go-build-cache \
18+
GOTELEMETRY=off \
19+
XDG_CONFIG_HOME=/tmp/go-config \
20+
PATH=/go/bin:/usr/local/go/bin:${PATH}
21+
22+
RUN set -eux; \
23+
. /licenses/ubuntu-apt.lock; \
24+
rm -f /etc/apt/sources.list /etc/apt/sources.list.d/*.list /etc/apt/sources.list.d/*.sources; \
25+
rm -rf /tmp/ca-bootstrap; \
26+
mkdir -p /tmp/ca-bootstrap /etc/ssl/certs; \
27+
dpkg-deb --extract /tmp/ca-certificates.deb /tmp/ca-bootstrap; \
28+
find /tmp/ca-bootstrap/usr/share/ca-certificates -type f -name '*.crt' \
29+
| LC_ALL=C sort \
30+
| while IFS= read -r certificate; do sed -e '$a\' "${certificate}"; done \
31+
> /etc/ssl/certs/ca-certificates.crt; \
32+
test -s /etc/ssl/certs/ca-certificates.crt; \
33+
printf 'Types: deb\nURIs: https://snapshot.ubuntu.com/ubuntu/%s\nSuites: resolute resolute-updates resolute-backports resolute-security\nComponents: main universe restricted multiverse\nSigned-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg\nSnapshot: no\n' \
34+
"${UBUNTU_APT_SNAPSHOT}" > /etc/apt/sources.list.d/pasturestack-snapshot.sources; \
35+
printf 'Acquire::Retries "5";\nAcquire::http::Timeout "30";\nAcquire::https::Timeout "30";\nAcquire::http::Pipeline-Depth "0";\nAcquire::https::CaInfo "/etc/ssl/certs/ca-certificates.crt";\nAcquire::https::Verify-Peer "true";\nAcquire::https::Verify-Host "true";\nAcquire::AllowInsecureRepositories "false";\nAPT::Get::AllowUnauthenticated "false";\n' > /etc/apt/apt.conf.d/80pasturestack-retries; \
36+
apt-get update; \
37+
apt-get install -y --no-install-recommends \
38+
bash="${UBUNTU_APT_BASH_VERSION}" \
39+
ca-certificates="${UBUNTU_APT_CA_CERTIFICATES_VERSION}" \
40+
curl="${UBUNTU_APT_CURL_VERSION}" \
41+
default-jre="${UBUNTU_APT_DEFAULT_JRE_VERSION}" \
42+
gcc="${UBUNTU_APT_GCC_VERSION}" \
43+
git="${UBUNTU_APT_GIT_VERSION}" \
44+
gzip="${UBUNTU_APT_GZIP_VERSION}" \
45+
libc6-dev="${UBUNTU_APT_LIBC6_DEV_VERSION}" \
46+
make="${UBUNTU_APT_MAKE_VERSION}" \
47+
python3="${UBUNTU_APT_PYTHON3_VERSION}" \
48+
python3-virtualenv="${UBUNTU_APT_PYTHON3_VIRTUALENV_VERSION}" \
49+
rsync="${UBUNTU_APT_RSYNC_VERSION}" \
50+
tar="${UBUNTU_APT_TAR_VERSION}" \
51+
tox="${UBUNTU_APT_TOX_VERSION}" \
52+
xz-utils="${UBUNTU_APT_XZ_UTILS_VERSION}" \
53+
zip="${UBUNTU_APT_ZIP_VERSION}"; \
54+
{ \
55+
printf 'snapshot\t%s\n' "${UBUNTU_APT_SNAPSHOT}"; \
56+
dpkg-query -W -f='${binary:Package}\t${Version}\n' | LC_ALL=C sort; \
57+
} > /licenses/COMPOSE-CLI-UBUNTU-APT-PACKAGES.tsv; \
58+
apt-get clean; \
59+
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/bin/pebble
60+
61+
RUN git config --system --add safe.directory '*'
62+
63+
RUN case "${DAPPER_HOST_ARCH}" in \
64+
amd64) go_arch=amd64; go_sha="${GO_SHA256_amd64}" ;; \
65+
arm64) go_arch=arm64; go_sha="${GO_SHA256_arm64}" ;; \
66+
*) echo "unsupported DAPPER_HOST_ARCH=${DAPPER_HOST_ARCH}" >&2; exit 1 ;; \
67+
esac && \
68+
curl -fsSL -o /tmp/go.tgz "https://go.dev/dl/go${GO_VERSION}.linux-${go_arch}.tar.gz" && \
69+
echo "${go_sha} /tmp/go.tgz" | sha256sum -c - && \
70+
tar -C /usr/local -xzf /tmp/go.tgz && \
71+
rm -f /tmp/go.tgz && \
72+
go version
73+
74+
RUN case "${DAPPER_HOST_ARCH}" in \
75+
amd64) docker_arch=x86_64; docker_sha="${DOCKER_SHA256_amd64}" ;; \
76+
arm64) docker_arch=aarch64; docker_sha="${DOCKER_SHA256_arm64}" ;; \
77+
*) echo "unsupported DAPPER_HOST_ARCH=${DAPPER_HOST_ARCH}" >&2; exit 1 ;; \
78+
esac && \
79+
curl -fsSL -o /tmp/docker.tgz \
80+
"https://download.docker.com/linux/static/stable/${docker_arch}/docker-${DOCKER_VERSION}.tgz" && \
81+
echo "${docker_sha} /tmp/docker.tgz" | sha256sum -c - && \
82+
tar xzf /tmp/docker.tgz -C /usr/bin --strip-components=1 docker/docker && \
83+
rm -f /tmp/docker.tgz && \
84+
chmod +x /usr/bin/docker && \
85+
docker --version
86+
87+
ENV DAPPER_SOURCE=/go/src/github.com/PastureStack/compose-cli
88+
ENV DAPPER_OUTPUT="bin dist"
89+
ENV DAPPER_DOCKER_SOCKET=true
90+
ENV DAPPER_ENV="TAG REPO CROSS VERSION_OVERRIDE SKIP_INTEGRATION PLATFORM_COMPAT_JAR_URL PLATFORM_COMPAT_JAR_SHA256 CATTLE_JAR_URL"
91+
1592
WORKDIR ${DAPPER_SOURCE}
1693
ENTRYPOINT ["./scripts/entry"]
1794
CMD ["ci"]

0 commit comments

Comments
 (0)