Skip to content

Commit 8feb946

Browse files
authored
Merge pull request #31336 from zakkak/2023-02-22-podman-fix
Pass `--userns=keep-id` to podman only when in rootless mode
2 parents 46ac546 + 2bace22 commit 8feb946

File tree

3 files changed

+55
-71
lines changed

3 files changed

+55
-71
lines changed

core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildLocalContainerRunner.java

Lines changed: 4 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,16 @@
22

33
import static io.quarkus.deployment.pkg.steps.LinuxIDUtil.getLinuxID;
44

5-
import java.io.File;
6-
import java.io.IOException;
7-
import java.nio.file.Files;
8-
import java.nio.file.LinkOption;
95
import java.nio.file.Path;
10-
import java.time.Duration;
116
import java.util.ArrayList;
127
import java.util.Arrays;
138
import java.util.Collections;
149
import java.util.List;
15-
import java.util.Objects;
16-
import java.util.Set;
17-
import java.util.function.Predicate;
18-
import java.util.stream.Collectors;
1910

2011
import org.apache.commons.lang3.SystemUtils;
2112
import org.jboss.logging.Logger;
2213

23-
import io.quarkus.deployment.OutputFilter;
2414
import io.quarkus.deployment.pkg.NativeConfig;
25-
import io.quarkus.deployment.util.ExecUtil;
2615
import io.quarkus.deployment.util.FileUtil;
2716
import io.quarkus.runtime.util.ContainerRuntimeUtil;
2817

