diff --git a/distribution/docker/build.gradle b/distribution/docker/build.gradle index a71180c7df78d..b54cb91c25450 100644 --- a/distribution/docker/build.gradle +++ b/distribution/docker/build.gradle @@ -58,7 +58,7 @@ ext.expansions = { Architecture architecture, boolean oss, DockerBase base, bool RUN curl --retry 8 -S -L \\ --output /opt/elasticsearch.tar.gz \\ https://artifacts.elastic.co/downloads/elasticsearch/$elasticsearch -""" +""".trim() } return [ diff --git a/distribution/docker/docker-test-entrypoint.sh b/distribution/docker/docker-test-entrypoint.sh index 1dca4b6a35e73..f1b83a56c598b 100755 --- a/distribution/docker/docker-test-entrypoint.sh +++ b/distribution/docker/docker-test-entrypoint.sh @@ -4,4 +4,4 @@ cd /usr/share/elasticsearch/bin/ echo "testnode" > /tmp/password cat /tmp/password | ./elasticsearch-keystore add -x -f -v 'xpack.security.transport.ssl.keystore.secure_password' cat /tmp/password | ./elasticsearch-keystore add -x -f -v 'xpack.security.http.ssl.keystore.secure_password' -/usr/local/bin/docker-entrypoint.sh | tee > /usr/share/elasticsearch/logs/console.log +/usr/local/bin/docker-entrypoint.sh | tee /usr/share/elasticsearch/logs/console.log diff --git a/distribution/docker/src/docker/Dockerfile b/distribution/docker/src/docker/Dockerfile index 61df01049fa5f..c0c98651ca7f6 100644 --- a/distribution/docker/src/docker/Dockerfile +++ b/distribution/docker/src/docker/Dockerfile @@ -14,16 +14,17 @@ layout/presentation here has been adjusted so that it looks reasonable when rendered, at the slight expense of how it looks here. */ %> +<% if (docker_base == "ubi") { %> ################################################################################ # Build stage 0 `builder`: # Extract Elasticsearch artifact ################################################################################ FROM ${base_image} AS builder -<% if (docker_base == 'ubi') { %> + # Install required packages to extract the Elasticsearch distribution RUN ${package_manager} install -y 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. # @@ -41,21 +42,167 @@ RUN set -eux ; \\ curl --retry 8 -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} /tini ; \\ - chmod +x /tini + mv \${tini_bin} /bin/tini ; \\ + chmod +x /bin/tini +<% } else { %> +################################################################################ +# Stage 1. Build curl statically. Installing it from RPM on CentOS pulls in too +# many dependencies. +################################################################################ +FROM alpine:latest AS curl + +ENV VERSION 7.71.0 +ENV TARBALL_URL https://curl.haxx.se/download/curl-\${VERSION}.tar.xz +ENV TARBALL_PATH curl-\${VERSION}.tar.xz + +# Install dependencies +RUN apk add gnupg gcc make musl-dev openssl-dev openssl-libs-static file + +RUN mkdir /work +WORKDIR /work + +# Fetch curl sources and files for validation +RUN wget "https://daniel.haxx.se/mykey.asc" -O "curl-gpg.pub" && \\ + wget "\${TARBALL_URL}.asc" -O "\${TARBALL_PATH}.asc" && \\ + wget "\${TARBALL_URL}" -O "\${TARBALL_PATH}" + +# Validate source +RUN gpg --import --always-trust "curl-gpg.pub" && \\ + gpg --verify "\${TARBALL_PATH}.asc" "\${TARBALL_PATH}" + +# Unpack and build +RUN tar xfJ "\${TARBALL_PATH}" && \\ + cd "curl-\${VERSION}" && \\ + ./configure --disable-shared --with-ca-fallback --with-ca-bundle=/etc/pki/tls/certs/ca-bundle.crt && \\ + make curl_LDFLAGS="-all-static" && \\ + cp src/curl /work/curl && \\ + strip /work/curl + +################################################################################ +# Step 2. Create a minimal root filesystem directory. This will form the basis +# for our image. +################################################################################ +FROM ${base_image} AS rootfs + +ENV BUSYBOX_VERSION 1.31.0 +ENV TINI_VERSION 0.19.0 + +# Start off with an up-to-date system +RUN ${package_manager} update --setopt=tsflags=nodocs -y + +# Create a directory into which we will install files +RUN mkdir /rootfs + +# Create required devices +RUN mkdir -m 755 /rootfs/dev && \\ + mknod -m 600 /rootfs/dev/console c 5 1 && \\ + mknod -m 600 /rootfs/dev/initctl p && \\ + mknod -m 666 /rootfs/dev/full c 1 7 && \\ + mknod -m 666 /rootfs/dev/null c 1 3 && \\ + mknod -m 666 /rootfs/dev/ptmx c 5 2 && \\ + mknod -m 666 /rootfs/dev/random c 1 8 && \\ + mknod -m 666 /rootfs/dev/tty c 5 0 && \\ + mknod -m 666 /rootfs/dev/tty0 c 4 0 && \\ + mknod -m 666 /rootfs/dev/urandom c 1 9 && \\ + mknod -m 666 /rootfs/dev/zero c 1 5 + +# Install a minimal set of dependencies, and some for Elasticsearch +RUN ${package_manager} --installroot=/rootfs --releasever=/ --setopt=tsflags=nodocs \\ + --setopt=group_package_types=mandatory -y \\ + --skip-broken \\ + install basesystem bash zip zlib + +# `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. +# +# Also, we use busybox instead of installing utility RPMs, which pulls in +# all kinds of stuff we don't want. +RUN set -e ; \\ + TINI_BIN="" ; \\ + BUSYBOX_ARCH="" ; \\ + case "\$(arch)" in \\ + aarch64) \\ + BUSYBOX_ARCH='armv8l' ; \\ + TINI_BIN='tini-arm64' ; \\ + ;; \\ + x86_64) \\ + BUSYBOX_ARCH='x86_64' ; \\ + TINI_BIN='tini-amd64' ; \\ + ;; \\ + *) echo >&2 "Unsupported architecture \$(arch)" ; exit 1 ;; \\ + esac ; \\ + curl --retry 8 -S -L -O "https://github.com/krallin/tini/releases/download/v0.19.0/\${TINI_BIN}" ; \\ + curl --retry 8 -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}" /rootfs/bin/tini ; \\ + chmod +x /rootfs/bin/tini ; \\ + curl --retry 10 -L -o /rootfs/bin/busybox \\ + "https://busybox.net/downloads/binaries/\${BUSYBOX_VERSION}-defconfig-multiarch-musl/busybox-\${BUSYBOX_ARCH}" ; \\ + chmod +x /rootfs/bin/busybox + +# Add links for most of the Busybox utilities +RUN set -e ; \\ + for path in \$( /rootfs/bin/busybox --list-full | grep -v bin/sh | grep -v telnet | grep -v unzip); do \\ + ln /rootfs/bin/busybox /rootfs/\$path ; \\ + done + +# Curl needs files under here. More importantly, we change Elasticsearch's +# bundled JDK to use /etc/pki/ca-trust/extracted/java/cacerts instead of +# the bundled cacerts. +RUN mkdir -p /rootfs/etc && \\ + cp -a /etc/pki /rootfs/etc/ + +# Cleanup the filesystem +RUN ${package_manager} --installroot=/rootfs -y clean all && \\ + cd /rootfs && \\ + rm -rf \\ + etc/{X11,centos-release*,csh*,profile*,skel*,yum*} \\ + sbin/sln \\ + usr/bin/rpm \\ + {usr,var}/games \\ + usr/lib/{dracut,systemd,udev} \\ + usr/lib64/X11 \\ + usr/local \\ + usr/share/{awk,centos-release,cracklib,desktop-directories,gcc-*,i18n,icons,licenses,xsessions,zoneinfo} \\ + usr/share/{man,doc,info,games,gdb,ghostscript,gnome,groff,icons,pixmaps,sounds,backgrounds,themes,X11} \\ + usr/{{lib,share}/locale,{lib,lib64}/gconv,bin/localedef,sbin/build-locale-archive} \\ + var/cache/yum \\ + var/lib/{rpm,yum} \\ + var/log/yum.log + +# ldconfig +RUN rm -rf /rootfs/etc/ld.so.cache /rootfs/var/cache/ldconfig && \\ + mkdir -p --mode=0755 /rootfs/var/cache/ldconfig + +COPY --from=curl /work/curl /rootfs/usr/bin/curl + +################################################################################ +# Step 3. Fetch the Elasticsearch distribution and configure it for Docker +################################################################################ +FROM ${base_image} AS builder +<% } %> RUN mkdir /usr/share/elasticsearch WORKDIR /usr/share/elasticsearch +# Fetch the appropriate Elasticsearch distribution for this architecture ${source_elasticsearch} RUN tar zxf /opt/elasticsearch.tar.gz --strip-components=1 + +# Configure the distribution for Docker RUN sed -i -e 's/ES_DISTRIBUTION_TYPE=tar/ES_DISTRIBUTION_TYPE=docker/' /usr/share/elasticsearch/bin/elasticsearch-env RUN mkdir -p config config/jvm.options.d data logs RUN chmod 0775 config config/jvm.options.d data logs COPY config/elasticsearch.yml config/log4j2.properties config/ RUN chmod 0660 config/elasticsearch.yml config/log4j2.properties +<% if (docker_base == "ubi") { %> ################################################################################ # Build stage 1 (the actual Elasticsearch image): # @@ -65,12 +212,10 @@ RUN chmod 0660 config/elasticsearch.yml config/log4j2.properties FROM ${base_image} -ENV ELASTIC_CONTAINER true - RUN for iter in {1..10}; do \\ ${package_manager} update --setopt=tsflags=nodocs -y && \\ ${package_manager} install --setopt=tsflags=nodocs -y \\ - nc shadow-utils zip unzip <%= docker_base == 'ubi' ? 'findutils procps-ng' : '' %> && \\ + nc shadow-utils zip unzip findutils procps-ng && \\ ${package_manager} clean all && exit_code=0 && break || exit_code=\$? && echo "${package_manager} error: retry \$iter in 10s" && \\ sleep 10; \\ done; \\ @@ -80,10 +225,30 @@ RUN groupadd -g 1000 elasticsearch && \\ adduser -u 1000 -g 1000 -G 0 -d /usr/share/elasticsearch elasticsearch && \\ chmod 0775 /usr/share/elasticsearch && \\ chown -R 1000:0 /usr/share/elasticsearch +<% } else { %> +################################################################################ +# Stage 4. Build the final image, using the rootfs above as the basis, and +# copying in the Elasticsearch distribution +################################################################################ +FROM scratch + +# Setup the initial filesystem. +COPY --from=rootfs /rootfs / + +RUN addgroup -g 1000 elasticsearch && \\ + adduser -D -u 1000 -G elasticsearch -g elasticsearch -h /usr/share/elasticsearch elasticsearch && \\ + addgroup elasticsearch root && \\ + chmod 0775 /usr/share/elasticsearch && \\ + chgrp 0 /usr/share/elasticsearch +<% } %> + +ENV ELASTIC_CONTAINER true WORKDIR /usr/share/elasticsearch COPY --from=builder --chown=1000:0 /usr/share/elasticsearch /usr/share/elasticsearch -COPY --from=builder --chown=0:0 /tini /tini +<% if (docker_base == "ubi") { %> +COPY --from=builder --chown=0:0 /bin/tini /bin/tini +<% } %> # Replace OpenJDK's built-in CA certificate keystore with the one from the OS # vendor. The latter is superior in several ways. @@ -94,14 +259,15 @@ ENV PATH /usr/share/elasticsearch/bin:\$PATH COPY bin/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh -# The JDK's directories' permissions don't allow `java` to be executed under a different -# group to the default. Fix this. +# 1. The JDK's directories' permissions don't allow `java` to be executed under a different +# group to the default. Fix this. +# 2. Sync the user and group permissions of /etc/passwd +# 3. Set correct permissions of the entrypoint +# 4. Ensure that there are no files with setuid or setgid, in order to mitigate "stackclash" attacks. RUN find /usr/share/elasticsearch/jdk -type d -exec chmod 0755 '{}' \\; && \\ chmod g=u /etc/passwd && \\ - chmod 0775 /usr/local/bin/docker-entrypoint.sh - -# Ensure that there are no files with setuid or setgid, in order to mitigate "stackclash" attacks. -RUN find / -xdev -perm -4000 -exec chmod ug-s {} + + chmod 0775 /usr/local/bin/docker-entrypoint.sh && \\ + find / -xdev -perm -4000 -exec chmod ug-s {} + EXPOSE 9200 9300 @@ -138,7 +304,10 @@ RUN mkdir /licenses && \\ <% } %> USER elasticsearch:root -ENTRYPOINT ["/tini", "--", "/usr/local/bin/docker-entrypoint.sh"] +# 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"] diff --git a/docs/reference/setup/install/docker.asciidoc b/docs/reference/setup/install/docker.asciidoc index 4a5e52c34c9e6..33ba28a3624ab 100644 --- a/docs/reference/setup/install/docker.asciidoc +++ b/docs/reference/setup/install/docker.asciidoc @@ -1,8 +1,9 @@ [[docker]] === Install {es} with Docker -{es} is also available as Docker images. -The images use https://hub.docker.com/_/centos/[centos:8] as the base image. +{es} is also available as Docker images. Starting with version 8.0.0, these +are based upon a tiny core of essential files. Prior versions used +https://hub.docker.com/_/centos/[centos:8] as the base image. A list of all published Docker images and tags is available at https://www.docker.elastic.co[www.docker.elastic.co]. The source files @@ -424,4 +425,23 @@ You must explicitly accept them either by: See {plugins}/_other_command_line_parameters.html[Plugin management] for more information. +The {es} Docker image only includes what is required to run {es}, and does +not provide a package manager. It is possible to add additional utilities +with a multi-phase Docker build. You must also copy any dependencies, for +example shared libraries. + +[source,sh,subs="attributes"] +-------------------------------------------- +FROM centos:8 AS builder +yum install -y some-package + +FROM docker.elastic.co/elasticsearch/elasticsearch:{version} +COPY --from=builder /usr/bin/some-utility /usr/bin/ +COPY --from=builder /usr/lib/some-lib.so /usr/lib/ +-------------------------------------------- + +You should use `centos:8` as a base in order to avoid incompatibilities. +Use http://man7.org/linux/man-pages/man1/ldd.1.html[`ldd`] to list the +shared libraries required by a utility. + include::next-steps.asciidoc[] diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/test/DockerTests.java b/qa/os/src/test/java/org/elasticsearch/packaging/test/DockerTests.java index 9bc3faede7944..c2cf1908e487a 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/test/DockerTests.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/test/DockerTests.java @@ -24,6 +24,7 @@ import org.elasticsearch.packaging.util.Distribution; import org.elasticsearch.packaging.util.Installation; import org.elasticsearch.packaging.util.Platforms; +import org.elasticsearch.packaging.util.ProcessInfo; import org.elasticsearch.packaging.util.ServerUtils; import org.elasticsearch.packaging.util.Shell; import org.elasticsearch.packaging.util.Shell.Result; @@ -35,10 +36,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; import static java.nio.file.attribute.PosixFilePermissions.fromString; import static org.elasticsearch.packaging.util.Docker.chownWithPrivilegeEscalation; @@ -62,13 +61,11 @@ import static org.elasticsearch.packaging.util.FileUtils.append; import static org.elasticsearch.packaging.util.FileUtils.rm; import static org.hamcrest.Matchers.arrayContaining; -import static org.hamcrest.Matchers.arrayWithSize; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasKey; -import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; @@ -465,7 +462,7 @@ public void test085EnvironmentVariablesAreRespectedUnderDockerExec() { final Result result = sh.runIgnoreExitCode("elasticsearch-setup-passwords auto"); assertFalse("elasticsearch-setup-passwords command should have failed", result.isSuccess()); - assertThat(result.stdout, containsString("java.net.UnknownHostException: this.is.not.valid: Name or service not known")); + assertThat(result.stdout, containsString("java.net.UnknownHostException: this.is.not.valid")); } /** @@ -535,7 +532,7 @@ public void test101AllFilesAreGroupZero() { // expected group. final Shell localSh = new Shell(); final String findResults = localSh.run( - "docker run --rm --tty " + getImageName(distribution) + " bash -c ' touch data/test && find . -not -gid 0 ' " + "docker run --rm --tty " + getImageName(distribution) + " bash -c ' touch data/test && find . \\! -group 0 ' " ).stdout; assertThat("Found some files whose GID != 0", findResults, is(emptyString())); @@ -631,16 +628,13 @@ public void test120DockerLogsIncludeElasticsearchLogs() throws Exception { * Check that the Java process running inside the container has the expected UID, GID and username. */ public void test130JavaHasCorrectOwnership() { - final List processes = sh.run("ps -o uid,gid,user -C java").stdout.lines().skip(1).collect(Collectors.toList()); + final ProcessInfo info = ProcessInfo.getProcessInfo(sh, "java"); - assertThat("Expected a single java process", processes, hasSize(1)); + assertThat("Incorrect UID", info.uid, equalTo(1000)); + assertThat("Incorrect username", info.username, equalTo("elasticsearch")); - final String[] fields = processes.get(0).trim().split("\\s+"); - - assertThat(fields, arrayWithSize(3)); - assertThat("Incorrect UID", fields[0], equalTo("1000")); - assertThat("Incorrect GID", fields[1], equalTo("0")); - assertThat("Incorrect username", fields[2], equalTo("elasticsearch")); + assertThat("Incorrect GID", info.gid, equalTo(0)); + assertThat("Incorrect group", info.group, equalTo("root")); } /** @@ -648,17 +642,15 @@ public void test130JavaHasCorrectOwnership() { * The PID is particularly important because PID 1 handles signal forwarding and child reaping. */ public void test131InitProcessHasCorrectPID() { - final List processes = sh.run("ps -o pid,uid,gid,user -p 1").stdout.lines().skip(1).collect(Collectors.toList()); + final ProcessInfo info = ProcessInfo.getProcessInfo(sh, "tini"); - assertThat("Expected a single process", processes, hasSize(1)); + assertThat("Incorrect PID", info.pid, equalTo(1)); - final String[] fields = processes.get(0).trim().split("\\s+"); + assertThat("Incorrect UID", info.uid, equalTo(1000)); + assertThat("Incorrect username", info.username, equalTo("elasticsearch")); - assertThat(fields, arrayWithSize(4)); - assertThat("Incorrect PID", fields[0], equalTo("1")); - assertThat("Incorrect UID", fields[1], equalTo("1000")); - assertThat("Incorrect GID", fields[2], equalTo("0")); - assertThat("Incorrect username", fields[3], equalTo("elasticsearch")); + assertThat("Incorrect GID", info.gid, equalTo(0)); + assertThat("Incorrect group", info.group, equalTo("root")); } /** diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/Docker.java b/qa/os/src/test/java/org/elasticsearch/packaging/util/Docker.java index f19fc8067be4e..6ba74d19e388f 100644 --- a/qa/os/src/test/java/org/elasticsearch/packaging/util/Docker.java +++ b/qa/os/src/test/java/org/elasticsearch/packaging/util/Docker.java @@ -231,7 +231,8 @@ public static void waitForElasticsearchToStart() { // Give the container a chance to crash out Thread.sleep(STARTUP_SLEEP_INTERVAL_MILLISECONDS); - psOutput = dockerShell.run("ps -ww ax").stdout; + // Set COLUMNS so that `ps` doesn't truncate its output + psOutput = dockerShell.run("bash -c 'COLUMNS=2000 ps ax'").stdout; if (psOutput.contains("org.elasticsearch.bootstrap.Elasticsearch")) { isElasticsearchRunning = true; @@ -475,7 +476,7 @@ public static void chownWithPrivilegeEscalation(Path localPath, String ownership public static void assertPermissionsAndOwnership(Path path, Set expectedPermissions) { logger.debug("Checking permissions and ownership of [" + path + "]"); - final String[] components = dockerShell.run("stat --format=\"%U %G %A\" " + path).stdout.split("\\s+"); + final String[] components = dockerShell.run("stat -c \"%U %G %A\" " + path).stdout.split("\\s+"); final String username = components[0]; final String group = components[1]; @@ -548,13 +549,15 @@ private static void verifyOssInstallation(Installation es) { Stream.of("LICENSE.txt", "NOTICE.txt", "README.asciidoc").forEach(doc -> assertPermissionsAndOwnership(es.home.resolve(doc), p644)); - // These are installed to help users who are working with certificates. - Stream.of("zip", "unzip").forEach(cliPackage -> { - // We could run `yum list installed $pkg` but that causes yum to call out to the network. - // rpm does the job just as well. - final Shell.Result result = dockerShell.runIgnoreExitCode("rpm -q " + cliPackage); - assertTrue(cliPackage + " ought to be installed. " + result, result.isSuccess()); - }); + // nc is useful for checking network issues + // zip/unzip are installed to help users who are working with certificates. + Stream.of("nc", "unzip", "zip") + .forEach( + cliBinary -> assertTrue( + cliBinary + " ought to be available.", + dockerShell.runIgnoreExitCode("hash " + cliBinary).isSuccess() + ) + ); } private static void verifyDefaultInstallation(Installation es) { diff --git a/qa/os/src/test/java/org/elasticsearch/packaging/util/ProcessInfo.java b/qa/os/src/test/java/org/elasticsearch/packaging/util/ProcessInfo.java new file mode 100644 index 0000000000000..58dc5f16ce128 --- /dev/null +++ b/qa/os/src/test/java/org/elasticsearch/packaging/util/ProcessInfo.java @@ -0,0 +1,81 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.packaging.util; + +import java.util.List; +import java.util.stream.Collectors; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; + +/** + * Encapsulates the fetching of information about a running process. + *

+ * This is helpful on stripped-down Docker images where, in order to fetch full information about a process, + * we have to consult /proc. Although this class hides the implementation details, it only + * works in Linux containers. At the moment that isn't a problem, because we only publish Docker images + * for Linux. + */ +public class ProcessInfo { + public final int pid; + public final int uid; + public final int gid; + public final String username; + public final String group; + + public ProcessInfo(int pid, int uid, int gid, String username, String group) { + this.pid = pid; + this.uid = uid; + this.gid = gid; + this.username = username; + this.group = group; + } + + /** + * Fetches process information about command, using sh to execute commands. + * @return a populated ProcessInfo object + */ + public static ProcessInfo getProcessInfo(Shell sh, String command) { + final List processes = sh.run("pgrep " + command).stdout.lines().collect(Collectors.toList()); + + assertThat("Expected a single process", processes, hasSize(1)); + + // Ensure we actually have a number + final int pid = Integer.parseInt(processes.get(0).trim()); + + int uid = -1; + int gid = -1; + + for (String line : sh.run("cat /proc/" + pid + "/status | grep '^[UG]id:'").stdout.split("\\n")) { + final String[] fields = line.split("\\s+"); + + if (fields[0].equals("Uid:")) { + uid = Integer.parseInt(fields[1]); + } else { + gid = Integer.parseInt(fields[1]); + } + } + + final String username = sh.run("getent passwd " + uid + " | cut -f1 -d:").stdout.trim(); + final String group = sh.run("getent group " + gid + " | cut -f1 -d:").stdout.trim(); + + return new ProcessInfo(pid, uid, gid, username, group); + } +} diff --git a/qa/remote-clusters/docker-test-entrypoint.sh b/qa/remote-clusters/docker-test-entrypoint.sh index 1dca4b6a35e73..f1b83a56c598b 100755 --- a/qa/remote-clusters/docker-test-entrypoint.sh +++ b/qa/remote-clusters/docker-test-entrypoint.sh @@ -4,4 +4,4 @@ cd /usr/share/elasticsearch/bin/ echo "testnode" > /tmp/password cat /tmp/password | ./elasticsearch-keystore add -x -f -v 'xpack.security.transport.ssl.keystore.secure_password' cat /tmp/password | ./elasticsearch-keystore add -x -f -v 'xpack.security.http.ssl.keystore.secure_password' -/usr/local/bin/docker-entrypoint.sh | tee > /usr/share/elasticsearch/logs/console.log +/usr/local/bin/docker-entrypoint.sh | tee /usr/share/elasticsearch/logs/console.log