Some months ago Christian Kotzbauer started a community project called SBOM operator that created SBOMs based on the container images used by the Pods and stored them in a Git repository. His main intentions were to have a Kubernetes-native way to generate and store Software Bill of Materials for all running container.
Thinking about the importance of storing a tamperproof Bill of Material and having a way to automatically relate the SBOM with the unique container image ID, he decided to extend the SBOM operator to Codenotary CAS and Codenotary Cloud.
“I am pleased to contribute to the wider adoption and use of SBOMs with the Codenotary integration in my Kubernetes operator.” said Christian, “especially the additional security, timestamp and search capabilities across the infrastructure were key to developing the extension.”
Christian Kotzbauer, https://github.com/ckotzbauer/sbom-operator
For sure you still remember the last couple of months with plenty of requests, if there are any vulnerable Log4J (Log4Shell) or spring-webmvc (Spring4Shell) versions running in the infrastructure. Just checking the Git repositories source code or image scanning doesn’t really cut it. You need to know if these components are already in your environment and you need to know it immediately.
Codenotary Cloud and CAS make it very easy to gather the SBOM information and not simply store it, but store them auditable and fully searchable.
Example Codenotary Cloud:
vcn authenticate --bom-what-includes --name log4j-core --version "<2.15.0"
Based on the included in information, you can simply check what is currently using the dependency:
vcn authenticate --hash 6bd1bad7c9c664580428c35fe354a5f92dc87d6a24db566a5421a1abdf5ed733
or use the dashboard
The SBOM viewer in the Codenotary Cloud dashboard allows to search and filter dependencies and immediately detect untrusted or unwanted components – Log4J-Core component in the example below.
Create a secret for the API Key:
kubectl create secret generic SBOM_JOB_VCN_LC_API_KEY --from-literal=secret=<Your Codenotary Cloud API Key>
or using CAS:
kubectl create secret generic SBOM_JOB_CAS_API_KEY --from-literal=secret=<Your cas.Codenotary.com API Key>
deployment.yaml for Codenotary Cloud (make sure to change SBOM_JOB_VCN_LC_HOST value):
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/name: sbom-operator
name: sbom-operator
namespace: default
spec:
selector:
matchLabels:
app.kubernetes.io/name: sbom-operator
template:
metadata:
labels:
app.kubernetes.io/name: sbom-operator
spec:
containers:
- image: Codenotary/sbom-operator:0.10.0
name: operator
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_UID
valueFrom:
fieldRef:
fieldPath: metadata.uid
# Free account
# - name: SBOM_JOB_CAS_API_KEY
# value: ""
- name: SBOM_JOB_VCN_LC_HOST
value: "YourCNC.Codenotary.com"
- name: SBOM_JOB_VCN_LC_API_KEY
valueFrom:
secretKeyRef:
name: cncapikey
key: secret
args:
# example values
- --cron="0 6 * * *"
- --job-image=Codenotary/sbom-operator:vcn-0.11.0
#- --pod-label-selector=sbom-operator\=true
ports:
- containerPort: 8080
name: http
protocol: TCP
securityContext:
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 101
seccompProfile:
type: RuntimeDefault
resources:
limits:
cpu: 500m
memory: 500Mi
requests:
cpu: 100m
memory: 100Mi
livenessProbe:
timeoutSeconds: 3
httpGet:
path: "/health"
port: 8080
readinessProbe:
timeoutSeconds: 3
httpGet:
path: "/health"
port: 8080
securityContext:
fsGroup: 101
serviceAccountName: sbom-operator
or deployment.yaml for CAS
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/name: sbom-operator
name: sbom-operator
namespace: default
spec:
selector:
matchLabels:
app.kubernetes.io/name: sbom-operator
template:
metadata:
labels:
app.kubernetes.io/name: sbom-operator
spec:
containers:
- image: Codenotary/sbom-operator:0.10.0
name: operator
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_UID
valueFrom:
fieldRef:
fieldPath: metadata.uid
- name: SBOM_JOB_CAS_API_KEY
valueFrom:
secretKeyRef:
name: casapikey
key: secret
args:
# example values
- --cron="0 6 * * *"
- --job-image=Codenotary/sbom-operator:cas-0.11.0
#- --pod-label-selector=sbom-operator\=true
ports:
- containerPort: 8080
name: http
protocol: TCP
securityContext:
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 101
seccompProfile:
type: RuntimeDefault
resources:
limits:
cpu: 500m
memory: 500Mi
requests:
cpu: 100m
memory: 100Mi
livenessProbe:
timeoutSeconds: 3
httpGet:
path: "/health"
port: 8080
readinessProbe:
timeoutSeconds: 3
httpGet:
path: "/health"
port: 8080
securityContext:
fsGroup: 101
serviceAccountName: sbom-operator
and the rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: sbom-operator
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: sbom-operator
rules:
- apiGroups:
- ""
resources:
- pods
- namespaces
verbs:
- list
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- update
- apiGroups:
- ""
resources:
- secrets
verbs:
- get
- create
- delete
- apiGroups:
- batch
resources:
- jobs
verbs:
- get
- create
- delete
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: sbom-operator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: sbom-operator
subjects:
- kind: ServiceAccount
name: sbom-operator
namespace: default
Install everything using kubectl:
kubectl apply -f deployment.yaml rbac.yaml
After the deployment has been completed, the cron job (based on the configuration in the deployment.yaml) will detect the running images, generate a Software Bill of Materials for each container image and notarize it in either CAS or Codenotary Cloud.
You are all set!