Skip to content

Commit 3c98468

Browse files
Paul Hewletteccles
authored andcommitted
SBOM scraper script
Problem: Extract SBOM from docker image and upload to archivist Solution: sbom_scraper.sh using app registrations flow. Signed-off-by: Paul Hewlett <[email protected]>
1 parent f396d4d commit 3c98468

File tree

1 file changed

+177
-0
lines changed

1 file changed

+177
-0
lines changed

scripts/sbom_scraper.sh

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Scrape a docker image and upload as SBOM file
4+
#
5+
# Preparation:
6+
#
7+
# Install syft - https://github.com/anchore/syft
8+
#
9+
# Read the docs on syft usage.
10+
#
11+
# Create App registration called "SBOM scraper" following the flow described in
12+
# https://docs.rkvst.com/docs/setup-and-administration/getting-access-tokens-using-app-registrations/#using-the-rkvst-ui-(required-for-first-time-setup)
13+
# and note down the CLIENT_ID and SECRET.
14+
#
15+
# Copy the SECRET generated to the file specified by ${CLIENTSECRET_FILE} below. This
16+
# file should reside in a subdirectory with 0600 permissions.
17+
#
18+
# Use the CLIENT_ID as the first fixed argument to this script.
19+
#
20+
21+
SCRIPTNAME=$(basename "$0")
22+
23+
SYFT=$(which syft)
24+
if [ -z "${SYFT}" ]
25+
then
26+
echo "syft command not found"
27+
exit 10
28+
fi
29+
30+
set -e
31+
set -u
32+
33+
LOGTAG=$$
34+
log() {
35+
echo "${LOGTAG}:$(date --rfc-3339=seconds):$* ..."
36+
}
37+
38+
# defaults
39+
FORMAT=cyclonedx
40+
41+
# credentials directory has 0600 permissions
42+
CLIENTSECRET_FILE=credentials/client_secret
43+
SBOM=false
44+
45+
URL=https://app.rkvst.io
46+
47+
usage() {
48+
cat >&2 <<EOF
49+
50+
Scrape an SBOM from a docker image
51+
52+
Usage: $SCRIPTNAME [-c clientsecretfile] [-o output format] [-s sbomFile ] [-u url] client_id [docker-image|sbom file]
53+
54+
-c clientsecretfile containing client secret (default ${CLIENTSECRET_FILE})
55+
-o FORMAT default ($FORMAT) [cyclonedx]
56+
-s default ($SBOM) if specified the second argument is an sbom file
57+
and -o is ignored.
58+
-u URL URL Default ($URL)
59+
60+
Example:
61+
62+
$0 -o spdx 29b48af4-45ca-465b-b136-206674f8aa9b ubuntu:21.10
63+
64+
EOF
65+
66+
exit 1
67+
}
68+
69+
while getopts "c:ho:su:" o; do
70+
case "${o}" in
71+
c) CLIENTSECRET_FILE="${OPTARG}"
72+
;;
73+
o) FORMAT=${OPTARG}
74+
;;
75+
s) SBOM=true
76+
;;
77+
u) URL=$OPTARG
78+
;;
79+
*)
80+
usage
81+
;;
82+
esac
83+
done
84+
shift $((OPTIND-1))
85+
86+
[ $# -lt 1 ] && usage
87+
CLIENT_ID=$1
88+
shift 1
89+
[ $# -lt 1 ] && usage
90+
DOCKER_IMAGE=$1
91+
shift 1
92+
93+
[ $# -ge 1 ] && usage
94+
95+
# ----------------------------------------------------------------------------
96+
# Setup exit handling and temporary directory
97+
# ----------------------------------------------------------------------------
98+
TEMPDIR=$( mktemp -d /tmp/.sbom_scraper.XXXXXXXX )
99+
100+
# report on exit
101+
function finalise {
102+
CODE=$?
103+
rm -rf "$TEMPDIR"
104+
exit $CODE
105+
}
106+
trap finalise EXIT INT TERM
107+
108+
OUTFILE=$(echo "${DOCKER_IMAGE}" | tr '/:' '-').${FORMAT}.sbom
109+
110+
# ----------------------------------------------------------------------------
111+
# Extract client secrets
112+
# ----------------------------------------------------------------------------
113+
if [ ! -s "${CLIENTSECRET_FILE}" ]
114+
then
115+
log "${CLIENTSECRET_FILE} does not exist or is empty"
116+
exit 1
117+
fi
118+
SECRET=$(cat "${CLIENTSECRET_FILE}")
119+
120+
# ----------------------------------------------------------------------------
121+
# Extract SBOM
122+
# ----------------------------------------------------------------------------
123+
if [ "${SBOM}" = "false" ]
124+
then
125+
log "Scrape ${FORMAT} SBOM from ${DOCKER_IMAGE} to ${OUTFILE}..."
126+
OUTPUT="${TEMPDIR}/${OUTFILE}"
127+
${SYFT} -q packages -o "${FORMAT}" "${DOCKER_IMAGE}"> "${OUTPUT}"
128+
else
129+
OUTPUT="${DOCKER_IMAGE}"
130+
fi
131+
132+
# ----------------------------------------------------------------------------
133+
# Handle client id and secrets for SBOM scraper via App registrations
134+
# ----------------------------------------------------------------------------
135+
HTTP_STATUS=""
136+
# get token
137+
log "Get token"
138+
HTTP_STATUS=$(curl -sS -w "%{http_code}" \
139+
-o "${TEMPDIR}/access_token" \
140+
--data-urlencode "grant_type=client_credentials" \
141+
--data-urlencode "client_id=${CLIENT_ID}" \
142+
--data-urlencode "client_secret=${SECRET}" \
143+
"${URL}/archivist/iam/v1/appidp/token")
144+
if [ "${HTTP_STATUS}" != "200" ]
145+
then
146+
log "Get token failure ${HTTP_STATUS}"
147+
exit 2
148+
fi
149+
150+
TOKEN=$(jq -r .access_token "${TEMPDIR}"/access_token )
151+
152+
# create token file
153+
BEARER_TOKEN_FILE=${TEMPDIR}/token
154+
cat > "${BEARER_TOKEN_FILE}" <<EOF
155+
Authorization: Bearer $TOKEN
156+
EOF
157+
#
158+
# ----------------------------------------------------------------------------
159+
# Upload SBOM
160+
# ----------------------------------------------------------------------------
161+
log "Upload ${OUTPUT}"
162+
163+
HTTP_STATUS=$(curl -s -w "%{http_code}" -X POST \
164+
-o "${TEMPDIR}/upload" \
165+
-H "@${BEARER_TOKEN_FILE}" \
166+
-H "content_type=text/xml" \
167+
-F "sbom=@${OUTPUT}" \
168+
"${URL}/archivist/v1/sboms")
169+
170+
if [ "${HTTP_STATUS}" != "200" ]
171+
then
172+
log "Upload failure ${HTTP_STATUS}"
173+
exit 4
174+
fi
175+
UUID=$(cat "${TEMPDIR}/upload")
176+
log "Upload success: uuid=${UUID}"
177+
exit 0

0 commit comments

Comments
 (0)