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.
- 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:
- 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 .
- 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.
- 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.
- 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.
- 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:
- First build your app with node:16.19.0-bullseye-slim or node:16.19.0-alpine3.17 image as build stage
- 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!!
2 Comments
Comments are closed.