Skip to content

Commit

Permalink
Quarkus NativeImageBuildStep fails with perm denied with docker rootless
Browse files Browse the repository at this point in the history
  • Loading branch information
reda-alaoui committed Sep 11, 2022
1 parent 3f51f74 commit 0c47727
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@

package io.quarkus.deployment;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.net.InetSocketAddress;
import java.net.Socket;
Expand All @@ -15,7 +12,6 @@
import java.util.List;
import java.util.Optional;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
import java.util.function.Supplier;

import org.eclipse.microprofile.config.ConfigProvider;
Expand Down Expand Up @@ -176,29 +172,6 @@ public Result get() {
}
}

public static class OutputFilter implements Function<InputStream, Runnable> {
private final StringBuilder builder = new StringBuilder();

@Override
public Runnable apply(InputStream is) {
return () -> {

try (InputStreamReader isr = new InputStreamReader(is);
BufferedReader reader = new BufferedReader(isr)) {

for (String line = reader.readLine(); line != null; line = reader.readLine()) {
builder.append(line);
}
} catch (IOException e) {
throw new RuntimeException("Error reading stream.", e);
}
};
}

public String getOutput() {
return builder.toString();
}
}
}

private enum Result {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.quarkus.deployment;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.function.Function;

public class OutputFilter implements Function<InputStream, Runnable> {
private final StringBuilder builder = new StringBuilder();

@Override
public Runnable apply(InputStream is) {
return () -> {

try (InputStreamReader isr = new InputStreamReader(is);
BufferedReader reader = new BufferedReader(isr)) {

for (String line = reader.readLine(); line != null; line = reader.readLine()) {
builder.append(line);
}
} catch (IOException e) {
throw new RuntimeException("Error reading stream.", e);
}
};
}

public String getOutput() {
return builder.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,102 @@

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

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.apache.commons.lang3.SystemUtils;
import org.jboss.logging.Logger;

import io.quarkus.deployment.OutputFilter;
import io.quarkus.deployment.pkg.NativeConfig;
import io.quarkus.deployment.util.ExecUtil;
import io.quarkus.deployment.util.FileUtil;
import io.quarkus.runtime.util.ContainerRuntimeUtil;

public class NativeImageBuildLocalContainerRunner extends NativeImageBuildContainerRunner {

private static final Logger LOGGER = Logger.getLogger(NativeImageBuildLocalContainerRunner.class.getName());

public NativeImageBuildLocalContainerRunner(NativeConfig nativeConfig, Path outputDir) {
super(nativeConfig, outputDir);
if (SystemUtils.IS_OS_LINUX) {
ArrayList<String> containerRuntimeArgs = new ArrayList<>(Arrays.asList(baseContainerRuntimeArgs));
String uid = getLinuxID("-ur");
String gid = getLinuxID("-gr");
if (uid != null && gid != null && !uid.isEmpty() && !gid.isEmpty()) {
Collections.addAll(containerRuntimeArgs, "--user", uid + ":" + gid);
if (containerRuntime == ContainerRuntimeUtil.ContainerRuntime.PODMAN) {
// Needed to avoid AccessDeniedExceptions
containerRuntimeArgs.add("--userns=keep-id");
if (isDockerRootless(containerRuntime)) {
Collections.addAll(containerRuntimeArgs, "--user", String.valueOf(0));
} else {
String uid = getLinuxID("-ur");
String gid = getLinuxID("-gr");
if (uid != null && gid != null && !uid.isEmpty() && !gid.isEmpty()) {
Collections.addAll(containerRuntimeArgs, "--user", uid + ":" + gid);
if (containerRuntime == ContainerRuntimeUtil.ContainerRuntime.PODMAN) {
// Needed to avoid AccessDeniedExceptions
containerRuntimeArgs.add("--userns=keep-id");
}
}
}
baseContainerRuntimeArgs = containerRuntimeArgs.toArray(baseContainerRuntimeArgs);
}
}

private static boolean isDockerRootless(ContainerRuntimeUtil.ContainerRuntime containerRuntime) {
if (containerRuntime != ContainerRuntimeUtil.ContainerRuntime.DOCKER) {
return false;
}
String dockerEndpoint = fetchDockerEndpoint();
// docker socket?
String socketUriPrefix = "unix://";
if (dockerEndpoint == null || !dockerEndpoint.startsWith(socketUriPrefix)) {
return false;
}
String dockerSocket = dockerEndpoint.substring(socketUriPrefix.length());
String currentUid = getLinuxID("-ur");
if (currentUid == null || currentUid.isEmpty() || currentUid.equals(String.valueOf(0))) {
return false;
}

int socketOwnerUid;
try {
socketOwnerUid = (int) Files.getAttribute(Path.of(dockerSocket), "unix:uid", LinkOption.NOFOLLOW_LINKS);
} catch (IOException e) {
LOGGER.infof("Owner UID lookup on '%s' failed with '%s'", dockerSocket, e.getMessage());
return false;
}
return currentUid.equals(String.valueOf(socketOwnerUid));
}

private static String fetchDockerEndpoint() {
// DOCKER_HOST environment variable overrides the active context
String dockerHost = System.getenv("DOCKER_HOST");
if (dockerHost != null) {
return dockerHost;
}

OutputFilter outputFilter = new OutputFilter();
if (!ExecUtil.execWithTimeout(new File("."), outputFilter, Duration.ofMillis(3000), "docker",
"context", "ls", "--format", "'{{- if .Current -}} {{- .DockerEndpoint -}} {{- end -}}'")) {
return null;
}

Set<String> endpoints = outputFilter.getOutput().lines()
.filter(Predicate.not(String::isBlank))
.collect(Collectors.toSet());
if (endpoints.size() != 1) {
return null;
}
return endpoints.stream().findFirst().orElse(null);
}

@Override
protected List<String> getContainerRuntimeBuildArgs() {
List<String> containerRuntimeArgs = super.getContainerRuntimeBuildArgs();
Expand All @@ -45,4 +110,5 @@ protected List<String> getContainerRuntimeBuildArgs() {
volumeOutputPath + ":" + NativeImageBuildStep.CONTAINER_BUILD_VOLUME_PATH + ":z");
return containerRuntimeArgs;
}

}

0 comments on commit 0c47727

Please sign in to comment.