This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new df119580f61b CAMEL-26685: camel-docker - imagepull/imagepush use the
endpoint auth only for its own registry (#29314)
df119580f61b is described below
commit df119580f61bed2dfc84413178a098f00d12eee6
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Nov 6 10:49:53 2026 +0100
CAMEL-26685: camel-docker - imagepull/imagepush use the endpoint auth only
for its own registry (#29314)
The imagepull and imagepush operations added the registry authentication of
the endpoint to every pull and push, whatever registry the image reference
names, and failed with a NullPointerException ("Configured username is
null.")
when the endpoint had no username.
They now add the authentication only when a username is configured and the
registry host and port of the image (Docker Hub for an image without a
registry host) match those of serverAddress. Otherwise the pull or push runs
without authentication.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---
.../camel/catalog/docs/docker-component.adoc | 9 +++
.../src/main/docs/docker-component.adoc | 9 +++
.../camel/component/docker/DockerHelper.java | 59 ++++++++++++++++++++
.../docker/producer/AsyncDockerProducer.java | 35 +++++++++++-
.../camel/component/docker/DockerHelperTest.java | 34 +++++++++++
.../docker/headers/BaseDockerHeaderTest.java | 9 +++
.../docker/headers/PullImageCmdHeaderTest.java | 16 ++++++
...Test.java => PullImageCmdRegistryAuthTest.java} | 65 +++++++++++++++++-----
.../docker/headers/PushImageCmdHeaderTest.java | 38 +++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 11 ++++
10 files changed, 268 insertions(+), 17 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/docker-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/docker-component.adoc
index 3fb41fd24d6d..83eab4153dd2 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/docker-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/docker-component.adoc
@@ -88,6 +88,15 @@ The other headers, such as `CamelDockerContainerId`, still
take precedence over
messages come from an untrusted producer through a consumer that does not
filter out `Camel*` headers, remove these
headers with `removeHeaders("CamelDocker*")` before the `docker` endpoint.
+=== Registry authentication
+
+The `imagepull` and `imagepush` operations send the `username`, `password` and
`email` of the endpoint only for an
+image in the registry of `serverAddress`: the registry host and port of the
image reference must match those of
+`serverAddress`. An image reference without a registry host, such as `ubuntu`
or `team/app`, is on Docker Hub, which
+matches the default `serverAddress` (`https://index.docker.io/v1/`). An image
in another registry is pulled or pushed
+without authentication, and so is every image when the endpoint has no
`username`. To pull or push with
+authentication from several registries, use one endpoint per registry.
+
=== Streaming operations
The operations `imagebuild`, `imagepull`, `imagepush`, `containerattach`,
`containerlog`, `containerwait` and
diff --git a/components/camel-docker/src/main/docs/docker-component.adoc
b/components/camel-docker/src/main/docs/docker-component.adoc
index 3fb41fd24d6d..83eab4153dd2 100644
--- a/components/camel-docker/src/main/docs/docker-component.adoc
+++ b/components/camel-docker/src/main/docs/docker-component.adoc
@@ -88,6 +88,15 @@ The other headers, such as `CamelDockerContainerId`, still
take precedence over
messages come from an untrusted producer through a consumer that does not
filter out `Camel*` headers, remove these
headers with `removeHeaders("CamelDocker*")` before the `docker` endpoint.
+=== Registry authentication
+
+The `imagepull` and `imagepush` operations send the `username`, `password` and
`email` of the endpoint only for an
+image in the registry of `serverAddress`: the registry host and port of the
image reference must match those of
+`serverAddress`. An image reference without a registry host, such as `ubuntu`
or `team/app`, is on Docker Hub, which
+matches the default `serverAddress` (`https://index.docker.io/v1/`). An image
in another registry is pulled or pushed
+without authentication, and so is every image when the endpoint has no
`username`. To pull or push with
+authentication from several registries, use one endpoint per registry.
+
=== Streaming operations
The operations `imagebuild`, `imagepull`, `imagepush`, `containerattach`,
`containerlog`, `containerwait` and
diff --git
a/components/camel-docker/src/main/java/org/apache/camel/component/docker/DockerHelper.java
b/components/camel-docker/src/main/java/org/apache/camel/component/docker/DockerHelper.java
index 634ceda8634c..2d66a7d358d6 100644
---
a/components/camel-docker/src/main/java/org/apache/camel/component/docker/DockerHelper.java
+++
b/components/camel-docker/src/main/java/org/apache/camel/component/docker/DockerHelper.java
@@ -17,6 +17,7 @@
package org.apache.camel.component.docker;
import java.lang.reflect.Array;
+import java.util.Locale;
import org.apache.camel.Message;
import org.apache.camel.util.StringHelper;
@@ -28,6 +29,7 @@ import org.apache.commons.lang3.BooleanUtils;
public final class DockerHelper {
private static final String STRING_DELIMITER = ";";
+ private static final String DOCKER_HUB = "docker.io";
private DockerHelper() {
// Helper class
@@ -148,6 +150,63 @@ public final class DockerHelper {
return null;
}
+ /**
+ * Returns the registry host (with the port, if any) of an image
reference, following the rules of Docker: the first
+ * path component is a registry when it contains a dot or a colon, is
<tt>localhost</tt> or has upper case letters;
+ * otherwise the image is on Docker Hub. The names of Docker Hub are
returned as <tt>docker.io</tt>, and the host is
+ * in lower case.
+ *
+ * @param image the image reference, such as <tt>ubuntu:24.04</tt> or
<tt>myregistry:5000/team/app@sha256:...</tt>
+ * @return the registry host, or <tt>null</tt> if the image is empty
+ */
+ public static String getImageRegistryHost(String image) {
+ if (image == null || image.isBlank()) {
+ return null;
+ }
+ int slash = image.indexOf('/');
+ if (slash == -1) {
+ return DOCKER_HUB;
+ }
+ String first = image.substring(0, slash);
+ if (first.indexOf('.') == -1 && first.indexOf(':') == -1 &&
!"localhost".equals(first)
+ && first.equals(first.toLowerCase(Locale.ROOT))) {
+ return DOCKER_HUB;
+ }
+ return normalizeRegistryHost(first);
+ }
+
+ /**
+ * Returns the registry host (with the port, if any) of a registry server
address, such as
+ * <tt>https://index.docker.io/v1/</tt> or <tt>myregistry:5000</tt>. The
scheme and the path are ignored, the names
+ * of Docker Hub are returned as <tt>docker.io</tt>, and the host is in
lower case.
+ *
+ * @param serverAddress the server address of the registry
+ * @return the registry host, or <tt>null</tt> if the server
address is empty
+ */
+ public static String getServerAddressRegistryHost(String serverAddress) {
+ if (serverAddress == null || serverAddress.isBlank()) {
+ return null;
+ }
+ String host = serverAddress.trim();
+ int scheme = host.indexOf("://");
+ if (scheme != -1) {
+ host = host.substring(scheme + 3);
+ }
+ int slash = host.indexOf('/');
+ if (slash != -1) {
+ host = host.substring(0, slash);
+ }
+ return host.isEmpty() ? null : normalizeRegistryHost(host);
+ }
+
+ private static String normalizeRegistryHost(String host) {
+ String answer = host.toLowerCase(Locale.ROOT);
+ if ("index.docker.io".equals(answer) ||
"registry-1.docker.io".equals(answer)) {
+ return DOCKER_HUB;
+ }
+ return answer;
+ }
+
/**
* @param headerName name of the header
* @param message the Camel message
diff --git
a/components/camel-docker/src/main/java/org/apache/camel/component/docker/producer/AsyncDockerProducer.java
b/components/camel-docker/src/main/java/org/apache/camel/component/docker/producer/AsyncDockerProducer.java
index ce8986214dee..803af6353afb 100644
---
a/components/camel-docker/src/main/java/org/apache/camel/component/docker/producer/AsyncDockerProducer.java
+++
b/components/camel-docker/src/main/java/org/apache/camel/component/docker/producer/AsyncDockerProducer.java
@@ -330,7 +330,7 @@ public class AsyncDockerProducer extends
DefaultAsyncProducer {
pullImageCmd.withTag(tag);
}
- AuthConfig authConfig = client.authConfig();
+ AuthConfig authConfig = getRegistryAuthConfig(client, message,
repository);
if (authConfig != null) {
pullImageCmd.withAuthConfig(authConfig);
@@ -359,7 +359,7 @@ public class AsyncDockerProducer extends
DefaultAsyncProducer {
pushImageCmd.withTag(tag);
}
- AuthConfig authConfig = client.authConfig();
+ AuthConfig authConfig = getRegistryAuthConfig(client, message, name);
if (authConfig != null) {
pushImageCmd.withAuthConfig(authConfig);
@@ -369,6 +369,37 @@ public class AsyncDockerProducer extends
DefaultAsyncProducer {
}
+ /**
+ * The registry authentication of the endpoint for a pull or push of the
given image. It applies only when a
+ * username is configured and the image is in the registry of the server
address of the endpoint; otherwise the
+ * request is sent without authentication.
+ */
+ private AuthConfig getRegistryAuthConfig(DockerClient client, Message
message, String image) {
+ // the username header applies only when the endpoint allows it, as in
DockerClientFactory
+ Message credentialHeaders =
configuration.isAllowConnectionAndCredentialsFromHeaders() ? message : null;
+ String username =
DockerHelper.getProperty(DockerConstants.DOCKER_USERNAME, configuration,
credentialHeaders,
+ String.class, configuration.getUsername());
+ if (username == null) {
+ LOG.debug("No registry username configured: sending the request
for image {} without authentication", image);
+ return null;
+ }
+
+ AuthConfig authConfig = client.authConfig();
+ if (authConfig == null) {
+ return null;
+ }
+
+ String imageRegistry = DockerHelper.getImageRegistryHost(image);
+ String endpointRegistry =
DockerHelper.getServerAddressRegistryHost(authConfig.getRegistryAddress());
+ if (imageRegistry == null || !imageRegistry.equals(endpointRegistry)) {
+ LOG.debug("Image {} is in registry {}, not in the registry {} of
the endpoint:"
+ + " sending the request without the registry
authentication of the endpoint",
+ image, imageRegistry, endpointRegistry);
+ return null;
+ }
+ return authConfig;
+ }
+
/**
* Produce a attach container request
*/
diff --git
a/components/camel-docker/src/test/java/org/apache/camel/component/docker/DockerHelperTest.java
b/components/camel-docker/src/test/java/org/apache/camel/component/docker/DockerHelperTest.java
index 14d26adec5d4..592f82fd4025 100644
---
a/components/camel-docker/src/test/java/org/apache/camel/component/docker/DockerHelperTest.java
+++
b/components/camel-docker/src/test/java/org/apache/camel/component/docker/DockerHelperTest.java
@@ -19,6 +19,7 @@ package org.apache.camel.component.docker;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
public class DockerHelperTest {
@@ -28,4 +29,37 @@ public class DockerHelperTest {
assertEquals("registry", headerField);
}
+ @Test
+ void registryHostOfImageReference() {
+ assertEquals("docker.io", DockerHelper.getImageRegistryHost("ubuntu"));
+ assertEquals("docker.io",
DockerHelper.getImageRegistryHost("ubuntu:24.04"));
+ assertEquals("docker.io",
DockerHelper.getImageRegistryHost("library/ubuntu"));
+ assertEquals("docker.io",
DockerHelper.getImageRegistryHost("team/app:1.0"));
+ assertEquals("docker.io",
DockerHelper.getImageRegistryHost("docker.io/library/ubuntu"));
+ assertEquals("docker.io",
DockerHelper.getImageRegistryHost("index.docker.io/team/app"));
+ assertEquals("docker.io",
DockerHelper.getImageRegistryHost("registry-1.docker.io/team/app"));
+ assertEquals("docker.io",
DockerHelper.getImageRegistryHost("ubuntu@sha256:0123456789abcdef"));
+ assertEquals("localhost",
DockerHelper.getImageRegistryHost("localhost/app"));
+ assertEquals("localhost:5000",
DockerHelper.getImageRegistryHost("localhost:5000/team/app:1.0"));
+ assertEquals("registry.example.com",
DockerHelper.getImageRegistryHost("registry.example.com/team/app"));
+ assertEquals("registry.example.com:5000",
+
DockerHelper.getImageRegistryHost("Registry.Example.com:5000/team/app@sha256:0123456789abcdef"));
+ assertEquals("myregistry",
DockerHelper.getImageRegistryHost("MyRegistry/app"));
+ assertNull(DockerHelper.getImageRegistryHost(null));
+ assertNull(DockerHelper.getImageRegistryHost(""));
+ }
+
+ @Test
+ void registryHostOfServerAddress() {
+ assertEquals("docker.io",
DockerHelper.getServerAddressRegistryHost("https://index.docker.io/v1/"));
+ assertEquals("docker.io",
DockerHelper.getServerAddressRegistryHost("http://docker.io/v1"));
+ assertEquals("docker.io",
DockerHelper.getServerAddressRegistryHost("docker.io"));
+ assertEquals("docker.io",
DockerHelper.getServerAddressRegistryHost("registry-1.docker.io"));
+ assertEquals("registry.example.com:5000",
+
DockerHelper.getServerAddressRegistryHost("https://Registry.Example.com:5000/v2/"));
+ assertEquals("localhost:5000",
DockerHelper.getServerAddressRegistryHost("localhost:5000"));
+ assertNull(DockerHelper.getServerAddressRegistryHost(null));
+ assertNull(DockerHelper.getServerAddressRegistryHost(" "));
+ }
+
}
diff --git
a/components/camel-docker/src/test/java/org/apache/camel/component/docker/headers/BaseDockerHeaderTest.java
b/components/camel-docker/src/test/java/org/apache/camel/component/docker/headers/BaseDockerHeaderTest.java
index b4b2eacbc1a8..0647fcd6592c 100644
---
a/components/camel-docker/src/test/java/org/apache/camel/component/docker/headers/BaseDockerHeaderTest.java
+++
b/components/camel-docker/src/test/java/org/apache/camel/component/docker/headers/BaseDockerHeaderTest.java
@@ -75,6 +75,7 @@ public abstract class BaseDockerHeaderTest<T> extends
CamelTestSupport {
dockerConfiguration.setHost(getHost());
dockerConfiguration.setPort(getPort());
dockerConfiguration.setServerAddress(getServerAddress());
+ dockerConfiguration.setUsername(getUsername());
dockerConfiguration.setSecure(isSecure());
dockerConfiguration.setTlsVerify(isTlsVerify());
dockerConfiguration.setSocket(isSocket());
@@ -112,6 +113,13 @@ public abstract class BaseDockerHeaderTest<T> extends
CamelTestSupport {
return "https://index.docker.io/v1/";
}
+ /**
+ * The registry username of the endpoint, none by default.
+ */
+ protected String getUsername() {
+ return null;
+ }
+
public boolean isSecure() {
return false;
}
@@ -161,6 +169,7 @@ public abstract class BaseDockerHeaderTest<T> extends
CamelTestSupport {
clientProfile.setPort(getPort());
clientProfile.setEmail(getEmail());
clientProfile.setServerAddress(getServerAddress());
+ clientProfile.setUsername(getUsername());
clientProfile.setMaxPerRouteConnections(getMaxPerRouteConnections());
clientProfile.setMaxTotalConnections(getMaxTotalConnections());
clientProfile.setSecure(isSecure());
diff --git
a/components/camel-docker/src/test/java/org/apache/camel/component/docker/headers/PullImageCmdHeaderTest.java
b/components/camel-docker/src/test/java/org/apache/camel/component/docker/headers/PullImageCmdHeaderTest.java
index 101e0642f994..1e483431b56e 100644
---
a/components/camel-docker/src/test/java/org/apache/camel/component/docker/headers/PullImageCmdHeaderTest.java
+++
b/components/camel-docker/src/test/java/org/apache/camel/component/docker/headers/PullImageCmdHeaderTest.java
@@ -64,6 +64,22 @@ public class PullImageCmdHeaderTest extends
BaseDockerHeaderTest<PullImageCmd> {
}
+ @Test
+ void pullImageWithoutUsernameSendsNoCredentials() {
+ // docker-java fails like this when the registry authentication is
asked for without a username
+ Mockito.lenient().when(dockerClient.authConfig())
+ .thenThrow(new NullPointerException("Configured username is
null."));
+
+ Map<String, Object> headers = getDefaultParameters();
+ headers.put(DockerConstants.DOCKER_REPOSITORY, "docker/empty");
+
+ template.sendBodyAndHeaders("direct:in", "", headers);
+
+ Mockito.verify(dockerClient, Mockito.never()).authConfig();
+ Mockito.verify(mockObject, Mockito.never()).withAuthConfig(any());
+ Mockito.verify(mockObject, Mockito.times(1)).exec(any());
+ }
+
@Override
protected void setupMocks() {
Mockito.when(dockerClient.pullImageCmd(anyString())).thenReturn(mockObject);
diff --git
a/components/camel-docker/src/test/java/org/apache/camel/component/docker/headers/PullImageCmdHeaderTest.java
b/components/camel-docker/src/test/java/org/apache/camel/component/docker/headers/PullImageCmdRegistryAuthTest.java
similarity index 53%
copy from
components/camel-docker/src/test/java/org/apache/camel/component/docker/headers/PullImageCmdHeaderTest.java
copy to
components/camel-docker/src/test/java/org/apache/camel/component/docker/headers/PullImageCmdRegistryAuthTest.java
index 101e0642f994..d5e02fee511d 100644
---
a/components/camel-docker/src/test/java/org/apache/camel/component/docker/headers/PullImageCmdHeaderTest.java
+++
b/components/camel-docker/src/test/java/org/apache/camel/component/docker/headers/PullImageCmdRegistryAuthTest.java
@@ -20,23 +20,24 @@ import java.util.Map;
import com.github.dockerjava.api.command.PullImageCmd;
import com.github.dockerjava.api.command.PullImageResultCallback;
+import com.github.dockerjava.api.model.AuthConfig;
import org.apache.camel.component.docker.DockerConstants;
import org.apache.camel.component.docker.DockerOperation;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
/**
- * Validates Pull Image Request headers are applied properly
+ * Validates that a pull image request uses the registry authentication of the
endpoint only for images of the registry
+ * of the endpoint
*/
-public class PullImageCmdHeaderTest extends BaseDockerHeaderTest<PullImageCmd>
{
- private static final Logger LOG =
LoggerFactory.getLogger(PullImageCmdHeaderTest.class);
+public class PullImageCmdRegistryAuthTest extends
BaseDockerHeaderTest<PullImageCmd> {
+
+ private static final String USERNAME = "jdoe";
+ private static final String SERVER_ADDRESS =
"https://registry.example.com:5000/v2/";
@Mock
private PullImageCmd mockObject;
@@ -45,23 +46,57 @@ public class PullImageCmdHeaderTest extends
BaseDockerHeaderTest<PullImageCmd> {
private PullImageResultCallback callback;
@Test
- void pullImageHeaderTest() {
+ void pullImageOfEndpointRegistrySendsEndpointCredentials() {
+ AuthConfig authConfig = endpointAuthConfig();
+ Mockito.when(dockerClient.authConfig()).thenReturn(authConfig);
+
+ pull("registry.example.com:5000/team/app");
+
+ Mockito.verify(mockObject,
Mockito.times(1)).withAuthConfig(authConfig);
+ Mockito.verify(mockObject, Mockito.times(1)).exec(any());
+ }
+
+ @Test
+ void pullImageOfOtherRegistrySendsNoCredentials() {
+
Mockito.when(dockerClient.authConfig()).thenReturn(endpointAuthConfig());
+
+ pull("registry.example.org/team/app");
+
+ Mockito.verify(mockObject, Mockito.never()).withAuthConfig(any());
+ Mockito.verify(mockObject, Mockito.times(1)).exec(any());
+ }
+
+ @Test
+ void pullImageOfDockerHubSendsNoCredentials() {
+
Mockito.when(dockerClient.authConfig()).thenReturn(endpointAuthConfig());
+
+ pull("team/app");
- String repository = "docker/empty";
- String tag = "1.0";
- String registry = "registry";
+ Mockito.verify(mockObject, Mockito.never()).withAuthConfig(any());
+ Mockito.verify(mockObject, Mockito.times(1)).exec(any());
+ }
+ private void pull(String repository) {
Map<String, Object> headers = getDefaultParameters();
headers.put(DockerConstants.DOCKER_REPOSITORY, repository);
- headers.put(DockerConstants.DOCKER_TAG, tag);
- headers.put(DockerConstants.DOCKER_REGISTRY, registry);
template.sendBodyAndHeaders("direct:in", "", headers);
Mockito.verify(dockerClient,
Mockito.times(1)).pullImageCmd(repository);
- Mockito.verify(mockObject, Mockito.times(1)).withTag(eq(tag));
- Mockito.verify(mockObject,
Mockito.times(1)).withRegistry(eq(registry));
+ }
+
+ private static AuthConfig endpointAuthConfig() {
+ return new
AuthConfig().withUsername(USERNAME).withPassword("secret").withRegistryAddress(SERVER_ADDRESS);
+ }
+ @Override
+ protected String getServerAddress() {
+ return SERVER_ADDRESS;
+ }
+
+ @Override
+ protected String getUsername() {
+ return USERNAME;
}
@Override
@@ -71,7 +106,7 @@ public class PullImageCmdHeaderTest extends
BaseDockerHeaderTest<PullImageCmd> {
try {
Mockito.when(callback.awaitCompletion()).thenReturn(callback);
} catch (InterruptedException e) {
- LOG.warn("Interrupted while setting up mocks", e);
+ Thread.currentThread().interrupt();
}
}
diff --git
a/components/camel-docker/src/test/java/org/apache/camel/component/docker/headers/PushImageCmdHeaderTest.java
b/components/camel-docker/src/test/java/org/apache/camel/component/docker/headers/PushImageCmdHeaderTest.java
index f3f5fa2e789f..f976ee130960 100644
---
a/components/camel-docker/src/test/java/org/apache/camel/component/docker/headers/PushImageCmdHeaderTest.java
+++
b/components/camel-docker/src/test/java/org/apache/camel/component/docker/headers/PushImageCmdHeaderTest.java
@@ -20,6 +20,7 @@ import java.util.Map;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.command.PushImageCmd;
+import com.github.dockerjava.api.model.AuthConfig;
import com.github.dockerjava.api.model.Frame;
import org.apache.camel.component.docker.DockerClientProfile;
import org.apache.camel.component.docker.DockerConstants;
@@ -70,6 +71,43 @@ public class PushImageCmdHeaderTest extends
BaseDockerHeaderTest<PushImageCmd> {
}
+ @Test
+ void pushImageOfEndpointRegistrySendsEndpointCredentials() {
+ AuthConfig authConfig = endpointAuthConfig();
+ Mockito.when(dockerClient.authConfig()).thenReturn(authConfig);
+
+ template.sendBodyAndHeaders("direct:in", "",
credentialHeaders("jdoe/imagename"));
+
+ Mockito.verify(dockerClient,
Mockito.times(1)).pushImageCmd("jdoe/imagename");
+ Mockito.verify(mockObject,
Mockito.times(1)).withAuthConfig(authConfig);
+ }
+
+ @Test
+ void pushImageOfOtherRegistrySendsNoCredentials() {
+
Mockito.when(dockerClient.authConfig()).thenReturn(endpointAuthConfig());
+
+ template.sendBodyAndHeaders("direct:in", "",
credentialHeaders("registry.example.com:5000/jdoe/imagename"));
+
+ Mockito.verify(dockerClient,
Mockito.times(1)).pushImageCmd("registry.example.com:5000/jdoe/imagename");
+ Mockito.verify(mockObject, Mockito.never()).withAuthConfig(any());
+ Mockito.verify(mockObject, Mockito.times(1)).exec(any());
+ }
+
+ private Map<String, Object> credentialHeaders(String imageName) {
+ Map<String, Object> headers = getDefaultParameters();
+ headers.put(DockerConstants.DOCKER_USERNAME, userName);
+ headers.put(DockerConstants.DOCKER_PASSWORD, password);
+ headers.put(DockerConstants.DOCKER_EMAIL, email);
+ headers.put(DockerConstants.DOCKER_SERVER_ADDRESS, serverAddress);
+ headers.put(DockerConstants.DOCKER_NAME, imageName);
+ return headers;
+ }
+
+ private AuthConfig endpointAuthConfig() {
+ return new
AuthConfig().withUsername(userName).withPassword(password).withEmail(email)
+ .withRegistryAddress(serverAddress);
+ }
+
@Override
protected void setupMocks() {
Mockito.when(dockerClient.pushImageCmd(anyString())).thenReturn(mockObject);
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index 4ff53d3c933a..df5abd97c5b6 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -2166,6 +2166,17 @@ The other headers, such as `CamelDockerContainerId`,
`CamelDockerEmail` and `Cam
precedence over the options of the endpoint, as before. The `events` and
`stats` consumers do not change: they always
used the options of the endpoint.
+=== camel-docker - imagepull and imagepush authenticate only to the registry
of serverAddress
+
+The `imagepull` and `imagepush` operations of the `docker` component added the
registry authentication of the
+endpoint (`username`, `password`, `email` and `serverAddress`) to every pull
and push, whatever registry the image
+reference names. They now add it only when the registry host and port of the
image (Docker Hub for an image without a
+registry host, such as `ubuntu` or `team/app`) match those of `serverAddress`.
An image in another registry is pulled
+or pushed without authentication. Set `serverAddress` to the registry of the
images, or use one endpoint per registry.
+
+Without a `username` on the endpoint, `imagepull` and `imagepush` failed with
a `NullPointerException`
+("Configured username is null."). They now pull or push without authentication.
+
=== camel-cli-connector - every action with a request id gets an answer
An action written to `~/.camel/<pid>-action-<requestId>.json` now always gets
an answer in