From faf5fcd8bec9bb7dcc87f24524df23fdcc5b25c2 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Tue, 29 Nov 2022 18:40:15 +0100 Subject: [PATCH] Improve Docker detection with Unix sockets Fixes #29426 --- .../quarkus/deployment/IsDockerWorking.java | 46 ++++++++++++++----- docs/src/main/asciidoc/podman.adoc | 24 ++++++++-- 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/IsDockerWorking.java b/core/deployment/src/main/java/io/quarkus/deployment/IsDockerWorking.java index de3618be44ac8..c504de2b24410 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/IsDockerWorking.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/IsDockerWorking.java @@ -9,6 +9,8 @@ import java.net.Socket; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; import java.time.Duration; import java.util.List; import java.util.Optional; @@ -73,8 +75,8 @@ private TestContainersStrategy(boolean silent) { @Override public Result get() { - //testcontainers uses the Unreliables library to test if docker is started - //this runs in threads that start with 'ducttape' + // Testcontainers uses the Unreliables library to test if docker is started + // this runs in threads that start with 'ducttape' StartupLogCompressor compressor = new StartupLogCompressor("Checking Docker Environment", Optional.empty(), null, (s) -> s.getName().startsWith("ducttape")); try { @@ -104,7 +106,7 @@ public Result get() { } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { if (!silent) { compressor.closeAndDumpCaptured(); - LOGGER.debug("Unable to use testcontainers to determine if Docker is working", e); + LOGGER.debug("Unable to use Testcontainers to determine if Docker is working", e); } return Result.UNKNOWN; } finally { @@ -122,26 +124,46 @@ public Result get() { */ private static class DockerHostStrategy implements Strategy { + private static final String UNIX_SCHEME = "unix"; + @Override public Result get() { - String dockerHost = System.getenv("DOCKER_HOST"); - if (dockerHost != null && !dockerHost.startsWith("unix:")) { - try { - URI url = new URI(dockerHost); + + if (dockerHost == null) { + return Result.UNKNOWN; + } + + try { + URI dockerHostUri = new URI(dockerHost); + + if (UNIX_SCHEME.equals(dockerHostUri.getScheme())) { + // Java 11 does not support connecting to Unix sockets so for now let's use a naive approach + Path dockerSocketPath = Path.of(dockerHostUri.getPath()); + + if (Files.isWritable(dockerSocketPath)) { + return Result.AVAILABLE; + } else { + LOGGER.warnf( + "Unix socket defined in DOCKER_HOST %s is not writable, make sure Docker is running on the specified host", + dockerHost); + } + } else { try (Socket s = new Socket()) { - s.connect(new InetSocketAddress(url.getHost(), url.getPort()), DOCKER_HOST_CHECK_TIMEOUT); + s.connect(new InetSocketAddress(dockerHostUri.getHost(), dockerHostUri.getPort()), + DOCKER_HOST_CHECK_TIMEOUT); return Result.AVAILABLE; } catch (IOException e) { LOGGER.warnf( - "Unable to connect to DOCKER_HOST URI %s, make sure docker is running on the specified host", + "Unable to connect to DOCKER_HOST URI %s, make sure Docker is running on the specified host", dockerHost); } - } catch (URISyntaxException | IllegalArgumentException e) { - LOGGER.warnf("Unable to parse DOCKER_HOST URI %s, it will be ignored for working docker detection", - dockerHost); } + } catch (URISyntaxException | IllegalArgumentException e) { + LOGGER.warnf("Unable to parse DOCKER_HOST URI %s, it will be ignored for working Docker detection", + dockerHost); } + return Result.UNKNOWN; } } diff --git a/docs/src/main/asciidoc/podman.adoc b/docs/src/main/asciidoc/podman.adoc index 79668648ac844..3dc68c1c3e842 100644 --- a/docs/src/main/asciidoc/podman.adoc +++ b/docs/src/main/asciidoc/podman.adoc @@ -93,21 +93,35 @@ sudo apt install podman podman-docker docker-compose Podman supports two modes of operation: rootful, in which case the container runs as root on the host system, and rootless, where the container runs under a standard Unix user account. On Linux, the REST API Unix socket is, by default, restricted to only allow the root user to access it. -This prevents someone from using a container to achieve a privilege escalation on the syetem. +This prevents someone from using a container to achieve a privilege escalation on the system. While these restrictions can be softened to allow a special group instead of just root, the recommended approach is to use rootless Podman on Linux. -To use rootless Podman, you need to set a DOCKER_HOST environment variable to point to the user-specific socket. +To use rootless Podman, you need to set a `DOCKER_HOST` environment variable to point to the user-specific socket. In both cases, you need to start the REST API by enabling the Podman socket service through systemd. [source] ---- - # Enable the podman socket with Docker REST API (only needs to be done once) systemctl --user enable podman.socket --now -# Set the required environment variables (need to be run everytime or added to profile) +---- + +Then, you can obtain the path of the socket with the following command: + +[source] +---- +$ podman info | grep -A2 'remoteSocket' + +remoteSocket: + exists: true + path: /path/to/podman.sock +---- -export DOCKER_HOST=unix:///run/user/${UID}/podman/podman.sock +Setting the `DOCKER_HOST` environment variable must be done every time or added to the profile: +[source] +---- +export DOCKER_HOST=unix:///path/to/podman.sock <1> ---- +<1> Replace `/path/to/podman.sock` with the path you obtained previously. For a detailed explanation, see this https://quarkus.io/blog/quarkus-devservices-testcontainers-podman/[blog article].