16:45 to 18:00 Attendee registration link: https://community.cncf.io/e/mryy58/ Please note that the content must be live coding/demo - no presentations with slides and no product pitches are allowed. # Building, Analyzing, Optimizing, and Securing Containerized Apps Built from the popular "Containers 101" Twitch stream on [https://twitch.tv/SlimDevOps](https://github.com/slimdevops/slim-containers), this workshop and tutorial leverages open source tools, free SaaS platforms, and the command line to show developers how to inspect their containers from the inside out, looking for optimizations, ways to slim and secure them, and final checks to do before sending a container to production. * What are the most important things to consider when containerizing an app? * What's the difference between a production-ready container and one used for development? * What are some open-source tools I should know about when doing container development? # Introduction As highlighted in the recent CNCF [Software Supply Chain Best Practices](https://github.com/cncf/tag-security/blob/main/supply-chain-security/supply-chain-security-paper/sscsp.md) whitepaper tools like DockerSlim can be used to limit the number of files in a container image, thus limiting the attack surface. While our talk today applies to any OCI-compliant container paradigm, we'll be focusing on Docker images, as they are the most familiar and most prevalent. We're going to cover: * Dockerfile best practice * Using `docker-slim` to analyse the container layer construction * Security scan container images * Generate an SBOM for container images * Using `docker-slim` to minify a container and analyse what changed * Exploring and diffing container images ## Dockerfile best practice Review the Dockerfile and highlight the good practice. ```Dockerfile #References: # - https://dev.to/wimpress/creating-production-ready-containers-the-basics-3k6f # - https://pythonspeed.com/articles/smaller-docker-images/ FROM python:3.10.0-slim WORKDIR /app COPY --chown=www-data:www-data app/app.py app/requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt && \ apt-get -y autoremove && \ apt-get -y clean && \ rm -rf /var/lib/apt/lists/* USER www-data EXPOSE 8008 ENTRYPOINT ["python","app.py"] ``` * 🛑 **TALKING POINT**: Not using Alpine, why is that? ### Dockerfile linter Let's see how our `Dockerfile` checks out with `docker-slim lint` ```bash docker-slim lint --target Dockerfile ``` Now take a look at `slim.report.json` and we see things are good shape although we have one finding: *Missing .dockerignore* ### Build the Docker image * Build: `docker build -t slim-demo:prod-fat .` * See: `docker images` * Run: `docker run -it --rm -p 8008:8008 slim-demo:prod-fat` Add some dev tools (`git` and `nano`) to the `Dockerfile` and rebuild. * Build: `docker build -f Dockerfile.dev -t slim-demo:dev-fat .` * See: `docker images` * Run: `docker run -it --rm -p 8008:8008 slim-demo:dev-fat` ## Security scanning containers Container images should be securty scanned and analysed to generate a software bill of materials (SBOM) prior to being slimmed, and that metadata stored and published. Scanning tools effectiveness can be reduced as a result of the slimming process. ### docker scan ```bash docker scan slim-demo:prod-fat ``` ``` Package manager: deb Project name: docker-image|wimpress/slim-demo Docker image: wimpress/slim-demo:prod-fat Platform: linux/amd64 Base image: python:3.10.0-slim-bullseye Licenses: enabled Tested 106 dependencies for known issues, found 39 issues. According to our scan, you are currently using the most secure version of the selected base image ``` ### grype **[`gripe`](https://github.com/anchore/grype) is a vulnerability scanner for container images and filesystems**. ```bash grype slim-demo:prod-fat grype slim-demo:prod-dev ``` This is just the summary information ``` ✔ Vulnerability DB [updated] ✔ Loaded image ✔ Parsed image ✔ Cataloged packages [114 packages] ✔ Scanned image [67 vulnerabilities] ``` * **Go and find**: `libgnutls30 3.7.1-5 CVE-2011-3389 Medium` What about that dev container? ```bash grype slim-demo:dev-fat ``` ``` ✔ Cataloged packages [185 packages] ✔ Scanned image [120 vulnerabilities] ``` ### trivy **[`trivy`](https://github.com/aquasecurity/trivy) is a simple and comprehensive scanner for vulnerabilities in container images**. ```bash trivy slim-demo:prod-fat ``` Just the headlines: ``` slim-demo:prod-fat (debian 11.1) ================================ Total: 65 (UNKNOWN: 0, LOW: 59, MEDIUM: 2, HIGH: 2, CRITICAL: 2) ``` And the dev container? ```bash trivy slim-demo:dev-fat ``` ``` slim-demo:dev-fat (debian 11.1) =============================== Total: 118 (UNKNOWN: 0, LOW: 92, MEDIUM: 15, HIGH: 4, CRITICAL: 7) ``` ## Software Bill of Materials ### syft **[`syft`](https://github.com/anchore/syft) CLI tool and library for generating a Software Bill of Materials from container images and filesystems**. ```bash syft slim-demo:prod-fat ``` An example of the output. ``` ✔ Loaded image ✔ Parsed image ✔ Cataloged packages [114 packages] NAME VERSION TYPE Flask 2.0.2 python Jinja2 3.0.3 python MarkupSafe 2.0.1 python Werkzeug 2.0.2 python adduser 3.118 deb apt 2.2.4 deb base-files 11.1+deb11u1 deb . . . zlib1g 1:1.2.11.dfsg-2 deb ``` And the dev container? ```bash syft slim-demo:dev-fat ``` ``` ✔ Loaded image ✔ Parsed image ✔ Cataloged packages [185 packages] ``` The attach tooling in the container for a hijacker to use is looing much more tasty too, with the inclusion of openssh-client. 71 new packages and nearly twice as many vulnerabilies. ## Considerations when slimming Circle back to the CNCF comments about the impact of slimming. * As the whitepaper notes, *"tools like DockerSlim and MiniCon can be used to limit the number of files the container image that are only used by the container runtime process, thus limiting the attack surface to that process."* * *"Container scanners often rely on standard OS files and package manifests for programming language ecosystems to determine package names and versions. If you choose to use these slimming techniques, make sure to store and publish all the metadata using the recommendations in this whitepaper. It should be noted that complications can arise from use of container slimming which impact the information necessary to produce content leveraged later in the supply chain1. For organizations considering this, be sure to weigh this impact into your overall supply chain security strategy."* ## Using docker-slim xray `docker-slim` can create a detailed breakdown of Layers, Instructions, and Files. While much of this information is more interesting for container authors and maintainers, it is useful to provide insight to what happen during the layer construction. ```bash docker-slim xray slim-demo:prod-fat > slim-demo-prod-fat.txt ``` Useful search strings: * `cmd=xray info=layer.start` - find the start of each layer analysis * `cmd=xray info=change.instruction index` - find instructions used in layer construction * `cmd=xray info=image.exposed_ports` - find the exposed ports The output from `docker-slim xray` is verbose, but we can visualise this data in a human-friendly way using the [Slim.AI Developer Platform](https://portal.slim.dev/). ```bash docker-slim xray --export-all-data-artifacts . slim-demo:prod-fat ``` Upload `data-artifacts.tar` to [https://portal.slim.dev/xrayupload](https://portal.slim.dev/xrayupload) ## Slim the container 🛑 **TALKING POINTS:** * Other techniques exist for creating "slim" containers * Container size is not a vanity metric, but an indicator of container quality. * Smaller container images a faster to deploy. * Container report - dev tools left in "production" containers. * Recently highlighted in the [ContainerDays 2021 - Hijack a Kubernetes Cluster - a Walkthrough](https://www.youtube.com/watch?v=kmtvUTYynmM), which highlights how leaving unnecessary files in a container can be used to island hop. * [Hijack Kubernetes Cluster Hands On](https://github.com/nmeisenzahl/hijack-kubernetes/blob/main/docs/hands-on.md) Let's slim the container * Slim: `docker-slim build --tag slim-demo:prod-slim slim-demo:prod-fat` * See: `docker images` * Run: `docker run -it --rm -p 8008:8008 slim-demo:prod-slim` ### Stimulating the container By default `docker-slim` will issue a `GET /` request with https and then https on every exposed port and then attempt to crawl any URIs is discovers. Your app may require more comprehensive probing to fully stimulate the app while it is observed by `docker-slim` to ensure all runtime requirements are included in the slim container image. `docker-slim` has [http probe commands](https://github.com/docker-slim/docker-slim#http-probe-commands) to help facilitate that. Here's just a couple of examples: My app only has two endpoints `/` and `/hello`. If `/hello` relies on decencies that `/` do not, we could end up with a slim container that is not fully functional. #### --http-probe-cmd The `--http-probe-cmd` option is good when you want to specify a small number of simple commands. We can add `--http-probe-cmd /hello` to `docker-slim build` to add the additional `GET /hello` to the probe. ```bash docker-slim build --http-probe-cmd /hello --tag slim-demo:prod-slim slim-demo:prod-fat ``` #### --http-probe-cmd-file The `--http-probe-cmd-file` option is good when you have a lot of commands and/or you want to select additional http command options: Here a `probe.json` for my simple app: ```json { "commands": [ { "protocol": "http", "method": "GET", "resource": "/" }, { "protocol": "http", "method": "GET", "resource": "/hello" } ] } ``` Which would be invoked like this: ```bash docker-slim build --http-probe-cmd-file probe.json --tag slim-demo:prod-slim slim-demo:prod-fat ``` #### --http-probe-exec You can use the `--http-probe-exec` option to run user provided commands when the http probes are executed. This example shows how you can run `curl` against the temporary docker-slim created container when the http probes are executed. ```bash docker-slim build --http-probe-exec 'curl http://localhost:8008/hello' --publish-port 8008 --tag slim-demo:prod-slim slim-demo:prod-fat ``` The `--http-probe-exec-file` option can be used to provide a file of commands to run that stimulate the container when the http probes run. ## Slim the dev container Now, let's also slim our dev container. * Slim: `docker-slim build --http-probe-cmd /hello --tag slim-demo:dev-slim slim-demo:dev-fat` * See `docker images` Both slimmed containers are the same size. * Run: `docker run -it --rm -p 8008:8008 slim-demo:dev-slim` ## Using Slim.AI We can simplify the ability to explore and diff containers by pushing our container images to Docker Hub (or other registry) and connecting them to the [Slim Developer Platform](https://portal.slim.dev/) I pushed the fat and slim images to DockerHub like this: ```bash docker login -u YOUR_DOCKERHUB_NAME docker tag slim-demo:prod-fat YOUR_DOCKERHUB_NAME/slim-demo:prod-fat docker push YOUR_DOCKERHUB_NAME/slim-demo:prod-fat docker tag slim-demo:prod-slim YOUR_DOCKERHUB_NAME/slim-demo:prod-slim docker push YOUR_DOCKERHUB_NAME/slim-demo:prod-slim ``` ### Connect Docker Hub * Login to [https://portal.slim.dev/](https://portal.slim.dev/) and * Add *slim-demo* (prod-fat and prod-slim) to Favorites ### Exploring images * Explore prod-fat * Overview: Ports, Shell, Special Permissions * File Explorer: Each Layer with Instructions open * Dockerfile: Everything * Explore prod-slim * Overview: No shells, only `/tmp` with special permissions * File Explorer: There is only one layer * **libgnutls** ### Diffing images * Diff the fat vs. slim containers * File system diff: Flask had some removals, in though we use it. * Image metadata: Shells, they were removed. Attack surface reduced. # Wrap up * Twitch Tomorrow: twitch.tv/SlimDevOps * 3PM ET/US * 8PM GMT * Twitter: `@SlimDevOps` * Discord: Links on the [slim.ai](https://slim.ai) website: * * <https://discord.gg/uBttmfyYNB>