Skip to content

Commit 8cba913

Browse files
committed
Scan SBOMs instead of images
1 parent 16c8425 commit 8cba913

File tree

3 files changed

+88
-32
lines changed

3 files changed

+88
-32
lines changed

.github/workflows/scan.yaml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Scan all images
2+
on:
3+
workflow_dispatch:
4+
schedule:
5+
- cron: '30 2 * * *'
6+
7+
jobs:
8+
scan_images:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v4
12+
- name: Init submodules
13+
run: git submodule update --init --recursive
14+
- name: Set up Cosign
15+
uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0
16+
- uses: actions/setup-python@v5
17+
with:
18+
python-version: 3.11
19+
- name: Run image
20+
uses: abatilo/actions-poetry@v2
21+
with:
22+
poetry-version: 1.7.1
23+
- name: Install deps
24+
run: poetry install
25+
- name: Scan dev images
26+
run: poetry run python stack_scanner/main.py scan-release ${{ secrets.SECOBSERVE_API_TOKEN }} 0.0.0-dev
27+
- name: Scan 24.7.0
28+
run: poetry run python stack_scanner/main.py scan-release ${{ secrets.SECOBSERVE_API_TOKEN }} 24.7.0

docker-images

Submodule docker-images updated 238 files

stack_scanner/main.py

Lines changed: 59 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import os
55
import subprocess
66
import sys
7+
import json
8+
import base64
79

810
excluded_products = [
911
"hello-world",
@@ -12,7 +14,12 @@
1214
"stackable-base",
1315
"trino-cli",
1416
"vector",
17+
"tools",
1518
"omid",
19+
"kcat",
20+
"kafka-testing-tools",
21+
"java-devel",
22+
"statsd_exporter"
1623
]
1724

1825
REGISTRY_URL = "docker.stackable.tech"
@@ -28,8 +35,10 @@ def main():
2835
)
2936
sys.exit(1)
3037

31-
os.makedirs("/tmp/stackable", exist_ok=True)
3238
os.system("rm -rf /tmp/stackable/*")
39+
os.makedirs("/tmp/stackable/trivy_tmp", exist_ok=True)
40+
os.makedirs("/tmp/stackable/trivy_cache", exist_ok=True)
41+
os.makedirs("/tmp/stackable/grype_db_cache", exist_ok=True)
3342

3443
with tempfile.TemporaryDirectory() as tempdir:
3544
# dump argv to console
@@ -69,49 +78,69 @@ def main():
6978
"zookeeper",
7079
]
7180

72-
for operator_name in operators:
73-
product_name = f"{operator_name}-operator"
74-
scan_image(secobserve_api_token, f"{REGISTRY_URL}/stackable/{product_name}:{release}", product_name, release)
81+
for arch in ["amd64", "arm64"]:
82+
for operator_name in operators:
83+
product_name = f"{operator_name}-operator"
84+
scan_image(secobserve_api_token, f"{REGISTRY_URL}/stackable/{product_name}:{release}-{arch}", product_name, release)
7585

76-
# Load product versions from that file using the image-tools functionality
77-
sys.path.append("docker-images")
78-
product_versions = load_configuration("docker-images/conf.py")
86+
# Load product versions from that file using the image-tools functionality
87+
sys.path.append("docker-images")
88+
product_versions = load_configuration("docker-images/conf.py")
7989

80-
for product in product_versions.products:
81-
product_name: str = product["name"]
90+
for product in product_versions.products:
91+
product_name: str = product["name"]
8292

83-
if product_name in excluded_products:
84-
continue
85-
for version_dict in product.get("versions", []):
86-
version: str = version_dict["product"]
87-
product_version = f"{version}-stackable{release}"
88-
scan_image(
89-
secobserve_api_token,
90-
f"{REGISTRY_URL}/stackable/{product_name}:{product_version}",
91-
product_name,
92-
product_version,
93-
)
93+
if product_name in excluded_products:
94+
continue
95+
for version_dict in product.get("versions", []):
96+
version: str = version_dict["product"]
97+
product_version = f"{version}-stackable{release}"
98+
scan_image(
99+
secobserve_api_token,
100+
f"{REGISTRY_URL}/stackable/{product_name}:{product_version}-{arch}",
101+
product_name,
102+
product_version,
103+
)
94104

