Skip to content

Commit 7b60eee

Browse files
author
Matthew John Cheetham
authored
Add control over use of unsafe remotes (#1721)
Today, all the custom host providers (Azure Repos, Bitbucket, GitHub, GitLab) block the use of HTTP (unencrypted) remote URLs and error out. Only the generic host provider permits HTTP remotes. From #1694, we learn that a common use case for self/corporate hosted Git servers is to use HTTP remotes. Even if this is **_not recommended_**, GCM should not outright block these. Instead, we now add an option, `GCM_ALLOW_UNSAFE_REMOTES` or `credential.allowUnsafeRemotes`, for the user to explicitly set to allow the use of these unsafe remotes. For the generic host provider we only print a warning when using HTTP remotes to reduce the churn for existing users who rely on GCM for HTTP remotes.
2 parents 2d10c92 + fc067e8 commit 7b60eee

File tree

11 files changed

+127
-24
lines changed

11 files changed

+127
-24
lines changed

docs/configuration.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,24 @@ Defaults to false (use hardware acceleration where available).
255255

256256
---
257257

258+
### credential.allowUnsafeRemotes
259+
260+
Allow transmitting credentials to unsafe remote URLs such as unencrypted HTTP
261+
URLs. This setting is not recommended for general use and should only be used
262+
when necessary.
263+
264+
Defaults false (disallow unsafe remote URLs).
265+
266+
#### Example
267+
268+
```shell
269+
git config --global credential.allowUnsafeRemotes true
270+
```
271+
272+
**Also see: [GCM_ALLOW_UNSAFE_REMOTES][gcm-allow-unsafe-remotes]**
273+
274+
---
275+
258276
### credential.autoDetectTimeout
259277

260278
Set the maximum length of time, in milliseconds, that GCM should wait for a
@@ -1024,6 +1042,7 @@ Defaults to disabled.
10241042
[envars]: environment.md
10251043
[freedesktop-ss]: https://specifications.freedesktop.org/secret-service-spec/
10261044
[gcm-allow-windowsauth]: environment.md#GCM_ALLOW_WINDOWSAUTH
1045+
[gcm-allow-unsafe-remotes]: environment.md#GCM_ALLOW_UNSAFE_REMOTES
10271046
[gcm-authority]: environment.md#GCM_AUTHORITY-deprecated
10281047
[gcm-autodetect-timeout]: environment.md#GCM_AUTODETECT_TIMEOUT
10291048
[gcm-azrepos-credentialtype]: environment.md#GCM_AZREPOS_CREDENTIALTYPE

docs/environment.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,32 @@ Defaults to false (use hardware acceleration where available).
302302

303303
---
304304

305+
### GCM_ALLOW_UNSAFE_REMOTES
306+
307+
Allow transmitting credentials to unsafe remote URLs such as unencrypted HTTP
308+
URLs. This setting is not recommended for general use and should only be used
309+
when necessary.
310+
311+
Defaults false (disallow unsafe remote URLs).
312+
313+
#### Example
314+
315+
##### Windows
316+
317+
```batch
318+
SET GCM_ALLOW_UNSAFE_REMOTES=true
319+
```
320+
321+
##### macOS/Linux
322+
323+
```bash
324+
export GCM_ALLOW_UNSAFE_REMOTES=true
325+
```
326+
327+
**Also see: [credential.allowUnsafeRemotes][credential-allowunsaferemotes]**
328+
329+
---
330+
305331
### GCM_AUTODETECT_TIMEOUT
306332

307333
Set the maximum length of time, in milliseconds, that GCM should wait for a
@@ -1153,7 +1179,8 @@ Defaults to disabled.
11531179
[autodetect]: autodetect.md
11541180
[azure-access-tokens]: azrepos-users-and-tokens.md
11551181
[configuration]: configuration.md
1156-
[credential-allowwindowsauth]: environment.md#credentialallowWindowsAuth
1182+
[credential-allowwindowsauth]: configuration.md#credentialallowwindowsauth
1183+
[credential-allowunsaferemotes]: configuration.md#credentialallowunsaferemotes
11571184
[credential-authority]: configuration.md#credentialauthority-deprecated
11581185
[credential-autodetecttimeout]: configuration.md#credentialautodetecttimeout
11591186
[credential-azrepos-credential-type]: configuration.md#credentialazreposcredentialtype

docs/netconfig.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,22 @@ network traffic inspection tool such as [Telerik Fiddler][telerik-fiddler]. If
191191
you are using such tools please consult their documentation for trusting the
192192
proxy root certificates.
193193

194+
---
195+
196+
## Unsafe Remote URLs
197+
198+
If you are using a remote URL that is not considered safe, such as unencrypted
199+
HTTP (remote URLs that start with `http://`), host providers may prevent you
200+
from authenticating with your credentials.
201+
202+
In this case, you should consider using a HTTPS (starting with `https://`)
203+
remote URL to ensure your credentials are transmitted securely.
204+
205+
If you accept the risks associated with using an unsafe remote URL, you can
206+
configure GCM to allow the use of unsafe remote URLS by setting the environment
207+
variable [`GCM_ALLOW_UNSAFE_REMOTES`][unsafe-envar], or by using the Git
208+
configuration option [`credential.allowUnsafeRemotes`][unsafe-config] to `true`.
209+
194210
[environment]: environment.md
195211
[configuration]: configuration.md
196212
[git-http-proxy]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpproxy
@@ -212,3 +228,5 @@ proxy root certificates.
212228
[git-ssl-no-verify]: https://git-scm.com/book/en/v2/Git-Internals-Environment-Variables#_networking
213229
[git-http-ssl-verify]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpsslVerify
214230
[telerik-fiddler]: https://www.telerik.com/fiddler
231+
[unsafe-envar]: environment.md#gcm_allow_unsafe_remotes
232+
[unsafe-config]: configuration.md#credentialallowunsaferemotes

src/shared/Atlassian.Bitbucket/BitbucketHostProvider.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ public bool IsSupported(InputArguments input)
5555
return false;
5656
}
5757

