|

Building Secure Docker Images – Kubernetes Security Hardening

A secure Kubernetes cluster is the prime focus of DevOps due to so many cyber threats out there. Since Kubernetes is pluggable and very configurable and flexible it also makes it more prone to security issues left unattended. A very secure Kubernetes cluster is still insecure, if the docker images we are deploying to it are vulnerable. Although Kubernetes is not limited to docker as container runtime, we will focus on docker images in this blog.

Why secure docker images?

You may be scanning your application code for security issues before building docker images. But when you create your docker image you are not just packaging your app but also packaging lot of OS level dependencies which may be vulnerable.

So when deploy to your k8s cluster, you may be deploying docker images which may have vulnerabilities which may lead to potential threats to your system and damage your security footprint.

Hence it is very important to build your docker images with security in mind.

How to secure a docker image?

There are certain best practices that one should follow file creating a secure Docker image.

  1. Use the precise docker base image rather than generic or latest. And also only used official images.
Do not use: From node
Do not use: From node:16

Use: From node:16.19.0-bullseye-slim

2. Use minimalistic image. Minimalistic images contains minimum packages and is also very small in size than full image and significantly improve security posture.

Do not use: From node:16.19.0-bullseye

Use: From node:16.19.0-bullseye-slim

3. Install only what is needed. For example, for a nodejs image only install required dependencies

Do not use: Run npm ci
Do not use: Run npm install

Use: Run npm ci --omit=dev

4. Do not run applications as root. Create a user and use that user for all tasks as below

From node:16.19.0-bullseye-slim
RUN groupadd -r myuser && useradd -r -g myuser myuser
<HERE DO WHAT YOU HAVE TO DO AS A ROOT USER LIKE INSTALLING PACKAGES ETC.>
USER myuser
COPY --chown myuser:myuser . /usr/source/app

5. Use .dockerignore to skip files to be copied into images. These files could be containing secrets like .npmrc file which may not be present in your git repo and you might be adding it while build image but if you do not add this to .dockerignore then you might end up adding .npmrc to your docker image.

6. Use multi-stage builds to keep docker image size in check and also to avoid adding build time dependencies to your docker image which are not required while runtime. Hence reducing attack surface.

Let’s security harden a Docker image

Now let’s try to security harden a docker image.

We will use the below docker image and try to improve its security.

FROM node

WORKDIR /usr/src/app

COPY package.json .
RUN npm install
COPY . .

EXPOSE 3000

CMD ["node", "index.js"]

As you can see we are not following any of the best practices and hence even though we can successfully build and run this docker image but it may be having security issues.

Tool to scan the vulnerability

How do we know that what vulnerabilities are present in our docker image?

For this purpose we need to use a tool which we can use to scan the images.

We will use the tool called Trivy

Trivy is a open-source tool and hence free to use.

NOTE: For production level projects you should look into commercial solutions as well. I would leave it to you google and try their trial versions. For demo purpose we will use Trivy. Do not reject Trivy though it is a great tool and you can use it as well.

Use installation guide from Trivy to install: https://aquasecurity.github.io/trivy/v0.35/getting-started/installation/

Let’s first check out the code from git:

git clone https://github.com/ld-singh/demo-app-hello-world.git

## cd to checked out code
cd demo-app-hello-world

## rename Dockerfile to Dockerfile_secure
mv Dockerfile Dockerfile_secure

## rename Dockerfile_insecure to Dockerfile
mv Dockerfile_insecure Dockerfile
Build insecure image and test with Trivy

Build the image as follows (Please note I am using Dockerfile_insecure):

docker build -t demo-docker-image-security:v1  .

Let’s check the built image

 docker images
REPOSITORY                   TAG                     IMAGE ID       CREATED          SIZE
demo-docker-image-security   v1                      b4ce685094b5   46 seconds ago   1GB

It is a 1 GB docker image.

Let’s run trivy to scan dockerfile

trivy image --security-checks config demo-docker-image-security:v1

The following output is produced

022-12-27T06:22:58.576Z        INFO    Misconfiguration scanning is enabled

2022-12-27T06:23:34.076Z        INFO    Detected config files: 1

usr/src/app/Dockerfile (dockerfile)

