From 32d205b8ea623dfdb8d663a1ff8b2d5da3a8903b Mon Sep 17 00:00:00 2001 From: Matthew John Cheetham Date: Mon, 7 Oct 2024 12:47:13 +0100 Subject: [PATCH 1/6] settings: add allow unsafe remotes option Add a new setting that allows users to express an explicit consent to using unsafe remote URLs (such as those using HTTP rather than HTTPS). --- docs/configuration.md | 19 ++++++++++++ docs/environment.md | 29 ++++++++++++++++++- docs/netconfig.md | 18 ++++++++++++ src/shared/Core/Constants.cs | 3 ++ src/shared/Core/Settings.cs | 11 +++++++ .../Objects/TestSettings.cs | 4 +++ 6 files changed, 83 insertions(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index a4fecf395..6a38098e3 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -255,6 +255,24 @@ Defaults to false (use hardware acceleration where available). --- +### credential.allowUnsafeRemotes + +Allow transmitting credentials to unsafe remote URLs such as unencrypted HTTP +URLs. This setting is not recommended for general use and should only be used +when necessary. + +Defaults false (disallow unsafe remote URLs). + +#### Example + +```shell +git config --global credential.allowUnsafeRemotes true +``` + +**Also see: [GCM_ALLOW_UNSAFE_REMOTES][gcm-allow-unsafe-remotes]** + +--- + ### credential.autoDetectTimeout Set the maximum length of time, in milliseconds, that GCM should wait for a @@ -1024,6 +1042,7 @@ Defaults to disabled. [envars]: environment.md [freedesktop-ss]: https://specifications.freedesktop.org/secret-service/ [gcm-allow-windowsauth]: environment.md#GCM_ALLOW_WINDOWSAUTH +[gcm-allow-unsafe-remotes]: environment.md#GCM_ALLOW_UNSAFE_REMOTES [gcm-authority]: environment.md#GCM_AUTHORITY-deprecated [gcm-autodetect-timeout]: environment.md#GCM_AUTODETECT_TIMEOUT [gcm-azrepos-credentialtype]: environment.md#GCM_AZREPOS_CREDENTIALTYPE diff --git a/docs/environment.md b/docs/environment.md index edda0d714..293a86ae0 100644 --- a/docs/environment.md +++ b/docs/environment.md @@ -302,6 +302,32 @@ Defaults to false (use hardware acceleration where available). --- +### GCM_ALLOW_UNSAFE_REMOTES + +Allow transmitting credentials to unsafe remote URLs such as unencrypted HTTP +URLs. This setting is not recommended for general use and should only be used +when necessary. + +Defaults false (disallow unsafe remote URLs). + +#### Example + +##### Windows + +```batch +SET GCM_ALLOW_UNSAFE_REMOTES=true +``` + +##### macOS/Linux + +```bash +export GCM_ALLOW_UNSAFE_REMOTES=true +``` + +**Also see: [credential.allowUnsafeRemotes][credential-allowunsaferemotes]** + +--- + ### GCM_AUTODETECT_TIMEOUT Set the maximum length of time, in milliseconds, that GCM should wait for a @@ -1153,7 +1179,8 @@ Defaults to disabled. [autodetect]: autodetect.md [azure-access-tokens]: azrepos-users-and-tokens.md [configuration]: configuration.md -[credential-allowwindowsauth]: environment.md#credentialallowWindowsAuth +[credential-allowwindowsauth]: configuration.md#credentialallowwindowsauth +[credential-allowunsaferemotes]: configuration.md#credentialallowunsaferemotes [credential-authority]: configuration.md#credentialauthority-deprecated [credential-autodetecttimeout]: configuration.md#credentialautodetecttimeout [credential-azrepos-credential-type]: configuration.md#credentialazreposcredentialtype diff --git a/docs/netconfig.md b/docs/netconfig.md index cf312336f..920344f15 100644 --- a/docs/netconfig.md +++ b/docs/netconfig.md @@ -191,6 +191,22 @@ network traffic inspection tool such as [Telerik Fiddler][telerik-fiddler]. If you are using such tools please consult their documentation for trusting the proxy root certificates. +--- + +## Unsafe Remote URLs + +If you are using a remote URL that is not considered safe, such as unencrypted +HTTP (remote URLs that start with `http://`), host providers may prevent you +from authenticating with your credentials. + +In this case, you should consider using a HTTPS (starting with `https://`) +remote URL to ensure your credentials are transmitted securely. + +If you accept the risks associated with using an unsafe remote URL, you can +configure GCM to allow the use of unsafe remote URLS by setting the environment +variable [`GCM_ALLOW_UNSAFE_REMOTES`][unsafe-envar], or by using the Git +configuration option [`credential.allowUnsafeRemotes`][unsafe-config] to `true`. + [environment]: environment.md [configuration]: configuration.md [git-http-proxy]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpproxy @@ -212,3 +228,5 @@ proxy root certificates. [git-ssl-no-verify]: https://git-scm.com/book/en/v2/Git-Internals-Environment-Variables#_networking [git-http-ssl-verify]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpsslVerify [telerik-fiddler]: https://www.telerik.com/fiddler +[unsafe-envar]: environment.md#gcm_allow_unsafe_remotes +[unsafe-config]: configuration.md#credentialallowunsaferemotes diff --git a/src/shared/Core/Constants.cs b/src/shared/Core/Constants.cs index 210c991bc..41ccb990c 100644 --- a/src/shared/Core/Constants.cs +++ b/src/shared/Core/Constants.cs @@ -119,6 +119,7 @@ public static class EnvironmentVariables public const string OAuthDefaultUserName = "GCM_OAUTH_DEFAULT_USERNAME"; public const string GcmDevUseLegacyUiHelpers = "GCM_DEV_USELEGACYUIHELPERS"; public const string GcmGuiSoftwareRendering = "GCM_GUI_SOFTWARE_RENDERING"; + public const string GcmAllowUnsafeRemotes = "GCM_ALLOW_UNSAFE_REMOTES"; } public static class Http @@ -163,6 +164,7 @@ public static class Credential public const string MsAuthUseDefaultAccount = "msauthUseDefaultAccount"; public const string GuiSoftwareRendering = "guiSoftwareRendering"; public const string GpgPassStorePath = "gpgPassStorePath"; + public const string AllowUnsafeRemotes = "allowUnsafeRemotes"; public const string OAuthAuthenticationModes = "oauthAuthModes"; public const string OAuthClientId = "oauthClientId"; @@ -226,6 +228,7 @@ public static class HelpUrls public const string GcmAutoDetect = "https://aka.ms/gcm/autodetect"; public const string GcmDefaultAccount = "https://aka.ms/gcm/defaultaccount"; public const string GcmMultipleUsers = "https://aka.ms/gcm/multipleusers"; + public const string GcmUnsafeRemotes = "https://aka.ms/gcm/unsaferemotes"; } private static Version _gcmVersion; diff --git a/src/shared/Core/Settings.cs b/src/shared/Core/Settings.cs index 2aa71edf4..0e24ce9a3 100644 --- a/src/shared/Core/Settings.cs +++ b/src/shared/Core/Settings.cs @@ -189,6 +189,11 @@ public interface ISettings : IDisposable /// bool UseSoftwareRendering { get; } + /// + /// Permit the use of unsafe remotes URLs such as regular HTTP. + /// + bool AllowUnsafeRemotes { get; } + /// /// Get TRACE2 settings. /// @@ -580,6 +585,12 @@ public bool UseSoftwareRendering } } + public bool AllowUnsafeRemotes => + TryGetSetting(KnownEnvars.GcmAllowUnsafeRemotes, + KnownGitCfg.Credential.SectionName, + KnownGitCfg.Credential.AllowUnsafeRemotes, + out string str) && str.ToBooleanyOrDefault(false); + public Trace2Settings GetTrace2Settings() { var settings = new Trace2Settings(); diff --git a/src/shared/TestInfrastructure/Objects/TestSettings.cs b/src/shared/TestInfrastructure/Objects/TestSettings.cs index f14bf6cc9..3e67e39b0 100644 --- a/src/shared/TestInfrastructure/Objects/TestSettings.cs +++ b/src/shared/TestInfrastructure/Objects/TestSettings.cs @@ -53,6 +53,8 @@ public class TestSettings : ISettings public bool UseMsAuthDefaultAccount { get; set; } + public bool AllowUnsafeRemotes { get; set; } = false; + public Trace2Settings GetTrace2Settings() { return new Trace2Settings() @@ -189,6 +191,8 @@ ProxyConfiguration ISettings.GetProxyConfiguration() bool ISettings.UseSoftwareRendering => false; + bool ISettings.AllowUnsafeRemotes => AllowUnsafeRemotes; + #endregion #region IDisposable From 2fbe3d615d6fb8013e14e55a837a9c8e95742b16 Mon Sep 17 00:00:00 2001 From: Matthew John Cheetham Date: Mon, 7 Oct 2024 12:48:29 +0100 Subject: [PATCH 2/6] bitbucket: support GCM_ALLOW_UNSAFE_REMOTES option --- .../Atlassian.Bitbucket/BitbucketHostProvider.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/shared/Atlassian.Bitbucket/BitbucketHostProvider.cs b/src/shared/Atlassian.Bitbucket/BitbucketHostProvider.cs index 35472682c..286398de9 100644 --- a/src/shared/Atlassian.Bitbucket/BitbucketHostProvider.cs +++ b/src/shared/Atlassian.Bitbucket/BitbucketHostProvider.cs @@ -55,8 +55,8 @@ public bool IsSupported(InputArguments input) return false; } - // We do not support unencrypted HTTP communications to Bitbucket, - // but we report `true` here for HTTP so that we can show a helpful + // We do not recommend unencrypted HTTP communications to Bitbucket, but it is possible. + // Therefore, we report `true` here for HTTP so that we can show a helpful // error message for the user in `GetCredentialAsync`. return (StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http") || StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "https")) && @@ -81,11 +81,14 @@ public bool IsSupported(HttpResponseMessage response) public async Task GetCredentialAsync(InputArguments input) { // We should not allow unencrypted communication and should inform the user - if (StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http") - && BitbucketHelper.IsBitbucketOrg(input)) + if (!_context.Settings.AllowUnsafeRemotes && + StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http") && + BitbucketHelper.IsBitbucketOrg(input)) { throw new Trace2Exception(_context.Trace2, - "Unencrypted HTTP is not supported for Bitbucket.org. Ensure the repository remote URL is using HTTPS."); + "Unencrypted HTTP is not recommended for Bitbucket.org. " + + "Ensure the repository remote URL is using HTTPS " + + $"or see {Constants.HelpUrls.GcmUnsafeRemotes} about how to allow unsafe remotes."); } var authModes = await GetSupportedAuthenticationModesAsync(input); From 6b87cc7f21b11da541e1b2c194ec06975d5d1265 Mon Sep 17 00:00:00 2001 From: Matthew John Cheetham Date: Mon, 7 Oct 2024 12:48:45 +0100 Subject: [PATCH 3/6] github: support GCM_ALLOW_UNSAFE_REMOTES option --- src/shared/GitHub/GitHubHostProvider.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/shared/GitHub/GitHubHostProvider.cs b/src/shared/GitHub/GitHubHostProvider.cs index 918e859a0..21d29f651 100644 --- a/src/shared/GitHub/GitHubHostProvider.cs +++ b/src/shared/GitHub/GitHubHostProvider.cs @@ -285,10 +285,13 @@ public virtual Task EraseCredentialAsync(InputArguments input) ThrowIfDisposed(); // We should not allow unencrypted communication and should inform the user - if (StringComparer.OrdinalIgnoreCase.Equals(remoteUri.Scheme, "http")) + if (!_context.Settings.AllowUnsafeRemotes && + StringComparer.OrdinalIgnoreCase.Equals(remoteUri.Scheme, "http")) { throw new Trace2Exception(_context.Trace2, - "Unencrypted HTTP is not supported for GitHub. Ensure the repository remote URL is using HTTPS."); + "Unencrypted HTTP is not recommended for GitHub. " + + "Ensure the repository remote URL is using HTTPS " + + $"or see {Constants.HelpUrls.GcmUnsafeRemotes} about how to allow unsafe remotes."); } string service = GetServiceName(remoteUri); From f2652f3bad072ce8bcdcdf3615068a9cf815aeaa Mon Sep 17 00:00:00 2001 From: Matthew John Cheetham Date: Mon, 7 Oct 2024 12:49:00 +0100 Subject: [PATCH 4/6] gitlab: support GCM_ALLOW_UNSAFE_REMOTES option --- src/shared/GitLab/GitLabHostProvider.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/shared/GitLab/GitLabHostProvider.cs b/src/shared/GitLab/GitLabHostProvider.cs index eda6e2f0f..6cda3c0e1 100644 --- a/src/shared/GitLab/GitLabHostProvider.cs +++ b/src/shared/GitLab/GitLabHostProvider.cs @@ -95,10 +95,13 @@ public override async Task GenerateCredentialAsync(InputArguments i ThrowIfDisposed(); // We should not allow unencrypted communication and should inform the user - if (StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http")) + if (!Context.Settings.AllowUnsafeRemotes && + StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http")) { throw new Trace2Exception(Context.Trace2, - "Unencrypted HTTP is not supported for GitHub. Ensure the repository remote URL is using HTTPS."); + "Unencrypted HTTP is not recommended for GitLab. " + + "Ensure the repository remote URL is using HTTPS " + + $"or see {Constants.HelpUrls.GcmUnsafeRemotes} about how to allow unsafe remotes."); } Uri remoteUri = input.GetRemoteUri(); From 7a613f3c2b8064d973b8733411ee1262993cda39 Mon Sep 17 00:00:00 2001 From: Matthew John Cheetham Date: Mon, 7 Oct 2024 12:49:16 +0100 Subject: [PATCH 5/6] azrepos: support GCM_ALLOW_UNSAFE_REMOTES option --- .../AzureReposHostProvider.cs | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/shared/Microsoft.AzureRepos/AzureReposHostProvider.cs b/src/shared/Microsoft.AzureRepos/AzureReposHostProvider.cs index 1d5c649d0..525704886 100644 --- a/src/shared/Microsoft.AzureRepos/AzureReposHostProvider.cs +++ b/src/shared/Microsoft.AzureRepos/AzureReposHostProvider.cs @@ -59,7 +59,7 @@ public bool IsSupported(InputArguments input) return false; } - // We do not support unencrypted HTTP communications to Azure Repos, + // We do not recommend unencrypted HTTP communications to Azure Repos, // but we report `true` here for HTTP so that we can show a helpful // error message for the user in `CreateCredentialAsync`. return input.TryGetHostAndPort(out string hostName, out _) @@ -208,16 +208,22 @@ protected override void ReleaseManagedResources() base.ReleaseManagedResources(); } - private async Task GeneratePersonalAccessTokenAsync(InputArguments input) + private void ThrowIfUnsafeRemote(InputArguments input) { - ThrowIfDisposed(); - - // We should not allow unencrypted communication and should inform the user - if (StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http")) + if (!_context.Settings.AllowUnsafeRemotes && + StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http")) { throw new Trace2Exception(_context.Trace2, - "Unencrypted HTTP is not supported for Azure Repos. Ensure the repository remote URL is using HTTPS."); + "Unencrypted HTTP is not recommended for Azure Repos. " + + "Ensure the repository remote URL is using HTTPS " + + $"or see {Constants.HelpUrls.GcmUnsafeRemotes} about how to allow unsafe remotes."); } + } + + private async Task GeneratePersonalAccessTokenAsync(InputArguments input) + { + ThrowIfDisposed(); + ThrowIfUnsafeRemote(input); Uri remoteUserUri = input.GetRemoteUri(includeUser: true); Uri orgUri = UriHelpers.CreateOrganizationUri(remoteUserUri, out _); @@ -257,16 +263,11 @@ private async Task GeneratePersonalAccessTokenAsync(InputArguments private async Task GetAzureAccessTokenAsync(InputArguments input) { + ThrowIfUnsafeRemote(input); + Uri remoteWithUserUri = input.GetRemoteUri(includeUser: true); string userName = input.UserName; - // We should not allow unencrypted communication and should inform the user - if (StringComparer.OrdinalIgnoreCase.Equals(remoteWithUserUri.Scheme, "http")) - { - throw new Trace2Exception(_context.Trace2, - "Unencrypted HTTP is not supported for Azure Repos. Ensure the repository remote URL is using HTTPS."); - } - Uri orgUri = UriHelpers.CreateOrganizationUri(remoteWithUserUri, out string orgName); _context.Trace.WriteLine($"Determining Microsoft Authentication authority for Azure DevOps organization '{orgName}'..."); From fc067e8c00d95d1ac520d78744bb962829567552 Mon Sep 17 00:00:00 2001 From: Matthew John Cheetham Date: Mon, 7 Oct 2024 12:49:30 +0100 Subject: [PATCH 6/6] generic: support GCM_ALLOW_UNSAFE_REMOTES option Note that we only emit a warning for the generic host provider rather than failing-fast like the other providers do. This is because we never blocked HTTP remotes previously in the generic provider (which is often used for localhost, custom hosts, etc) and don't want to break any existing scenarios or scripts. The new option can be used to dismiss this warning message. --- src/shared/Core/GenericHostProvider.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/shared/Core/GenericHostProvider.cs b/src/shared/Core/GenericHostProvider.cs index 447e465d5..9f087ca5b 100644 --- a/src/shared/Core/GenericHostProvider.cs +++ b/src/shared/Core/GenericHostProvider.cs @@ -54,6 +54,17 @@ public override async Task GenerateCredentialAsync(InputArguments i { ThrowIfDisposed(); + // We only want to *warn* about HTTP remotes for the generic provider because it supports all protocols + // and, historically, we never blocked HTTP remotes in this provider. + // The user can always set the 'GCM_ALLOW_UNSAFE' setting to silence the warning. + if (!Context.Settings.AllowUnsafeRemotes && + StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http")) + { + Context.Streams.Error.WriteLine( + "warning: use of unencrypted HTTP remote URLs is not recommended; " + + $"see {Constants.HelpUrls.GcmUnsafeRemotes} for more information."); + } + Uri uri = input.GetRemoteUri(); // Determine the if the host supports Windows Integration Authentication (WIA) or OAuth