diff --git a/doc/changelog.md b/doc/changelog.md
index bb596d92c..95be01fa4 100644
--- a/doc/changelog.md
+++ b/doc/changelog.md
@@ -3,6 +3,7 @@
- Automatically create parent directories of portPropertyFile path
- Added support for `platform` attribute of a container in the docker-compose configuration.
- `docker:push` failed with build `ARG` in `FROM` ([1778](https://github.com/fabric8io/docker-maven-plugin/issues/1778))
+ - Add the possibility to give secret to buildx build ([1798](https://github.com/fabric8io/docker-maven-plugin/issues/1798)
* **0.44.0** (2024-02-17):
- Add new option "useDefaultExclusion" for build configuration to handle exclusion of hidden files ([1708](https://github.com/fabric8io/docker-maven-plugin/issues/1708))
diff --git a/it/buildx-dockerfile-secret/pom.xml b/it/buildx-dockerfile-secret/pom.xml
new file mode 100644
index 000000000..6585692ee
--- /dev/null
+++ b/it/buildx-dockerfile-secret/pom.xml
@@ -0,0 +1,51 @@
+
+
+ 4.0.0
+
+
+ io.fabric8.dmp.itests
+ dmp-it-parent
+ 0.45-SNAPSHOT
+ ../pom.xml
+
+
+ dmp-it-buildx-dockerfile-secret
+
+
+
+
+ io.fabric8
+ docker-maven-plugin
+
+
+
+ dmp/alpine:${project.version}
+
+ ${project.basedir}/src/main/docker/Dockerfile
+
+
+
+ something
+
+
+ ${project.basedir}/../README.md
+
+
+
+
+
+
+
+
+
+ default
+
+ build
+
+ package
+
+
+
+
+
+
diff --git a/it/buildx-dockerfile-secret/src/main/docker/Dockerfile b/it/buildx-dockerfile-secret/src/main/docker/Dockerfile
new file mode 100644
index 000000000..be731eb17
--- /dev/null
+++ b/it/buildx-dockerfile-secret/src/main/docker/Dockerfile
@@ -0,0 +1,4 @@
+FROM alpine
+
+RUN --mount=type=secret,id=myEnvVar cat /run/secrets/myEnvVar
+RUN --mount=type=secret,id=myFile cat /run/secrets/myFile
diff --git a/it/pom.xml b/it/pom.xml
index 966cddbeb..56659ca53 100644
--- a/it/pom.xml
+++ b/it/pom.xml
@@ -25,6 +25,7 @@
buildx-contextdir
buildx-dependencyset
buildx-dockerfile
+ buildx-dockerfile-secret
buildx-dockerfile_and_contextdir
buildx-push
docker-compose
diff --git a/src/main/asciidoc/inc/build/_buildx.adoc b/src/main/asciidoc/inc/build/_buildx.adoc
index c465bb481..4c05c134c 100644
--- a/src/main/asciidoc/inc/build/_buildx.adoc
+++ b/src/main/asciidoc/inc/build/_buildx.adoc
@@ -60,6 +60,8 @@ element defaults to `min` and the `` element defaults to `false`.
| A value to be passed through to the `--cache-from` option of `docker buildx build`. See https://docs.docker.com/engine/reference/commandline/buildx_build/#cache-from[docker buildx reference docs].
| *cacheTo*
| A value to be passed through to the `--cache-to` option of `docker buildx build`. See https://docs.docker.com/engine/reference/commandline/buildx_build/#cache-to[docker buildx reference docs].
+| *secret*
+| Two Maps, under `envs` and `files` of `VALUE` elements specifying the values of https://docs.docker.com/reference/cli/docker/buildx/build/#secret[Docker Buildx secret] to give to the build as `--secret id=ID[,[env\|src]=VALUE]`.
|===
.Examples
diff --git a/src/main/java/io/fabric8/maven/docker/config/BuildXConfiguration.java b/src/main/java/io/fabric8/maven/docker/config/BuildXConfiguration.java
index c28d44a79..55f3c371e 100644
--- a/src/main/java/io/fabric8/maven/docker/config/BuildXConfiguration.java
+++ b/src/main/java/io/fabric8/maven/docker/config/BuildXConfiguration.java
@@ -61,6 +61,12 @@ public class BuildXConfiguration implements Serializable {
@Parameter
private Map driverOpts;
+ /**
+ * Secret to expose to the build
+ */
+ @Parameter
+ private SecretConfiguration secret;
+
public String getBuilderName() {
return builderName;
}
@@ -86,7 +92,7 @@ public String getCacheTo() {
}
public boolean isBuildX() {
- return !getPlatforms().isEmpty();
+ return !getPlatforms().isEmpty() || hasSecret();
}
@Nonnull
@@ -102,6 +108,14 @@ public Map getDriverOpts() {
return driverOpts;
}
+ public boolean hasSecret() {
+ return secret != null;
+ }
+
+ public SecretConfiguration getSecret() {
+ return secret;
+ }
+
public static class Builder {
private final BuildXConfiguration config = new BuildXConfiguration();
@@ -159,7 +173,6 @@ public Builder attestations(AttestationConfiguration attestations) {
return this;
}
-
public Builder cacheFrom(String cacheFrom) {
config.cacheFrom = cacheFrom;
if (cacheFrom != null) {
@@ -183,5 +196,13 @@ public Builder cacheTo(String cacheTo) {
}
return this;
}
+
+ public Builder secret(SecretConfiguration secret) {
+ config.secret = secret;
+ if (secret != null) {
+ isEmpty = false;
+ }
+ return this;
+ }
}
}
diff --git a/src/main/java/io/fabric8/maven/docker/config/SecretConfiguration.java b/src/main/java/io/fabric8/maven/docker/config/SecretConfiguration.java
new file mode 100644
index 000000000..cd938285e
--- /dev/null
+++ b/src/main/java/io/fabric8/maven/docker/config/SecretConfiguration.java
@@ -0,0 +1,52 @@
+package io.fabric8.maven.docker.config;
+
+import org.apache.maven.plugins.annotations.Parameter;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * @since 15/07/24
+ */
+public class SecretConfiguration implements Serializable {
+
+ @Parameter
+ private Map envs;
+
+ @Parameter
+ private Map files;
+
+ public Map getEnvs() {
+ return envs;
+ }
+
+ public Map getFiles() {
+ return files;
+ }
+
+ public static class Builder {
+
+ private final SecretConfiguration config = new SecretConfiguration();
+ private boolean isEmpty = true;
+
+ public SecretConfiguration build() {
+ return isEmpty ? null : config;
+ }
+
+ public SecretConfiguration.Builder envs(Map envs) {
+ config.envs = envs;
+ if (envs != null && !envs.isEmpty()) {
+ isEmpty = false;
+ }
+ return this;
+ }
+
+ public SecretConfiguration.Builder files(Map files) {
+ config.files = files;
+ if (files != null && !files.isEmpty()) {
+ isEmpty = false;
+ }
+ return this;
+ }
+ }
+}
diff --git a/src/main/java/io/fabric8/maven/docker/config/handler/property/ConfigKey.java b/src/main/java/io/fabric8/maven/docker/config/handler/property/ConfigKey.java
index cc734c292..bc82e0ba1 100644
--- a/src/main/java/io/fabric8/maven/docker/config/handler/property/ConfigKey.java
+++ b/src/main/java/io/fabric8/maven/docker/config/handler/property/ConfigKey.java
@@ -50,6 +50,8 @@ public enum ConfigKey {
BUILDX_ATTESTATION_SBOM("buildx.attestations.sbom"),
BUILDX_CACHE_FROM("buildx.cacheFrom"),
BUILDX_CACHE_TO("buildx.cacheTo"),
+ BUILDX_SECRET_ENVS("buildx.secret.envs", ValueCombinePolicy.Merge),
+ BUILDX_SECRET_FILES("buildx.secret.files", ValueCombinePolicy.Merge),
CAP_ADD,
CAP_DROP,
SYSCTLS,
diff --git a/src/main/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandler.java b/src/main/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandler.java
index 82a0d7cd1..3d1cd495b 100644
--- a/src/main/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandler.java
+++ b/src/main/java/io/fabric8/maven/docker/config/handler/property/PropertyConfigHandler.java
@@ -36,6 +36,7 @@
import io.fabric8.maven.docker.config.RestartPolicy;
import io.fabric8.maven.docker.config.RunImageConfiguration;
import io.fabric8.maven.docker.config.RunVolumeConfiguration;
+import io.fabric8.maven.docker.config.SecretConfiguration;
import io.fabric8.maven.docker.config.UlimitConfig;
import io.fabric8.maven.docker.config.WaitConfiguration;
import io.fabric8.maven.docker.config.WatchImageConfiguration;
@@ -342,6 +343,7 @@ private BuildXConfiguration extractBuildx(BuildXConfiguration config, ValueProvi
.attestations(extractAttestations(config.getAttestations(), valueProvider))
.cacheFrom(valueProvider.getString(BUILDX_CACHE_FROM, config.getCacheFrom()))
.cacheTo(valueProvider.getString(BUILDX_CACHE_TO, config.getCacheTo()))
+ .secret(extractSecret(config.getSecret(), valueProvider))
.build();
}
@@ -356,6 +358,17 @@ private AttestationConfiguration extractAttestations(AttestationConfiguration co
.build();
}
+ private SecretConfiguration extractSecret(SecretConfiguration config, ValueProvider valueProvider) {
+ if (config == null) {
+ config = new SecretConfiguration();
+ }
+
+ return new SecretConfiguration.Builder()
+ .envs(valueProvider.getMap(BUILDX_SECRET_ENVS, config.getEnvs()))
+ .files(valueProvider.getMap(BUILDX_SECRET_FILES, config.getFiles()))
+ .build();
+ }
+
// Extract only the values of the port mapping
private List extractPortValues(List config, ValueProvider valueProvider) {
diff --git a/src/main/java/io/fabric8/maven/docker/service/BuildXService.java b/src/main/java/io/fabric8/maven/docker/service/BuildXService.java
index d262f0f19..1f63cd3e9 100644
--- a/src/main/java/io/fabric8/maven/docker/service/BuildXService.java
+++ b/src/main/java/io/fabric8/maven/docker/service/BuildXService.java
@@ -10,6 +10,7 @@
import io.fabric8.maven.docker.config.BuildXConfiguration;
import io.fabric8.maven.docker.config.ConfigHelper;
import io.fabric8.maven.docker.config.ImageConfiguration;
+import io.fabric8.maven.docker.config.SecretConfiguration;
import io.fabric8.maven.docker.util.EnvUtil;
import io.fabric8.maven.docker.util.ImageName;
import io.fabric8.maven.docker.util.Logger;
@@ -35,6 +36,8 @@
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
public class BuildXService {
private static final String DOCKER = "docker";
@@ -121,7 +124,7 @@ protected void buildAndLoadSinglePlatform(List buildX, String builderNam
String nativePlatform = dockerAccess.getNativePlatform();
if (platforms.size() == 1) {
buildX(buildX, builderName, buildDirs, imageConfig, configuredRegistry, platforms, buildArchive, "--load");
- } else if (platforms.contains(nativePlatform)) {
+ } else if (platforms.isEmpty() || platforms.contains(nativePlatform)) {
buildX(buildX, builderName, buildDirs, imageConfig, configuredRegistry, Collections.singletonList(nativePlatform), buildArchive, "--load");
} else {
logger.info("More than one platform specified not including native %s, no image built", nativePlatform);
@@ -130,7 +133,11 @@ protected void buildAndLoadSinglePlatform(List buildX, String builderNam
protected void pushMultiPlatform(List buildX, String builderName, BuildDirs buildDirs, ImageConfiguration imageConfig, String configuredRegistry, File buildArchive) throws MojoExecutionException {
// build and push all images. The native platform may be re-built, image should be cached and build should be quick
- buildX(buildX, builderName, buildDirs, imageConfig, configuredRegistry, imageConfig.getBuildConfiguration().getBuildX().getPlatforms(), buildArchive, "--push");
+ List platforms = new ArrayList<>(imageConfig.getBuildConfiguration().getBuildX().getPlatforms());
+ if (platforms.isEmpty()) {
+ platforms.add(dockerAccess.getNativePlatform());
+ }
+ buildX(buildX, builderName, buildDirs, imageConfig, configuredRegistry, platforms, buildArchive, "--push");
}
protected void buildX(List buildX, String builderName, BuildDirs buildDirs, ImageConfiguration imageConfig, String configuredRegistry, List platforms, File buildArchive, String extraParam)
@@ -190,6 +197,15 @@ protected void buildX(List buildX, String builderName, BuildDirs buildDi
if (buildXConfiguration.getCacheTo() != null) {
cmdLine.add("--cache-to=" + buildXConfiguration.getCacheTo());
}
+ SecretConfiguration secret = buildXConfiguration.getSecret();
+ if (secret != null) {
+ if (secret.getEnvs() != null) {
+ secret.getEnvs().forEach(buildXSecretConsumerFor("env", cmdLine::add));
+ }
+ if (secret.getFiles() != null) {
+ secret.getFiles().forEach(buildXSecretConsumerFor("src", cmdLine::add));
+ }
+ }
if (buildConfiguration.squash()) {
cmdLine.add("--squash");
@@ -213,6 +229,17 @@ protected void buildX(List buildX, String builderName, BuildDirs buildDi
}
}
+ protected BiConsumer buildXSecretConsumerFor(String attribute, Consumer cmdLineConsumer) {
+ return (arg0, arg1) -> {
+ cmdLineConsumer.accept("--secret");
+ String secretParameter = "id=" + arg0;
+ if (arg1 != null) {
+ secretParameter += "," + attribute + "=" + arg1;
+ }
+ cmdLineConsumer.accept(secretParameter);
+ };
+ }
+
protected Path getContextPath(File buildArchive) throws MojoExecutionException {
String archiveName = buildArchive.getName();
String fileName = archiveName.substring(0, archiveName.indexOf('.'));
diff --git a/src/test/java/io/fabric8/maven/docker/BuildMojoTest.java b/src/test/java/io/fabric8/maven/docker/BuildMojoTest.java
index 46dc7969a..138c1e78a 100644
--- a/src/test/java/io/fabric8/maven/docker/BuildMojoTest.java
+++ b/src/test/java/io/fabric8/maven/docker/BuildMojoTest.java
@@ -8,6 +8,7 @@
import io.fabric8.maven.docker.config.BuildImageConfiguration;
import io.fabric8.maven.docker.config.BuildXConfiguration;
import io.fabric8.maven.docker.config.ImageConfiguration;
+import io.fabric8.maven.docker.config.SecretConfiguration;
import io.fabric8.maven.docker.service.BuildService;
import io.fabric8.maven.docker.service.BuildXService;
import io.fabric8.maven.docker.service.ImagePullManager;
@@ -95,7 +96,7 @@ void buildUsingBuildx() throws IOException, MojoExecutionException {
whenMojoExecutes();
- thenBuildxRun(null, null, true, null );
+ thenBuildxRun(null, null, true, Collections.emptyList());
}
@Test
@@ -108,7 +109,7 @@ void buildUsingConfiguredBuildx() throws IOException, MojoExecutionException {
whenMojoExecutes();
- thenBuildxRun("src/docker/builder.toml", null, true, null );
+ thenBuildxRun("src/docker/builder.toml", null, true, Collections.emptyList());
}
@Test
@@ -126,7 +127,7 @@ void buildUsingConfiguredBuildxWithContext() throws IOException, MojoExecutionEx
whenMojoExecutes();
- thenBuildxRun(null, "src/main/docker", true,null );
+ thenBuildxRun(null, "src/main/docker", true, Collections.emptyList());
}
@Test
@@ -156,7 +157,7 @@ void buildUsingBuildxWithSquash() throws IOException, MojoExecutionException {
whenMojoExecutes();
- thenBuildxRun(null, null, true, "--squash");
+ thenBuildxRun(null, null, true, Collections.singletonList("--squash"));
}
@Test
@@ -169,7 +170,7 @@ void buildUsingBuildxWithSbom() throws IOException, MojoExecutionException {
whenMojoExecutes();
- thenBuildxRun(null, null, true, "--sbom=true");
+ thenBuildxRun(null, null, true, Collections.singletonList("--sbom=true"));
}
@Test
@@ -182,7 +183,7 @@ void buildUsingBuildxWithMaxProvenance() throws IOException, MojoExecutionExcept
whenMojoExecutes();
- thenBuildxRun(null, null, true, "--provenance=mode=max");
+ thenBuildxRun(null, null, true, Collections.singletonList("--provenance=mode=max"));
}
@Test
@@ -195,7 +196,7 @@ void buildUsingBuildxWithNoProvenance() throws IOException, MojoExecutionExcepti
whenMojoExecutes();
- thenBuildxRun(null, null, true, "--provenance=false");
+ thenBuildxRun(null, null, true, Collections.singletonList("--provenance=false"));
}
@Test
@@ -208,7 +209,61 @@ void buildUsingBuildxWithIncorrectProvenanceMode() throws IOException, MojoExecu
whenMojoExecutes();
- thenBuildxRun(null, null, true, null);
+ thenBuildxRun(null, null, true, Collections.emptyList());
+ }
+
+ @Test
+ void buildUsingBuildxWithSecretEnvs() throws IOException, MojoExecutionException {
+ givenBuildXService();
+
+ givenMavenProject(buildMojo);
+ givenResolvedImages(buildMojo, Collections.singletonList(singleBuildXImageWithSecret(
+ Collections.singletonMap("githubToken", "GITHUB_TOKEN"), Collections.emptyMap())));
+ givenPackaging("jar");
+
+ whenMojoExecutes();
+
+ List secrets = new ArrayList<>();
+ secrets.add("--secret");
+ secrets.add("id=githubToken,env=GITHUB_TOKEN");
+ thenBuildxRun(null, null, true, secrets);
+ }
+
+ @Test
+ void buildUsingBuildxWithSecretFiles() throws IOException, MojoExecutionException {
+ givenBuildXService();
+
+ givenMavenProject(buildMojo);
+ givenResolvedImages(buildMojo, Collections.singletonList(singleBuildXImageWithSecret(Collections.emptyMap(),
+ Collections.singletonMap("privateRepo", "../private.repo"))));
+ givenPackaging("jar");
+
+ whenMojoExecutes();
+
+ List secrets = new ArrayList<>();
+ secrets.add("--secret");
+ secrets.add("id=privateRepo,src=../private.repo");
+ thenBuildxRun(null, null, true, secrets);
+ }
+
+ @Test
+ void buildUsingBuildxWithSecretEnvsFilesMultiplePlatforms() throws IOException, MojoExecutionException {
+ givenBuildXService();
+
+ givenMavenProject(buildMojo);
+ givenResolvedImages(buildMojo, Collections.singletonList(singleBuildXImageWithSecret(
+ Collections.singletonMap("githubToken", "GITHUB_TOKEN"),
+ Collections.singletonMap("privateRepo", "../private.repo"), TWO_BUILDX_PLATFORMS)));
+ givenPackaging("jar");
+
+ whenMojoExecutes();
+
+ List secrets = new ArrayList<>();
+ secrets.add("--secret");
+ secrets.add("id=githubToken,env=GITHUB_TOKEN");
+ secrets.add("--secret");
+ secrets.add("id=privateRepo,src=../private.repo");
+ thenBuildxRun(null, null, true, secrets);
}
@Test
@@ -269,7 +324,7 @@ void buildWithTagByBuildx(boolean skipTag) throws IOException, MojoExecutionExce
List fullTags = skipTag ? Collections.emptyList() : tags.stream()
.map(tag -> new ImageName(imageConfiguration.getName(), tag).getFullName())
.collect(Collectors.toList());
- thenBuildxRun(null, null, true, null, fullTags);
+ thenBuildxRun(null, null, true, Collections.emptyList(), fullTags);
}
@ParameterizedTest
@@ -347,12 +402,13 @@ private void verifyBuild(int wantedNumberOfInvocations) throws DockerAccessExcep
}
private void thenBuildxRun(String relativeConfigFile, String contextDir, boolean nativePlatformIncluded,
- String attestation) throws MojoExecutionException {
- thenBuildxRun(relativeConfigFile, contextDir, nativePlatformIncluded, attestation, Collections.emptyList());
+ List additionalParameters) throws MojoExecutionException {
+ thenBuildxRun(relativeConfigFile, contextDir, nativePlatformIncluded, additionalParameters,
+ Collections.emptyList());
}
- private void thenBuildxRun(String relativeConfigFile, String contextDir,
- boolean nativePlatformIncluded, String attestation, List tags)
+ private void thenBuildxRun(String relativeConfigFile, String contextDir, boolean nativePlatformIncluded,
+ List additionalParameters, List tags)
throws MojoExecutionException {
Path buildPath = projectBaseDirectory.toPath().resolve("target/docker/example/latest");
String config = getOsDependentBuild(buildPath, "docker");
@@ -380,9 +436,7 @@ private void thenBuildxRun(String relativeConfigFile, String contextDir,
buildXLine.add("--build-arg");
buildXLine.add("foo=bar");
- if (attestation != null) {
- buildXLine.add(attestation);
- }
+ buildXLine.addAll(additionalParameters);
if (contextDir == null) {
buildXLine.add(getOsDependentBuild(buildPath, "build"));
@@ -446,6 +500,17 @@ private ImageConfiguration singleBuildXImageWithAttestations(Boolean sbom, Strin
.build(), null);
}
+ private ImageConfiguration singleBuildXImageWithSecret(Map envs, Map files) {
+ return singleBuildXImageWithSecret(envs, files, NATIVE_PLATFORM);
+ }
+
+ private ImageConfiguration singleBuildXImageWithSecret(Map envs, Map files,
+ String... platform) {
+ return singleImageConfiguration(getBuildXPlatforms(platform).secret(
+ new SecretConfiguration.Builder().envs(envs).files(files).build())
+ .build(), null);
+ }
+
protected ImageConfiguration singleImageWithAuthRegistry(String dockerFile) {
BuildImageConfiguration buildImageConfiguration = new BuildImageConfiguration.Builder()
.dockerFile(dockerFile)