Tests: 22 (SUCCESSES: 20, FAILURES: 2, EXCEPTIONS: 0)
Failures: 2 (UNKNOWN: 0, LOW: 0, MEDIUM: 1, HIGH: 1, CRITICAL: 0)

MEDIUM: Specify a tag in the 'FROM' statement for image 'node'
═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
When using a 'FROM' statement you should use a specific tag to avoid uncontrolled behavior when the image is updated.

See https://avd.aquasec.com/misconfig/ds001
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 usr/src/app/Dockerfile:1
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   1 [ FROM node
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


HIGH: Specify at least 1 USER command in Dockerfile with non-root user as argument
═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Running containers with 'root' user can lead to a container escape situation. It is a best practice to run containers as non-root users, which can be done by adding a 'USER' statement to the Dockerfile.

See https://avd.aquasec.com/misconfig/ds002
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

As you can see it is asking us to use a precise base image and also asking us to use a USER for running applications.

Let’s run the trivy vulnerability scanner now

trivy image --security-checks vuln demo-docker-image-security:v1

It will provide a huge output. I am pasting here only summary

2022-12-27T06:26:13.595Z        INFO    Need to update DB
2022-12-27T06:26:13.595Z        INFO    DB Repository: ghcr.io/aquasecurity/trivy-db
2022-12-27T06:26:13.595Z        INFO    Downloading DB...
35.87 MiB / 35.87 MiB [------------------------------------------------------------------------------------------------------------------] 100.00% 21.50 MiB p/s 1.9s
2022-12-27T06:26:16.640Z        INFO    Vulnerability scanning is enabled
2022-12-27T06:26:57.389Z        INFO    Detected OS: debian
2022-12-27T06:26:57.389Z        INFO    Detecting Debian vulnerabilities...
2022-12-27T06:26:57.639Z        INFO    Number of language-specific files: 1
2022-12-27T06:26:57.639Z        INFO    Detecting node-pkg vulnerabilities...
demo-docker-image-security:v1 (debian 11.6)

Total: 1061 (UNKNOWN: 4, LOW: 574, MEDIUM: 266, HIGH: 200, CRITICAL: 17)
.
.

As you can see that we have so many vulnerabilities.

NOTE: You can run both config and vuln together as well

trivy image --security-checks config, vuln demo-docker-image-security:v1
Change the From base image

Let’s fix the base image and see how many issues it fixes. Modify FROM in file to following

FROM node:16.19.0-bullseye-slim

Now save the file and rebuild the image

docker build -t demo-docker-image-security:v2  .

Let’s check the image created

$ docker images
REPOSITORY                   TAG                     IMAGE ID       CREATED         SIZE
demo-docker-image-security   v2                      bfd95a3d8c6d   19 hours ago    198MB
demo-docker-image-security   v1                      8d9d871fa01e   19 hours ago    1GB

As you can see the size of image has significantly reduced. Generally this should be enough for any application to run as you do not need full OS to run your app. If there is any additional OS level package needed you can simply install it in Dockerfile.

Now small size is not just faster to install and consumes less resources, it also reduces attack surface significantly and image produced is more secure than v1 version.

Let’s re-run trivy commands.

The below command to check the Dockerfile.

trivy image --security-checks config demo-docker-image-security:v2

The below is output

2022-12-28T01:30:46.886Z        INFO    Misconfiguration scanning is enabled
2022-12-28T01:30:54.768Z        INFO    Detected config files: 1

usr/src/app/Dockerfile (dockerfile)

Tests: 22 (SUCCESSES: 21, FAILURES: 1, EXCEPTIONS: 0)
Failures: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0)

HIGH: Specify at least 1 USER command in Dockerfile with non-root user as argument
═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Running containers with 'root' user can lead to a container escape situation. It is a best practice to run containers as non-root users, which can be done by adding a 'USER' statement to the Dockerfile.

See https://avd.aquasec.com/misconfig/ds002
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Well alert for base image is gone!! That’s great we are making progress.

Let’s check the vulnerability as well.

trivy image --security-checks vuln demo-docker-image-security:v2

The output is as follows:

