copyright | lastupdated | keywords | subcollection | content-type | services | account-plan | completion-time | ||
---|---|---|---|---|---|---|---|---|---|
|
2024-10-26 |
Vulnerability Advisor, tutorial, workflow, image, vulnerabilities, registry, services, container, namespace, vulnerability, cluster, vulnerable image |
Registry |
tutorial |
containers |
lite |
2h |
{{site.data.keyword.attribute-definition-list}}
{: #registry_tutorial_workflow} {: toc-content-type="tutorial"} {: toc-services="containers"} {: toc-completion-time="2h"}
Use this tutorial to find out about the basic functions of both {{site.data.keyword.registrylong}} and Vulnerability Advisor. {: shortdesc}
These two services are pre-integrated and work together seamlessly in {{site.data.keyword.cloud_notm}}, 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 {{site.data.keyword.registrylong_notm}} and Vulnerability Advisor. To learn more about each task, click the relevant link.
{: #registry_tutorial_workflow_objectives}
The tutorial has the following objectives.
- Understand the core features of {{site.data.keyword.registrylong_notm}} and Vulnerability Advisor.
- Use the functions of these services to create a workflow.
{: #registry_tutorial_workflow_services}
This tutorial uses the following {{site.data.keyword.cloud_notm}} services:
{: #registry_tutorial_workflow_prereqs}
Before you begin, complete the following tasks:
- Install Git{: external}.
- Install {{site.data.keyword.cloud_notm}} Developer Tools{: external}, 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 namespaces{: term}, see Access roles for configuring {{site.data.keyword.registrylong_notm}}.
{: #registry_tutorial_workflow_code_run} {: step}
Using {{site.data.keyword.registrylong_notm}}{: external} to store your container images is the easiest way to get an application up and running with {{site.data.keyword.containerlong_notm}}. The following steps show you how to build a container image, store it in {{site.data.keyword.registrylong_notm}}, and create a Kubernetes deployment that uses that image.
{: #registry_tutorial_workflow_create_namespace}
Create a namespace to store your container images in {{site.data.keyword.registrylong_notm}}. Namespaces are created in a resource group{: term}. For more information, see Planning namespaces.
-
To log in to {{site.data.keyword.cloud_notm}} and target the
us-south
region, run the following command.ibmcloud login -r us-south [--sso]
{: pre}
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. {: requirement} -
Set
us-south
as the target region for the {{site.data.keyword.registrylong_notm}} commands.ibmcloud cr region-set us-south
{: pre}
-
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 {{site.data.keyword.cloud_notm}} 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. {: requirement}
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. {: tip}
ibmcloud cr namespace-add <my_namespace>
{: pre}
{: #registry_tutorial_workflow_build_push_image}
To build a container image and push it to {{site.data.keyword.registrylong_notm}}, you require an application and a Dockerfile{: term}. To get the application and the Dockerfile, and the other artifacts that you require, clone the GitHub repository{: external} 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 .
{: pre}
Docker must be running on your computer or the
docker
commands fail. {{site.data.keyword.registrylong_notm}} supports other clients as well as Docker. To log in by using other clients, see Accessing your namespaces interactively. {: tip} -
Log your local Docker daemon into {{site.data.keyword.registrylong_notm}} by running the
ibmcloud cr login
command:ibmcloud cr login
{: pre}
If you have a problem when you try to log in, see Why can't I log in to {{site.data.keyword.registryshort_notm}}? for assistance. {: tip}
-
Push the image by running the following command:
docker push us.icr.io/<my_namespace>/hello-world:1
{: pre}
-
Confirm that your image uploaded successfully by running the following command:
ibmcloud cr images
{: pre}
You can automate access to {{site.data.keyword.registrylong_notm}} with API keys{: term} and grant access to {{site.data.keyword.registrylong_notm}} resources by using IAM. {: tip}
{: #registry_tutorial_workflow_deploy}
Now that you have an image that is stored in {{site.data.keyword.registrylong_notm}}, you can run a container on {{site.data.keyword.containerlong_notm}} 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
{: pre}
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
{: pre}
-
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
{: pre}
-
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
{: pre}
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>
{: pre}
-
Access your service by running the following command. You can also use a web browser.
curl <public_ip>:<node_port>
{: pre}
If you see "Hello, world!" you're good to go.
{: #registry_tutorial_workflow_secure} {: step}
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. {: note}
{: #registry_tutorial_workflow_vulnerability_report}
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 .
{: pre}
-
Push the vulnerable image by running the following command:
docker push us.icr.io/<my_namespace>/hello-world:2
{: pre}
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
{: pre}
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
{: pre}
Among other things, this output includes the ID of the vulnerability (if applicable), the affected package, and the steps to resolve the issue.
{: #registry_tutorial_workflow_enforce_security}
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{: external}, 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>/*
{: pre}
-
Apply the custom policies:
kubectl apply -f security.yaml
{: pre}
-
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
{: pre}
-
Try to patch the existing deployment by running the following command:
kubectl apply -f hello-world.yaml
{: pre}
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.
{: screen}
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. {: note}
{: #registry_tutorial_workflow_resolve_vulnerabilities}
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
{: pre}
-
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 .
{: pre}
-
Push the image again by running the following command:
docker push us.icr.io/<my_namespace>/hello-world:2
{: pre}
-
-
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
{: pre}
-
To patch the deployment, run the following command:
kubectl apply -f hello-world.yaml
{: pre}
-
Wait for the deployment to complete. To check whether the deployment is complete, run the following command:
kubectl rollout status deployment hello-world
{: pre}
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
{: pre}
{: #registry_tutorial_workflow_deploy_nondefault_namespaces} {: step}
An {{site.data.keyword.containerlong_notm}} cluster can automatically pull images from {{site.data.keyword.registrylong_notm}} to the default
Kubernetes namespace. However, if you want to deploy to namespaces other than default
, you must take further steps.
Kubernetes and {{site.data.keyword.registrylong_notm}} namespaces are different. For more information about {{site.data.keyword.registrylong_notm}} namespaces, see Registry namespace. For more information about Kubernetes namespaces, see Namespaces{: external}. {: tip}
-
In your cluster, create a Kubernetes namespace called
test
:kubectl create namespace test
{: pre}
-
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
{: codeblock}
-
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
{: pre}
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
{: screen}
This error is because Portieris determines that this deployment can't succeed because the
test
namespace is unable to pull images from your {{site.data.keyword.registrylong_notm}} namespace. Thedefault
Kubernetes namespace in an {{site.data.keyword.containerlong_notm}} cluster comes preconfigured with image pull secrets to pull images from {{site.data.keyword.registrylong_notm}}. 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
{: pre}
The
kubectl apply
command completes successfully. However, when you inspect the deployment's pod{: term} 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
. {: tip}
-
-
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 -
{: pre}
-
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
{: codeblock}
-
Delete your deployment and reapply the configuration:
-
Delete your deployment by running the following command:
kubectl delete -f hello-world.yaml
{: pre}
-
Reapply the configuration by running the following command:
kubectl apply -f hello-world.yaml
{: pre}
This time the command succeeds, and you can access your container by using a
curl
command or a web browser. -