From f2128713ad7d7b453d2ce0a1d59802b42988e676 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 0908c1d9feb5..1b7a43f0ac8e 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs @@ -183,7 +183,6 @@ internal static JsonDocument GetDockerConfig() dockerCommand.CaptureStdErr(); CommandResult dockerCommandResult = dockerCommand.Execute(); - if (dockerCommandResult.ExitCode != 0) { throw new DockerLoadException(Resource.FormatString( @@ -193,9 +192,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 971658d01b1eed1194d6915cbd7391de28e242c2 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 4bd0470b6848..92800f7f6f16 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 c0f98b7e447bee318a364fd567df6cfea7d435f5 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 92800f7f6f16..0ae4c3f6a938 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 1b7a43f0ac8e..059977924382 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs @@ -191,24 +191,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 0d9f82ac5ca09632e83ab084a4b32282b60961d4 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 0ae4c3f6a938..20e015b42af2 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 6782899a9ce91d2919862d90b535ed07196f5b99 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 20e015b42af2..0ae4c3f6a938 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 059977924382..48335cf48bca 100644 --- a/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs +++ b/src/Containers/Microsoft.NET.Build.Containers/LocalDaemons/DockerCli.cs @@ -206,41 +206,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 3e97a7b1a06957a5787e6d7fc68439f306f42604 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 0ae4c3f6a938..7fff1a2cec24 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 48335cf48bca..dadb0888bf22 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) @@ -162,6 +171,7 @@ public bool IsAvailable() public string? GetCommand() => GetCommandAsync(default).GetAwaiter().GetResult(); +#endif /// /// Gets docker configuration. @@ -191,7 +201,7 @@ internal static JsonDocument GetDockerConfig() dockerCommandResult.StdOut, dockerCommandResult.StdErr)); } - + return JsonDocument.Parse(dockerCommandResult.StdOut); } catch (Exception e) when (e is not DockerLoadException) @@ -206,7 +216,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; @@ -214,11 +224,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)) { @@ -228,13 +238,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)) { @@ -254,6 +264,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(); @@ -359,6 +370,7 @@ await Task.WhenAll( return _command; } +#endif private static bool IsPodmanAlias() { @@ -380,6 +392,7 @@ private static bool IsPodmanAlias() } } +#if NET private async Task TryRunVersionCommandAsync(string command, CancellationToken cancellationToken) { try @@ -402,6 +415,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 6d7041c4c4f5..a8b45e6993f9 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 @@ -44,6 +44,8 @@ + +