Ensuring your containers are secure and performant means "shifting left" and applying best practices early in the design and build stages. Anchore recommends the following practices for your container images.
Limit images to a single application
Restricting each image to a single application ensures that the surface area for vulnerabilities is minimized and makes the image useful in a wider variety of contexts.
Keep images small
Small images take up less storage space and use fewer resources when run. Small images can be built more quickly, scanned more quickly, and will have a smaller surface area for potential attacks.
Use common image layers
Layers are often shared among images, and using common layers reduces storage requirements and helps standardize image contents.
Choose small base images
If the specific application that is being built is just a single binary or another base image, building from scratch is a way to keep these image sizes down. Building distroless images is another option which results in images with your application and necessary dependencies but without any extraneous artifacts, such as a shell.
Use multistage builds
Multistage builds allow you to selectively copy artifacts from one build stage to another, discarding all unnecessary artifacts. This can result in substantially reduced image sizes. Refer to the Docker documentation for more information.
Remove unnecessary tools
All tools contain potential attack vectors. Therefore, minimizing the number of tools your image contains reduces the surface area for attacks. For example, if your application doesn’t need git to run properly, then remove it from the image. This will remove all of the vulnerabilities that your container inherited by having git in it.
Avoid running containers as root
If a malicious actor is able to gain access to your container, they can cause far more damage as root than as a less privileged user.
Specify the container user explicitly
If the container user is not specified when an image is built, the user will be inherited from the base image.This may grant the user unneeded permissions and increase the surface area for attacks..
Do not expose unnecessary ports
Certain ports, such as telnet port 23, are known vulnerability points in any system . Never expose ports unless absolutely necessary, and be sure you are aware of, and are willing to accept, any associated risks.
Pull images only from known trusted sources
Docker Hub and other public resources are home to many malicious images which are often disguised as commonly used images such as MySQL. Verify the source of your images and use checksum verification to ensure they haven’t been tampered with during the download process.
Verify registry and repository names before pulling
Typosquatting (the use of a subtle misspelling of a legitimate registry or repository to host malware) is a serious and growing problem. Carefully review the names of all registry or repositories used in your build to ensure you are only pulling from legitimate sources.
Scan completed images
Use a tool like Anchore to scan containers against regularly updated security data feeds. These feeds provide insight into what CVEs and other known vulnerabilities are present in the container image. Many times these vulnerabilities are present in dependencies that you may not be aware of, even if you know about vulnerabilities within your own code.
Always find and mitigate security findings in your images before containers are run to ensure a robust security posture.
General code security practices
Consider and apply all secure code practices when containerizing an application. Examples include proper input validation to prevent SQL injections and proper error handling and logging. More examples can be found in the OWASP Secure Coding Practices.
You can specify any registry in your FROM instructions but it is a best practice to use only trusted base images from reputable public registries such as Docker Hub, GCR, or Quay.
When pulling a base image, always use a specific tag instead of "latest". The "latest" tag may be reassigned to another image version without notice, potentially introducing security or application issues in your final image.
COPY and ADD instructions
Consider using ADD rather than COPY. ADD works similarly to COPY, except it unpacks compressed files when it copies them. Using ADD reduces image size by eliminating the need to run a tar command and the additional image layer this requires. ADD can also fetch remote content from a URL at build time, however it is best to avoid this functionality to maintain a robust security posture.
Treat RUN instructions as you would any operations executed on the command line. Any security risks that can be introduced from the command line can be introduced using RUN. Be sure of the effect of every command before using it with RUN.
RUN executes commands as the user specified in the preceding USER statement, so be sure this user is appropriate for the commands you intend to run. If the current user isn’t correct, be sure to specify the desired user before executing your commands.
ENTRYPOINT and CMD instructions
These instructions execute code in the container, so carefully consider any security or performance implications your instructions may have.