IBM Cloud Docs
Container Registry and Vulnerability Advisor workflow tutorial

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:

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.

  1. 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.

  2. Set us-south as the target region for the IBM Cloud Container Registry commands.

    ibmcloud cr region-set us-south
    
  3. 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.

  1. 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.

  2. 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.

  3. Push the image by running the following command:

    docker push us.icr.io/<my_namespace>/hello-world:1
    
  4. 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.

  1. Run the following command:

    ibmcloud ks cluster-config <my_cluster> --export
    

    This command produces an export command that sets the KUBECONFIG environment variable.

  2. Run the export command, which was generated by the previous command, by copying and pasting it.

  3. 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
    
  4. 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
    
  5. 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>.

  6. 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>
    
  7. 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.

  1. Build and push a vulnerable image:

    1. Build a vulnerable image by running the following command:

      docker build -t us.icr.io/<my_namespace>/hello-world:2 -f Dockerfile-vulnerable .
      
    2. 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 to CVE-2019-3462.

  2. 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.

  3. Run the ibmcloud cr vulnerability-assessment (alias ibmcloud 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.

  1. Install Portieris.

  2. 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.

  3. Update the following text in the security.yaml file, where <my_namespace> is your namespace:

    repositories:
      - name: us.icr.io/<my_namespace>/*
    
  4. Apply the custom policies:

    kubectl apply -f security.yaml
    
  5. To update hello-world.yaml so that it references your vulnerable image, change the tag from 1 to 2 as shown here:

    image: us.icr.io/<my_namespace>/hello-world:2
    
  6. 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 the ibmcloud cr va command, and you can also list your exemptions by running the ibmcloud 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.

  1. To prevent apt from being rolled back, comment out the following line in Dockerfile-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
    
  2. Build and push the image again:

    1. Build the image again by running the following command:

      docker build -t us.icr.io/<my_namespace>/hello-world:2 -f Dockerfile-vulnerable .
      
    2. Push the image again by running the following command:

      docker push us.icr.io/<my_namespace>/hello-world:2
      
  3. 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
    
  4. To patch the deployment, run the following command:

    kubectl apply -f hello-world.yaml
    
  5. 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.

  6. 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.

  1. In your cluster, create a Kubernetes namespace called test:

    kubectl create namespace test
    
  2. To deploy your deployment and service into this Kubernetes namespace, in the hello-world.yaml file change the metadata.namespace fields for both the deployment and the service from default to test. This snippet shows the metadata.namespace field in context:

    metadata:
        name: hello-world
        namespace: test
    
  3. Apply the configuration.

    1. 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. The default 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.

    2. Apply the configuration after Portieris is removed from your cluster.

      1. Remove Portieris.

      2. 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 the kubectl 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.

  4. 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 the icr.io secrets, you can copy the us.icr.io secret because your image is in that local registry. The following command copies the default-us-icr-io secret to the test namespace, giving it the name test-us-icr-io:

    kubectl get secret default-us-icr-io -o yaml | sed 's/default/test/g' | kubectl -n test create -f -
    
  5. 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 the spec.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
    
  6. Delete your deployment and reapply the configuration:

    1. Delete your deployment by running the following command:

      kubectl delete -f hello-world.yaml
      
    2. 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.