58-
// We do not support unencrypted HTTP communications to Bitbucket,
59-
// but we report `true` here for HTTP so that we can show a helpful
58+
// We do not recommend unencrypted HTTP communications to Bitbucket, but it is possible.
59+
// Therefore, we report `true` here for HTTP so that we can show a helpful
6060
// error message for the user in `GetCredentialAsync`.
6161
return (StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http") ||
6262
StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "https")) &&
@@ -81,11 +81,14 @@ public bool IsSupported(HttpResponseMessage response)
8181
public async Task<ICredential> GetCredentialAsync(InputArguments input)
8282
{
8383
// We should not allow unencrypted communication and should inform the user
84-
if (StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http")
85-
&& BitbucketHelper.IsBitbucketOrg(input))
84+
if (!_context.Settings.AllowUnsafeRemotes &&
85+
StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http") &&
86+
BitbucketHelper.IsBitbucketOrg(input))
8687
{
8788
throw new Trace2Exception(_context.Trace2,
88-
"Unencrypted HTTP is not supported for Bitbucket.org. Ensure the repository remote URL is using HTTPS.");
89+
"Unencrypted HTTP is not recommended for Bitbucket.org. " +
90+
"Ensure the repository remote URL is using HTTPS " +
91+
$"or see {Constants.HelpUrls.GcmUnsafeRemotes} about how to allow unsafe remotes.");
8992
}
9093

9194
var authModes = await GetSupportedAuthenticationModesAsync(input);

src/shared/Core/Constants.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ public static class EnvironmentVariables
119119
public const string OAuthDefaultUserName = "GCM_OAUTH_DEFAULT_USERNAME";
120120
public const string GcmDevUseLegacyUiHelpers = "GCM_DEV_USELEGACYUIHELPERS";
121121
public const string GcmGuiSoftwareRendering = "GCM_GUI_SOFTWARE_RENDERING";
122+
public const string GcmAllowUnsafeRemotes = "GCM_ALLOW_UNSAFE_REMOTES";
122123
}
123124

