Container Registry and Vulnerability Advisor workflow tutorial
Use this tutorial to find out about the basic functions of both IBM Cloud® Container Registry and Vulnerability Advisor.
These two services are pre-integrated and work together seamlessly in IBM Cloud, and their features provide a robust but straightforward workflow for users of containers. You can use these services to store your container images, ensure the security of your images and Kubernetes clusters, control the images that you can use to deploy to your clusters, and more.
Much of the information that is provided in this tutorial is available in greater detail in the "How To" section of the documentation. This tutorial combines all those tasks into a workflow that helps you to use IBM Cloud Container Registry and Vulnerability Advisor. To learn more about each task, click the relevant link.
Objectives
The tutorial has the following objectives.
- Understand the core features of IBM Cloud Container Registry and Vulnerability Advisor.
- Use the functions of these services to create a workflow.
Services used
This tutorial uses the following IBM Cloud services:
Before you begin
Before you begin, complete the following tasks:
- Install Git.
- Install IBM Cloud Developer Tools, a script that installs
docker
,kubectl
,helm
,ibmcloud
CLI, and required plug-ins by following the instructions in theREADME.md
file in the repository. - Create a cluster.
- Ensure that you have the correct access permissions to add and remove namespacesA collection of repositories that store images in a registry. A namespace is associated with an IBM Cloud account, which can include multiple namespaces., see Access roles for configuring IBM Cloud Container Registry.
From code to a running container
Using IBM Cloud Container Registry to store your container images is the easiest way to get an application up and running with IBM Cloud Kubernetes Service. The following steps show you how to build a container image, store it in IBM Cloud Container Registry, and create a Kubernetes deployment that uses that image.
Create a namespace
Create a namespace to store your container images in IBM Cloud Container Registry. Namespaces are created in a resource groupThe environment, and constraints, in which contained resource instances adhere to. A user can be associated with a resource group to enable collaboration.. For more information, see Planning namespaces.
-
To log in to IBM Cloud and target the
us-south
region, run the following command.ibmcloud login -r us-south [--sso]
If you have a federated ID, use
ibmcloud login -r us-south --sso
to log in. Enter your username and use the provided URL in your CLI output to retrieve your one-time passcode. If you have a federated ID, the login fails without the--sso
and succeeds with the--sso
option. -
Set
us-south
as the target region for the IBM Cloud Container Registry commands.ibmcloud cr region-set us-south
-
Create a namespace by running the following command. Choose a name for your namespace, and replace
<my_namespace>
with that name. Throughout this tutorial, replace<my_namespace>
with your chosen namespace.The namespace must be unique across all IBM Cloud accounts in the same region. Namespaces must have 4 - 30 characters and include lowercase letters, numbers, hyphens (-), and underscores (_) only. Namespaces must start and end with a letter or number.
If you want to create the namespace in a specific resource group, see Set up a namespace. If you have a problem when you try to add a namespace, see Why can't I add a namespace? for assistance.
ibmcloud cr namespace-add <my_namespace>
Build and push an image
To build a container image and push it to IBM Cloud Container Registry, you require an application and a DockerfileA text file that contains instructions to build a Docker image.. To get the application and the Dockerfile, and the other artifacts that you require, clone the GitHub repository that is associated with this tutorial. For the rest of this tutorial, ensure that you run all commands from the directory of the cloned repository.
-
To build the image, run the following command:
docker build -t us.icr.io/<my_namespace>/hello-world:1 .
Docker must be running on your computer or the
docker
commands fail. IBM Cloud Container Registry supports other clients as well as Docker. To log in by using other clients, see Accessing your namespaces interactively. -
Log your local Docker daemon into IBM Cloud Container Registry by running the
ibmcloud cr login
command:ibmcloud cr login
If you have a problem when you try to log in, see Why can't I log in to Container Registry? for assistance.
-
Push the image by running the following command:
docker push us.icr.io/<my_namespace>/hello-world:1
-
Confirm that your image uploaded successfully by running the following command:
ibmcloud cr images
You can automate access to IBM Cloud Container Registry with API keysA unique code that is passed to an API to identify the calling application or user. An API key is used to track and control how the API is being used, for example, to prevent malicious use or abuse of the API. and grant access to IBM Cloud Container Registry resources by using IAM.
Deploy a container that uses your image
Now that you have an image that is stored in IBM Cloud Container Registry, you can run a container on IBM Cloud Kubernetes Service that uses that image, see Deploying apps with the CLI.
Throughout this tutorial, replace <my_cluster>
with the name of your free Kubernetes cluster.
-
Run the following command:
ibmcloud ks cluster-config <my_cluster> --export
This command produces an
export
command that sets theKUBECONFIG
environment variable. -
Run the
export
command, which was generated by the previous command, by copying and pasting it. -
Update the following line in the
hello-world.yaml
file by replacing<my_namespace>
with your namespace:image: us.icr.io/<my_namespace>/hello-world:1
-
Run your image as a deployment and expose it by creating a service that is accessed through the IP address of the worker node:
kubectl apply -f hello-world.yaml
-
Find the port that is used on the worker node by examining your new service by running the following command:
kubectl describe service hello-world
Note the number on the
NodePort:
line. Throughout this tutorial, use the number to replace the variable<node_port>
. -
In the output of the following command, note the public IP address. Throughout this tutorial, replace the variable
<public_ip>
with the IP address:ibmcloud ks workers <my_cluster>
-
Access your service by running the following command. You can also use a web browser.
curl <public_ip>:<node_port>
If you see "Hello, world!" you're good to go.
Secure your images and clusters
When you push an image to a namespace, the image is automatically scanned by Vulnerability Advisor to find potential vulnerabilities. If vulnerabilities are found, instructions are provided to help fix the reported vulnerabilities.
To demonstrate these features, you must push an intentionally vulnerable image.
Images are continually updated and new CVEs are discovered. As a result, you might see more vulnerabilities present in your image. If so, fix those vulnerabilities by using the information that is provided by Vulnerability Advisor.
View the vulnerability report for your image
When a vulnerability is found in one of your images, a report is produced that gives you more information about the vulnerability and the steps to resolve the vulnerability.
-
Build and push a vulnerable image:
-
Build a vulnerable image by running the following command:
docker build -t us.icr.io/<my_namespace>/hello-world:2 -f Dockerfile-vulnerable .
-
Push the vulnerable image by running the following command:
docker push us.icr.io/<my_namespace>/hello-world:2
You can read the Dockerfile to better understand how this image was made vulnerable. In short, a Debian base image is used, and the
apt
package is rolled back to a version that is vulnerable toCVE-2019-3462
. -
-
List your images, and take note of the
SECURITY STATUS
column by running the following command:ibmcloud cr images --va
This column conveys the number of issues present in your image. Because the number isn't zero, this image is vulnerable.
-
Run the
ibmcloud cr vulnerability-assessment
(aliasibmcloud cr va
) command to get more information about the vulnerability:ibmcloud cr va us.icr.io/<my_namespace>/hello-world:1
Among other things, this output includes the ID of the vulnerability (if applicable), the affected package, and the steps to resolve the issue.
Enforce security in your cluster
Despite the vulnerability that is present in your image, you're still able to deploy a container to your cluster by using this image, which you might not want. By using Portieris, you can enforce security in several ways. For example, you can prevent vulnerable images from being used in deployments to your cluster.
-
The Portieris default policies are too restrictive for this tutorial because they involve image signing. Therefore, you must create custom policies. View the
security.yaml
file in the GitHub repository, and read about customizing policies to understand this file's contents. In short, this policy requires all images in your namespace to have no issues reported by Vulnerability Advisor. -
Update the following text in the
security.yaml
file, where<my_namespace>
is your namespace:repositories: - name: us.icr.io/<my_namespace>/*
-
Apply the custom policies:
kubectl apply -f security.yaml
-
To update
hello-world.yaml
so that it references your vulnerable image, change the tag from1
to2
as shown here:image: us.icr.io/<my_namespace>/hello-world:2
-
Try to patch the existing deployment by running the following command:
kubectl apply -f hello-world.yaml
You see the following error message:
Deny "us.icr.io/<my_namespace>/hello-world:2", the Vulnerability Advisor image scan assessment found issues with the container image that are not exempted. Refer to your image vulnerability report for more details by using the `ibmcloud cr va` command.
The Vulnerability Advisor verdict is subject to any exemption policies that you create. If you want to use an image that Vulnerability Advisor considers vulnerable, you can exempt one or more vulnerabilities so that Vulnerability Advisor doesn't consider them in its verdict. You can see whether an issue is exempted by looking at the
Policy Status
column in the output of theibmcloud cr va
command, and you can also list your exemptions by running theibmcloud cr exemption-list
command.
Resolve vulnerabilities in your image
Vulnerability Advisor provides steps to resolve each vulnerability that is present in an image. The steps are displayed in the How To Resolve
column in the output of the ibmcloud cr va
command. If you follow the steps,
you can resolve the issues in your image.
Because CVEs are frequently discovered and patched, this Dockerfile includes a contrived vulnerability that is introduced by rolling a package back to a known vulnerable version. Therefore, to fix the vulnerability, you can comment out the
line that rolls back apt
.
-
To prevent
apt
from being rolled back, comment out the following line inDockerfile-vulnerable
by putting a number sign (#
) at the beginning of the line as shown here:# RUN apt-get install --allow-downgrades -y apt=1.4.8
-
Build and push the image again:
-
Build the image again by running the following command:
docker build -t us.icr.io/<my_namespace>/hello-world:2 -f Dockerfile-vulnerable .
-
Push the image again by running the following command:
docker push us.icr.io/<my_namespace>/hello-world:2
-
-
Wait for the scan to complete and then run the following command to ensure that no issues are present in the image:
ibmcloud cr images --va
-
To patch the deployment, run the following command:
kubectl apply -f hello-world.yaml
-
Wait for the deployment to complete. To check whether the deployment is complete, run the following command:
kubectl rollout status deployment hello-world
This deployment succeeds, and you can access your service and see "Hello, world!" displayed.
-
Delete the deployment and the service before proceeding:
kubectl delete -f hello-world.yaml
Deploying to nondefault Kubernetes namespaces
An IBM Cloud Kubernetes Service cluster can automatically pull images from IBM Cloud Container Registry to the default
Kubernetes namespace. However, if you want to deploy to namespaces other than default
, you must
take further steps.
Kubernetes and IBM Cloud Container Registry namespaces are different. For more information about IBM Cloud Container Registry namespaces, see Registry namespace. For more information about Kubernetes namespaces, see Namespaces.
-
In your cluster, create a Kubernetes namespace called
test
:kubectl create namespace test
-
To deploy your deployment and service into this Kubernetes namespace, in the
hello-world.yaml
file change themetadata.namespace
fields for both the deployment and the service fromdefault
totest
. This snippet shows themetadata.namespace
field in context:metadata: name: hello-world namespace: test
-
Apply the configuration.
-
Apply the configuration with Portieris that is still enabled in your cluster by running the following command:
kubectl apply -f hello-world.yaml
Because Portieris is still enabled in your cluster, your deployment fails immediately, and you see the following message:
Error from server: error when creating "hello-world.yaml": admission webhook "va.hooks.securityenforcement.admission.cloud.ibm.com" denied the request: Deny "us.icr.io/<my_namespace>/hello-world:2", no valid ImagePullSecret defined for us.icr.io
This error is because Portieris determines that this deployment can't succeed because the
test
namespace is unable to pull images from your IBM Cloud Container Registry namespace. Thedefault
Kubernetes namespace in an IBM Cloud Kubernetes Service cluster comes preconfigured with image pull secrets to pull images from IBM Cloud Container Registry. However, these secrets aren't present in your new namespace. -
Apply the configuration after Portieris is removed from your cluster.
-
Apply the configuration by running the following command:
kubectl apply -f hello-world.yaml
The
kubectl apply
command completes successfully. However, when you inspect the deployment's podA group of containers that are running on a Kubernetes cluster. A pod is a runnable unit of work, which can be a either a stand-alone application or a microservice. by running thekubectl describe pod <pod_name> -n test
command, where<pod_name>
is the name of the pod, the events log indicates that the cluster isn't authorized to pull the image.You can find the pod name by running
kubectl get pod -n test
.
-
-
You must set up an image pull secret in your namespace so that you can deploy containers to that namespace. Several options are available, but this tutorial follows the steps to copy an image pull secret to the
test
namespace. Rather than copying all theicr.io
secrets, you can copy theus.icr.io
secret because your image is in that local registry. The following command copies thedefault-us-icr-io
secret to thetest
namespace, giving it the nametest-us-icr-io
:kubectl get secret default-us-icr-io -o yaml | sed 's/default/test/g' | kubectl -n test create -f -
-
Two options are available to use the image pull secret. This tutorial uses the option to refer to the image pull secret in the deployment
.yaml
file by populating thespec.imagePullSecrets
field. The following snippet shows the required lines in context; you must add the final two lines:spec: containers: - name: hello-world image: us.icr.io/<my_namespace>/hello-world:1 imagePullPolicy: Always imagePullSecrets: - name: test-us-icr-io
-
Delete your deployment and reapply the configuration:
-
Delete your deployment by running the following command:
kubectl delete -f hello-world.yaml
-
Reapply the configuration by running the following command:
kubectl apply -f hello-world.yaml
This time the command succeeds, and you can access your container by using a
curl
command or a web browser. -