Skip to content
logo-white
All posts

Always know about the dependency of your Pods – here comes SBOM operator for Kubernetes

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

What do you gain as a DevOps engineer?

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.

Installation of the Operator in Kubernetes

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!