Repository: quarkusio/quarkus
Issue: Quarkus doesn't detect Docker rootless anymore

### Describe the bug

Starting from ``2.16.4.Final``, Quarkus cannot detect ```Docker rootless``` anymore. Reason for that is a faulty implemenation in ``ContainerRuntimeUtil.getRootlessStateFor``:


```java
private static boolean getRootlessStateFor(ContainerRuntime containerRuntime) {
      ......
            try (InputStream inputStream = rootlessProcess.getInputStream();
                    InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
                    BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
                Predicate<String> stringPredicate;
                // Docker includes just "rootless" under SecurityOptions, while podman includes "rootless: <boolean>"
       --->  if (containerRuntime == ContainerRuntime.DOCKER)  {  <-- ContainerRuntime.DOCKER is null 
                    stringPredicate = line -> line.trim().equals("rootless");
                } else {
                    stringPredicate = line -> line.trim().equals("rootless: true");
                }
                return bufferedReader.lines().anyMatch(stringPredicate);
            }
        .....
    }

    /**
     * Supported Container runtimes
     */
    public enum ContainerRuntime {
        DOCKER,
        PODMAN;

        private final boolean rootless;

        ContainerRuntime() {
            this.rootless = getRootlessStateFor(this);
        }

        public String getExecutableName() {
            return this.name().toLowerCase();
        }

        public boolean isRootless() {
            return rootless;
        }
    }
```

This condition ``containerRuntime == ContainerRuntime.DOCKER`` always evaluates to false, since ContainerRuntime.DOCKER is null. That's  because ``getRootlessStateFor`` is being called during constructor execution of ContainerRuntime. Thus we end up in the ``podman`` case, even when ``Docker`` is being detected!


### Expected behavior

Quarkus should detect ``Docker rootless`` properly :-)

### Actual behavior

Quarkus native container builds aren't working on Docker rootless anymore, because the rootless detection fails. Therefor the container isn't started as root, but rather non root which leads to file permission issues ..

### How to Reproduce?

_No response_

### Output of `uname -a` or `ver`

5.4.228 ...... x86_64 x86_64 x86_64 GNU/Linux

### Output of `java -version`

17

### GraalVM version (if different from Java)

GraalVM 22.3.0 Java 17 CE

### Quarkus version or git rev

2.16.4.Final

### Build tool (ie. output of `mvnw --version` or `gradlew --version`)

Maven 3.8.6

### Additional information

```
Docker version 20.10.22, build 3a2c30b
runner@....:/$ docker info
Client:
 Context:    default
 Debug Mode: false

Server:
 Containers: 0
  Running: 0
  Paused: 0
  Stopped: 0
 Images: 0
 Server Version: 20.10.22
 Storage Driver: vfs
 Logging Driver: json-file
 Cgroup Driver: none
 Cgroup Version: 1
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
 Swarm: inactive
 Runtimes: io.containerd.runc.v2 io.containerd.runtime.v1.linux runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 78f51771157abb6c9ed224c22013cdf09962315d
 runc version: v1.1.4-0-g5fd4c4d1
 init version: de40ad0
 Security Options:
  seccomp
   Profile: default
  **rootless**
 Kernel Version: 5.4.228-132.418.amzn2.x86_64
 Operating System: Ubuntu 22.04.1 LTS (containerized)
 OSType: linux
 Architecture: x86_64
 CPUs: 4
 Total Memory: 15.34GiB
 Name: ......
 Docker Root Dir: /home/runner/.local/share/docker
 Debug Mode: false
 Registry: https://index.docker.io/v1/
 Labels: ...
 Experimental: false
 Insecure Registries:
  127.0.0.0/8
 Live Restore Enabled: false
 Product License: Community Engine

WARNING: Running in rootless-mode without cgroups. To enable cgroups in rootless-mode, you need to boot the system in cgroup v2 mode.
```


Changed files:
core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildLocalContainerRunner.java
core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildContainerRunner.java
core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/UpxCompressionBuildStep.java
core/runtime/src/main/java/io/quarkus/runtime/util/ContainerRuntimeUtil.java

diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildContainerRunner.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildContainerRunner.java
index ef53e1b4f7b..ccca09727a2 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildContainerRunner.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildContainerRunner.java
@@ -42,8 +42,7 @@ public boolean isContainer() {
 
     @Override
     public void setup(boolean processInheritIODisabled) {
-        if (containerRuntime == ContainerRuntimeUtil.ContainerRuntime.DOCKER
-                || containerRuntime == ContainerRuntimeUtil.ContainerRuntime.PODMAN) {
+        if (containerRuntime != ContainerRuntimeUtil.ContainerRuntime.UNAVAILABLE) {
             log.infof("Using %s to run the native image builder", containerRuntime.getExecutableName());
             // we pull the docker image in order to give users an indication of which step the process is at
             // it's not strictly necessary we do this, however if we don't the subsequent version command
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildLocalContainerRunner.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildLocalContainerRunner.java
index e9f1c1125ef..c5e50652d4d 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildLocalContainerRunner.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/NativeImageBuildLocalContainerRunner.java
@@ -1,8 +1,6 @@
 package io.quarkus.deployment.pkg.steps;
 
 import static io.quarkus.deployment.pkg.steps.LinuxIDUtil.getLinuxID;
-import static io.quarkus.runtime.util.ContainerRuntimeUtil.ContainerRuntime.DOCKER;
-import static io.quarkus.runtime.util.ContainerRuntimeUtil.ContainerRuntime.PODMAN;
 
 import java.nio.file.Path;
 import java.util.ArrayList;
@@ -21,14 +19,14 @@ public NativeImageBuildLocalContainerRunner(NativeConfig nativeConfig) {
         super(nativeConfig);
         if (SystemUtils.IS_OS_LINUX) {
             final ArrayList<String> containerRuntimeArgs = new ArrayList<>(Arrays.asList(baseContainerRuntimeArgs));
-            if (containerRuntime == DOCKER && containerRuntime.isRootless()) {
+            if (containerRuntime.isDocker() && containerRuntime.isRootless()) {
                 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 == PODMAN && containerRuntime.isRootless()) {
+                    if (containerRuntime.isPodman() && containerRuntime.isRootless()) {
                         // Needed to avoid AccessDeniedExceptions
                         containerRuntimeArgs.add("--userns=keep-id");
                     }
@@ -47,7 +45,7 @@ protected List<String> getContainerRuntimeBuildArgs(Path outputDir) {
         }
 
         final String selinuxBindOption;
-        if (SystemUtils.IS_OS_MAC && containerRuntime == PODMAN) {
+        if (SystemUtils.IS_OS_MAC && containerRuntime.isPodman()) {
             selinuxBindOption = "";
         } else {
             selinuxBindOption = ":z";
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/UpxCompressionBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/UpxCompressionBuildStep.java
index 9174db0e4c7..a4880647973 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/UpxCompressionBuildStep.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/UpxCompressionBuildStep.java
@@ -126,8 +126,7 @@ private boolean runUpxInContainer(NativeImageBuildItem nativeImage, NativeConfig
             String gid = getLinuxID("-gr");
             if (uid != null && gid != null && !uid.isEmpty() && !gid.isEmpty()) {
                 Collections.addAll(commandLine, "--user", uid + ":" + gid);
-                if (containerRuntime == ContainerRuntimeUtil.ContainerRuntime.PODMAN
-                        && containerRuntime.isRootless()) {
+                if (containerRuntime.isPodman() && containerRuntime.isRootless()) {
                     // Needed to avoid AccessDeniedExceptions
                     commandLine.add("--userns=keep-id");
                 }
diff --git a/core/runtime/src/main/java/io/quarkus/runtime/util/ContainerRuntimeUtil.java b/core/runtime/src/main/java/io/quarkus/runtime/util/ContainerRuntimeUtil.java
index 1ae65cc8a5b..c2d254d25ed 100644
--- a/core/runtime/src/main/java/io/quarkus/runtime/util/ContainerRuntimeUtil.java
+++ b/core/runtime/src/main/java/io/quarkus/runtime/util/ContainerRuntimeUtil.java
@@ -31,10 +31,7 @@ private ContainerRuntimeUtil() {
     }
 
     /**
-     * @return {@link ContainerRuntime#DOCKER} if it's available, or {@link ContainerRuntime#PODMAN}
-     *         if the podman
-     *         executable exists in the environment or if the docker executable is an alias to podman,
-     *         or {@link ContainerRuntime#UNAVAILABLE} if no container runtime is available and the required arg is false.
+     * @return a fully resolved {@link ContainerRuntime} indicating if Docker or Podman is available and in rootless mode or not
      * @throws IllegalStateException if no container runtime was found to build the image
      */
     public static ContainerRuntime detectContainerRuntime() {
@@ -42,66 +39,88 @@ public static ContainerRuntime detectContainerRuntime() {
     }
 
     public static ContainerRuntime detectContainerRuntime(boolean required) {
-        final ContainerRuntime containerRuntime = loadContainerRuntimeFromSystemProperty();
+        ContainerRuntime containerRuntime = loadContainerRuntimeFromSystemProperty();
         if (containerRuntime != null) {
             return containerRuntime;
-        } else {
-            // Docker version 19.03.14, build 5eb3275d40
-            String dockerVersionOutput;
-            boolean dockerAvailable;
-            // Check if Podman is installed
-            // podman version 2.1.1
-            String podmanVersionOutput;
-            boolean podmanAvailable;
-            if (CONTAINER_EXECUTABLE != null) {
-                if (CONTAINER_EXECUTABLE.trim().equalsIgnoreCase("docker")) {
-                    dockerVersionOutput = getVersionOutputFor(ContainerRuntime.DOCKER);
-                    dockerAvailable = dockerVersionOutput.contains("Docker version");
-                    if (dockerAvailable) {
-                        storeContainerRuntimeInSystemProperty(ContainerRuntime.DOCKER);
-                        return ContainerRuntime.DOCKER;
-                    }
-                }
-                if (CONTAINER_EXECUTABLE.trim().equalsIgnoreCase("podman")) {
-                    podmanVersionOutput = getVersionOutputFor(ContainerRuntime.PODMAN);
-                    podmanAvailable = podmanVersionOutput.startsWith("podman version");
-                    if (podmanAvailable) {
-                        storeContainerRuntimeInSystemProperty(ContainerRuntime.PODMAN);
-                        return ContainerRuntime.PODMAN;
-                    }
+        }
+
+        ContainerRuntime containerRuntimeEnvironment = getContainerRuntimeEnvironment();
+        if (containerRuntimeEnvironment == ContainerRuntime.UNAVAILABLE) {
+            storeContainerRuntimeInSystemProperty(ContainerRuntime.UNAVAILABLE);
+
+            if (required) {
+                throw new IllegalStateException("No container runtime was found. "
+                        + "Make sure you have either Docker or Podman installed in your environment.");
+            }
+
+            return ContainerRuntime.UNAVAILABLE;
+        }
+
+        // we have a working container environment, let's resolve it fully
+        containerRuntime = fullyResolveContainerRuntime(containerRuntimeEnvironment);
+
+        storeContainerRuntimeInSystemProperty(containerRuntime);
+
+        return containerRuntime;
+    }
+
+    private static ContainerRuntime getContainerRuntimeEnvironment() {
+        // Docker version 19.03.14, build 5eb3275d40
+        String dockerVersionOutput;
+        boolean dockerAvailable;
+        // Check if Podman is installed
+        // podman version 2.1.1
+        String podmanVersionOutput;
+        boolean podmanAvailable;
+
+        if (CONTAINER_EXECUTABLE != null) {
+            if (CONTAINER_EXECUTABLE.trim().equalsIgnoreCase("docker")) {
+                dockerVersionOutput = getVersionOutputFor(ContainerRuntime.DOCKER);
+                dockerAvailable = dockerVersionOutput.contains("Docker version");
+                if (dockerAvailable) {
+                    return ContainerRuntime.DOCKER;
                 }
-                log.warn("quarkus.native.container-runtime config property must be set to either podman or docker " +
-                        "and the executable must be available. Ignoring it.");
             }
-            dockerVersionOutput = getVersionOutputFor(ContainerRuntime.DOCKER);
-            dockerAvailable = dockerVersionOutput.contains("Docker version");
-            if (dockerAvailable) {
-                // Check if "docker" is an alias to "podman"
-                if (dockerVersionOutput.startsWith("podman version") ||
-                        dockerVersionOutput.startsWith("podman.exe version")) {
-                    storeContainerRuntimeInSystemProperty(ContainerRuntime.PODMAN);
+            if (CONTAINER_EXECUTABLE.trim().equalsIgnoreCase("podman")) {
+                podmanVersionOutput = getVersionOutputFor(ContainerRuntime.PODMAN);
+                podmanAvailable = podmanVersionOutput.startsWith("podman version");
+                if (podmanAvailable) {
                     return ContainerRuntime.PODMAN;
                 }
-                storeContainerRuntimeInSystemProperty(ContainerRuntime.DOCKER);
-                return ContainerRuntime.DOCKER;
             }
-            podmanVersionOutput = getVersionOutputFor(ContainerRuntime.PODMAN);
-            podmanAvailable = podmanVersionOutput.startsWith("podman version") ||
-                    podmanVersionOutput.startsWith("podman.exe version");
-            if (podmanAvailable) {
-                storeContainerRuntimeInSystemProperty(ContainerRuntime.PODMAN);
+            log.warn("quarkus.native.container-runtime config property must be set to either podman or docker " +
+                    "and the executable must be available. Ignoring it.");
+        }
+
+        dockerVersionOutput = getVersionOutputFor(ContainerRuntime.DOCKER);
+        dockerAvailable = dockerVersionOutput.contains("Docker version");
+        if (dockerAvailable) {
+            // Check if "docker" is an alias to "podman"
+            if (dockerVersionOutput.startsWith("podman version") ||
+                    dockerVersionOutput.startsWith("podman.exe version")) {
                 return ContainerRuntime.PODMAN;
             }
+            return ContainerRuntime.DOCKER;
+        }
+        podmanVersionOutput = getVersionOutputFor(ContainerRuntime.PODMAN);
+        podmanAvailable = podmanVersionOutput.startsWith("podman version") ||
+                podmanVersionOutput.startsWith("podman.exe version");
+        if (podmanAvailable) {
+            return ContainerRuntime.PODMAN;
+        }
 
-            storeContainerRuntimeInSystemProperty(ContainerRuntime.UNAVAILABLE);
+        return ContainerRuntime.UNAVAILABLE;
+    }
 
-            if (required) {
-                throw new IllegalStateException("No container runtime was found. "
-                        + "Make sure you have either Docker or Podman installed in your environment.");
-            }
+    private static ContainerRuntime fullyResolveContainerRuntime(ContainerRuntime containerRuntimeEnvironment) {
+        boolean rootless = getRootlessStateFor(containerRuntimeEnvironment);
 
-            return ContainerRuntime.UNAVAILABLE;
+        if (!rootless) {
+            return containerRuntimeEnvironment;
         }
+
+        return containerRuntimeEnvironment == ContainerRuntime.DOCKER ? ContainerRuntime.DOCKER_ROOTLESS
+                : ContainerRuntime.PODMAN_ROOTLESS;
     }
 
     private static ContainerRuntime loadContainerRuntimeFromSystemProperty() {
@@ -195,16 +214,19 @@ private static boolean getRootlessStateFor(ContainerRuntime containerRuntime) {
      * Supported Container runtimes
      */
     public enum ContainerRuntime {
-        DOCKER("docker" + (OS.current() == OS.WINDOWS ? ".exe" : "")),
-        PODMAN("podman" + (OS.current() == OS.WINDOWS ? ".exe" : "")),
-        UNAVAILABLE(null);
+        DOCKER("docker", false),
+        DOCKER_ROOTLESS("docker", true),
+        PODMAN("podman", false),
+        PODMAN_ROOTLESS("podman", true),
+        UNAVAILABLE(null, false);
 
-        private Boolean rootless;
+        private final String executableName;
 
-        private String executableName;
+        private final boolean rootless;
 
-        ContainerRuntime(String executableName) {
-            this.executableName = executableName;
+        ContainerRuntime(String executableName, boolean rootless) {
+            this.executableName = executableName + (OS.current() == OS.WINDOWS ? ".exe" : "");
+            this.rootless = rootless;
         }
 
         public String getExecutableName() {
@@ -215,17 +237,15 @@ public String getExecutableName() {
             return executableName;
         }
 
+        public boolean isDocker() {
+            return this == DOCKER || this == DOCKER_ROOTLESS;
+        }
+
+        public boolean isPodman() {
+            return this == PODMAN || this == PODMAN_ROOTLESS;
+        }
+
         public boolean isRootless() {
-            if (rootless != null) {
-                return rootless;
-            } else {
-                if (this != ContainerRuntime.UNAVAILABLE) {
-                    rootless = getRootlessStateFor(this);
-                } else {
-                    throw new IllegalStateException("No container runtime was found. "
-                            + "Make sure you have either Docker or Podman installed in your environment.");
-                }
-            }
             return rootless;
         }