2022-12-28T01:32:46.462Z        INFO    Need to update DB
2022-12-28T01:32:46.462Z        INFO    DB Repository: ghcr.io/aquasecurity/trivy-db
2022-12-28T01:32:46.462Z        INFO    Downloading DB...
35.89 MiB / 35.89 MiB [------------------------------------------------------------------------------------------------------------------] 100.00% 24.93 MiB p/s 1.6s
2022-12-28T01:32:49.257Z        INFO    Vulnerability scanning is enabled
2022-12-28T01:32:58.252Z        INFO    Detected OS: debian
2022-12-28T01:32:58.252Z        INFO    Detecting Debian vulnerabilities...
2022-12-28T01:32:58.278Z        INFO    Number of language-specific files: 1
2022-12-28T01:32:58.278Z        INFO    Detecting node-pkg vulnerabilities...

demo-docker-image-security:v2 (debian 11.6)

Total: 78 (UNKNOWN: 0, LOW: 61, MEDIUM: 6, HIGH: 10, CRITICAL: 1)

As we can see that vulnerabilities are reduced significantly. So like we read above, smaller minimalistic base images reduce attack surface and improcess security of images.

How to remove the vulnerabilities from images?

Now we will try to remove rest of the vulnerabilities. The best way to do this to keep the installed packages upto date. So we will modify the Dockerfile to add step for updating installed packages.

FROM node:16.19.0-bullseye-slim

RUN apt-get update && apt-get install -y

WORKDIR /usr/src/app

COPY package.json .
RUN npm install
COPY . .

EXPOSE 3000

CMD ["node", "index.js"]

Let’s rebuild image and check if we still have any vulnerabilities.

docker build -t demo-docker-image-security:v3  .

The docker image gets created as follows

$ docker images
REPOSITORY                   TAG                     IMAGE ID       CREATED         SIZE
demo-docker-image-security   v3                      2ba6c26f3930   4 seconds ago   216MB
demo-docker-image-security   v2                      bfd95a3d8c6d   21 hours ago    198MB
demo-docker-image-security   v1                      8d9d871fa01e   21 hours ago    1GB

There is slight increase in size of image but still it is a quite small image.

Now let’s check vuln again. (We are not running Dockerfile scan as we have not used USER)

$ trivy image --security-checks vuln demo-docker-image-security:v3
2022-12-28T03:32:19.048Z        INFO    Vulnerability scanning is enabled
2022-12-28T03:32:25.429Z        INFO    Detected OS: debian
2022-12-28T03:32:25.429Z        INFO    Detecting Debian vulnerabilities...
2022-12-28T03:32:25.469Z        INFO    Number of language-specific files: 1
2022-12-28T03:32:25.470Z        INFO    Detecting node-pkg vulnerabilities...

demo-docker-image-security:v3 (debian 11.6)

Total: 78 (UNKNOWN: 0, LOW: 61, MEDIUM: 6, HIGH: 10, CRITICAL: 1)

As you can see it did not help. The reason is that base image used by node official image here is not updated. Also since this bullseye (Code name for Debian 11) slim is just trimmed down version of full debian based image, so it is likely to have same issues as full image if packages in question are core and common in both.

We have following ways to fix this:

  1. Try using more stable version of base image. Sometimes it is higher node version of same OS. But in our case all node version bullseye (Code name for Debian 11) based images have same fixes. This means nodejs images have not upgraded to latest point releases of Debian 11 mentioned in url https://www.debian.org/releases/stable/errata .
  2. The second way is to not use node image and use Debian 11.6 as base image and then install node on it. This can be your way of fixing issues as like here node has not upgraded the OS image to latest security fixed releases. But this will again probably end up in same scenario as it might come with additional packages which are vulnerable. So not really a solution.
  3. The third way is to use Alpine Image which is likely to have fewer or no security issues. This can be a good solution but Alpine comes with own issues while installing dependencies for your applications or node or python if you application is based on python.
  4. The fourth solution is to use super minimal images called distroless (provided by Google) or micro images (provided by RedHat) as base images. Distorless provided by Google are based on Debian and they provide node images as well. This is the most suitable option among all.
  5. Specifically targeting at least HIGH and CRITICAL vulnerabilities few of which are as follows. (This is output of trivy and each finding comes with a CVE url about the security issue where you can find affected version and then upgrade them to fixed versions). Yes this is tedious job but who said security fixes are easy. You will have to use apt-get install in this case as we have debian based image to install fixed versions or if we do not need them uninstall them. This can get really hard as you find more security issues.
