diff --git a/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs b/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs index 34cda5315b1e..c27d1ea25874 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs @@ -9,6 +9,7 @@ using System.Diagnostics.CodeAnalysis; #endif using System.Text; +using System.Text.Json.Nodes; using System.Text.RegularExpressions; using Microsoft.NET.Build.Containers.Resources; @@ -143,13 +144,27 @@ internal static bool IsValidImageTag(string imageTag) return ReferenceParser.anchoredTagRegexp.IsMatch(imageTag); } + /// /// Given an already-validated registry domain, this is our hueristic to determine what HTTP protocol should be used to interact with it. + /// If the domain is localhost, we default to HTTP. Otherwise, we check the Docker config to see if the registry is marked as insecure. /// This is primarily for testing - in the real world almost all usage should be through HTTPS! /// internal static Uri TryExpandRegistryToUri(string alreadyValidatedDomain) { - var prefix = alreadyValidatedDomain.StartsWith("localhost", StringComparison.Ordinal) ? "http" : "https"; + string prefix = "https"; + if (alreadyValidatedDomain.StartsWith("localhost", StringComparison.Ordinal)) + { + prefix = "http"; + } + + //check the docker config to see if the registry is marked as insecure + else if (DockerCli.IsInsecureRegistry(alreadyValidatedDomain)) + { + prefix = "http"; + } + + return new Uri($"{prefix}://{alreadyValidatedDomain}"); } diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs index b7b0733ad52a..fb0e09b87d5a 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +#if NET using System.Formats.Tar; +#endif using System.Text.Json; using System.Text.Json.Nodes; using Microsoft.DotNet.Cli.Utils; @@ -12,7 +14,10 @@ namespace Microsoft.NET.Build.Containers; // Wraps the 'docker'/'podman' cli. -internal sealed class DockerCli : ILocalRegistry +internal sealed class DockerCli +#if NET +: ILocalRegistry +#endif { public const string DockerCommand = "docker"; public const string PodmanCommand = "podman"; @@ -21,7 +26,10 @@ internal sealed class DockerCli : ILocalRegistry private readonly ILogger _logger; private string? _command; + +#if NET private string? _fullCommandPath; +#endif public DockerCli(string? command, ILoggerFactory loggerFactory) { @@ -53,6 +61,7 @@ private static string FindFullPathFromPath(string command) return command; } +#if NET private async ValueTask FindFullCommandPath(CancellationToken cancellationToken) { if (_fullCommandPath != null) @@ -164,6 +173,7 @@ public bool IsAvailable() public string? GetCommand() => GetCommandAsync(default).GetAwaiter().GetResult(); +#endif /// /// Gets docker configuration. @@ -185,7 +195,6 @@ internal static JsonDocument GetDockerConfig() dockerCommand.CaptureStdErr(); CommandResult dockerCommandResult = dockerCommand.Execute(); - if (dockerCommandResult.ExitCode != 0) { throw new DockerLoadException(Resource.FormatString( @@ -196,17 +205,68 @@ internal static JsonDocument GetDockerConfig() } return JsonDocument.Parse(dockerCommandResult.StdOut); - - } catch (Exception e) when (e is not DockerLoadException) { throw new DockerLoadException(Resource.FormatString(nameof(Strings.DockerInfoFailed_Ex), e.Message)); } } + /// + /// Checks if the registry is marked as insecure in the docker/podman config. + /// + /// + /// + public static bool IsInsecureRegistry(string registryDomain) + { + try + { + //check the docker config to see if the registry is marked as insecure + var rootElement = GetDockerConfig().RootElement; + + //for docker + if (rootElement.TryGetProperty("RegistryConfig", out var registryConfig) && registryConfig.ValueKind == JsonValueKind.Object) + { + if (registryConfig.TryGetProperty("IndexConfigs", out var indexConfigs) && indexConfigs.ValueKind == JsonValueKind.Object) + { + foreach (var property in indexConfigs.EnumerateObject()) + { + if (property.Value.ValueKind == JsonValueKind.Object && property.Value.TryGetProperty("Secure", out var secure) && !secure.GetBoolean()) + { + if (property.Name.Equals(registryDomain, StringComparison.Ordinal)) + { + return true; + } + } + } + } + } + + //for podman + if (rootElement.TryGetProperty("registries", out var registries) && registries.ValueKind == JsonValueKind.Object) + { + foreach (var property in registries.EnumerateObject()) + { + if (property.Value.ValueKind == JsonValueKind.Object && property.Value.TryGetProperty("Insecure", out var insecure) && insecure.GetBoolean()) + { + if (property.Name.Equals(registryDomain, StringComparison.Ordinal)) + { + return true; + } + } + } + } + return false; + } + catch (DockerLoadException) + { + //if docker load fails, we can't check the config so we assume the registry is secure + return false; + } + } private static void Proc_OutputDataReceived(object sender, DataReceivedEventArgs e) => throw new NotImplementedException(); +#if NET public static async Task WriteImageToStreamAsync(BuiltImage image, SourceImageReference sourceReference, DestinationImageReference destinationReference, Stream imageStream, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -312,6 +372,7 @@ await Task.WhenAll( return _command; } +#endif private static bool IsPodmanAlias() { @@ -333,6 +394,7 @@ private static bool IsPodmanAlias() } } +#if NET private async Task TryRunVersionCommandAsync(string command, CancellationToken cancellationToken) { try @@ -355,6 +417,7 @@ private async Task TryRunVersionCommandAsync(string command, CancellationT return false; } } +#endif public override string ToString() { diff --git a/src/Containers/Microsoft.NET.Build.Containers/Microsoft.NET.Build.Containers.csproj b/src/Containers/Microsoft.NET.Build.Containers/Microsoft.NET.Build.Containers.csproj index 040677b24bf5..2c6b5818c1af 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/Microsoft.NET.Build.Containers.csproj +++ b/src/Containers/Microsoft.NET.Build.Containers/Microsoft.NET.Build.Containers.csproj @@ -43,6 +43,8 @@ + +