124125
public static class Http
@@ -163,6 +164,7 @@ public static class Credential
163164
public const string MsAuthUseDefaultAccount = "msauthUseDefaultAccount";
164165
public const string GuiSoftwareRendering = "guiSoftwareRendering";
165166
public const string GpgPassStorePath = "gpgPassStorePath";
167+
public const string AllowUnsafeRemotes = "allowUnsafeRemotes";
166168

167169
public const string OAuthAuthenticationModes = "oauthAuthModes";
168170
public const string OAuthClientId = "oauthClientId";
@@ -226,6 +228,7 @@ public static class HelpUrls
226228
public const string GcmAutoDetect = "https://aka.ms/gcm/autodetect";
227229
public const string GcmDefaultAccount = "https://aka.ms/gcm/defaultaccount";
228230
public const string GcmMultipleUsers = "https://aka.ms/gcm/multipleusers";
231+
public const string GcmUnsafeRemotes = "https://aka.ms/gcm/unsaferemotes";
229232
}
230233

231234
private static Version _gcmVersion;

src/shared/Core/GenericHostProvider.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,17 @@ public override async Task<ICredential> GenerateCredentialAsync(InputArguments i
5454
{
5555
ThrowIfDisposed();
5656

57+
// We only want to *warn* about HTTP remotes for the generic provider because it supports all protocols
58+
// and, historically, we never blocked HTTP remotes in this provider.
59+
// The user can always set the 'GCM_ALLOW_UNSAFE' setting to silence the warning.
60+
if (!Context.Settings.AllowUnsafeRemotes &&
61+
StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http"))
62+
{
63+
Context.Streams.Error.WriteLine(
64+
"warning: use of unencrypted HTTP remote URLs is not recommended; " +
65+
$"see {Constants.HelpUrls.GcmUnsafeRemotes} for more information.");
66+
}
67+
5768
Uri uri = input.GetRemoteUri();
5869

5970
// Determine the if the host supports Windows Integration Authentication (WIA) or OAuth

src/shared/Core/Settings.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,11 @@ public interface ISettings : IDisposable
189189
/// </summary>
190190
bool UseSoftwareRendering { get; }
191191

192+
/// <summary>
193+
/// Permit the use of unsafe remotes URLs such as regular HTTP.
194+
/// </summary>
195+
bool AllowUnsafeRemotes { get; }
196+
192197
/// <summary>
193198
/// Get TRACE2 settings.
194199
/// </summary>
@@ -580,6 +585,12 @@ public bool UseSoftwareRendering
580585
}
581586
}
582587

588+
public bool AllowUnsafeRemotes =>
589+
TryGetSetting(KnownEnvars.GcmAllowUnsafeRemotes,
590+
KnownGitCfg.Credential.SectionName,
591+
KnownGitCfg.Credential.AllowUnsafeRemotes,
592+
out string str) && str.ToBooleanyOrDefault(false);
593+
583594
public Trace2Settings GetTrace2Settings()
584595
{
585596
var settings = new Trace2Settings();

src/shared/GitHub/GitHubHostProvider.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,10 +285,13 @@ public virtual Task EraseCredentialAsync(InputArguments input)
285285
ThrowIfDisposed();
286286

287287
// We should not allow unencrypted communication and should inform the user
288-
if (StringComparer.OrdinalIgnoreCase.Equals(remoteUri.Scheme, "http"))
288+
if (!_context.Settings.AllowUnsafeRemotes &&
289+
StringComparer.OrdinalIgnoreCase.Equals(remoteUri.Scheme, "http"))
289290
{
290291
throw new Trace2Exception(_context.Trace2,
291-
"Unencrypted HTTP is not supported for GitHub. Ensure the repository remote URL is using HTTPS.");
292+
"Unencrypted HTTP is not recommended for GitHub. " +
293+
"Ensure the repository remote URL is using HTTPS " +
294+
$"or see {Constants.HelpUrls.GcmUnsafeRemotes} about how to allow unsafe remotes.");
292295
}
293296

294297
string service = GetServiceName(remoteUri);

src/shared/GitLab/GitLabHostProvider.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,13 @@ public override async Task<ICredential> GenerateCredentialAsync(InputArguments i
9595
ThrowIfDisposed();
9696

9797
// We should not allow unencrypted communication and should inform the user
98-
if (StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http"))
98+
if (!Context.Settings.AllowUnsafeRemotes &&
99+
StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http"))
99100
{
100101
throw new Trace2Exception(Context.Trace2,
101-
"Unencrypted HTTP is not supported for GitHub. Ensure the repository remote URL is using HTTPS.");
102+
"Unencrypted HTTP is not recommended for GitLab. " +
103+
"Ensure the repository remote URL is using HTTPS " +
104+
$"or see {Constants.HelpUrls.GcmUnsafeRemotes} about how to allow unsafe remotes.");
102105
}
103106

104107
Uri remoteUri = input.GetRemoteUri();

src/shared/Microsoft.AzureRepos/AzureReposHostProvider.cs

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public bool IsSupported(InputArguments input)
5959
return false;
6060
}
6161

62-
// We do not support unencrypted HTTP communications to Azure Repos,
62+
// We do not recommend unencrypted HTTP communications to Azure Repos,
6363
// but we report `true` here for HTTP so that we can show a helpful
6464
// error message for the user in `CreateCredentialAsync`.
6565
return input.TryGetHostAndPort(out string hostName, out _)
@@ -208,16 +208,22 @@ protected override void ReleaseManagedResources()
208208
base.ReleaseManagedResources();
209209
}
210210

211-
private async Task<ICredential> GeneratePersonalAccessTokenAsync(InputArguments input)
211+
private void ThrowIfUnsafeRemote(InputArguments input)
212212
{
213-
ThrowIfDisposed();
214-
215-
// We should not allow unencrypted communication and should inform the user
216-
if (StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http"))
213+
if (!_context.Settings.AllowUnsafeRemotes &&
214+
StringComparer.OrdinalIgnoreCase.Equals(input.Protocol, "http"))
217215
{
218216
throw new Trace2Exception(_context.Trace2,
219-
"Unencrypted HTTP is not supported for Azure Repos. Ensure the repository remote URL is using HTTPS.");
217+
"Unencrypted HTTP is not recommended for Azure Repos. " +
218+
"Ensure the repository remote URL is using HTTPS " +
219+
$"or see {Constants.HelpUrls.GcmUnsafeRemotes} about how to allow unsafe remotes.");
220220
}
221+
}
222+
223+
private async Task<ICredential> GeneratePersonalAccessTokenAsync(InputArguments input)
224+
{
225+
ThrowIfDisposed();
226+
ThrowIfUnsafeRemote(input);
221227

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

258264
private async Task<IMicrosoftAuthenticationResult> GetAzureAccessTokenAsync(InputArguments input)
259265
{
266+
ThrowIfUnsafeRemote(input);
267+
260268
Uri remoteWithUserUri = input.GetRemoteUri(includeUser: true);
261269
string userName = input.UserName;
262270

263-
// We should not allow unencrypted communication and should inform the user
264-
if (StringComparer.OrdinalIgnoreCase.Equals(remoteWithUserUri.Scheme, "http"))
265-
{
266-
throw new Trace2Exception(_context.Trace2,
267-
"Unencrypted HTTP is not supported for Azure Repos. Ensure the repository remote URL is using HTTPS.");
268-
}
269-
270271
Uri orgUri = UriHelpers.CreateOrganizationUri(remoteWithUserUri, out string orgName);
271272

272273
_context.Trace.WriteLine($"Determining Microsoft Authentication authority for Azure DevOps organization '{orgName}'...");

0 commit comments

Comments
 (0)