95105

96106
def scan_image(secobserve_api_token: str, image: str, product_name: str, product_version: str) -> None:
107+
extract_sbom_cmd = [
108+
"cosign",
109+
"verify-attestation",
110+
"--type",
111+
"cyclonedx",
112+
"--certificate-identity-regexp",
113+
"^https://github.com/stackabletech/.+/.github/workflows/.+@.+",
114+
"--certificate-oidc-issuer",
115+
"https://token.actions.githubusercontent.com",
116+
image.replace("docker.stackable.tech/stackable/", "oci.stackable.tech/sdp/"),
117+
];
118+
print(" ".join(extract_sbom_cmd))
119+
120+
result = subprocess.run(extract_sbom_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
121+
cosign_output = json.loads(result.stdout.decode('utf-8'))
122+
payload = base64.b64decode(cosign_output["payload"]).decode('utf-8')
123+
sbom = json.loads(payload)["predicate"]
124+
with open("/tmp/stackable/bom.json", "w") as f:
125+
json.dump(sbom, f)
126+
97127
# Run Trivy
98128
env = {}
99-
env["TARGET"] = image
129+
env["TARGET"] = "/tmp/bom.json"
100130
env["SO_UPLOAD"] = "true"
101131
env["SO_PRODUCT_NAME"] = product_name
102132
env["SO_API_BASE_URL"] = "https://secobserve-backend.stackable.tech"
103133
env["SO_API_TOKEN"] = secobserve_api_token
104134
env["SO_BRANCH_NAME"] = product_version
105-
env["TMPDIR"] = "/tmp"
135+
env["TMPDIR"] = "/tmp/trivy_tmp"
136+
env["TRIVY_CACHE_DIR"] = "/tmp/trivy_cache"
106137
env["REPORT_NAME"] = "trivy.json"
107138

108-
print(f"Scanning {env['TARGET']} with Trivy")
109-
110139
cmd = [
111140
"docker",
112141
"run",
113142
"--entrypoint",
114-
"/entrypoints/entrypoint_trivy_image.sh",
143+
"/entrypoints/entrypoint_trivy_sbom.sh",
115144
"-v",
116145
"/tmp/stackable:/tmp",
117146
"-v",
@@ -122,22 +151,21 @@ def scan_image(secobserve_api_token: str, image: str, product_name: str, product
122151
cmd.append("-e")
123152
cmd.append(f"{key}={value}")
124153

125-
cmd.append("maibornwolff/secobserve-scanners:latest")
154+
cmd.append("oci.stackable.tech/sandbox/secobserve-scanners:latest")
126155

156+
print(" ".join(cmd))
127157
subprocess.run(cmd)
128158

129159
# Run Grype
130-
print(f"Scanning {env['TARGET']} with Grype")
131-
132160
env["FURTHER_PARAMETERS"] = "--by-cve"
133-
env["GRYPE_DB_CACHE_DIR"] = "/tmp"
161+
env["GRYPE_DB_CACHE_DIR"] = "/tmp/grype_db_cache"
134162
env["REPORT_NAME"] = "grype.json"
135163

136164
cmd = [
137165
"docker",
138166
"run",
139167
"--entrypoint",
140-
"/entrypoints/entrypoint_grype_image.sh",
168+
"/entrypoints/entrypoint_grype_sbom.sh",
141169
"-v",
142170
"/tmp/stackable:/tmp",
143171
"-v",
@@ -148,7 +176,7 @@ def scan_image(secobserve_api_token: str, image: str, product_name: str, product
148176
cmd.append("-e")
149177
cmd.append(f"{key}={value}")
150178

151-
cmd.append("maibornwolff/secobserve-scanners:latest")
179+
cmd.append("oci.stackable.tech/sandbox/secobserve-scanners:latest")
152180

153181
subprocess.run(cmd)
154182

0 commit comments

Comments
 (0)