Skip to content

Getting Started

Before you Begin

You need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. If you do not already have a cluster, you can create one by installing minikube or kind, or you can use one of these Kubernetes playgrounds:

You also need the Trivy-Operator to be installed in the trivy-system namespace, e.g. with kubectl or Helm. Let's also assume that the operator is configured to discover built-in Kubernetes resources in all namespaces, except kube-system and trivy-system.

Workloads Scanning

Let's create the nginx Deployment that we know is vulnerable:

kubectl create deployment nginx --image nginx:1.16

When the nginx Deployment is created, the operator immediately detects its current revision (aka active ReplicaSet) and scans the nginx:1.16 image for vulnerabilities. It also audits the ReplicaSet's specification for common pitfalls such as running the nginx container as root.

If everything goes fine, the operator saves scan reports as VulnerabilityReport and ConfigAuditReport resources in the default namespace. Reports are named after the scanned ReplicaSet. For image vulnerability scans, the operator creates a VulnerabilityReport for each different container. In this example there is just one container image called nginx:

kubectl get vulnerabilityreports -o wide
Result
NAME                                REPOSITORY      TAG    SCANNER   AGE   CRITICAL   HIGH   MEDIUM   LOW   UNKNOWN
replicaset-nginx-78449c65d4-nginx   library/nginx   1.16   Trivy     85s   33         62     49       114   1
kubectl get configauditreports -o wide
Result
NAME                          SCANNER     AGE    CRITICAL  HIGH   MEDIUM   LOW
replicaset-nginx-78449c65d4   Trivy-Operator   2m7s   0         0      6        7

Notice that scan reports generated by the operator are controlled by Kubernetes workloads. In our example, VulnerabilityReport and ConfigAuditReport resources are controlled by the active ReplicaSet of the nginx Deployment:

kubectl tree deploy nginx
Result
NAMESPACE  NAME                                                       READY  REASON  AGE
default    Deployment/nginx                                           -              7h2m
default    └─ReplicaSet/nginx-78449c65d4                              -              7h2m
default      ├─ConfigAuditReport/replicaset-nginx-78449c65d4          -              2m31s
default      ├─Pod/nginx-78449c65d4-5wvdx                             True           7h2m
default      └─VulnerabilityReport/replicaset-nginx-78449c65d4-nginx  -              2m7s

Note

The tree command is a kubectl plugin to browse Kubernetes object hierarchies as a tree.

Moving forward, let's update the container image of the nginx Deployment from nginx:1.16 to nginx:1.17. This will trigger a rolling update of the Deployment and eventually create another ReplicaSet.

kubectl set image deployment nginx nginx=nginx:1.17

Even this time the operator will pick up changes and rescan our Deployment with updated configuration:

kubectl tree deploy nginx
Result
NAMESPACE  NAME                                                       READY  REASON  AGE
default    Deployment/nginx                                           -              7h5m
default    ├─ReplicaSet/nginx-5fbc65fff                               -              2m36s
default    │ ├─ConfigAuditReport/replicaset-nginx-5fbc65fff           -              2m36s
default    │ ├─Pod/nginx-5fbc65fff-j7zl2                              True           2m36s
default    │ └─VulnerabilityReport/replicaset-nginx-5fbc65fff-nginx   -              2m22s
default    └─ReplicaSet/nginx-78449c65d4                              -              7h5m
default      ├─ConfigAuditReport/replicaset-nginx-78449c65d4          -              5m46s
default      └─VulnerabilityReport/replicaset-nginx-78449c65d4-nginx  -              5m22s

By following this guide you could realize that the operator knows how to attach VulnerabilityReport and ConfigAuditReport resources to build-in Kubernetes objects. What's more, in this approach where a custom resource inherits a life cycle of the built-in resource we could leverage Kubernetes garbage collection. For example, when the previous ReplicaSet named nginx-78449c65d4 is deleted the VulnerabilityReport named replicaset-nginx-78449c65d4-nginx as well as the ConfigAuditReport named replicaset-nginx-78449c65d46 are automatically garbage collected.

Tip

If you only want the latest ReplicaSet in your Deployment to be scanned for vulnerabilities, you can set the value of the OPERATOR_VULNERABILITY_SCANNER_SCAN_ONLY_CURRENT_REVISIONS environment variable to true in the operator's deployment descriptor. This is useful to identify vulnerabilities that impact only the running workloads.

