-
Notifications
You must be signed in to change notification settings - Fork 6
188 lines (166 loc) · 7.86 KB
/
Copy pathsecurity-rebuild.yml
File metadata and controls
188 lines (166 loc) · 7.86 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
name: Weekly Security Rebuild
# Rebuild weekly to pick up Go toolchain and OS patches, republishing only when it clears a CVE (COSY-926).
on:
schedule:
# ~22h ahead of the secops Wednesday 06:00 UTC scan, so anything cleared here is never filed.
- cron: "0 8 * * TUE"
workflow_dispatch:
inputs:
dry_run:
description: "Scan and rebuild, but never publish"
type: boolean
default: false
image:
description: "Image to scan and republish. Override to test in a fork."
type: string
default: localstack/localstack-docker-desktop
concurrency:
group: security-rebuild
cancel-in-progress: false
jobs:
security-rebuild:
name: Rebuild and republish if CVEs clear
runs-on: ubuntu-latest
permissions:
contents: write
env:
PUBLISHED_IMAGE: ${{ inputs.image || 'localstack/localstack-docker-desktop' }}
PLATFORMS: linux/amd64,linux/arm64
GIT_AUTHOR_NAME: localstack[bot]
GIT_AUTHOR_EMAIL: 88328844+localstack-bot@users.noreply.github.com
GIT_COMMITTER_NAME: localstack[bot]
GIT_COMMITTER_EMAIL: 88328844+localstack-bot@users.noreply.github.com
# Mirror the Trivy DBs to dodge GHCR rate limits (trivy-action#389), as aws_flink.yml does.
TRIVY_DB_REPOSITORY: "ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db"
TRIVY_JAVA_DB_REPOSITORY: "ghcr.io/aquasecurity/trivy-java-db,public.ecr.aws/aquasecurity/trivy-java-db"
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Read published version
id: version
run: |
TAG=$(sed -n 's/^TAG?=\(.*\)$/\1/p' Makefile)
echo "Currently published: ${PUBLISHED_IMAGE}:${TAG}"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
- name: Scan published image
# --ignore-unfixed matches what the secops pipeline files, so an unfixable advisory can't loop here forever.
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
image-ref: ${{ env.PUBLISHED_IMAGE }}:${{ steps.version.outputs.tag }}
scanners: vuln
severity: HIGH,CRITICAL
ignore-unfixed: true
format: json
output: published.json
- name: Determine fixable CVEs on the published image
id: before
run: |
jq -r '[.Results[]?.Vulnerabilities[]?.VulnerabilityID] | unique | .[]' published.json | sort -u > published_cves.txt
echo "Fixable HIGH/CRITICAL CVEs on the published image:"; cat published_cves.txt || true
if [ -s published_cves.txt ]; then
echo "proceed=true" >> "$GITHUB_OUTPUT"
else
echo "Published image is clean — nothing to rebuild."
echo "proceed=false" >> "$GITHUB_OUTPUT"
fi
- name: Set up QEMU
if: steps.before.outputs.proceed == 'true'
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
- name: Set up Buildx
if: steps.before.outputs.proceed == 'true'
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
with:
platforms: linux/amd64,linux/arm64
- name: Build and smoke-test candidate (amd64, for scanning)
if: steps.before.outputs.proceed == 'true'
run: make smoke-test IMAGE=localstack-docker-desktop TAG=candidate
- name: Scan candidate
if: steps.before.outputs.proceed == 'true'
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
image-ref: localstack-docker-desktop:candidate
scanners: vuln
severity: HIGH,CRITICAL
ignore-unfixed: true
format: json
output: candidate.json
- name: Compute cleared CVEs (published - candidate)
id: delta
if: steps.before.outputs.proceed == 'true'
run: |
jq -r '[.Results[]?.Vulnerabilities[]?.VulnerabilityID] | unique | .[]' candidate.json | sort -u > candidate_cves.txt
comm -23 published_cves.txt candidate_cves.txt > cleared.txt
comm -13 published_cves.txt candidate_cves.txt > introduced.txt
echo "Cleared by the rebuild:"; cat cleared.txt || true
# Reported, not gated: we'd still rather ship a net improvement.
if [ -s introduced.txt ]; then
echo "::warning::Rebuild introduced new CVEs: $(paste -sd', ' introduced.txt)"
fi
if [ -s cleared.txt ]; then
echo "cleared=true" >> "$GITHUB_OUTPUT"
else
echo "cleared=false" >> "$GITHUB_OUTPUT"
fi
- name: Report CVEs a rebuild cannot fix
# Surface these instead of no-op'ing weekly until the SLA on the secops ticket burns.
if: steps.before.outputs.proceed == 'true' && steps.delta.outputs.cleared == 'false'
run: |
echo "::warning::Rebuild cleared nothing; these need a dependency bump: $(paste -sd', ' published_cves.txt)"
{
echo "### Rebuild cleared no CVEs"
echo
echo "Still present after a clean rebuild — these need a \`vm/go.mod\` bump, not a rebuild:"
echo
sed 's/^/- /' published_cves.txt
} >> "$GITHUB_STEP_SUMMARY"
- name: Bump version
id: bump
if: steps.delta.outputs.cleared == 'true' && !inputs.dry_run
run: |
NEW=$(./scripts/bump-version.sh cleared.txt)
echo "Releasing ${NEW}"
echo "version=${NEW}" >> "$GITHUB_OUTPUT"
- name: Login to Docker Hub
if: steps.delta.outputs.cleared == 'true' && !inputs.dry_run
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Publish (multi-arch)
# Published here rather than via build-push-docker.yml: GITHUB_TOKEN pushes don't trigger workflows.
# v${NEW} is what the secops scan resolves; latest is what docker pull and the marketplace read.
if: steps.delta.outputs.cleared == 'true' && !inputs.dry_run
env:
NEW: ${{ steps.bump.outputs.version }}
run: |
echo "Publishing ${PUBLISHED_IMAGE}:${NEW} (also v${NEW}, latest), clearing:"; cat cleared.txt
docker buildx build --push --pull --no-cache \
--platform "${PLATFORMS}" \
--tag "${PUBLISHED_IMAGE}:${NEW}" \
--tag "${PUBLISHED_IMAGE}:v${NEW}" \
--tag "${PUBLISHED_IMAGE}:latest" .
# After the publish on purpose: a failed publish must not leave TAG pointing at an unpushed image.
- name: Commit, tag and release
if: steps.delta.outputs.cleared == 'true' && !inputs.dry_run
env:
NEW: ${{ steps.bump.outputs.version }}
GH_TOKEN: ${{ github.token }}
run: |
git add Makefile Dockerfile CHANGELOG.md
git commit -m "Security rebuild ${NEW}"
git tag "v${NEW}"
git push origin HEAD:main "v${NEW}"
# The secops scan reads our GitHub releases, not our tags; a bare tag leaves it on the old version.
gh release create "v${NEW}" --title "${NEW}" \
--notes "Weekly security rebuild (COSY-926). Cleared: $(paste -sd', ' cleared.txt)."
- name: Summary
if: always()
run: |
{
echo "### Weekly security rebuild"
echo
echo "- Published image: \`${PUBLISHED_IMAGE}:${{ steps.version.outputs.tag }}\`"
echo "- Fixable HIGH/CRITICAL on the published image: $(wc -l < published_cves.txt 2>/dev/null || echo 0)"
echo "- Cleared by rebuild: $(wc -l < cleared.txt 2>/dev/null || echo 0)"
echo "- Released: ${{ steps.bump.outputs.version || 'no (nothing cleared)' }}"
} >> "$GITHUB_STEP_SUMMARY"