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

11 files changed

Lines changed: 127 additions & 24 deletions

File tree

‎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
returnfalse;
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
publicasyncTask<ICredential>GetCredentialAsync(InputArgumentsinput)
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
thrownewTrace2Exception(_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
varauthModes=awaitGetSupportedAuthenticationModesAsync(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
publicconststringOAuthDefaultUserName="GCM_OAUTH_DEFAULT_USERNAME";
120120
publicconststringGcmDevUseLegacyUiHelpers="GCM_DEV_USELEGACYUIHELPERS";
121121
publicconststringGcmGuiSoftwareRendering="GCM_GUI_SOFTWARE_RENDERING";
122+
publicconststringGcmAllowUnsafeRemotes="GCM_ALLOW_UNSAFE_REMOTES";
122123
}
123124

124125
publicstaticclassHttp
@@ -163,6 +164,7 @@ public static class Credential
163164
publicconststringMsAuthUseDefaultAccount="msauthUseDefaultAccount";
164165
publicconststringGuiSoftwareRendering="guiSoftwareRendering";
165166
publicconststringGpgPassStorePath="gpgPassStorePath";
167+
publicconststringAllowUnsafeRemotes="allowUnsafeRemotes";
166168

167169
publicconststringOAuthAuthenticationModes="oauthAuthModes";
168170
publicconststringOAuthClientId="oauthClientId";
@@ -226,6 +228,7 @@ public static class HelpUrls
226228
publicconststringGcmAutoDetect="https://aka.ms/gcm/autodetect";
227229
publicconststringGcmDefaultAccount="https://aka.ms/gcm/defaultaccount";
228230
publicconststringGcmMultipleUsers="https://aka.ms/gcm/multipleusers";
231+
publicconststringGcmUnsafeRemotes="https://aka.ms/gcm/unsaferemotes";
229232
}
230233

231234
privatestaticVersion_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
Uriuri=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
boolUseSoftwareRendering{get;}
191191

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

588+
publicboolAllowUnsafeRemotes=>
589+
TryGetSetting(KnownEnvars.GcmAllowUnsafeRemotes,
590+
KnownGitCfg.Credential.SectionName,
591+
KnownGitCfg.Credential.AllowUnsafeRemotes,
592+
outstringstr)&&str.ToBooleanyOrDefault(false);
593+
583594
publicTrace2SettingsGetTrace2Settings()
584595
{
585596
varsettings=newTrace2Settings();

‎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
thrownewTrace2Exception(_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
stringservice=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
thrownewTrace2Exception(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
UriremoteUri=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
returnfalse;
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
returninput.TryGetHostAndPort(outstringhostName,out_)
@@ -208,16 +208,22 @@ protected override void ReleaseManagedResources()
208208
base.ReleaseManagedResources();
209209
}
210210

211-
privateasyncTask<ICredential>GeneratePersonalAccessTokenAsync(InputArgumentsinput)
211+
privatevoidThrowIfUnsafeRemote(InputArgumentsinput)
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
thrownewTrace2Exception(_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+
privateasyncTask<ICredential>GeneratePersonalAccessTokenAsync(InputArgumentsinput)
224+
{
225+
ThrowIfDisposed();
226+
ThrowIfUnsafeRemote(input);
221227

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

258264
privateasyncTask<IMicrosoftAuthenticationResult>GetAzureAccessTokenAsync(InputArgumentsinput)
259265
{
266+
ThrowIfUnsafeRemote(input);
267+
260268
UriremoteWithUserUri=input.GetRemoteUri(includeUser:true);
261269
stringuserName=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-
thrownewTrace2Exception(_context.Trace2,
267-
"Unencrypted HTTP is not supported for Azure Repos. Ensure the repository remote URL is using HTTPS.");
268-
}
269-
270271
UriorgUri=UriHelpers.CreateOrganizationUri(remoteWithUserUri,outstringorgName);
271272

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

0 commit comments

Comments
 (0)