Skip to content

Commit

Permalink
Improve Docker detection with Unix sockets
Browse files Browse the repository at this point in the history
  • Loading branch information
gsmet committed Nov 29, 2022
1 parent c9a8ec2 commit fbcf30b
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 17 deletions.
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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;
}
}
Expand Down
24 changes: 19 additions & 5 deletions docs/src/main/asciidoc/podman.adoc
Expand Up @@ -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].

Expand Down

0 comments on commit fbcf30b

Please sign in to comment.