Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add control over use of unsafe remotes #1721

Merged
merged 6 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
29 changes: 28 additions & 1 deletion docs/environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions docs/netconfig.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
13 changes: 8 additions & 5 deletions src/shared/Atlassian.Bitbucket/BitbucketHostProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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")) &&
Expand All @@ -81,11 +81,14 @@ public bool IsSupported(HttpResponseMessage response)
public async Task<ICredential> 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);
Expand Down
3 changes: 3 additions & 0 deletions src/shared/Core/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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";
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shortlink has been configured to point to

https://github.com/git-ecosystem/git-credential-manager/blob/release/docs/netconfig.md#unsafe-remote-urls

}

private static Version _gcmVersion;
Expand Down
11 changes: 11 additions & 0 deletions src/shared/Core/GenericHostProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@ public override async Task<ICredential> 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
Expand Down
11 changes: 11 additions & 0 deletions src/shared/Core/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ public interface ISettings : IDisposable
/// </summary>
bool UseSoftwareRendering { get; }

/// <summary>
/// Permit the use of unsafe remotes URLs such as regular HTTP.
/// </summary>
bool AllowUnsafeRemotes { get; }

/// <summary>
/// Get TRACE2 settings.
/// </summary>
Expand Down Expand Up @@ -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();
Expand Down
7 changes: 5 additions & 2 deletions src/shared/GitHub/GitHubHostProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
7 changes: 5 additions & 2 deletions src/shared/GitLab/GitLabHostProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,13 @@ public override async Task<ICredential> 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();
Expand Down
29 changes: 15 additions & 14 deletions src/shared/Microsoft.AzureRepos/AzureReposHostProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 _)
Expand Down Expand Up @@ -208,16 +208,22 @@ protected override void ReleaseManagedResources()
base.ReleaseManagedResources();
}

private async Task<ICredential> 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<ICredential> GeneratePersonalAccessTokenAsync(InputArguments input)
{
ThrowIfDisposed();
ThrowIfUnsafeRemote(input);

Uri remoteUserUri = input.GetRemoteUri(includeUser: true);
Uri orgUri = UriHelpers.CreateOrganizationUri(remoteUserUri, out _);
Expand Down Expand Up @@ -257,16 +263,11 @@ private async Task<ICredential> GeneratePersonalAccessTokenAsync(InputArguments

private async Task<IMicrosoftAuthenticationResult> 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}'...");
Expand Down
4 changes: 4 additions & 0 deletions src/shared/TestInfrastructure/Objects/TestSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -189,6 +191,8 @@ ProxyConfiguration ISettings.GetProxyConfiguration()

bool ISettings.UseSoftwareRendering => false;

bool ISettings.AllowUnsafeRemotes => AllowUnsafeRemotes;

#endregion

#region IDisposable
Expand Down
Loading