diff --git a/src/Testcontainers/Builders/DockerRegistryAuthenticationProvider.cs b/src/Testcontainers/Builders/DockerRegistryAuthenticationProvider.cs
index a510858c9..5db612a21 100644
--- a/src/Testcontainers/Builders/DockerRegistryAuthenticationProvider.cs
+++ b/src/Testcontainers/Builders/DockerRegistryAuthenticationProvider.cs
@@ -84,7 +84,11 @@ private IDockerRegistryAuthenticationConfiguration GetUncachedAuthConfig(string
{
authConfig = new IDockerRegistryAuthenticationProvider[]
{
- new CredsHelperProvider(dockerConfigDocument, this.logger), new CredsStoreProvider(dockerConfigDocument, this.logger), new Base64Provider(dockerConfigDocument, this.logger),
+ // environment variable provider goes first as it should overwrite the Docker configuration file when set
+ new EnvironmentVariableBase64Provider(this.logger),
+ new CredsHelperProvider(dockerConfigDocument, this.logger),
+ new CredsStoreProvider(dockerConfigDocument, this.logger),
+ new Base64Provider(dockerConfigDocument, this.logger),
}
.AsParallel()
.Select(authenticationProvider => authenticationProvider.GetAuthConfig(hostname))
diff --git a/src/Testcontainers/Builders/EnvironmentVariableBase64Provider.cs b/src/Testcontainers/Builders/EnvironmentVariableBase64Provider.cs
new file mode 100644
index 000000000..9ffbc8cad
--- /dev/null
+++ b/src/Testcontainers/Builders/EnvironmentVariableBase64Provider.cs
@@ -0,0 +1,90 @@
+namespace DotNet.Testcontainers.Builders
+{
+ using System;
+ using System.Linq;
+ using System.Text;
+ using System.Text.Json;
+ using DotNet.Testcontainers.Configurations;
+ using JetBrains.Annotations;
+ using Microsoft.Extensions.Logging;
+
+ internal sealed class EnvironmentVariableBase64Provider : IDockerRegistryAuthenticationProvider
+ {
+ private readonly JsonElement rootElement;
+ private readonly ILogger logger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The logger.
+ [PublicAPI]
+ public EnvironmentVariableBase64Provider(ILogger logger)
+ {
+ var environmentVariableValue = Environment.GetEnvironmentVariable("DOCKER_AUTH_CONFIG");
+ if (!string.IsNullOrEmpty(environmentVariableValue))
+ {
+ try
+ {
+ this.rootElement = JsonDocument.Parse(environmentVariableValue).RootElement.TryGetProperty("auths", out var auths) ? auths : default;
+ }
+ catch (JsonException)
+ {
+ // silent
+ }
+ }
+
+ this.logger = logger;
+ }
+
+ public bool IsApplicable(string hostname)
+ {
+#if NETSTANDARD2_1_OR_GREATER
+ return !default(JsonElement).Equals(this.rootElement) && !JsonValueKind.Null.Equals(this.rootElement.ValueKind) && this.rootElement.EnumerateObject().Any(property => property.Name.Contains(hostname, StringComparison.OrdinalIgnoreCase));
+#else
+ return !default(JsonElement).Equals(this.rootElement) && !JsonValueKind.Null.Equals(this.rootElement.ValueKind) && this.rootElement.EnumerateObject().Any(property => property.Name.IndexOf(hostname, StringComparison.OrdinalIgnoreCase) >= 0);
+#endif
+ }
+
+ public IDockerRegistryAuthenticationConfiguration GetAuthConfig(string hostname)
+ {
+ this.logger.SearchingDockerRegistryCredential("EnvironmentVariableAuths");
+
+ if (!this.IsApplicable(hostname))
+ {
+ return null;
+ }
+
+#if NETSTANDARD2_1_OR_GREATER
+ var authProperty = this.rootElement.EnumerateObject().LastOrDefault(property => property.Name.Contains(hostname, StringComparison.OrdinalIgnoreCase));
+#else
+ var authProperty = this.rootElement.EnumerateObject().LastOrDefault(property => property.Name.IndexOf(hostname, StringComparison.OrdinalIgnoreCase) >= 0);
+#endif
+
+ if (JsonValueKind.Undefined.Equals(authProperty.Value.ValueKind))
+ {
+ return null;
+ }
+
+ if (!authProperty.Value.TryGetProperty("auth", out var auth))
+ {
+ return null;
+ }
+
+ if (string.IsNullOrEmpty(auth.GetString()))
+ {
+ return null;
+ }
+
+ var credentialInBytes = Convert.FromBase64String(auth.GetString());
+ var credential = Encoding.UTF8.GetString(credentialInBytes).Split(new[] { ':' }, 2);
+
+ if (credential.Length != 2)
+ {
+ return null;
+ }
+
+ this.logger.DockerRegistryCredentialFound(hostname);
+ return new DockerRegistryAuthenticationConfiguration(authProperty.Name, credential[0], credential[1]);
+ }
+ }
+}
diff --git a/tests/Testcontainers.Tests/Unit/Configurations/DockerRegistryAuthenticationProviderTest.cs b/tests/Testcontainers.Tests/Unit/Configurations/DockerRegistryAuthenticationProviderTest.cs
index ca7fd799b..84b91f6a8 100644
--- a/tests/Testcontainers.Tests/Unit/Configurations/DockerRegistryAuthenticationProviderTest.cs
+++ b/tests/Testcontainers.Tests/Unit/Configurations/DockerRegistryAuthenticationProviderTest.cs
@@ -104,6 +104,56 @@ public void ShouldGetAuthConfig()
}
}
+ public sealed class EnvironmentVariableBase64ProviderTest
+ {
+ private static void SetEnvironmentVariable(string authConfigValue)
+ {
+ Environment.SetEnvironmentVariable("DOCKER_AUTH_CONFIG", authConfigValue);
+ }
+
+ [Theory]
+ [InlineData("{}", false)]
+ [InlineData("{\"auths\":null}", false)]
+ [InlineData("{\"auths\":{}}", false)]
+ [InlineData("{\"auths\":{\"ghcr.io\":{}}}", false)]
+ [InlineData("{\"auths\":{\"" + DockerRegistry + "\":{}}}", true)]
+ [InlineData("{\"auths\":{\"" + DockerRegistry + "\":{\"auth\":null}}}", true)]
+ [InlineData("{\"auths\":{\"" + DockerRegistry + "\":{\"auth\":\"\"}}}", true)]
+ [InlineData("{\"auths\":{\"" + DockerRegistry + "\":{\"auth\":\"dXNlcm5hbWU=\"}}}", true)]
+ public void ShouldGetNull(string jsonAuthConfig, bool isApplicable)
+ {
+ // Given
+ SetEnvironmentVariable(jsonAuthConfig);
+
+ // When
+ var authenticationProvider = new EnvironmentVariableBase64Provider(TestcontainersSettings.Logger);
+ var authConfig = authenticationProvider.GetAuthConfig(DockerRegistry);
+
+ // Then
+ Assert.Equal(isApplicable, authenticationProvider.IsApplicable(DockerRegistry));
+ Assert.Null(authConfig);
+ }
+
+ [Fact]
+ public void ShouldGetAuthConfig()
+ {
+ // Given
+ const string jsonAuthConfig = "{\"auths\":{\"" + DockerRegistry + "\":{\"auth\":\"dXNlcm5hbWU6cGFzc3dvcmQ=\"}}}";
+ SetEnvironmentVariable(jsonAuthConfig);
+
+ // When
+ var authenticationProvider = new EnvironmentVariableBase64Provider(TestcontainersSettings.Logger);
+ var authConfig = authenticationProvider.GetAuthConfig(DockerRegistry);
+
+ // Then
+ Assert.True(authenticationProvider.IsApplicable(DockerRegistry));
+ Assert.NotNull(authConfig);
+ Assert.Equal(DockerRegistry, authConfig.RegistryEndpoint);
+ Assert.Equal("username", authConfig.Username);
+ Assert.Equal("password", authConfig.Password);
+ }
+ }
+
public sealed class CredsStoreProviderTest : SetEnvVarPath
{
[Theory]