From 7ce8661619559d2d9145af15cc6b7647068c7f60 Mon Sep 17 00:00:00 2001 From: Dameng <313880747@qq.com> Date: Thu, 28 Mar 2024 15:03:39 +0800 Subject: [PATCH 1/6] throw `DockerLoadException` when GetDockerConfig while docker daemon is not running. --- .../LocalDaemons/DockerCli.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs index b7b0733ad52a..802477bd5af0 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs @@ -185,7 +185,6 @@ internal static JsonDocument GetDockerConfig() dockerCommand.CaptureStdErr(); CommandResult dockerCommandResult = dockerCommand.Execute(); - if (dockerCommandResult.ExitCode != 0) { throw new DockerLoadException(Resource.FormatString( @@ -195,9 +194,17 @@ internal static JsonDocument GetDockerConfig() dockerCommandResult.StdErr)); } - return JsonDocument.Parse(dockerCommandResult.StdOut); + var result = JsonDocument.Parse(dockerCommandResult.StdOut); + if(result.RootElement.TryGetProperty("ID", out var id) ==false || string.IsNullOrWhiteSpace(id.GetString())) + { + var serverErrors = result.RootElement.TryGetProperty("ServerErrors",out var error) + ? string.Join(Environment.NewLine, error.EnumerateArray().Select(o=>o.GetString())) + : string.Empty; + throw new DockerLoadException(Resource.FormatString(nameof(Strings.DockerInfoFailed_Ex), serverErrors)); + } + return result; } catch (Exception e) when (e is not DockerLoadException) { From e621b0ad7384700512e1cdf645a3cd5909564b81 Mon Sep 17 00:00:00 2001 From: Dameng <313880747@qq.com> Date: Thu, 28 Mar 2024 15:07:24 +0800 Subject: [PATCH 2/6] add `http` prefix when docker config detect a insecure registry --- .../ContainerHelpers.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs b/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs index 3aac2935fe27..a8971ff103a5 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,39 @@ 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"; + } +#if !NET472 + //check the docker config to see if the registry is marked as insecure + if (DockerCli.GetDockerConfig().RootElement.TryGetProperty("RegistryConfig", out var registryConfig) && registryConfig.ValueKind == System.Text.Json.JsonValueKind.Object) + { + if(registryConfig.TryGetProperty("IndexConfigs",out var indexConfigs) && indexConfigs.ValueKind == System.Text.Json.JsonValueKind.Object){ + foreach (var property in indexConfigs.EnumerateObject()) + { + if(property.Value.ValueKind == System.Text.Json.JsonValueKind.Object && property.Value.TryGetProperty("Secure",out var secure) && !secure.GetBoolean()) + { + if (property.Name.Equals(alreadyValidatedDomain,StringComparison.Ordinal)) + { + prefix = "http"; + break; + } + } + } + } + } +#endif + return new Uri($"{prefix}://{alreadyValidatedDomain}"); } From 3856a7741ffb55bdb10d30312dde4d1dfb2e6453 Mon Sep 17 00:00:00 2001 From: Dameng <313880747@qq.com> Date: Fri, 29 Mar 2024 13:24:38 +0800 Subject: [PATCH 3/6] add IsInsecureRegistry; add podman support --- .../ContainerHelpers.cs | 16 +----- .../LocalDaemons/DockerCli.cs | 57 +++++++++++++++---- 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs b/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs index a8971ff103a5..4d6764107891 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs @@ -159,21 +159,9 @@ internal static Uri TryExpandRegistryToUri(string alreadyValidatedDomain) } #if !NET472 //check the docker config to see if the registry is marked as insecure - if (DockerCli.GetDockerConfig().RootElement.TryGetProperty("RegistryConfig", out var registryConfig) && registryConfig.ValueKind == System.Text.Json.JsonValueKind.Object) + else if (DockerCli.IsInsecureRegistry(alreadyValidatedDomain)) { - if(registryConfig.TryGetProperty("IndexConfigs",out var indexConfigs) && indexConfigs.ValueKind == System.Text.Json.JsonValueKind.Object){ - foreach (var property in indexConfigs.EnumerateObject()) - { - if(property.Value.ValueKind == System.Text.Json.JsonValueKind.Object && property.Value.TryGetProperty("Secure",out var secure) && !secure.GetBoolean()) - { - if (property.Name.Equals(alreadyValidatedDomain,StringComparison.Ordinal)) - { - prefix = "http"; - break; - } - } - } - } + prefix = "http"; } #endif diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs index 802477bd5af0..930c9c386b5a 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs @@ -193,24 +193,57 @@ internal static JsonDocument GetDockerConfig() dockerCommandResult.StdOut, dockerCommandResult.StdErr)); } - - - var result = JsonDocument.Parse(dockerCommandResult.StdOut); - if(result.RootElement.TryGetProperty("ID", out var id) ==false || string.IsNullOrWhiteSpace(id.GetString())) - { - var serverErrors = result.RootElement.TryGetProperty("ServerErrors",out var error) - ? string.Join(Environment.NewLine, error.EnumerateArray().Select(o=>o.GetString())) - : string.Empty; - throw new DockerLoadException(Resource.FormatString(nameof(Strings.DockerInfoFailed_Ex), serverErrors)); - } - - return result; + + 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) + { + //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; + } private static void Proc_OutputDataReceived(object sender, DataReceivedEventArgs e) => throw new NotImplementedException(); From 1cc85d7495accbf0c14f25c249b92e6bee1d3fea Mon Sep 17 00:00:00 2001 From: Dameng <313880747@qq.com> Date: Fri, 29 Mar 2024 20:12:39 +0800 Subject: [PATCH 4/6] add available check before check is insecure. --- .../Microsoft.NET.Build.Containers/ContainerHelpers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs b/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs index 4d6764107891..97dfe78b1499 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs @@ -159,7 +159,7 @@ internal static Uri TryExpandRegistryToUri(string alreadyValidatedDomain) } #if !NET472 //check the docker config to see if the registry is marked as insecure - else if (DockerCli.IsInsecureRegistry(alreadyValidatedDomain)) + else if (DockerCli.IsAvailable() && DockerCli.IsInsecureRegistry(alreadyValidatedDomain)) { prefix = "http"; } From 03816b089f17fbf80151f5c96a24c7022af29783 Mon Sep 17 00:00:00 2001 From: Dameng <313880747@qq.com> Date: Fri, 29 Mar 2024 20:20:46 +0800 Subject: [PATCH 5/6] fix stupid mistake. --- .../ContainerHelpers.cs | 2 +- .../LocalDaemons/DockerCli.cs | 47 +++++++++++-------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs b/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs index 97dfe78b1499..4d6764107891 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs @@ -159,7 +159,7 @@ internal static Uri TryExpandRegistryToUri(string alreadyValidatedDomain) } #if !NET472 //check the docker config to see if the registry is marked as insecure - else if (DockerCli.IsAvailable() && DockerCli.IsInsecureRegistry(alreadyValidatedDomain)) + else if (DockerCli.IsInsecureRegistry(alreadyValidatedDomain)) { prefix = "http"; } diff --git a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs index 930c9c386b5a..f67a3856df8d 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs @@ -208,41 +208,50 @@ internal static JsonDocument GetDockerConfig() /// public static bool IsInsecureRegistry(string registryDomain) { - //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) + try { - if(registryConfig.TryGetProperty("IndexConfigs",out var indexConfigs) && indexConfigs.ValueKind == JsonValueKind.Object) + //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) { - foreach (var property in indexConfigs.EnumerateObject()) + if(registryConfig.TryGetProperty("IndexConfigs",out var indexConfigs) && indexConfigs.ValueKind == JsonValueKind.Object) { - if(property.Value.ValueKind == JsonValueKind.Object && property.Value.TryGetProperty("Secure",out var secure) && !secure.GetBoolean()) + foreach (var property in indexConfigs.EnumerateObject()) { - if (property.Name.Equals(registryDomain, StringComparison.Ordinal)) + if(property.Value.ValueKind == JsonValueKind.Object && property.Value.TryGetProperty("Secure",out var secure) && !secure.GetBoolean()) { - return true; + 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()) + + //for podman + if(rootElement.TryGetProperty("registries",out var registries) && registries.ValueKind == JsonValueKind.Object) { - if(property.Value.ValueKind == JsonValueKind.Object && property.Value.TryGetProperty("Insecure",out var insecure) && insecure.GetBoolean()) + foreach (var property in registries.EnumerateObject()) { - if (property.Name.Equals(registryDomain, StringComparison.Ordinal)) + if(property.Value.ValueKind == JsonValueKind.Object && property.Value.TryGetProperty("Insecure",out var insecure) && insecure.GetBoolean()) { - return true; + 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; } - - return false; } private static void Proc_OutputDataReceived(object sender, DataReceivedEventArgs e) => throw new NotImplementedException(); From 94dedc62fc3b22e8dca6db6221234f7dde6c7668 Mon Sep 17 00:00:00 2001 From: Chet Husk Date: Fri, 12 Apr 2024 22:04:38 -0500 Subject: [PATCH 6/6] Add enough of DockerCli to be able to use it to check insecure registries. --- .../ContainerHelpers.cs | 4 +-- .../LocalDaemons/DockerCli.cs | 30 ++++++++++++++----- .../Microsoft.NET.Build.Containers.csproj | 2 ++ 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs b/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs index 4d6764107891..3324adff837a 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/ContainerHelpers.cs @@ -157,13 +157,13 @@ internal static Uri TryExpandRegistryToUri(string alreadyValidatedDomain) { prefix = "http"; } -#if !NET472 + //check the docker config to see if the registry is marked as insecure else if (DockerCli.IsInsecureRegistry(alreadyValidatedDomain)) { prefix = "http"; } -#endif + 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 f67a3856df8d..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. @@ -193,7 +203,7 @@ internal static JsonDocument GetDockerConfig() dockerCommandResult.StdOut, dockerCommandResult.StdErr)); } - + return JsonDocument.Parse(dockerCommandResult.StdOut); } catch (Exception e) when (e is not DockerLoadException) @@ -208,7 +218,7 @@ internal static JsonDocument GetDockerConfig() /// public static bool IsInsecureRegistry(string registryDomain) { - try + try { //check the docker config to see if the registry is marked as insecure var rootElement = GetDockerConfig().RootElement; @@ -216,11 +226,11 @@ public static bool IsInsecureRegistry(string registryDomain) //for docker if (rootElement.TryGetProperty("RegistryConfig", out var registryConfig) && registryConfig.ValueKind == JsonValueKind.Object) { - if(registryConfig.TryGetProperty("IndexConfigs",out var indexConfigs) && indexConfigs.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.Value.ValueKind == JsonValueKind.Object && property.Value.TryGetProperty("Secure", out var secure) && !secure.GetBoolean()) { if (property.Name.Equals(registryDomain, StringComparison.Ordinal)) { @@ -230,13 +240,13 @@ public static bool IsInsecureRegistry(string registryDomain) } } } - + //for podman - if(rootElement.TryGetProperty("registries",out var registries) && registries.ValueKind == JsonValueKind.Object) + 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.Value.ValueKind == JsonValueKind.Object && property.Value.TryGetProperty("Insecure", out var insecure) && insecure.GetBoolean()) { if (property.Name.Equals(registryDomain, StringComparison.Ordinal)) { @@ -256,6 +266,7 @@ public static bool IsInsecureRegistry(string registryDomain) 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(); @@ -361,6 +372,7 @@ await Task.WhenAll( return _command; } +#endif private static bool IsPodmanAlias() { @@ -382,6 +394,7 @@ private static bool IsPodmanAlias() } } +#if NET private async Task TryRunVersionCommandAsync(string command, CancellationToken cancellationToken) { try @@ -404,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 @@ + +