Tip

If you only want the latest ReplicaSet in your Deployment to be scanned for config audit, you can set the value of the OPERATOR_CONFIG_AUDIT_SCANNER_SCAN_ONLY_CURRENT_REVISIONS environment variable to true in the operator's deployment descriptor. This is useful to identify config issues that impact only the running workloads.

Tip

You can get and describe vulnerabilityreports and configauditreports as built-in Kubernetes objects:

kubectl get vulnerabilityreport replicaset-nginx-5fbc65fff-nginx -o json
kubectl describe configauditreport replicaset-nginx-5fbc65fff

Notice that scaling up the nginx Deployment will not schedule new scans because all replica Pods refer to the same Pod template defined by the nginx-5fbc65fff ReplicaSet.

kubectl scale deploy nginx --replicas 3
kubectl tree deploy nginx
Result
NAMESPACE  NAME                                                       READY  REASON  AGE
default    Deployment/nginx                                           -              7h6m
default    ├─ReplicaSet/nginx-5fbc65fff                               -              4m7s
default    │ ├─ConfigAuditReport/replicaset-nginx-5fbc65fff           -              4m7s
default    │ ├─Pod/nginx-5fbc65fff-458n7                              True           8s
default    │ ├─Pod/nginx-5fbc65fff-fk847                              True           8s
default    │ ├─Pod/nginx-5fbc65fff-j7zl2                              True           4m7s
default    │ └─VulnerabilityReport/replicaset-nginx-5fbc65fff-nginx   -              3m53s
default    └─ReplicaSet/nginx-78449c65d4                              -              7h6m
default      ├─ConfigAuditReport/replicaset-nginx-78449c65d4          -              7m17s
default      └─VulnerabilityReport/replicaset-nginx-78449c65d4-nginx  -              6m53s

Finally, when you delete the nginx Deployment, orphaned security reports will be deleted in the background by the Kubernetes garbage collection controller.

kubectl delete deploy nginx
kubectl get vuln,configaudit
Result
No resources found in default namespace.

Tip

Use vuln and configaudit as short names for vulnerabilityreports and configauditreports resources.

Note

You can define the validity period for VulnerabilityReports by setting the duration as the value of the OPERATOR_VULNERABILITY_SCANNER_REPORT_TTL environment variable. For example, setting the value to 24h would delete reports after 24 hours. When a VulnerabilityReport gets deleted Trivy-Operator will automatically rescan the underlying workload. Assuming that the vulnerability scanner has updated its vulnerability database, new VulnerabilityReports will contain the latest vulnerabilities.

Infrastructure Scanning

The operator discovers also Kubernetes nodes and runs CIS Kubernetes Benchmark checks on each of them. The results are stored as CISKubeBenchReport objects. In other words, for a cluster with 3 nodes the operator will eventually create 3 benchmark reports:

kubectl get node
Result
NAME                 STATUS   ROLES    AGE     VERSION
kind-control-plane   Ready    master   3h27m   v1.18.8
kind-worker          Ready    <none>   3h26m   v1.18.8
kind-worker2         Ready    <none>   3h26m   v1.18.8
kubectl get ciskubebenchreports -o wide
Result
NAME                 SCANNER      AGE   FAIL   WARN   INFO   PASS
kind-control-plane   kube-bench   8s    12     40     0      70
kind-worker          kube-bench   9s    2      27     0      18
kind-worker2         kube-bench   9s    2      27     0      18

Notice that each CISKubeBenchReport is named after a node and is controlled by that node to inherit its life cycle:

kubectl tree node kind-control-plane -A
Result
NAMESPACE        NAME                                              READY  REASON        AGE
                 Node/kind-control-plane                           True   KubeletReady  48m
                 ├─CISKubeBenchReport/kind-control-plane           -                    44m
                 ├─CSINode/kind-control-plane                      -                    48m
kube-node-lease  ├─Lease/kind-control-plane                        -                    48m
kube-system      ├─Pod/etcd-kind-control-plane                     True                 48m
kube-system      ├─Pod/kube-apiserver-kind-control-plane           True                 48m
kube-system      ├─Pod/kube-controller-manager-kind-control-plane  True                 48m
kube-system      └─Pod/kube-scheduler-kind-control-plane           True                 48m

What's Next?