From ec4628c676a8462f4926a3c760878fab678eeebd Mon Sep 17 00:00:00 2001 From: Rene Groeschke Date: Mon, 19 May 2025 10:47:34 -0700 Subject: [PATCH 1/6] Restructure docker files for docker distributions (#127960) Restructures docker files for docker distributions - Put Dockerfiles in specific distro specific folders keeping "Dockerfile" naming convention - Allows better ide support - Allows easier renovate integration - Explicitly set base image in dockerfile - simplify renovate configuration - Cleanup DockerBase file to not contain ess fips base image information This lives now in the Dockerfile content directly * Workaround docker test issue * Fix labels for fips image (cherry picked from commit 38c90ca8d47001002a4c6c9bf34858088068d8c6) # Conflicts: # build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/DockerBase.java # distribution/docker/src/docker/dockerfiles/default/Dockerfile # renovate.json --- .../gradle/internal/DockerBase.java | 12 +- distribution/docker/build.gradle | 4 +- .../cloud_ess_fips/Dockerfile} | 4 +- .../src/docker/dockerfiles/default/Dockerfile | 162 ++++++++++++++++ .../src/docker/dockerfiles/wolfi/Dockerfile | 176 ++++++++++++++++++ .../packaging/util/docker/Docker.java | 5 +- 6 files changed, 352 insertions(+), 11 deletions(-) rename distribution/docker/src/docker/{Dockerfile.ess-fips => dockerfiles/cloud_ess_fips/Dockerfile} (97%) create mode 100644 distribution/docker/src/docker/dockerfiles/default/Dockerfile create mode 100644 distribution/docker/src/docker/dockerfiles/wolfi/Dockerfile diff --git a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/DockerBase.java b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/DockerBase.java index 63552a2ed5e56..395be8d132634 100644 --- a/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/DockerBase.java +++ b/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/DockerBase.java @@ -13,7 +13,7 @@ * This class models the different Docker base images that are used to build Docker distributions of Elasticsearch. */ public enum DockerBase { - DEFAULT("ubuntu:20.04", "", "apt-get", "Dockerfile"), + DEFAULT("ubuntu:20.04", "", "apt-get", "dockerfiles/default/Dockerfile"), // "latest" here is intentional, since the image name specifies "8" UBI("docker.elastic.co/ubi8/ubi-minimal:latest", "-ubi8", "microdnf", "Dockerfile"), @@ -22,13 +22,11 @@ public enum DockerBase { IRON_BANK("${BASE_REGISTRY}/${BASE_IMAGE}:${BASE_TAG}", "-ironbank", "yum", "Dockerfile"), // Chainguard based wolfi image with latest jdk - // This is usually updated via renovatebot - // spotless:off WOLFI( - "docker.elastic.co/wolfi/chainguard-base:latest@sha256:1c7f5aa0e7997455b8500d095c7a90e617102d3941eb0757ac62cfea509e09b9", + null, "-wolfi", "apk", - "Dockerfile" + "dockerfiles/wolfi/Dockerfile" ), // spotless:on @@ -37,10 +35,10 @@ public enum DockerBase { CLOUD_ESS(null, "-cloud-ess", "apk", "Dockerfile.ess"), CLOUD_ESS_FIPS( - "docker.elastic.co/wolfi/chainguard-base-fips:sha256-ebfc3f1d7dba992231747a2e05ad1b859843e81b5e676ad342859d7cf9e425a7", + null, "-cloud-ess-fips", "apk", - "Dockerfile.ess-fips" + "dockerfiles/cloud_ess_fips/Dockerfile" ); private final String image; diff --git a/distribution/docker/build.gradle b/distribution/docker/build.gradle index b59644411bd98..91640d7a98cf3 100644 --- a/distribution/docker/build.gradle +++ b/distribution/docker/build.gradle @@ -462,8 +462,10 @@ void addBuildDockerImageTask(Architecture architecture, DockerBase base) { baseImages = [baseImage] buildArgs = buildArgsMap - } else { + } else if(base.image != null) { baseImages = [base.image] + } else { + baseImages = [] } Provider serviceProvider = GradleUtils.getBuildService( diff --git a/distribution/docker/src/docker/Dockerfile.ess-fips b/distribution/docker/src/docker/dockerfiles/cloud_ess_fips/Dockerfile similarity index 97% rename from distribution/docker/src/docker/Dockerfile.ess-fips rename to distribution/docker/src/docker/dockerfiles/cloud_ess_fips/Dockerfile index 27f03a40a056f..cd190776da0d0 100644 --- a/distribution/docker/src/docker/Dockerfile.ess-fips +++ b/distribution/docker/src/docker/dockerfiles/cloud_ess_fips/Dockerfile @@ -24,7 +24,7 @@ # Extract Elasticsearch artifact ################################################################################ -FROM ${base_image} AS builder +FROM docker.elastic.co/wolfi/chainguard-base-fips:latest@sha256:ebfc3f1d7dba992231747a2e05ad1b859843e81b5e676ad342859d7cf9e425a7 AS builder # Install required packages to extract the Elasticsearch distribution RUN <%= retry.loop(package_manager, "export DEBIAN_FRONTEND=noninteractive && ${package_manager} update && ${package_manager} update && ${package_manager} add --no-cache curl") %> @@ -103,7 +103,7 @@ WORKDIR /usr/share/elasticsearch/config # Add entrypoint ################################################################################ -FROM ${base_image} +FROM docker.elastic.co/wolfi/chainguard-base-fips:latest@sha256:ebfc3f1d7dba992231747a2e05ad1b859843e81b5e676ad342859d7cf9e425a7 RUN <%= retry.loop(package_manager, "export DEBIAN_FRONTEND=noninteractive && \n" + diff --git a/distribution/docker/src/docker/dockerfiles/default/Dockerfile b/distribution/docker/src/docker/dockerfiles/default/Dockerfile new file mode 100644 index 0000000000000..47d4840e61698 --- /dev/null +++ b/distribution/docker/src/docker/dockerfiles/default/Dockerfile @@ -0,0 +1,162 @@ +<% /* + This file is passed through Groovy's SimpleTemplateEngine, so dollars and backslashes + have to be escaped in order for them to appear in the final Dockerfile. You + can also comment out blocks, like this one. See: + + https://docs.groovy-lang.org/latest/html/api/groovy/text/SimpleTemplateEngine.html + + We use control-flow tags in this file to conditionally render the content. The + layout/presentation here has been adjusted so that it looks reasonable when rendered, + at the slight expense of how it looks here. + + Note that this file is also filtered to squash together newlines, so we can + add as many newlines here as necessary to improve legibility. +*/ %> + + +################################################################################ +# Build stage 1 `builder`: +# Extract Elasticsearch artifact +################################################################################ + +FROM ${base_image} AS builder + +RUN microdnf install -y findutils tar gzip + +# `tini` is a tiny but valid init for containers. This is used to cleanly +# control how ES and any child processes are shut down. +# +# The tini GitHub page gives instructions for verifying the binary using +# gpg, but the keyservers are slow to return the key and this can fail the +# build. Instead, we check the binary against the published checksum. +RUN set -eux ; \\ + tini_bin="" ; \\ + arch="\$(rpm --query --queryformat='%{ARCH}' rpm)"; \ + case "\$(arch)" in \\ + aarch64) tini_bin='tini-arm64' ;; \\ + x86_64) tini_bin='tini-amd64' ;; \\ + *) echo >&2 ; echo >&2 "Unsupported architecture \$arch" ; echo >&2 ; exit 1 ;; \\ + esac ; \\ + curl -f --retry 10 -S -L -O https://github.com/krallin/tini/releases/download/v0.19.0/\${tini_bin} ; \\ + curl -f --retry 10 -S -L -O https://github.com/krallin/tini/releases/download/v0.19.0/\${tini_bin}.sha256sum ; \\ + sha256sum -c \${tini_bin}.sha256sum ; \\ + rm \${tini_bin}.sha256sum ; \\ + mv \${tini_bin} /bin/tini ; \\ + chmod 0555 /bin/tini + +WORKDIR /usr/share/elasticsearch +RUN arch="\$(rpm --query --queryformat='%{ARCH}' rpm)" && curl -f --retry 10 -S -L --output /tmp/elasticsearch.tar.gz https://artifacts-no-kpi.elastic.co/downloads/elasticsearch/elasticsearch-${version}-linux-\$(arch).tar.gz +RUN tar -zxf /tmp/elasticsearch.tar.gz --strip-components=1 && \\ +# Configure the distribution for Docker + sed -i -e 's/ES_DISTRIBUTION_TYPE=tar/ES_DISTRIBUTION_TYPE=docker/' bin/elasticsearch-env && \\ +# Create required directory + mkdir data && \\ +# Reset permissions on all directories + find . -type d -exec chmod 0555 {} + && \\ +# keep default elasticsearch log4j config + mv config/log4j2.properties config/log4j2.file.properties && \\ +# Reset permissions on all files + find . -type f -exec chmod 0444 {} + && \\ +# Make CLI tools executable + chmod 0555 bin/* jdk/bin/* jdk/lib/jspawnhelper modules/x-pack-ml/platform/linux-*/bin/* && \\ +# Make some directories writable. `bin` must be writable because +# plugins can install their own CLI utilities. + chmod 0775 bin config config/jvm.options.d data logs plugins && \\ +# Make some files writable + find config -type f -exec chmod 0664 {} + && \\ +# Tighten up permissions on the ES home dir (the permissions of the contents are handled below) + chmod 0775 . && \\ +# You can't install plugins that include configuration when running as `elasticsearch` and the `config` +# dir is owned by `root`, because the installed tries to manipulate the permissions on the plugin's +# config directory. + chown 1000:1000 bin config config/jvm.options.d data logs plugins + +# The distribution includes a `config` directory, no need to create it +COPY --chmod=664 config/elasticsearch.yml config/log4j2.properties config/ + + +################################################################################ +# Build stage 2 (the actual Elasticsearch image): +# +# Copy elasticsearch from stage 1 +# Add entrypoint +################################################################################ + +FROM ${base_image} + +RUN microdnf install --setopt=tsflags=nodocs -y \\ + nc shadow-utils zip unzip findutils procps-ng && \\ + microdnf clean all + +RUN groupadd -g 1000 elasticsearch && \\ + adduser -u 1000 -g 1000 -G 0 -d /usr/share/elasticsearch elasticsearch && \\ + chown -R 0:0 /usr/share/elasticsearch + +ENV ELASTIC_CONTAINER=true + +COPY --from=builder /bin/tini /bin/tini + +WORKDIR /usr/share/elasticsearch + +COPY --from=builder --chown=0:0 /usr/share/elasticsearch . + +# Replace OpenJDK's built-in CA certificate keystore with the one from the OS +# vendor. The latter is superior in several ways. +# REF: https://github.com/elastic/elasticsearch-docker/issues/171 +RUN ln -sf /etc/pki/ca-trust/extracted/java/cacerts jdk/lib/security/cacerts + +ENV PATH=/usr/share/elasticsearch/bin:\$PATH +ENV SHELL=/bin/bash + +COPY --chmod=0555 bin/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh + +RUN chmod g=u /etc/passwd && \\ + chmod 0555 /usr/local/bin/docker-entrypoint.sh && \\ + find / -xdev -perm -4000 -exec chmod ug-s {} + && \\ + chmod 0775 /usr/share/elasticsearch && \\ + chown elasticsearch bin config config/jvm.options.d data logs plugins + + +EXPOSE 9200 9300 + +LABEL org.label-schema.build-date="${build_date}" \\ + org.label-schema.license="${license}" \\ + org.label-schema.name="Elasticsearch" \\ + org.label-schema.schema-version="1.0" \\ + org.label-schema.url="https://www.elastic.co/products/elasticsearch" \\ + org.label-schema.usage="https://www.elastic.co/guide/en/elasticsearch/reference/index.html" \\ + org.label-schema.vcs-ref="${git_revision}" \\ + org.label-schema.vcs-url="https://github.com/elastic/elasticsearch" \\ + org.label-schema.vendor="Elastic" \\ + org.label-schema.version="${version}" \\ + org.opencontainers.image.created="${build_date}" \\ + org.opencontainers.image.documentation="https://www.elastic.co/guide/en/elasticsearch/reference/index.html" \\ + org.opencontainers.image.licenses="${license}" \\ + org.opencontainers.image.revision="${git_revision}" \\ + org.opencontainers.image.source="https://github.com/elastic/elasticsearch" \\ + org.opencontainers.image.title="Elasticsearch" \\ + org.opencontainers.image.url="https://www.elastic.co/products/elasticsearch" \\ + org.opencontainers.image.vendor="Elastic" \\ + org.opencontainers.image.version="${version}" + +LABEL name="Elasticsearch" \\ + maintainer="infra@elastic.co" \\ + vendor="Elastic" \\ + version="${version}" \\ + release="1" \\ + summary="Elasticsearch" \\ + description="You know, for search." + +RUN mkdir /licenses && ln LICENSE.txt /licenses/LICENSE + +# Our actual entrypoint is `tini`, a minimal but functional init program. It +# calls the entrypoint we provide, while correctly forwarding signals. +ENTRYPOINT ["/bin/tini", "--", "/usr/local/bin/docker-entrypoint.sh"] +# Dummy overridable parameter parsed by entrypoint +CMD ["eswrapper"] + +USER 1000:0 + +################################################################################ +# End of multi-stage Dockerfile +################################################################################ diff --git a/distribution/docker/src/docker/dockerfiles/wolfi/Dockerfile b/distribution/docker/src/docker/dockerfiles/wolfi/Dockerfile new file mode 100644 index 0000000000000..8d6e090dbdaac --- /dev/null +++ b/distribution/docker/src/docker/dockerfiles/wolfi/Dockerfile @@ -0,0 +1,176 @@ +################################################################################ +# This Dockerfile was generated from the template at distribution/src/docker/Dockerfile +# +# Beginning of multi stage Dockerfile +################################################################################ + +<% /* + This file is passed through Groovy's SimpleTemplateEngine, so dollars and backslashes + have to be escaped in order for them to appear in the final Dockerfile. You + can also comment out blocks, like this one. See: + + https://docs.groovy-lang.org/latest/html/api/groovy/text/SimpleTemplateEngine.html + + We use control-flow tags in this file to conditionally render the content. The + layout/presentation here has been adjusted so that it looks reasonable when rendered, + at the slight expense of how it looks here. + + Note that this file is also filtered to squash together newlines, so we can + add as many newlines here as necessary to improve legibility. +*/ %> + +################################################################################ +# Build stage 1 `builder`: +# Extract Elasticsearch artifact +################################################################################ + +FROM docker.elastic.co/wolfi/chainguard-base:latest@sha256:29150cd940cc7f69407d978d5a19c86f4d9e67cf44e4d6ded787a497e8f27c9a AS builder + +# Install required packages to extract the Elasticsearch distribution +RUN <%= retry.loop(package_manager, "export DEBIAN_FRONTEND=noninteractive && ${package_manager} update && ${package_manager} update && ${package_manager} add --no-cache curl") %> + +# `tini` is a tiny but valid init for containers. This is used to cleanly +# control how ES and any child processes are shut down. +# For wolfi we pick it from the blessed wolfi package registry. +# +# The tini GitHub page gives instructions for verifying the binary using +# gpg, but the keyservers are slow to return the key and this can fail the +# build. Instead, we check the binary against the published checksum. + +RUN mkdir /usr/share/elasticsearch +WORKDIR /usr/share/elasticsearch + + +# Fetch the appropriate Elasticsearch distribution for this architecture. +# Keep this command on one line - it is replaced with a `COPY` during local builds. +# It uses the `arch` shell command to fetch the correct distro for the build machine, +RUN curl --retry 10 -S -L --output /tmp/elasticsearch.tar.gz https://artifacts-no-kpi.elastic.co/downloads/elasticsearch/elasticsearch-${version}-linux-\${arch}.tar.gz + +RUN tar -zxf /tmp/elasticsearch.tar.gz --strip-components=1 + +# The distribution includes a `config` directory, no need to create it +COPY ${config_dir}/elasticsearch.yml config/ +COPY ${config_dir}/log4j2.properties config/log4j2.docker.properties + +# 1. Configure the distribution for Docker +# 2. Create required directory +# 3. Move the distribution's default logging config aside +# 4. Move the generated docker logging config so that it is the default +# 5. Reset permissions on all directories +# 6. Reset permissions on all files +# 7. Make CLI tools executable +# 8. Make some directories writable. `bin` must be writable because +# plugins can install their own CLI utilities. +# 9. Make some files writable +RUN sed -i -e 's/ES_DISTRIBUTION_TYPE=tar/ES_DISTRIBUTION_TYPE=docker/' bin/elasticsearch-env && \\ + mkdir data && \\ + mv config/log4j2.properties config/log4j2.file.properties && \\ + mv config/log4j2.docker.properties config/log4j2.properties && \\ + find . -type d -exec chmod 0555 {} + && \\ + find . -type f -exec chmod 0444 {} + && \\ + chmod 0555 bin/* jdk/bin/* jdk/lib/jspawnhelper modules/x-pack-ml/platform/linux-*/bin/* && \\ + chmod 0775 bin config config/jvm.options.d data logs plugins && \\ + find config -type f -exec chmod 0664 {} + + +################################################################################ +# Build stage 2 (the actual Elasticsearch image): +# +# Copy elasticsearch from stage 1 +# Add entrypoint +################################################################################ + +FROM docker.elastic.co/wolfi/chainguard-base:latest@sha256:29150cd940cc7f69407d978d5a19c86f4d9e67cf44e4d6ded787a497e8f27c9a + +RUN <%= retry.loop(package_manager, + "export DEBIAN_FRONTEND=noninteractive && \n" + + " ${package_manager} update && \n" + + " ${package_manager} upgrade && \n" + + " ${package_manager} add --no-cache \n" + + " bash java-cacerts curl libstdc++ libsystemd netcat-openbsd p11-kit p11-kit-trust posix-libc-utils shadow tini unzip zip zstd && \n" + + " rm -rf /var/cache/apk/* " + ) %> + +# Set Bash as the default shell for future commands +SHELL ["/bin/bash", "-c"] + +# Optionally set Bash as the default shell in the container at runtime +CMD ["/bin/bash"] + +RUN groupadd -g 1000 elasticsearch && \ + adduser -G elasticsearch -u 1000 elasticsearch -D --home /usr/share/elasticsearch elasticsearch && \ + adduser elasticsearch root && \ + chown -R 0:0 /usr/share/elasticsearch + +ENV ELASTIC_CONTAINER=true + +WORKDIR /usr/share/elasticsearch + +COPY --from=builder --chown=0:0 /usr/share/elasticsearch /usr/share/elasticsearch + +ENV PATH=/usr/share/elasticsearch/bin:\$PATH +ENV SHELL=/bin/bash +COPY ${bin_dir}/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh + +# 1. Sync the user and group permissions of /etc/passwd +# 2. Set correct permissions of the entrypoint +# 3. Ensure that there are no files with setuid or setgid, in order to mitigate "stackclash" attacks. +# We've already run this in previous layers so it ought to be a no-op. +# 4. Replace OpenJDK's built-in CA certificate keystore with the one from the OS +# vendor. The latter is superior in several ways. +# REF: https://github.com/elastic/elasticsearch-docker/issues/171 +# 5. Tighten up permissions on the ES home dir (the permissions of the contents are handled earlier) +# 6. You can't install plugins that include configuration when running as `elasticsearch` and the `config` +# dir is owned by `root`, because the installed tries to manipulate the permissions on the plugin's +# config directory. +RUN chmod g=u /etc/passwd && \\ + chmod 0555 /usr/local/bin/docker-entrypoint.sh && \\ + find / -xdev -perm -4000 -exec chmod ug-s {} + && \\ + chmod 0775 /usr/share/elasticsearch && \\ + chown elasticsearch bin config config/jvm.options.d data logs plugins + +RUN ln -sf /etc/ssl/certs/java/cacerts /usr/share/elasticsearch/jdk/lib/security/cacerts + +EXPOSE 9200 9300 + + +LABEL org.label-schema.build-date="${build_date}" \\ + org.label-schema.license="${license}" \\ + org.label-schema.name="Elasticsearch" \\ + org.label-schema.schema-version="1.0" \\ + org.label-schema.url="https://www.elastic.co/products/elasticsearch" \\ + org.label-schema.usage="https://www.elastic.co/guide/en/elasticsearch/reference/index.html" \\ + org.label-schema.vcs-ref="${git_revision}" \\ + org.label-schema.vcs-url="https://github.com/elastic/elasticsearch" \\ + org.label-schema.vendor="Elastic" \\ + org.label-schema.version="${version}" \\ + org.opencontainers.image.created="${build_date}" \\ + org.opencontainers.image.documentation="https://www.elastic.co/guide/en/elasticsearch/reference/index.html" \\ + org.opencontainers.image.licenses="${license}" \\ + org.opencontainers.image.revision="${git_revision}" \\ + org.opencontainers.image.source="https://github.com/elastic/elasticsearch" \\ + org.opencontainers.image.title="Elasticsearch" \\ + org.opencontainers.image.url="https://www.elastic.co/products/elasticsearch" \\ + org.opencontainers.image.vendor="Elastic" \\ + org.opencontainers.image.version="${version}" + +LABEL name="Elasticsearch" \\ + maintainer="infra@elastic.co" \\ + vendor="Elastic" \\ + version="${version}" \\ + release="1" \\ + summary="Elasticsearch" \\ + description="You know, for search." + +RUN mkdir /licenses && ln LICENSE.txt /licenses/LICENSE + +# Our actual entrypoint is `tini`, a minimal but functional init program. It +# calls the entrypoint we provide, while correctly forwarding signals. +ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/docker-entrypoint.sh"] +# Dummy overridable parameter parsed by entrypoint +CMD ["eswrapper"] + +USER 1000:0 + +################################################################################ +# End of multi-stage Dockerfile +################################################################################ diff --git a/qa/packaging/src/test/java/org/elasticsearch/packaging/util/docker/Docker.java b/qa/packaging/src/test/java/org/elasticsearch/packaging/util/docker/Docker.java index ff1f3a9314597..ab167d7663be1 100644 --- a/qa/packaging/src/test/java/org/elasticsearch/packaging/util/docker/Docker.java +++ b/qa/packaging/src/test/java/org/elasticsearch/packaging/util/docker/Docker.java @@ -75,12 +75,15 @@ public class Docker { public static final int STARTUP_SLEEP_INTERVAL_MILLISECONDS = 1000; public static final int STARTUP_ATTEMPTS_MAX = 30; + /** + * The length of the command exceeds what we can use for COLUMNS so we use + * a workaround to find the process we're looking for + */ private static final String ELASTICSEARCH_FULL_CLASSNAME = "org.elasticsearch.bootstrap.Elasticsearch"; private static final String FIND_ELASTICSEARCH_PROCESS = "for pid in $(ps -eo pid,comm | grep java | awk '\\''{print $1}'\\''); " + "do cmdline=$(tr \"\\0\" \" \" < /proc/$pid/cmdline 2>/dev/null); [[ $cmdline == *" + ELASTICSEARCH_FULL_CLASSNAME + "* ]] && echo \"$pid: $cmdline\"; done"; - // The length of the command exceeds what we can use for COLUMNS so we use a pipe to detect the process we're looking for /** * Tracks the currently running Docker image. An earlier implementation used a fixed container name, From 4638cb28f117bf447c670c1b4fe7ed50b57b06a9 Mon Sep 17 00:00:00 2001 From: Rene Groeschke Date: Wed, 21 May 2025 20:12:14 +0200 Subject: [PATCH 2/6] Fix ubuntu based default docker image; also remove muted test --- .../src/docker/dockerfiles/default/Dockerfile | 74 ++++++++++--------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/distribution/docker/src/docker/dockerfiles/default/Dockerfile b/distribution/docker/src/docker/dockerfiles/default/Dockerfile index 47d4840e61698..5c2ae758c1a5a 100644 --- a/distribution/docker/src/docker/dockerfiles/default/Dockerfile +++ b/distribution/docker/src/docker/dockerfiles/default/Dockerfile @@ -21,7 +21,7 @@ FROM ${base_image} AS builder -RUN microdnf install -y findutils tar gzip +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y curl # `tini` is a tiny but valid init for containers. This is used to cleanly # control how ES and any child processes are shut down. @@ -31,45 +31,46 @@ RUN microdnf install -y findutils tar gzip # build. Instead, we check the binary against the published checksum. RUN set -eux ; \\ tini_bin="" ; \\ - arch="\$(rpm --query --queryformat='%{ARCH}' rpm)"; \ case "\$(arch)" in \\ aarch64) tini_bin='tini-arm64' ;; \\ x86_64) tini_bin='tini-amd64' ;; \\ - *) echo >&2 ; echo >&2 "Unsupported architecture \$arch" ; echo >&2 ; exit 1 ;; \\ + *) echo >&2 ; echo >&2 "Unsupported architecture \$(arch)" ; echo >&2 ; exit 1 ;; \\ esac ; \\ - curl -f --retry 10 -S -L -O https://github.com/krallin/tini/releases/download/v0.19.0/\${tini_bin} ; \\ - curl -f --retry 10 -S -L -O https://github.com/krallin/tini/releases/download/v0.19.0/\${tini_bin}.sha256sum ; \\ + curl --retry 10 -S -L -O https://github.com/krallin/tini/releases/download/v0.19.0/\${tini_bin} ; \\ + curl --retry 10 -S -L -O https://github.com/krallin/tini/releases/download/v0.19.0/\${tini_bin}.sha256sum ; \\ sha256sum -c \${tini_bin}.sha256sum ; \\ rm \${tini_bin}.sha256sum ; \\ mv \${tini_bin} /bin/tini ; \\ chmod 0555 /bin/tini WORKDIR /usr/share/elasticsearch -RUN arch="\$(rpm --query --queryformat='%{ARCH}' rpm)" && curl -f --retry 10 -S -L --output /tmp/elasticsearch.tar.gz https://artifacts-no-kpi.elastic.co/downloads/elasticsearch/elasticsearch-${version}-linux-\$(arch).tar.gz -RUN tar -zxf /tmp/elasticsearch.tar.gz --strip-components=1 && \\ -# Configure the distribution for Docker - sed -i -e 's/ES_DISTRIBUTION_TYPE=tar/ES_DISTRIBUTION_TYPE=docker/' bin/elasticsearch-env && \\ -# Create required directory +RUN curl --retry 10 -S -L --output /tmp/elasticsearch.tar.gz https://artifacts-no-kpi.elastic.co/downloads/elasticsearch/elasticsearch-${version}-linux-\$(arch).tar.gz + +RUN tar -zxf /tmp/elasticsearch.tar.gz --strip-components=1 + +# The distribution includes a `config` directory, no need to create it +COPY ${config_dir}/elasticsearch.yml config/ +COPY ${config_dir}/log4j2.properties config/log4j2.docker.properties + +# 1. Configure the distribution for Docker +# 2. Create required directory +# 3. Move the distribution's default logging config aside +# 4. Move the generated docker logging config so that it is the default +# 5. Reset permissions on all directories +# 6. Reset permissions on all files +# 7. Make CLI tools executable +# 8. Make some directories writable. `bin` must be writable because +# plugins can install their own CLI utilities. +# 9. Make some files writable +RUN sed -i -e 's/ES_DISTRIBUTION_TYPE=tar/ES_DISTRIBUTION_TYPE=docker/' bin/elasticsearch-env && \\ mkdir data && \\ -# Reset permissions on all directories - find . -type d -exec chmod 0555 {} + && \\ -# keep default elasticsearch log4j config mv config/log4j2.properties config/log4j2.file.properties && \\ -# Reset permissions on all files + mv config/log4j2.docker.properties config/log4j2.properties && \\ + find . -type d -exec chmod 0555 {} + && \\ find . -type f -exec chmod 0444 {} + && \\ -# Make CLI tools executable chmod 0555 bin/* jdk/bin/* jdk/lib/jspawnhelper modules/x-pack-ml/platform/linux-*/bin/* && \\ -# Make some directories writable. `bin` must be writable because -# plugins can install their own CLI utilities. chmod 0775 bin config config/jvm.options.d data logs plugins && \\ -# Make some files writable - find config -type f -exec chmod 0664 {} + && \\ -# Tighten up permissions on the ES home dir (the permissions of the contents are handled below) - chmod 0775 . && \\ -# You can't install plugins that include configuration when running as `elasticsearch` and the `config` -# dir is owned by `root`, because the installed tries to manipulate the permissions on the plugin's -# config directory. - chown 1000:1000 bin config config/jvm.options.d data logs plugins + find config -type f -exec chmod 0664 {} + # The distribution includes a `config` directory, no need to create it COPY --chmod=664 config/elasticsearch.yml config/log4j2.properties config/ @@ -84,12 +85,19 @@ COPY --chmod=664 config/elasticsearch.yml config/log4j2.properties config/ FROM ${base_image} -RUN microdnf install --setopt=tsflags=nodocs -y \\ - nc shadow-utils zip unzip findutils procps-ng && \\ - microdnf clean all +# Change default shell to bash, then install required packages with retries. +RUN yes no | dpkg-reconfigure dash && \\ + export DEBIAN_FRONTEND=noninteractive && \\ + apt-get update && \\ + apt-get upgrade -y && \\ + apt-get install -y --no-install-recommends \\ + ca-certificates curl netcat p11-kit unzip zip && \\ + apt-get clean && \\ + rm -rf /var/lib/apt/lists/* RUN groupadd -g 1000 elasticsearch && \\ - adduser -u 1000 -g 1000 -G 0 -d /usr/share/elasticsearch elasticsearch && \\ + adduser --uid 1000 --gid 1000 --home /usr/share/elasticsearch elasticsearch && \\ + adduser elasticsearch root && \\ chown -R 0:0 /usr/share/elasticsearch ENV ELASTIC_CONTAINER=true @@ -100,10 +108,10 @@ WORKDIR /usr/share/elasticsearch COPY --from=builder --chown=0:0 /usr/share/elasticsearch . -# Replace OpenJDK's built-in CA certificate keystore with the one from the OS -# vendor. The latter is superior in several ways. -# REF: https://github.com/elastic/elasticsearch-docker/issues/171 -RUN ln -sf /etc/pki/ca-trust/extracted/java/cacerts jdk/lib/security/cacerts +# Update "cacerts" bundle to use Ubuntu's CA certificates (and make sure it +# stays up-to-date with changes to Ubuntu's store) +COPY bin/docker-openjdk /etc/ca-certificates/update.d/docker-openjdk +RUN /etc/ca-certificates/update.d/docker-openjdk ENV PATH=/usr/share/elasticsearch/bin:\$PATH ENV SHELL=/bin/bash From 8ac0f0b568f0b0ea4332260da4f11bb9370cfe89 Mon Sep 17 00:00:00 2001 From: Rene Groeschke Date: Mon, 26 May 2025 09:08:18 +0200 Subject: [PATCH 3/6] Cleanup dockerfile --- .../src/docker/dockerfiles/default/Dockerfile | 62 +++++++++---------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/distribution/docker/src/docker/dockerfiles/default/Dockerfile b/distribution/docker/src/docker/dockerfiles/default/Dockerfile index 5c2ae758c1a5a..8747512ff3fc0 100644 --- a/distribution/docker/src/docker/dockerfiles/default/Dockerfile +++ b/distribution/docker/src/docker/dockerfiles/default/Dockerfile @@ -13,7 +13,6 @@ add as many newlines here as necessary to improve legibility. */ %> - ################################################################################ # Build stage 1 `builder`: # Extract Elasticsearch artifact @@ -21,6 +20,7 @@ FROM ${base_image} AS builder +# Install required packages to extract the Elasticsearch distribution RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y curl # `tini` is a tiny but valid init for containers. This is used to cleanly @@ -72,9 +72,6 @@ RUN sed -i -e 's/ES_DISTRIBUTION_TYPE=tar/ES_DISTRIBUTION_TYPE=docker/' bin/elas chmod 0775 bin config config/jvm.options.d data logs plugins && \\ find config -type f -exec chmod 0664 {} + -# The distribution includes a `config` directory, no need to create it -COPY --chmod=664 config/elasticsearch.yml config/log4j2.properties config/ - ################################################################################ # Build stage 2 (the actual Elasticsearch image): @@ -87,13 +84,16 @@ FROM ${base_image} # Change default shell to bash, then install required packages with retries. RUN yes no | dpkg-reconfigure dash && \\ - export DEBIAN_FRONTEND=noninteractive && \\ - apt-get update && \\ - apt-get upgrade -y && \\ - apt-get install -y --no-install-recommends \\ - ca-certificates curl netcat p11-kit unzip zip && \\ - apt-get clean && \\ - rm -rf /var/lib/apt/lists/* +<%= retry.loop( +package_manager, + "export DEBIAN_FRONTEND=noninteractive && \n" + + " ${package_manager} update && \n" + + " ${package_manager} upgrade -y && \n" + + " ${package_manager} install -y --no-install-recommends \n" + + " ca-certificates curl netcat p11-kit unzip zip ${docker_base == 'cloud' ? 'wget' : '' } && \n" + + " ${package_manager} clean && \n" + + " rm -rf /var/lib/apt/lists/*" +) %> RUN groupadd -g 1000 elasticsearch && \\ adduser --uid 1000 --gid 1000 --home /usr/share/elasticsearch elasticsearch && \\ @@ -102,28 +102,36 @@ RUN groupadd -g 1000 elasticsearch && \\ ENV ELASTIC_CONTAINER=true -COPY --from=builder /bin/tini /bin/tini - WORKDIR /usr/share/elasticsearch -COPY --from=builder --chown=0:0 /usr/share/elasticsearch . - -# Update "cacerts" bundle to use Ubuntu's CA certificates (and make sure it -# stays up-to-date with changes to Ubuntu's store) -COPY bin/docker-openjdk /etc/ca-certificates/update.d/docker-openjdk -RUN /etc/ca-certificates/update.d/docker-openjdk +COPY --from=builder --chown=0:0 /usr/share/elasticsearch /usr/share/elasticsearch +COPY --from=builder --chown=0:0 /bin/tini /bin/tini ENV PATH=/usr/share/elasticsearch/bin:\$PATH ENV SHELL=/bin/bash - -COPY --chmod=0555 bin/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh - +COPY ${bin_dir}/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh + +# 1. Sync the user and group permissions of /etc/passwd +# 2. Set correct permissions of the entrypoint +# 3. Ensure that there are no files with setuid or setgid, in order to mitigate "stackclash" attacks. +# We've already run this in previous layers so it ought to be a no-op. +# 4. Replace OpenJDK's built-in CA certificate keystore with the one from the OS +# vendor. The latter is superior in several ways. +# REF: https://github.com/elastic/elasticsearch-docker/issues/171 +# 5. Tighten up permissions on the ES home dir (the permissions of the contents are handled earlier) +# 6. You can't install plugins that include configuration when running as `elasticsearch` and the `config` +# dir is owned by `root`, because the installed tries to manipulate the permissions on the plugin's +# config directory. RUN chmod g=u /etc/passwd && \\ chmod 0555 /usr/local/bin/docker-entrypoint.sh && \\ find / -xdev -perm -4000 -exec chmod ug-s {} + && \\ chmod 0775 /usr/share/elasticsearch && \\ chown elasticsearch bin config config/jvm.options.d data logs plugins +# Update "cacerts" bundle to use Ubuntu's CA certificates (and make sure it +# stays up-to-date with changes to Ubuntu's store) +COPY bin/docker-openjdk /etc/ca-certificates/update.d/docker-openjdk +RUN /etc/ca-certificates/update.d/docker-openjdk EXPOSE 9200 9300 @@ -147,16 +155,6 @@ LABEL org.label-schema.build-date="${build_date}" \\ org.opencontainers.image.vendor="Elastic" \\ org.opencontainers.image.version="${version}" -LABEL name="Elasticsearch" \\ - maintainer="infra@elastic.co" \\ - vendor="Elastic" \\ - version="${version}" \\ - release="1" \\ - summary="Elasticsearch" \\ - description="You know, for search." - -RUN mkdir /licenses && ln LICENSE.txt /licenses/LICENSE - # Our actual entrypoint is `tini`, a minimal but functional init program. It # calls the entrypoint we provide, while correctly forwarding signals. ENTRYPOINT ["/bin/tini", "--", "/usr/local/bin/docker-entrypoint.sh"] From 08e60f0d7c98e5ebad8b0af04f3a4f0945568674 Mon Sep 17 00:00:00 2001 From: Rene Groeschke Date: Thu, 29 May 2025 00:18:26 +0200 Subject: [PATCH 4/6] Fix merge in docker default --- distribution/docker/src/docker/dockerfiles/default/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/distribution/docker/src/docker/dockerfiles/default/Dockerfile b/distribution/docker/src/docker/dockerfiles/default/Dockerfile index 8747512ff3fc0..19a9bfec17a71 100644 --- a/distribution/docker/src/docker/dockerfiles/default/Dockerfile +++ b/distribution/docker/src/docker/dockerfiles/default/Dockerfile @@ -43,7 +43,9 @@ RUN set -eux ; \\ mv \${tini_bin} /bin/tini ; \\ chmod 0555 /bin/tini +RUN mkdir /usr/share/elasticsearch WORKDIR /usr/share/elasticsearch + RUN curl --retry 10 -S -L --output /tmp/elasticsearch.tar.gz https://artifacts-no-kpi.elastic.co/downloads/elasticsearch/elasticsearch-${version}-linux-\$(arch).tar.gz RUN tar -zxf /tmp/elasticsearch.tar.gz --strip-components=1 From 74539a53e55ab9678d102e0cfa9b9d0cd30423d9 Mon Sep 17 00:00:00 2001 From: Rene Groeschke Date: Thu, 29 May 2025 00:32:11 +0200 Subject: [PATCH 5/6] Fix merge issue --- .../src/docker/dockerfiles/wolfi/Dockerfile | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/distribution/docker/src/docker/dockerfiles/wolfi/Dockerfile b/distribution/docker/src/docker/dockerfiles/wolfi/Dockerfile index 8d6e090dbdaac..7cc86fb395c7a 100644 --- a/distribution/docker/src/docker/dockerfiles/wolfi/Dockerfile +++ b/distribution/docker/src/docker/dockerfiles/wolfi/Dockerfile @@ -29,21 +29,9 @@ FROM docker.elastic.co/wolfi/chainguard-base:latest@sha256:29150cd940cc7f69407d9 # Install required packages to extract the Elasticsearch distribution RUN <%= retry.loop(package_manager, "export DEBIAN_FRONTEND=noninteractive && ${package_manager} update && ${package_manager} update && ${package_manager} add --no-cache curl") %> -# `tini` is a tiny but valid init for containers. This is used to cleanly -# control how ES and any child processes are shut down. -# For wolfi we pick it from the blessed wolfi package registry. -# -# The tini GitHub page gives instructions for verifying the binary using -# gpg, but the keyservers are slow to return the key and this can fail the -# build. Instead, we check the binary against the published checksum. - RUN mkdir /usr/share/elasticsearch WORKDIR /usr/share/elasticsearch - -# Fetch the appropriate Elasticsearch distribution for this architecture. -# Keep this command on one line - it is replaced with a `COPY` during local builds. -# It uses the `arch` shell command to fetch the correct distro for the build machine, RUN curl --retry 10 -S -L --output /tmp/elasticsearch.tar.gz https://artifacts-no-kpi.elastic.co/downloads/elasticsearch/elasticsearch-${version}-linux-\${arch}.tar.gz RUN tar -zxf /tmp/elasticsearch.tar.gz --strip-components=1 @@ -153,16 +141,6 @@ LABEL org.label-schema.build-date="${build_date}" \\ org.opencontainers.image.vendor="Elastic" \\ org.opencontainers.image.version="${version}" -LABEL name="Elasticsearch" \\ - maintainer="infra@elastic.co" \\ - vendor="Elastic" \\ - version="${version}" \\ - release="1" \\ - summary="Elasticsearch" \\ - description="You know, for search." - -RUN mkdir /licenses && ln LICENSE.txt /licenses/LICENSE - # Our actual entrypoint is `tini`, a minimal but functional init program. It # calls the entrypoint we provide, while correctly forwarding signals. ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/docker-entrypoint.sh"] From df23e78c7cca513b090b542150bd05c4f699447a Mon Sep 17 00:00:00 2001 From: Rene Groeschke Date: Thu, 29 May 2025 09:46:31 +0200 Subject: [PATCH 6/6] Fix exit code 100 on docker build apt-get run --- distribution/docker/src/docker/dockerfiles/default/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distribution/docker/src/docker/dockerfiles/default/Dockerfile b/distribution/docker/src/docker/dockerfiles/default/Dockerfile index 19a9bfec17a71..7a0fd7d5cd3a9 100644 --- a/distribution/docker/src/docker/dockerfiles/default/Dockerfile +++ b/distribution/docker/src/docker/dockerfiles/default/Dockerfile @@ -21,7 +21,7 @@ FROM ${base_image} AS builder # Install required packages to extract the Elasticsearch distribution -RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y curl +RUN apt-get update -y && DEBIAN_FRONTEND=noninteractive apt-get install -y curl # `tini` is a tiny but valid init for containers. This is used to cleanly # control how ES and any child processes are shut down.