forked from jupyterhub/zero-to-jupyterhub-k8s
-
Notifications
You must be signed in to change notification settings - Fork 0
232 lines (202 loc) · 9.36 KB
/
vuln-scan.yaml
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
231
232
# This is a GitHub workflow defining a set of jobs with a set of steps.
# ref: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions
#
# This workflow use aquasecurity/trivy to scan the images we have published for
# known vulnerabilities. If there are such that can be patched, we let this
# workflow fail to signal that unless we make an exception, which we do for the
# singleuser-sample image only.
#
#
# About environment: watch-dependencies
#
# To reduce the exposure of the secrets.jupyterhub_bot_pat token that was setup
# for the environment watch-dependencies, we have setup a dedicated environment
# according to steps in
# https://github.com/jupyterhub/team-compass/issues/516#issuecomment-1129961954.
#
name: Vuln. scan
on:
pull_request:
paths:
- ".github/workflows/vuln-scan.yaml"
push:
paths:
- ".github/workflows/vuln-scan.yaml"
branches: ["main"]
schedule:
# At 05:00 on Monday - https://crontab.guru
- cron: "0 5 * * 1"
workflow_dispatch:
jobs:
trivy_image_scan:
if: github.repository == 'jupyterhub/zero-to-jupyterhub-k8s'
runs-on: ubuntu-22.04
environment: watch-dependencies
strategy:
fail-fast: false
matrix:
include:
- image_ref: hub
accept_failure: false
- image_ref: secret-sync
accept_failure: false
- image_ref: network-tools
accept_failure: false
- image_ref: image-awaiter
accept_failure: false
- image_ref: singleuser-sample
accept_failure: false
steps:
- uses: actions/checkout@v4
with:
# chartpress requires git history to set chart version and image tags
# correctly
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install chartpress
run: |
pip install chartpress
# charpress --list-images output lines of name:tag format. We use it with
# a search string in matrix.image_ref to find the specific image to scan
# in this job.
- name: Identify image name:tag
id: image
run: |
IMAGE_SPEC=$(
chartpress --list-images \
| grep ${{ matrix.image_ref }}:
)
echo "Identified image: $IMAGE_SPEC"
echo "spec=$IMAGE_SPEC" >> $GITHUB_OUTPUT
echo "name=$(echo $IMAGE_SPEC | sed 's/\(.*\):.*/\1/')" >> $GITHUB_OUTPUT
echo "tag=$(echo $IMAGE_SPEC | sed 's/.*:\(.*\)/\1/')" >> $GITHUB_OUTPUT
- name: Create ./tmp dir
run: mkdir ./tmp
# Action reference: https://github.com/aquasecurity/trivy-action
- name: Scan latest published image
id: scan_1
uses: aquasecurity/trivy-action@6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8
with:
image-ref: ${{ steps.image.outputs.spec }}
format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json
output: tmp/scan_1.json
ignore-unfixed: true
exit-code: "1"
# Keep running the subsequent steps of the job, they are made to
# explicitly adjust based on this step's outcome.
continue-on-error: true
# Steps below is only executing if vulnerabilities have been detected.
# -----------------------------------------------------------------------
- name: Rebuild image
id: rebuild
if: steps.scan_1.outcome == 'failure'
env:
DOCKER_BUILDKIT: "1"
run: |
docker build -t rebuilt-image images/${{ matrix.image_ref }}
- name: Scan rebuilt image
id: scan_2
if: steps.rebuild.outcome == 'success'
uses: aquasecurity/trivy-action@6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8
with:
image-ref: rebuilt-image
format: json # ref: https://github.com/aquasecurity/trivy#save-the-results-as-json
output: tmp/scan_2.json
ignore-unfixed: true
# Analyze the scan reports. If they differ, we want to proceed and create
# or update a PR. We use a hash from the final scan report as an
# indication to rebuild or not.
- name: Analyze scan reports
id: analyze
if: steps.rebuild.outcome == 'success'
run: |
echo "utc_time=$(date --utc +'%F_%T')" >> $GITHUB_OUTPUT
json_to_misc() {
# Count vulnerabilities
VULNERABILITY_COUNT="$(cat tmp/scan_$1.json | jq -r '[.Results[].Vulnerabilities | select(type != null)] | add | select(. != null) | length')"
echo "VULNERABILITY_COUNT_$1=$VULNERABILITY_COUNT" >> $GITHUB_ENV
# Construct a markdown summary
if [[ "$VULNERABILITY_COUNT" == "0" ]]; then
echo "No vulnerabilities! :tada:" >> tmp/md_summary_$1.md
else
echo "Target | Vuln. ID | Package Name | Installed v. | Fixed v." >> tmp/md_summary_$1.md
echo "-|-|-|-|-" >> tmp/md_summary_$1.md
cat tmp/scan_$1.json | jq -r '.Results[] | select(.Vulnerabilities != null) | .Type + " | " + (.Vulnerabilities[] | .VulnerabilityID + " | " + .PkgName + " | " + .InstalledVersion + " | " + .FixedVersion)' | sort >> tmp/md_summary_$1.md
fi
# Set a multiline string output with the following technique:
# ref: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings
#
eof_marker=EOF_$RANDOM
echo "md_summary_$1<<$eof_marker" >> $GITHUB_OUTPUT
cat tmp/md_summary_$1.md >> $GITHUB_OUTPUT
echo "$eof_marker" >> $GITHUB_OUTPUT
# Calculate a hash of the markdown summary
HASH=$(cat tmp/md_summary_$1.md | sha1sum)
HASH=${HASH:0:10}
export HASH_$1=$HASH
echo "hash_$1=$HASH" >> $GITHUB_OUTPUT
}
json_to_misc 1
json_to_misc 2
# Did rebuilding the image change anything?
if [ "$HASH_1" == "$HASH_2" ]; then
echo "proceed=no" >> $GITHUB_OUTPUT
echo "No vulnerabilities were patched by rebuilding the image - won't proceed!"
else
echo "proceed=yes" >> $GITHUB_OUTPUT
echo "Vulnerabilities were patched by rebuilding the image - will proceed!"
fi
- name: Describe vulnerabilities
if: steps.rebuild.outcome == 'success'
uses: aquasecurity/trivy-action@6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8
with:
image-ref: rebuilt-image
format: table
ignore-unfixed: true
- name: Decision to not proceed
if: steps.analyze.outputs.proceed == 'no'
run: |
echo "::warning::None of the $VULNERABILITY_COUNT_1 vulnerabilities got patched by rebuilding the image :("
continue-on-error: ${{ matrix.accept_failure == true }}
# Steps below are executed if the analyze step decided to proceed.
# -----------------------------------------------------------------------
# Searches for the "# VULN_SCAN_TIME=..." in the Dockerfile and sets a new
# value, which can be used to submit a PR that when merged will trigger a
# rebuild of the image.
- name: Update VULN_SCAN_TIME in Dockerfile
if: steps.analyze.outputs.proceed == 'yes'
run: |
value_to_set="${{ steps.analyze.outputs.utc_time }}"
file_to_update="images/${{ matrix.image_ref }}/Dockerfile"
sed --in-place "s/\(#.*VULN_SCAN_TIME=\)\(.*\)/\1${value_to_set}/" "$file_to_update"
git --no-pager diff --color=always
# The create-pull-request action is smart enough to only create/update a
# PR if there is a change to anything not .gitignored. A change will be
# made only if the analyze steps outputted hash is changed.
#
# ref: https://github.com/peter-evans/create-pull-request
- name: Create or update a PR
if: steps.analyze.outputs.proceed == 'yes' && github.event_name != 'pull_request'
uses: peter-evans/create-pull-request@v7
with:
token: "${{ secrets.jupyterhub_bot_pat }}"
author: JupterHub Bot Account <[email protected]>
committer: JupterHub Bot Account <[email protected]>
reviewers: consideratio
branch: vuln-scan-${{ matrix.image_ref }}
title: Vulnerability patch in ${{ matrix.image_ref }}
labels: image:rebuild-to-patch-vuln
body: |
A rebuild of `${{ steps.image.outputs.name }}` has been found to influence the detected vulnerabilities! This PR will trigger a rebuild because it has updated a comment in the Dockerfile.
## About
This scan for known vulnerabilities has been made by [aquasecurity/trivy](https://github.com/aquasecurity/trivy). Trivy was configured to filter the vulnerabilities with the following settings:
- ignore-unfixed: `true`
## Before
Before trying to rebuild the image, the following vulnerabilities was detected in `${{ steps.image.outputs.spec }}`.
${{ steps.analyze.outputs.md_summary_1 }}
## After
${{ steps.analyze.outputs.md_summary_2 }}
commit-message: |
Patch known vulnerability in ${{ matrix.image_ref }}