─────────────────┼──────────────────┼──────────┼───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ libdb5.3         │ CVE-2019-8457    │ CRITICAL │ 5.3.28+dfsg1-0.8  │               │ sqlite: heap out-of-bound read in function rtreenode()       │
│                  │                  │          │                   │               │ https://avd.aquasec.com/nvd/cve-2019-8457                    │
├──────────────────┼──────────────────┼──────────┼───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
                 │                  │          │                   │               │ https://avd.aquasec.com/nvd/cve-2019-19882                   │
├──────────────────┼──────────────────┼──────────┼───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ perl-base        │ CVE-2020-16156   │ HIGH     │ 5.32.1-4+deb11u2  │               │ perl-CPAN: Bypass of verification of signatures in CHECKSUMS │
│                  │                  │          │                   │               │ files                                                        │
│                  │                  │          │                   │               │ https://avd.aquasec.com/nvd/cve-2020-16156                   │
│                  ├──────────────────┼──────────┤                   ├───────────────┼──────────────────────────────────────────────────────────────┤
─────────────────┼──────────────────┼──────────┤                   ├───────────────┼──────────────────────────────────────────────────────────────┤
│ ncurses-bin      │ CVE-2022-29458   │ HIGH     │                   │               │ ncurses: segfaulting OOB read                                │
│                  │                  │          │                   │               │ https://avd.aquasec.com/nvd/cve-2022-29458                   │
│                  ├──────────────────┼──────────┤                   ├───────────────┼──────────────────────────────────────────────────────────────┤

As we can see option 3 and 4 seems to be the best solutions to fix vulnerabilities. There is definitely more work for dependencies as in distroless or micro images also you need to figure out what is missing and add.

Recreate with Alpine base image

Lets try with Alpine image. Update your Dockerfile with below:

FROM node:16.19.0-alpine3.17

# update alpine packages using apk
RUN apk update && apk add --upgrade apk-tools && apk upgrade --available

WORKDIR /usr/src/app

COPY package.json .
RUN npm install
COPY . .

EXPOSE 3000

CMD ["node", "index.js"]

Now build the image

docker build -t demo-docker-image-security:v4  .

Let’s check docker image created

$ docker images
REPOSITORY                   TAG                     IMAGE ID       CREATED          SIZE
demo-docker-image-security   v4                      c1967fd3b466   32 seconds ago   127MB
demo-docker-image-security   v3                      2484d288664d   19 hours ago     216MB
demo-docker-image-security   v2                      bfd95a3d8c6d   42 hours ago     198MB
demo-docker-image-security   v1                      8d9d871fa01e   42 hours ago     1GB

We can see image is built and is smaller than all others at size 127MB.

Lets run trivy

trivy image --security-checks vuln demo-docker-image-security:v4

Yes!! Trivy is happy finally!!

$ trivy image --security-checks vuln demo-docker-image-security:v4
2022-12-29T00:06:59.966Z        INFO    Vulnerability scanning is enabled
2022-12-29T00:07:02.401Z        INFO    Detected OS: alpine
2022-12-29T00:07:02.401Z        INFO    This OS version is not on the EOL list: alpine 3.17
2022-12-29T00:07:02.401Z        INFO    Detecting Alpine vulnerabilities...
2022-12-29T00:07:02.408Z        INFO    Number of language-specific files: 1
2022-12-29T00:07:02.408Z        INFO    Detecting node-pkg vulnerabilities...

demo-docker-image-security:v4 (alpine 3.17.0)

Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)

We have a security issues free image now!!

Let’s try to run it as well to see if it works

## Run as daemon and expose port 3000
$ docker run -d -p 3000:3000 --name apline-image-app demo-docker-image-security:v4
1d4350449f47601f4813220e5b721d93d0301168261d3a15390e0f8de5c52660

## check if it is running
$ docker ps
CONTAINER ID   IMAGE                           COMMAND                  CREATED         STATUS         PORTS                                       NAMES
1d4350449f47   demo-docker-image-security:v4   "docker-entrypoint.s…"   3 seconds ago   Up 2 seconds   0.0.0.0:3000->3000/tcp, :::3000->3000/tcp   apline-image-app

