Browse Source

Switch to Ubuntu docker base image (#80640)

Switch the ES base Docker image for the default and Cloud images to `ubuntu:20.04`,
as Ubuntu has a more favourable posture on security updates.
Rory Hunter 3 years ago
parent
commit
7ec32669f6

+ 2 - 2
build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/DockerBase.java

@@ -12,7 +12,7 @@ package org.elasticsearch.gradle.internal;
  * This class models the different Docker base images that are used to build Docker distributions of Elasticsearch.
  */
 public enum DockerBase {
-    DEFAULT("almalinux:8.4-minimal", ""),
+    DEFAULT("ubuntu:20.04", ""),
 
     // "latest" here is intentional, since the image name specifies "8"
     UBI("docker.elastic.co/ubi8/ubi-minimal:latest", "-ubi8"),
@@ -21,7 +21,7 @@ public enum DockerBase {
     IRON_BANK("${BASE_REGISTRY}/${BASE_IMAGE}:${BASE_TAG}", "-ironbank"),
 
     // Base image with extras for Cloud
-    CLOUD("almalinux:8.4-minimal", "-cloud"),
+    CLOUD("ubuntu:20.04", "-cloud"),
 
     // Based on CLOUD above, with more extras. We don't set a base image because
     // we programmatically extend from the Cloud image.

+ 1 - 3
distribution/docker/build.gradle

@@ -96,7 +96,7 @@ ext.expansions = { Architecture architecture, DockerBase base ->
     'config_dir'         : base == DockerBase.IRON_BANK ? 'scripts' : 'config',
     'git_revision'       : BuildParams.gitRevision,
     'license'            : base == DockerBase.IRON_BANK ? 'Elastic License 2.0' : 'Elastic-License-2.0',
-    'package_manager'    : base == DockerBase.IRON_BANK ? 'yum' : 'microdnf',
+    'package_manager'    : base == DockerBase.IRON_BANK ? 'yum' : (base == DockerBase.UBI ? 'microdnf' : 'apt-get'),
     'docker_base'        : base.name().toLowerCase(),
     'version'            : VersionProperties.elasticsearch,
     'major_minor_version': "${major}.${minor}",
@@ -361,8 +361,6 @@ void addBuildDockerImageTask(Architecture architecture, DockerBase base) {
 
         baseImages = [baseImage]
         buildArgs = buildArgsMap
-      } else if (base == DockerBase.DEFAULT || base == DockerBase.CLOUD) {
-        baseImages = ['alpine:3.13', base.image]
       } else {
         baseImages = [base.image]
       }

+ 31 - 2
distribution/docker/src/docker/Dockerfile

@@ -44,7 +44,11 @@ RUN chmod 0555 /bin/tini
 FROM ${base_image} AS builder
 
 # Install required packages to extract the Elasticsearch distribution
+<% if (docker_base == 'default' || docker_base == 'cloud') { %>
+RUN <%= retry.loop(package_manager, "${package_manager} update && DEBIAN_FRONTEND=noninteractive ${package_manager} install -y curl ca-certificates-java") %>
+<% } else { %>
 RUN <%= retry.loop(package_manager, "${package_manager} 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.
@@ -152,6 +156,18 @@ RUN ${package_manager} update --setopt=tsflags=nodocs -y && \\
       nc shadow-utils zip findutils unzip procps-ng && \\
     ${package_manager} clean all
 
+<% } else if (docker_base == "default" || docker_base == "cloud") { %>
+
+RUN <%= 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 curl netcat zip unzip vim-tiny && \n" +
+      "${package_manager} clean && \n" +
+      "rm -rf /var/lib/apt/lists/*"
+  ) %>
+
 <% } else { %>
 
 RUN <%= retry.loop(
@@ -164,9 +180,17 @@ RUN <%= retry.loop(
 
 <% } %>
 
+
+<% if (docker_base == "default" || docker_base == "cloud") { %>
+RUN groupadd -g 1000 elasticsearch && \\
+    adduser --uid 1000 --gid 1000 --home /usr/share/elasticsearch elasticsearch && \\
+    adduser elasticsearch root && \\
+    chown -R 0:0 /usr/share/elasticsearch
+<% } else { %>
 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
 
@@ -175,6 +199,10 @@ COPY --from=builder --chown=0:0 /usr/share/elasticsearch /usr/share/elasticsearc
 
 COPY --from=builder --chown=0:0 /bin/tini /bin/tini
 
+<% if (docker_base == 'default' || docker_base == 'cloud') { %>
+COPY --from=builder --chown=0:0 /etc/ssl/certs/java/cacerts /etc/ssl/certs/java/cacerts
+<% } %>
+
 <% if (docker_base == 'cloud') { %>
 COPY --from=builder --chown=0:0 /opt /opt
 <% } %>
@@ -197,7 +225,8 @@ COPY ${bin_dir}/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 {} + && \\
-    ln -sf /etc/pki/ca-trust/extracted/java/cacerts /usr/share/elasticsearch/jdk/lib/security/cacerts && \\
+    ln -sf <%= docker_base == 'default' || docker_base == 'cloud' ? '/etc/ssl/certs/java/cacerts' : '/etc/pki/ca-trust/extracted/java/cacerts' %> \\
+      /usr/share/elasticsearch/jdk/lib/security/cacerts && \\
     chmod 0775 /usr/share/elasticsearch && \\
     chown elasticsearch bin config config/jvm.options.d data logs plugins
 
@@ -247,7 +276,7 @@ ENTRYPOINT ["/bin/tini", "--"]
 CMD ["/app/elasticsearch.sh"]
 # Generate a stub command that will be overwritten at runtime
 RUN mkdir /app && \\
-    echo -e '#!/bin/sh\\nexec /usr/local/bin/docker-entrypoint.sh eswrapper' > /app/elasticsearch.sh && \\
+    echo '#!/bin/bash\\nexec /usr/local/bin/docker-entrypoint.sh eswrapper' > /app/elasticsearch.sh && \\
     chmod 0555 /app/elasticsearch.sh
 
 <% } else { %>

+ 5 - 0
docs/changelog/80640.yaml

@@ -0,0 +1,5 @@
+pr: 80640
+summary: Switch Docker base image to Ubuntu 20.04
+area: Packaging
+type: enhancement
+issues: []

+ 10 - 2
qa/os/src/test/java/org/elasticsearch/packaging/test/DockerTests.java

@@ -373,15 +373,23 @@ public class DockerTests extends PackagingTestCase {
     public void test040JavaUsesTheOsProvidedKeystore() {
         final String path = sh.run("realpath jdk/lib/security/cacerts").stdout;
 
-        assertThat(path, equalTo("/etc/pki/ca-trust/extracted/java/cacerts"));
+        if (distribution.packaging == Packaging.DOCKER_UBI || distribution.packaging == Packaging.DOCKER_IRON_BANK) {
+            assertThat(path, equalTo("/etc/pki/ca-trust/extracted/java/cacerts"));
+        } else {
+            assertThat(path, equalTo("/etc/ssl/certs/java/cacerts"));
+        }
     }
 
     /**
      * Checks that there are Amazon trusted certificates in the cacaerts keystore.
      */
     public void test041AmazonCaCertsAreInTheKeystore() {
+        final String caName = distribution.packaging == Packaging.DOCKER_UBI || distribution.packaging == Packaging.DOCKER_IRON_BANK
+            ? "amazonrootca"
+            : "amazon_root_ca";
+
         final boolean matches = sh.run("jdk/bin/keytool -cacerts -storepass changeit -list | grep trustedCertEntry").stdout.lines()
-            .anyMatch(line -> line.contains("amazonrootca"));
+            .anyMatch(line -> line.contains(caName));
 
         assertTrue("Expected Amazon trusted cert in cacerts", matches);
     }