@@ -34,14 +23,16 @@ public NativeImageBuildLocalContainerRunner(NativeConfig nativeConfig, Path outp
3423
super(nativeConfig, outputDir);
3524
if (SystemUtils.IS_OS_LINUX) {
3625
ArrayList<String> containerRuntimeArgs = new ArrayList<>(Arrays.asList(baseContainerRuntimeArgs));
37-
if (isDockerRootless(containerRuntime)) {
26+
if (containerRuntime == ContainerRuntimeUtil.ContainerRuntime.DOCKER
27+
&& containerRuntime.isRootless()) {
3828
Collections.addAll(containerRuntimeArgs, "--user", String.valueOf(0));
3929
} else {
4030
String uid = getLinuxID("-ur");
4131
String gid = getLinuxID("-gr");
4232
if (uid != null && gid != null && !uid.isEmpty() && !gid.isEmpty()) {
4333
Collections.addAll(containerRuntimeArgs, "--user", uid + ":" + gid);
44-
if (containerRuntime == ContainerRuntimeUtil.ContainerRuntime.PODMAN) {
34+
if (containerRuntime == ContainerRuntimeUtil.ContainerRuntime.PODMAN
35+
&& containerRuntime.isRootless()) {
4536
// Needed to avoid AccessDeniedExceptions
4637
containerRuntimeArgs.add("--userns=keep-id");
4738
}
@@ -51,63 +42,6 @@ public NativeImageBuildLocalContainerRunner(NativeConfig nativeConfig, Path outp
5142
}
5243
}
5344

54-
private static boolean isDockerRootless(ContainerRuntimeUtil.ContainerRuntime containerRuntime) {
55-
if (containerRuntime != ContainerRuntimeUtil.ContainerRuntime.DOCKER) {
56-
return false;
57-
}
58-
String dockerEndpoint = fetchDockerEndpoint();
59-
// docker socket?
60-
String socketUriPrefix = "unix://";
61-
if (dockerEndpoint == null || !dockerEndpoint.startsWith(socketUriPrefix)) {
62-
return false;
63-
}
64-
String dockerSocket = dockerEndpoint.substring(socketUriPrefix.length());
65-
String currentUid = getLinuxID("-ur");
66-
if (currentUid == null || currentUid.isEmpty() || currentUid.equals(String.valueOf(0))) {
67-
return false;
68-
}
69-
70-
int socketOwnerUid;
71-
try {
72-
socketOwnerUid = (int) Files.getAttribute(Path.of(dockerSocket), "unix:uid", LinkOption.NOFOLLOW_LINKS);
73-
} catch (IOException e) {
74-
LOGGER.infof("Owner UID lookup on '%s' failed with '%s'", dockerSocket, e.getMessage());
75-
return false;
76-
}
77-
return currentUid.equals(String.valueOf(socketOwnerUid));
78-
}
79-
80-
private static String fetchDockerEndpoint() {
81-
// DOCKER_HOST environment variable overrides the active context
82-
String dockerHost = System.getenv("DOCKER_HOST");
83-
if (dockerHost != null) {
84-
return dockerHost;
85-
}
86-
87-
OutputFilter outputFilter = new OutputFilter();
88-
if (!ExecUtil.execWithTimeout(new File("."), outputFilter, Duration.ofMillis(3000),
89-
"docker", "context", "ls", "--format",
90-
"{{- if .Current -}} {{- .DockerEndpoint -}} {{- end -}}")) {
91-
LOGGER.debug("Docker context lookup didn't succeed in time");
92-
return null;
93-
}
94-
95-
Set<String> endpoints = outputFilter.getOutput().lines()
96-
.filter(Objects::nonNull)
97-
.filter(Predicate.not(String::isBlank))
98-
.collect(Collectors.toSet());
99-
if (endpoints.size() == 1) {
100-
return endpoints.stream().findFirst().orElse(null);
101-
}
102-
if (LOGGER.isDebugEnabled()) {
103-
LOGGER.debugf("Found too many active Docker endpoints: [%s]",
104-
endpoints.stream()
105-
.map(endpoint -> String.format("'%s'", endpoint))
106-
.collect(Collectors.joining(",")));
107-
}
108-
return null;
109-
}
110-
11145
@Override
11246
protected List<String> getContainerRuntimeBuildArgs() {
11347
List<String> containerRuntimeArgs = super.getContainerRuntimeBuildArgs();

core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/UpxCompressionBuildStep.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ private boolean runUpxInContainer(NativeImageBuildItem nativeImage, NativeConfig
124124
String gid = getLinuxID("-gr");
125125
if (uid != null && gid != null && !uid.isEmpty() && !gid.isEmpty()) {
126126
Collections.addAll(commandLine, "--user", uid + ":" + gid);
127-
if (containerRuntime == ContainerRuntimeUtil.ContainerRuntime.PODMAN) {
127+
if (containerRuntime == ContainerRuntimeUtil.ContainerRuntime.PODMAN
128+
&& containerRuntime.isRootless()) {
128129
// Needed to avoid AccessDeniedExceptions
129130
commandLine.add("--userns=keep-id");
130131
}

core/runtime/src/main/java/io/quarkus/runtime/util/ContainerRuntimeUtil.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package io.quarkus.runtime.util;
22

3+
import java.io.BufferedReader;
34
import java.io.IOException;
5+
import java.io.InputStream;
6+
import java.io.InputStreamReader;
47
import java.nio.charset.StandardCharsets;
8+
import java.util.function.Predicate;
59

610
import org.jboss.logging.Logger;
711

@@ -59,15 +63,60 @@ private static String getVersionOutputFor(ContainerRuntime containerRuntime) {
5963
}
6064
}
6165

66+
private static boolean getRootlessStateFor(ContainerRuntime containerRuntime) {
67+
Process rootlessProcess = null;
68+
try {
69+
ProcessBuilder pb = new ProcessBuilder(containerRuntime.getExecutableName(), "info")
70+
.redirectErrorStream(true);
71+
rootlessProcess = pb.start();
72+
int exitCode = rootlessProcess.waitFor();
73+
if (exitCode != 0) {
74+
log.warnf("Command \"%s\" exited with error code %d. " +
75+
"Rootless container runtime detection might not be reliable.",
76+
containerRuntime.getExecutableName(), exitCode);
77+
}
78+
try (InputStream inputStream = rootlessProcess.getInputStream();
79+
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
80+
BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
81+
Predicate<String> stringPredicate;
82+
// Docker includes just "rootless" under SecurityOptions, while podman includes "rootless: <boolean>"
83+
if (containerRuntime == ContainerRuntime.DOCKER) {
84+
stringPredicate = line -> line.trim().equals("rootless");
85+
} else {
86+
stringPredicate = line -> line.trim().equals("rootless: true");
87+
}
88+
return bufferedReader.lines().anyMatch(stringPredicate);
89+
}
90+
} catch (IOException | InterruptedException e) {
91+
// If an exception is thrown in the process, assume we are not running rootless (default docker installation)
92+
log.debugf(e, "Failure to read info output from %s", containerRuntime.getExecutableName());
93+
return false;
94+
} finally {
95+
if (rootlessProcess != null) {
96+
rootlessProcess.destroy();
97+
}
98+
}
99+
}
100+
62101
/**
63102
* Supported Container runtimes
64103
*/
65104
public enum ContainerRuntime {
66105
DOCKER,
67106
PODMAN;
68107

108+
private final boolean rootless;
109+
110+
ContainerRuntime() {
111+
this.rootless = getRootlessStateFor(this);
112+
}
113+
69114
public String getExecutableName() {
70115
return this.name().toLowerCase();
71116
}
117+
118+
public boolean isRootless() {
119+
return rootless;
120+
}
72121
}
73122
}

0 commit comments

Comments
 (0)