## Connect to app

$ curl http://localhost:3000
Hello, World!

As we can see we have the app running. So Alpine image works for us.

Alpine is a really good solution but main disadvantage is that managing backward compatibility is really hard and is not compatible with some advanced libraries in a native way. So it can be a little hard to install some packages or update them to other versions. So if you do not have any such issues please feel free to use Alpine

Stop the container

$ docker stop 1d4350449f47
Recreate with debian based distroless Image

Let’s try to recreate image with debian based distroless Image provided by Google: GitHub – GoogleContainerTools/distroless: 🥑 Language focused docker images, minus the operating system.

Now the distroless images works on Idea of multi stage Dockerfile. You need to do below:

  1. First build your app with node:16.19.0-bullseye-slim or node:16.19.0-alpine3.17 image as build stage
  2. Then insert built app in distroless image.

Update the Dockerfile as follows

#  stage 1
FROM node:16.19.0-bullseye-slim AS base

WORKDIR /base

COPY package.json .

RUN npm install

# stage 2

FROM gcr.io/distroless/nodejs16-debian11

WORKDIR /usr/src/app

COPY . .

COPY --from=base /base/node_modules ./node_modules

EXPOSE 3000

# removed node from cmd as it is not needed with distroless image
CMD ["index.js"]

Now let’s build docker image again

$ docker build -t demo-docker-image-security:v5  .

Now check the docker image

$ docker images
REPOSITORY                            TAG                     IMAGE ID       CREATED              SIZE
demo-docker-image-security            v5                      0eef8dba7a77   44 seconds ago       113MB
demo-docker-image-security   v4                      c1967fd3b466   32 seconds ago   127MB
demo-docker-image-security   v3                      2484d288664d   19 hours ago     216MB
demo-docker-image-security   v2                      bfd95a3d8c6d   42 hours ago     198MB
demo-docker-image-security   v1                      8d9d871fa01e   42 hours ago     1GB

Well it is as expected smallest of all with size 113MB. Let’s check trivy

$ trivy image --security-checks vuln demo-docker-image-security:v5
2022-12-29T00:42:40.290Z        INFO    Need to update DB
2022-12-29T00:42:40.290Z        INFO    DB Repository: ghcr.io/aquasecurity/trivy-db
2022-12-29T00:42:40.290Z        INFO    Downloading DB...
35.89 MiB / 35.89 MiB [-------------------------------------------------------------------------------------------------------------------] 100.00% 19.40 MiB p/s 2.1s
2022-12-29T00:42:43.515Z        INFO    Vulnerability scanning is enabled
2022-12-29T00:42:48.487Z        INFO    Detected OS: debian
2022-12-29T00:42:48.487Z        INFO    Detecting Debian vulnerabilities...
2022-12-29T00:42:48.495Z        INFO    Number of language-specific files: 1
2022-12-29T00:42:48.495Z        INFO    Detecting node-pkg vulnerabilities...

demo-docker-image-security:v5 (debian 11.6)

Total: 13 (UNKNOWN: 0, LOW: 11, MEDIUM: 2, HIGH: 0, CRITICAL: 0)

We do have some vulnerabilities here and we could not remove them like we did with Alpine Images as distroless Images do not have any shell or any package manager or utils like standard OS and hence very hard to hack as well. Hence even though we have few low and medium level issues these are still pretty secure.

So you can use Alpine or distroless. Both have some trade-offs.

Let’s check if app runs with this image

## start container
$ docker run -d -p 3000:3000 --name distroless-based-image demo-docker-image-security:v5
5c61b204b8786eee89bf8cf7f9ab5942dfb17726602ebc574fd71a40f46d5c6e

## check container

$ docker ps
CONTAINER ID   IMAGE                           COMMAND                  CREATED         STATUS         PORTS                                       NAMES
5c61b204b878   demo-docker-image-security:v5   "/nodejs/bin/node in…"   3 seconds ago   Up 2 seconds   0.0.0.0:3000->3000/tcp, :::3000->3000/tcp   distroless-based-image

## check app

$ curl http://localhost:3000
Hello, World!

## stop container
$ docker stop 5c61b204b878
Adding USER to Dockerfile

