forked from rancher/host-api
-
Notifications
You must be signed in to change notification settings - Fork 0
230 lines (214 loc) · 11.7 KB
/
Copy pathsecurity-release-gate.yml
File metadata and controls
230 lines (214 loc) · 11.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
name: Manual security release gate
on:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: host-api-security-${{ github.ref }}-${{ github.sha }}
cancel-in-progress: false
jobs:
verify:
name: Build, test, reproduce, scan, and inventory
runs-on: ubuntu-24.04
timeout-minutes: 120
env:
CANDIDATE_VERSION: 0.38.4
DAPPER_IMAGE: pasturestack-host-api-dapper:security-gate
TRIVY_IMAGE: ghcr.io/aquasecurity/trivy:0.73.0@sha256:7cced7cae583819fc7806d4cbc0dbbc7cad18b99f7d3e235192e6da8c091045c
COSIGN_IMAGE: ghcr.io/sigstore/cosign/cosign:v3.1.3@sha256:9e5c2f2edc34351160407ca3416c61855bdf9403c3c5936e0f0be7fc261611b8
TRIVY_CACHE: /tmp/pasturestack-host-api-trivy-cache
steps:
- name: Check out the complete reviewed history
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- name: Verify source, history, and privacy boundaries
shell: bash
run: |
set -euo pipefail
test -z "$(git status --porcelain)"
test "$(git rev-parse HEAD)" = "$GITHUB_SHA"
test "$(git rev-list --count d56f3d0..HEAD)" = 1
test "$(git rev-list --count --merges d56f3d0..HEAD)" = 0
private_pattern=$(printf '%s|%s|%s' 'chen''21019' '10\.0\.0\.''125' 'C:\\''Users\\')
if git grep -n -E "$private_pattern" -- .; then
echo 'Private operator data is present in the public source tree.' >&2
exit 1
fi
mkdir -p evidence
printf '%s\n' "$GITHUB_SHA" > evidence/source-revision.txt
docker version
- name: Build, race-test, validate, and package
shell: bash
run: |
set -euo pipefail
make ci DAPPER_IMAGE="$DAPPER_IMAGE" VERSION_OVERRIDE="$CANDIDATE_VERSION"
test "$(bin/host-api --version)" = "$CANDIDATE_VERSION"
cp bin/host-api evidence/host-api-linux-amd64
cp dist/coverage.out evidence/coverage.out
cp dist/coverage-summary.txt evidence/coverage-summary.txt
grep -F 'total:' evidence/coverage-summary.txt
cp "dist/artifacts/host-api-${CANDIDATE_VERSION}.tar.gz" evidence/first-package.tar.gz
sha256sum evidence/host-api-linux-amd64 > evidence/host-api.sha256
docker run --rm \
-v "$GITHUB_WORKSPACE:/go/src/github.com/PastureStack/host-api" \
-e DAPPER_UID="$(id -u)" \
-e DAPPER_GID="$(id -g)" \
-e ARCH=amd64 \
-e VERSION_OVERRIDE="$CANDIDATE_VERSION" \
"$DAPPER_IMAGE" bash -lc \
'go version -m /go/src/github.com/PastureStack/host-api/bin/host-api' \
> evidence/host-api-go-version.txt
awk '$1 == "#" && ($3 ~ /^v/ || $3 ~ /^[0-9]/) { print $2 "\t" $3 }' \
vendor/modules.txt > evidence/go-modules.tsv
test -s evidence/go-modules.tsv
if ! git diff --quiet || ! git diff --cached --quiet; then
echo 'A verification command modified tracked source before the reproducibility check.' >&2
git status --short >&2
exit 1
fi
- name: Prove byte-identical packaging
shell: bash
run: |
set -euo pipefail
docker run --rm \
-v "$GITHUB_WORKSPACE:/go/src/github.com/PastureStack/host-api" \
-e DAPPER_UID="$(id -u)" \
-e DAPPER_GID="$(id -g)" \
-e ARCH=amd64 \
-e VERSION_OVERRIDE="$CANDIDATE_VERSION" \
"$DAPPER_IMAGE" package
cp "dist/artifacts/host-api-${CANDIDATE_VERSION}.tar.gz" evidence/second-package.tar.gz
cmp evidence/first-package.tar.gz evidence/second-package.tar.gz
sha256sum evidence/first-package.tar.gz evidence/second-package.tar.gz \
> evidence/package-sha256.txt
- name: Run reachable Go vulnerability analysis
shell: bash
run: |
set -euo pipefail
docker run --rm \
-v "$GITHUB_WORKSPACE:/go/src/github.com/PastureStack/host-api" \
-e DAPPER_UID="$(id -u)" \
-e DAPPER_GID="$(id -g)" \
-e GOTOOLCHAIN=local \
-e GOFLAGS=-mod=mod \
"$DAPPER_IMAGE" bash -lc \
'cd /go/src/github.com/PastureStack/host-api && go run golang.org/x/vuln/cmd/govulncheck@v1.6.0 -json ./...' \
> evidence/govulncheck-source.json
if jq -e 'select(.finding != null)' evidence/govulncheck-source.json >/dev/null; then
echo 'A reachable Go vulnerability remains.' >&2
exit 1
fi
if ! git diff --quiet || ! git diff --cached --quiet; then
echo 'Vulnerability analysis modified tracked source.' >&2
git status --short >&2
exit 1
fi
- name: Scan source, artifact, and build environment
shell: bash
run: |
set -euo pipefail
mkdir -p "$TRIVY_CACHE"
docker pull "$COSIGN_IMAGE"
docker pull "$TRIVY_IMAGE"
docker image inspect "$TRIVY_IMAGE" --format '{{range .RepoDigests}}{{println .}}{{end}}' |
grep -Fq 'ghcr.io/aquasecurity/trivy@sha256:7cced7cae583819fc7806d4cbc0dbbc7cad18b99f7d3e235192e6da8c091045c'
docker run --rm "$COSIGN_IMAGE" verify \
--certificate-identity-regexp 'https://github\.com/aquasecurity/trivy/\.github/workflows/.+' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
--output json "$TRIVY_IMAGE" > evidence/trivy-cosign-verification.json
jq -e 'length > 0' evidence/trivy-cosign-verification.json >/dev/null
test "$(docker run --rm "$TRIVY_IMAGE" --version)" = 'Version: 0.73.0'
source_snapshot=$(mktemp -d "${RUNNER_TEMP}/host-api-source.XXXXXX")
git archive HEAD | tar -x -C "$source_snapshot"
docker run --rm --network none \
-v "$source_snapshot:/workspace:ro" \
-v "$GITHUB_WORKSPACE/evidence:/evidence" \
"$TRIVY_IMAGE" fs --scanners secret --format json \
--output /evidence/source-secret-scan.json /workspace
rm -rf -- "$source_snapshot"
docker run --rm \
-v "$TRIVY_CACHE:/root/.cache/trivy" \
"$TRIVY_IMAGE" image --cache-dir /root/.cache/trivy --download-db-only
docker run --rm --network none \
-v "$TRIVY_CACHE:/root/.cache/trivy" \
-v "$GITHUB_WORKSPACE:/workspace:ro" \
-v "$GITHUB_WORKSPACE/evidence:/evidence" \
"$TRIVY_IMAGE" fs --cache-dir /root/.cache/trivy --skip-db-update --offline-scan \
--scanners vuln --severity CRITICAL,HIGH --format json \
--output /evidence/source-vulnerability-scan.json /workspace
docker run --rm --network none \
-v "$TRIVY_CACHE:/root/.cache/trivy" \
-v "$GITHUB_WORKSPACE/dist/artifacts:/artifacts:ro" \
-v "$GITHUB_WORKSPACE/evidence:/evidence" \
"$TRIVY_IMAGE" fs --cache-dir /root/.cache/trivy --skip-db-update --offline-scan \
--scanners vuln,secret --severity CRITICAL,HIGH --format json \
--output /evidence/artifact-scan.json /artifacts
docker run --rm --network none \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "$TRIVY_CACHE:/root/.cache/trivy" \
-v "$GITHUB_WORKSPACE/evidence:/evidence" \
"$TRIVY_IMAGE" image --cache-dir /root/.cache/trivy --skip-db-update --offline-scan \
--scanners vuln,secret --severity CRITICAL,HIGH --format json \
--output /evidence/builder-scan.json "$DAPPER_IMAGE"
docker run --rm --network none \
-v "$TRIVY_CACHE:/root/.cache/trivy" \
-v "$GITHUB_WORKSPACE:/workspace:ro" \
-v "$GITHUB_WORKSPACE/evidence:/evidence" \
"$TRIVY_IMAGE" fs --cache-dir /root/.cache/trivy --skip-db-update --offline-scan \
--format cyclonedx --output /evidence/source-sbom.cdx.json /workspace
source_secrets=$(jq '[.Results[]?.Secrets[]?] | length' evidence/source-secret-scan.json)
source_findings=$(jq '[.Results[]?.Vulnerabilities[]?] | length' evidence/source-vulnerability-scan.json)
artifact_findings=$(jq '[.Results[]?.Vulnerabilities[]?] | length' evidence/artifact-scan.json)
artifact_secrets=$(jq '[.Results[]?.Secrets[]?] | length' evidence/artifact-scan.json)
builder_findings=$(jq '[.Results[]?.Vulnerabilities[]?] | length' evidence/builder-scan.json)
builder_secrets=$(jq '[.Results[]?.Secrets[]?] | length' evidence/builder-scan.json)
builder_fixable=$(jq '[.Results[]?.Vulnerabilities[]? | select((.FixedVersion // "") != "")] | length' evidence/builder-scan.json)
builder_unfixed_kernel_headers=$(jq '[.Results[]?.Vulnerabilities[]? | select((.FixedVersion // "") == "" and .PkgName == "linux-libc-dev" and .Status == "affected")] | length' evidence/builder-scan.json)
builder_unfixed_other=$(jq '[.Results[]?.Vulnerabilities[]? | select((.FixedVersion // "") == "" and (.PkgName != "linux-libc-dev" or .Status != "affected"))] | length' evidence/builder-scan.json)
jq -n \
--argjson source_secrets "$source_secrets" \
--argjson source_findings "$source_findings" \
--argjson artifact_findings "$artifact_findings" \
--argjson artifact_secrets "$artifact_secrets" \
--argjson builder_findings "$builder_findings" \
--argjson builder_secrets "$builder_secrets" \
--argjson builder_fixable "$builder_fixable" \
--argjson builder_unfixed_kernel_headers "$builder_unfixed_kernel_headers" \
--argjson builder_unfixed_other "$builder_unfixed_other" \
'{source:{secrets:$source_secrets,high_or_critical:$source_findings},artifact:{secrets:$artifact_secrets,high_or_critical:$artifact_findings},builder:{secrets:$builder_secrets,high_or_critical:$builder_findings,fixable_high_or_critical:$builder_fixable,accepted_unfixed_kernel_headers:$builder_unfixed_kernel_headers,unaccepted_unfixed_high_or_critical:$builder_unfixed_other}}' \
> evidence/security-counts.json
jq -e '
.source.secrets == 0 and .source.high_or_critical == 0 and
.artifact.secrets == 0 and .artifact.high_or_critical == 0 and
.builder.secrets == 0 and .builder.fixable_high_or_critical == 0 and
.builder.unaccepted_unfixed_high_or_critical == 0 and
.builder.high_or_critical == (.builder.accepted_unfixed_kernel_headers + .builder.fixable_high_or_critical + .builder.unaccepted_unfixed_high_or_critical)
' evidence/security-counts.json >/dev/null
- name: Record evidence checksums
if: always()
shell: bash
run: |
set -euo pipefail
find evidence -type f ! -name SHA256SUMS -print0 | LC_ALL=C sort -z |
xargs -0 sha256sum > evidence/SHA256SUMS
- name: Upload short-lived verification evidence
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: host-api-security-evidence-${{ github.run_id }}
path: evidence/
if-no-files-found: error
retention-days: 7
compression-level: 9
include-hidden-files: false
- name: Remove verification-only images and cache
if: always()
shell: bash
run: |
set -euo pipefail
sudo rm -rf -- "$TRIVY_CACHE"
docker image rm -f "$DAPPER_IMAGE" "$TRIVY_IMAGE" "$COSIGN_IMAGE" >/dev/null || true
docker builder prune --all --force >/dev/null || true
test ! -e "$TRIVY_CACHE"