🔐 Enhancing Software Supply Chain Security with Syft and Grype 🛠️
As software producers, we rely on various third-party components and technologies to build and distribute software. Ensuring the security of our entire supply chain has become more critical than ever. That’s where tools like Syft and Grype come into play!
A Guide to Generating SBOM with Syft and Grype
In this guide, I walk you through:
- 1️⃣ Generating SBOMs (Software Bill of Materials) with Syft
- 2️⃣ Scanning for vulnerabilities with Grype
- 3️⃣ Signing images with Cosign for integrity validation
- 4️⃣ Combining SBOM and vulnerability data for a comprehensive view of your software’s security posture.
📌 Key takeaways:
- Understand which components are vulnerable
- Prioritize remediation efforts based on vulnerability severity
- Ensure compliance with security regulations
- Continuously enhance your software’s security
DevSecOps #CloudSecurity #SoftwareSupplyChain #SBOM #Syft #Grype #Cosign #Containers #Kubernetes #Security #DevOps #SBOMGeneration
💡 Ready to boost your software supply chain security? Check out the detailed guide here:
Install cosign:
wget "https://github.com/sigstore/cosign/releases/download/v2.4.0/cosign-linux-amd64"
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
sudo chmod +x /usr/local/bin/cosign
Install Syft:
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
Install Grype:
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
Setup your info
export DOCKER_PASSWORD=<password>
export DOCKER_USERNAME=<username>
export IMAGE_NAME=hello-container
export IMAGE_TAG=latest
Build the docker image
cat<<EOF >Dockerfile
FROM nginx:1.27.1
MAINTAINER Narendranath Panda <naren4biz@gmail.com>
COPY static-html-directory /usr/share/nginx/html
EOF
#build
docker build --no-cache linux/amd64 -t $DOCKER_USERNAME/$IMAGE_NAME:$IMAGE_TAG .
# test
docker run --name=$IMAGE_NAME --rm -d --network host $DOCKER_USERNAME/$IMAGE_NAME:$IMAGE_TAG
curl http://localhost:80
# push
docker push $DOCKER_USERNAME/$IMAGE_NAME:$IMAGE_TAG
Generate and the cosign signing key and sign the image
cosign generate-key-pair
#cosign sign --key cosign.key docker-username/demo-container
cosign sign --key cosign.key $DOCKER_USERNAME/$IMAGE_NAME:$IMAGE_TAG
#cosign verify --key cosign.pub docker-username/demo-container
cosign verify --key cosign.pub $DOCKER_USERNAME/$IMAGE_NAME:$IMAGE_TAG
Generate the SBOM
#If you prefer to receive the output in JSON format, run the following command:
syft $DOCKER_USERNAME/$IMAGE_NAME:$IMAGE_TAG -o json > $IMAGE_NAME-$IMAGE_TAG-sbom.json
#To extract the package name, version, and license in a tabular format - enabling visibility into all packages and licenses used to create the product, run the following command:
cat $IMAGE_NAME-$IMAGE_TAG.json | jq -r '.artifacts[] | [.name, .version, (if .licenses == [] then "No license" else [ .licenses[].value ] | join(", ") end)] | @tsv' | awk -F'\t' 'BEGIN {print "Package Name\tPackage Version\tLicense"; print "-------------\t---------------\t-------"} {printf "%s\t%s\t%s\n", $1, $2, $3}'
#To extract the package name, version, and license in a tabular format - enabling visibility into all packages and licenses used to create the product, run the following command:
cat $IMAGE_NAME-$IMAGE_TAG.json | jq '[.artifacts[] | {packageName: .name, packageVersion: .version, license: (if .licenses | length == 0 then "No license" else [ .licenses[].value ] | join(", ") end), locations: [.locations[].path]}]' > packages_with_locations.json
#This may create separate sections for the same Package version and name but with different locations. The following command will unify these sections into one.
cat $IMAGE_NAME-$IMAGE_TAG.json | jq '[.artifacts[] | {packageName: .name, packageVersion: .version, license: (if .licenses | length == 0 then "No license" else [ .licenses[].value ] | join(", ") end), locations: [.locations[].path]}] | group_by(.packageName + .packageVersion) | map({packageName: .[0].packageName, packageVersion: .[0].packageVersion, license: .[0].license, locations: map(.locations[]) | unique})' > grouped_packages_with_locations.json
cat $IMAGE_NAME-$IMAGE_TAG.json | jq -r '[.artifacts[] | {packageName: .name, packageVersion: .version, license: (if .licenses | length == 0 then "No license" else [ .licenses[].value ] | join(", ") end), locations: [.locations[].path]}] | group_by(.packageName + .packageVersion) | map({packageName: .[0].packageName, packageVersion: .[0].packageVersion, license: .[0].license, locations: map(.locations[]) | unique}) | .[] | [.packageName, .packageVersion, .license, (.locations | join(", "))] | @tsv' | column -t -s $'\t' > grouped_packages_with_locations_tabular.txt
# You can filter by GPL, to focus on packages that typically violate the organization’s policy by running the following command:
cat $IMAGE_NAME-$IMAGE_TAG.json | jq -r '[.artifacts[] | {packageName: .name, packageVersion: .version, license: (if .licenses | length == 0 then "No license" else [ .licenses[].value ] | join(", ") end), locations: [.locations[].path]}] | group_by(.packageName + .packageVersion) | map({packageName: .[0].packageName, packageVersion: .[0].packageVersion, license: .[0].license, locations: map(.locations[]) | unique}) | map(select(.license | test("GPL"))) | .[] | [.packageName, .packageVersion, .license, (.locations | join(", "))] | @tsv' | column -t -s $'\t'> gpl_filtered.json
Generate the Vulnerability
#Once you have extracted your SBOM in your preferred format with Syft, Grype will scan the SBOMs to identify and report vulnerabilities, enriching the SBOM with crucial security insights.
grype sbom:sbom.json -o json > $IMAGE_NAME-$IMAGE_TAG-vulnerabilities.json
Combining SBOM and Vulnerability Data
To effectively safeguard your software supply chain, it’s essential to combine insights from SBOMs (Software Bill of Materials) with vulnerability data. This comprehensive approach allows you to better understand the current risk exposure of your technology stack.
Using tools like Syft for generating SBOMs and Grype for vulnerability scanning, we can: - ✅ Understand Exposure: Identify vulnerable components and assess their severity. - ✅ Prioritize Efforts: Focus on remediating the most critical vulnerabilities. - ✅ Ensure Compliance: Keep a clear record of components and vulnerabilities to meet regulatory standards. - ✅ Enhance Security Posture: Continuously improve security by regularly scanning and remediating threats. try the script here add_vulnerabilities_to_packages.py
Full credit to 🙏 a-guide-to-generating-sbom-with-syft-and-grype 🙏
# Define file paths
grouped_packages_path = '/path/to/grouped_packages_with_locations.json' # todo $IMAGE_NAME-$IMAGE_TAG-sbom.json
vulnerabilities_path = '/path/to/vulnerabilities.json' #$IMAGE_NAME-$IMAGE_TAG-vulnerabilities.json
output_path = '/path/to/final.json' # todo $IMAGE_NAME-$IMAGE_TAG.json
Sign the SBOM
SHA_ID=$(docker inspect --format='{{.Id}}' $DOCKER_USERNAME/$IMAGE_NAME:$IMAGE_TAG)
cosign sign --key cosign.key $DOCKER_USERNAME/$IMAGE_NAME:$SHA_ID.sbom
What Next
Setup the Kevyrno Policy
Test at a k8s cluster
Ref:
- https://www.jit.io/resources/appsec-tools/a-guide-to-generating-sbom-with-syft-and-grype
- https://edu.chainguard.dev/open-source/sigstore/policy-controller/how-to-install-policy-controller/
- https://www.docker.com/blog/generate-sboms-with-buildkit/
- https://anchore.com/blog/add-sbom-generation-to-your-github-project-with-syft/
- https://www.youtube.com/watch?v=8GKFzJaEHac&ab_channel=VMwareTanzu
- https://www.youtube.com/watch?v=nybVFJVXbww&ab_channel=Computerphile
- https://edu.chainguard.dev/open-source/sigstore/policy-controller/how-to-install-policy-controller/