We already have a multistage build, we can now try adding USER to Dockerfile to avoid using root to run application.

We will update Dockerfile as follows and use non root distroless image and then use user nonroot

 stage 1
FROM node:16.19.0-bullseye-slim AS base

WORKDIR /base

COPY package.json .

RUN npm install

# stage 2

FROM gcr.io/distroless/nodejs16-debian11:nonroot

USER nonroot

WORKDIR /usr/src/app

COPY --chown=nonroot:nonroot . .

COPY --chown=nonroot:nonroot --from=base /base/node_modules ./node_modules

EXPOSE 3000

CMD ["index.js"]

Let’s build image

docker build -t demo-docker-image-security:v6  .

Let’s check image

$ docker images
REPOSITORY                            TAG                     IMAGE ID       CREATED              SIZE
demo-docker-image-security            v6                      9c0cf906a8b0   About a minute ago   113MB

We have same size image as before which is expected.

Now let’s check our app

## Run docker container
$ docker run -d -p 3000:3000 --name distorless-nonroot-app demo-docker-image-security:v6
1411d83cec3a25458e0aa0f8c164caf6fd6c62c9cf69f9495a28d889dda49dab

## Check container
$ docker ps
CONTAINER ID   IMAGE                           COMMAND                  CREATED         STATUS        PORTS                                       NAMES
1411d83cec3a   demo-docker-image-security:v6   "/nodejs/bin/node in…"   2 seconds ago   Up 1 second   0.0.0.0:3000->3000/tcp, :::3000->3000/tcp   distorless-nonroot-app

## Access app using curl

$ curl http://localhost:3000
Hello, World!

## Stop docker container
$ docker stop 1411d83cec3a
1411d83cec3a

Great we have a secure image with us!!

Install only production dependencies

We left out one more best practice for the last. Let’s do the last change for our nodejs app to install only production based dependencies

Update Dockerfile as follows:

#  stage 1
FROM node:16.19.0-bullseye-slim AS base

WORKDIR /base
# Add package-lock.json
COPY package.json .
COPY package-lock.json .

# USe npm ci to use package-lock.json and --omit=dev to install production only dependencies
RUN npm ci --omit=dev

# stage 2

FROM gcr.io/distroless/nodejs16-debian11:nonroot

USER nonroot

WORKDIR /usr/src/app

COPY --chown=nonroot:nonroot . .

COPY --chown=nonroot:nonroot --from=base /base/node_modules ./node_modules

EXPOSE 3000

CMD ["index.js"]

Now let’s build image and check it

$ docker build -t demo-docker-image-security:v7  .

## check docker image

$ $ docker images
REPOSITORY                            TAG                     IMAGE ID       CREATED              SIZE
demo-docker-image-security            v7                      f23cb6483047   About a minute ago   113MB

Now let’s check Trivy output to see if our Dockerfile issues are fixed

$ trivy image --security-checks config demo-docker-image-security:v7
2022-12-29T02:27:58.210Z        INFO    Misconfiguration scanning is enabled
2022-12-29T02:28:00.337Z        INFO    Detected config files: 1

Great!! No issues!!

Let’s run this to see if it works

## Run docker container
$ docker run -d -p 3000:3000 --name distroless-nonroot-prodonly-app demo-docker-image-security:v7
82c0bdeee01576ba792fce070dec61a356a3a811dae8cd2baf5c08bf9b2892f6

## Check docker container
$ docker ps
CONTAINER ID   IMAGE                           COMMAND                  CREATED         STATUS         PORTS                                       NAMES
82c0bdeee015   demo-docker-image-security:v7   "/nodejs/bin/node in…"   4 seconds ago   Up 3 seconds   0.0.0.0:3000->3000/tcp, :::3000->3000/tcp   distroless-nonroot-prodonly-app

## Access app using curl

$ curl http://localhost:3000
Hello, World!


## Stop docker container
$ docker stop 82c0bdeee015

Great!! We are now able to create a very secure docker image following best practices.

Conclusion

I hope you have learned a lot about docker image security in this blog!! This is the first step towards having a secure Kubernetes cluster and often forgotten in many organisations. I hope this will makes it easy to adapt it now!! Thanks!!

Similar Posts