diff --git a/contrib/docker/minimal-harded-image/Dockerfile b/contrib/docker/minimal-harded-image/Dockerfile new file mode 100644 index 0000000000..8ee6a3d206 --- /dev/null +++ b/contrib/docker/minimal-harded-image/Dockerfile @@ -0,0 +1,61 @@ +# This dockerfile builds an image for the backend package. +# It should be executed with the root of the repo as docker context. +# +# Before building this image, be sure to have run the following commands in the repo root: +# +# yarn install +# yarn tsc +# yarn build:backend +# +# Once the commands have been run, you can build the image using `yarn docker-build` + +FROM cgr.dev/chainguard/wolfi-base:latest + +ENV NODE_VERSION 18=~18.19 +ENV PYTHON_VERSION 3.12=~3.12 + +RUN apk add nodejs-$NODE_VERSION yarn + +# Install sqlite3 dependencies. You can skip this if you don't use sqlite3 in the image, +# in which case you should also move better-sqlite3 to "devDependencies" in package.json. +# Additionally, we install dependencies for `techdocs.generator.runIn: local`. +# https://backstage.io/docs/features/techdocs/getting-started#disabling-docker-in-docker-situation-optional +RUN --mount=type=cache,target=/var/cache/apk,sharing=locked \ + --mount=type=cache,target=/var/lib/apk,sharing=locked \ + apk update && \ + apk add sqlite-dev python-$PYTHON_VERSION py3-pip python-3-dev py3-setuptools build-base gcc libffi-dev glibc-dev openssl-dev brotli-dev c-ares-dev nghttp2-dev icu-dev zlib-dev gcc-12 libuv-dev && \ + yarn config set python /usr/bin/python3 + +# Set up a virtual environment for mkdocs-techdocs-core. +ENV VIRTUAL_ENV=/opt/venv +RUN python3 -m venv $VIRTUAL_ENV +ENV PATH="$VIRTUAL_ENV/bin:$PATH" + +RUN pip3 install mkdocs-techdocs-core==1.3.3 + +# From here on we use the least-privileged `node` user to run the backend. +WORKDIR /app +RUN chown nonroot:nonroot /app +USER nonroot + +# This switches many Node.js dependencies to production mode. +ENV NODE_ENV production + +# Copy over Yarn 3 configuration, release, and plugins +COPY --chown=nonroot:nonroot .yarn ./.yarn +COPY --chown=nonroot:nonroot .yarnrc.yml ./ + +# Copy repo skeleton first, to avoid unnecessary docker cache invalidation. +# The skeleton contains the package.json of each package in the monorepo, +# and along with yarn.lock and the root package.json, that's enough to run yarn install. +COPY --chown=nonroot:nonroot yarn.lock package.json packages/backend/dist/skeleton.tar.gz ./ +RUN tar xzf skeleton.tar.gz && rm skeleton.tar.gz + +RUN --mount=type=cache,target=/home/node/.yarn/berry/cache,sharing=locked,uid=1000,gid=1000 \ + yarn workspaces focus --all --production + +# Then copy the rest of the backend bundle, along with any other files we might want. +COPY --chown=nonroot:nonroot packages/backend/dist/bundle.tar.gz app-config*.yaml ./ +RUN tar xzf bundle.tar.gz && rm bundle.tar.gz + +CMD ["node", "packages/backend", "--config", "app-config.yaml"] diff --git a/contrib/docker/minimal-harded-image/README.md b/contrib/docker/minimal-harded-image/README.md new file mode 100644 index 0000000000..4aa7b8b139 --- /dev/null +++ b/contrib/docker/minimal-harded-image/README.md @@ -0,0 +1,12 @@ +# Minimal Hardened Image for Backstage + +DockerHub images in general did not seem ideal for Backstage as the number of vulnerabilities were quite high regardless of the image used. + +The `Dockerfile` in this directory uses a [`wolfi-base`](https://github.com/wolfi-dev) image from Chainguard Images. This improves the security of the application and reduces false positives in scanners. + +## Considerations + +- Wolfi only releases the `latest` tag for public consumption however digests can be pinned. +- Wolfi OS uses packages from the [os repository](https://github.com/wolfi-dev/os) on GitHub. Some packages may be named differently. +- While Wolfi uses `apk`, the OS is designed to support `glibc`. +- Due to the stripped down nature of the base image, additional packages might be needed compared to a distribution like Debian or Ubuntu. diff --git a/docs/deployment/docker.md b/docs/deployment/docker.md index 534c217337..d0c12bc399 100644 --- a/docs/deployment/docker.md +++ b/docs/deployment/docker.md @@ -338,3 +338,15 @@ Here's an example of these flags in use: ```sh docker image build . -f packages/backend/Dockerfile --tag backstage --progress=plain --no-cache ``` + +## Community Contributed Dockerfile Alternatives + +The `Dockerfile` mentioned above located in `packages/backend` is maintained by the maintainers of Backstage, however there are also community contributed Dockerfile alternatives located in `contrib/docker`. The `Dockerfile`s in `contrib/docker` are not maintained by the maintainers of Backstage and are not necessarily updated when the `Dockerfile` located in `packages/backend` is updated. + +### Minimal Hardened Image + +A contributed `Dockerfile` exists within the directory of `contrib/docker/minimal-harded-image` which uses the [`wolfi-base`](https://github.com/wolfi-dev) image to reduce vulnerabilities. When this was contributed, this alternative `Dockerfile` reduced 98.2% of vulnerabilities in the built Backstage docker image when compared with the image built from `packages/backend/Dockerfile`. + +To reduce maintenance, the digest of the image has been removed from the `contrib/docker/minimal-harded-image/Dockerfile` file. A complete example with the digest would be `cgr.dev/chainguard/wolfi-base:latest@sha256:3d6dece13cdb5546cd03b20e14f9af354bc1a56ab5a7b47dca3e6c1557211fcf` and it is suggested to update the `FROM` line in the `Dockerfile` to use a digest. Please do a docker pull on the image to get the latest digest. Using the digest allows tools such as Dependabot or Renovatebot to know exactly which image digest is being utilized and allows for Pull Requests to be triggered when a new digest is available. + +It is suggested to setup Dependabot/Renovatebot or a smiliar tool to ensure the image is kept up to date so that vulnerability fixes that have been addressed are pulled in frequently.