Skip to content

Commit 6324efb

Browse files
Added support for basic auth for proxy setup (#5269)
* fix agent configuration behind flag --proxybasicauth * Fix git configuration issue in pipeline * Update on Review * fix for proxybasicauth flag default value * use cmdline proxybasicauth when proxy Knobs are set * Update#2 on Review comments * Update L0 tests * Remove unneccessary JobExtension L0 * Env Var setup * update#3 on review comments * Update#4 on Reveiew Comments * Update#5 on Reveiw Comments * Update#6 on Review Comments * SVN checkout fix * Rename proxy flag to UseBasicAuthForProxy * Revert "SVN checkout fix" This reverts commit 08cb9fb.
1 parent 5b1563f commit 6324efb

File tree

16 files changed

+291
-16
lines changed

16 files changed

+291
-16
lines changed

src/Agent.Listener/CommandLine/ConfigureAgent.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ public class ConfigureAgent : ConfigureOrRemoveBase
102102
[Option(Constants.Agent.CommandLine.Args.ProxyUrl)]
103103
public string ProxyUrl { get; set; }
104104

105+
[Option(Constants.Agent.CommandLine.Flags.UseBasicAuthForProxy)]
106+
public bool UseBasicAuthForProxy { get; set; }
107+
105108
[Option(Constants.Agent.CommandLine.Flags.Replace)]
106109
public bool Replace { get; set; }
107110

src/Agent.Listener/CommandSettings.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,11 @@ public string GetProxyPassword()
512512
return GetArg(Configure?.ProxyPassword, Constants.Agent.CommandLine.Args.ProxyPassword);
513513
}
514514

515+
public bool GetUseBasicAuthForProxy()
516+
{
517+
return TestFlag(Configure?.UseBasicAuthForProxy, Constants.Agent.CommandLine.Flags.UseBasicAuthForProxy);
518+
}
519+
515520
public bool GetSkipCertificateValidation()
516521
{
517522
return TestFlag(Configure?.SslSkipCertValidation, Constants.Agent.CommandLine.Flags.SslSkipCertValidation);

src/Agent.Listener/Configuration/ConfigurationManager.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1019,6 +1019,8 @@ private bool SetupVstsProxySetting(IVstsAgentWebProxy vstsProxy, CommandSettings
10191019

10201020
bool saveProxySetting = false;
10211021
string proxyUrl = command.GetProxyUrl();
1022+
bool useBasicAuthForProxy = command.GetUseBasicAuthForProxy();
1023+
10221024
if (!string.IsNullOrEmpty(proxyUrl))
10231025
{
10241026
if (!Uri.IsWellFormedUriString(proxyUrl, UriKind.Absolute))
@@ -1029,7 +1031,7 @@ private bool SetupVstsProxySetting(IVstsAgentWebProxy vstsProxy, CommandSettings
10291031
Trace.Info("Reset proxy base on commandline args.");
10301032
string proxyUserName = command.GetProxyUserName();
10311033
string proxyPassword = command.GetProxyPassword();
1032-
vstsProxy.SetupProxy(proxyUrl, proxyUserName, proxyPassword);
1034+
vstsProxy.SetupProxy(proxyUrl, proxyUserName, proxyPassword, useBasicAuthForProxy);
10331035
saveProxySetting = true;
10341036
}
10351037

src/Agent.Plugins/GitSourceProvider.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ public abstract class GitSourceProvider : ISourceProvider
191191
private const string _remoteRefsPrefix = "refs/remotes/origin/";
192192
private const string _pullRefsPrefix = "refs/pull/";
193193
private const string _remotePullRefsPrefix = "refs/remotes/pull/";
194+
private const string _gitUseBasicAuthForProxyConfig = "-c http.proxyAuthMethod=basic";
194195

195196
private const string _tenantId = "tenantid";
196197
private const string _clientId = "servicePrincipalId";
@@ -834,6 +835,14 @@ public async Task GetSourceAsync(
834835
ArgUtil.NotNullOrEmpty(proxyUrlWithCredString, nameof(proxyUrlWithCredString));
835836
additionalFetchArgs.Add($"-c http.proxy=\"{proxyUrlWithCredString}\"");
836837
additionalLfsFetchArgs.Add($"-c http.proxy=\"{proxyUrlWithCredString}\"");
838+
839+
// Add proxy authentication method if Basic auth is enabled
840+
if (agentProxy.UseBasicAuthForProxy)
841+
{
842+
executionContext.Debug("Config proxy to use Basic authentication for git fetch.");
843+
additionalFetchArgs.Add(_gitUseBasicAuthForProxyConfig);
844+
additionalLfsFetchArgs.Add(_gitUseBasicAuthForProxyConfig);
845+
}
837846
}
838847

839848
// Prepare ignore ssl cert error config for fetch.
@@ -1077,6 +1086,13 @@ public async Task GetSourceAsync(
10771086
executionContext.Debug($"Config proxy server '{agentProxy.ProxyAddress}' for git submodule update.");
10781087
ArgUtil.NotNullOrEmpty(proxyUrlWithCredString, nameof(proxyUrlWithCredString));
10791088
additionalSubmoduleUpdateArgs.Add($"-c http.proxy=\"{proxyUrlWithCredString}\"");
1089+
1090+
// Add proxy authentication method if Basic auth is enabled
1091+
if (agentProxy.UseBasicAuthForProxy)
1092+
{
1093+
executionContext.Debug("Config proxy to use Basic authentication for git submodule update.");
1094+
additionalSubmoduleUpdateArgs.Add(_gitUseBasicAuthForProxyConfig);
1095+
}
10801096
}
10811097

10821098
// Prepare ignore ssl cert error config for fetch.
@@ -1176,6 +1192,21 @@ public async Task GetSourceAsync(
11761192
{
11771193
throw new InvalidOperationException($"Git config failed with exit code: {exitCode_proxyconfig}");
11781194
}
1195+
1196+
// Add proxy authentication method if Basic auth is enabled
1197+
if (agentProxy.UseBasicAuthForProxy)
1198+
{
1199+
executionContext.Debug("Save proxy authentication method 'basic' to git config.");
1200+
string proxyAuthMethodKey = "http.proxyAuthMethod";
1201+
string proxyAuthMethodValue = "basic";
1202+
configModifications[proxyAuthMethodKey] = proxyAuthMethodValue;
1203+
1204+
int exitCode_proxyauth = await gitCommandManager.GitConfig(executionContext, targetPath, proxyAuthMethodKey, proxyAuthMethodValue);
1205+
if (exitCode_proxyauth != 0)
1206+
{
1207+
throw new InvalidOperationException($"Git config failed with exit code: {exitCode_proxyauth}");
1208+
}
1209+
}
11791210
}
11801211

11811212
// save ignore ssl cert error setting to git config.

src/Agent.Sdk/AgentWebProxy.cs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,20 @@ public class AgentWebProxySettings
1515
public static string AgentProxyUsernameKey = "Agent.ProxyUsername".ToLower();
1616
public static string AgentProxyPasswordKey = "Agent.ProxyPassword".ToLower();
1717
public static string AgentProxyBypassListKey = "Agent.ProxyBypassList".ToLower();
18+
public static string AgentUseBasicAuthForProxyKey = "Agent.UseBasicAuthForProxy".ToLower();
1819
public string ProxyAddress { get; set; }
1920
public string ProxyUsername { get; set; }
2021
public string ProxyPassword { get; set; }
2122
public List<string> ProxyBypassList { get; set; }
23+
public bool UseBasicAuthForProxy { get; set; }
2224
public IWebProxy WebProxy { get; set; }
2325
}
2426

2527
public class AgentWebProxy : IWebProxy
2628
{
2729
private string _proxyAddress;
2830
private readonly List<Regex> _regExBypassList = new List<Regex>();
31+
private bool _useBasicAuthForProxy = false; // Flag to control Basic auth usage
2932

3033
public ICredentials Credentials { get; set; }
3134

@@ -35,11 +38,17 @@ public AgentWebProxy()
3538

3639
public AgentWebProxy(string proxyAddress, string proxyUsername, string proxyPassword, List<string> proxyBypassList)
3740
{
38-
Update(proxyAddress, proxyUsername, proxyPassword, proxyBypassList);
41+
Update(proxyAddress, proxyUsername, proxyPassword, proxyBypassList, false);
3942
}
4043

41-
public void Update(string proxyAddress, string proxyUsername, string proxyPassword, List<string> proxyBypassList)
44+
public AgentWebProxy(string proxyAddress, string proxyUsername, string proxyPassword, List<string> proxyBypassList, bool useBasicAuthForProxy = false)
4245
{
46+
Update(proxyAddress, proxyUsername, proxyPassword, proxyBypassList, useBasicAuthForProxy);
47+
}
48+
49+
public void Update(string proxyAddress, string proxyUsername, string proxyPassword, List<string> proxyBypassList, bool useBasicAuthForProxy = false)
50+
{
51+
_useBasicAuthForProxy = useBasicAuthForProxy;
4352
_proxyAddress = proxyAddress?.Trim();
4453

4554
if (string.IsNullOrEmpty(proxyUsername) || string.IsNullOrEmpty(proxyPassword))
@@ -48,7 +57,21 @@ public void Update(string proxyAddress, string proxyUsername, string proxyPasswo
4857
}
4958
else
5059
{
51-
Credentials = new NetworkCredential(proxyUsername, proxyPassword);
60+
if (_useBasicAuthForProxy)
61+
{
62+
// Use CredentialCache to force Basic authentication and avoid NTLM negotiation issues
63+
// This fixes the 407 Proxy Authentication Required errors that occur when .NET
64+
// attempts NTLM authentication but fails to fall back to Basic authentication properly
65+
var credentialCache = new CredentialCache();
66+
var proxyUri = new Uri(_proxyAddress);
67+
credentialCache.Add(proxyUri, "Basic", new NetworkCredential(proxyUsername, proxyPassword));
68+
Credentials = credentialCache;
69+
}
70+
else
71+
{
72+
// Default behavior: Use NetworkCredential (default logic for .NET)
73+
Credentials = new NetworkCredential(proxyUsername, proxyPassword);
74+
}
5275
}
5376

5477
if (proxyBypassList != null)

src/Agent.Sdk/CommandPlugin.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ public VssConnection InitializeVssConnection()
142142
{
143143
if (!string.IsNullOrEmpty(proxySetting.ProxyAddress))
144144
{
145-
VssHttpMessageHandler.DefaultWebProxy = new AgentWebProxy(proxySetting.ProxyAddress, proxySetting.ProxyUsername, proxySetting.ProxyPassword, proxySetting.ProxyBypassList);
145+
VssHttpMessageHandler.DefaultWebProxy = new AgentWebProxy(proxySetting.ProxyAddress, proxySetting.ProxyUsername, proxySetting.ProxyPassword, proxySetting.ProxyBypassList, proxySetting.UseBasicAuthForProxy);
146146
}
147147
}
148148

@@ -195,12 +195,15 @@ private AgentWebProxySettings GetProxyConfiguration()
195195
string proxyUsername = this.Variables.GetValueOrDefault(AgentWebProxySettings.AgentProxyUsernameKey)?.Value;
196196
string proxyPassword = this.Variables.GetValueOrDefault(AgentWebProxySettings.AgentProxyPasswordKey)?.Value;
197197
List<string> proxyBypassHosts = StringUtil.ConvertFromJson<List<string>>(this.Variables.GetValueOrDefault(AgentWebProxySettings.AgentProxyBypassListKey)?.Value ?? "[]");
198+
bool useBasicAuthForProxy = StringUtil.ConvertToBoolean(this.Variables.GetValueOrDefault(AgentWebProxySettings.AgentUseBasicAuthForProxyKey)?.Value);
198199
return new AgentWebProxySettings()
199200
{
200201
ProxyAddress = proxyUrl,
201202
ProxyUsername = proxyUsername,
202203
ProxyPassword = proxyPassword,
203204
ProxyBypassList = proxyBypassHosts,
205+
UseBasicAuthForProxy = useBasicAuthForProxy,
206+
WebProxy = new AgentWebProxy(proxyUrl, proxyUsername, proxyPassword, proxyBypassHosts, useBasicAuthForProxy)
204207
};
205208
}
206209
else

src/Agent.Sdk/Knob/AgentKnobs.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,12 @@ public class AgentKnobs
351351
new EnvironmentKnobSource("VSTS_HTTP_PROXY_USERNAME"),
352352
new BuiltInDefaultKnobSource(string.Empty));
353353

354+
public static readonly Knob UseBasicAuthForProxy = new Knob(
355+
nameof(UseBasicAuthForProxy),
356+
"Enable proxy basic authentication to avoid NTLM negotiation issues",
357+
new EnvironmentKnobSource("VSTS_HTTP_PROXY_BASICAUTH"),
358+
new BuiltInDefaultKnobSource("false"));
359+
354360
// Secrets masking
355361
public static readonly Knob AllowUnsafeMultilineSecret = new Knob(
356362
nameof(AllowUnsafeMultilineSecret),

src/Agent.Sdk/LogPlugin.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ private VssConnection InitializeVssConnection()
193193
{
194194
if (!string.IsNullOrEmpty(WebProxySettings.ProxyAddress))
195195
{
196-
VssHttpMessageHandler.DefaultWebProxy = new AgentWebProxy(WebProxySettings.ProxyAddress, WebProxySettings.ProxyUsername, WebProxySettings.ProxyPassword, WebProxySettings.ProxyBypassList);
196+
VssHttpMessageHandler.DefaultWebProxy = new AgentWebProxy(WebProxySettings.ProxyAddress, WebProxySettings.ProxyUsername, WebProxySettings.ProxyPassword, WebProxySettings.ProxyBypassList, WebProxySettings.UseBasicAuthForProxy);
197197
}
198198
}
199199

@@ -248,13 +248,15 @@ private AgentWebProxySettings GetProxyConfiguration()
248248
string proxyUsername = this.Variables.GetValueOrDefault(AgentWebProxySettings.AgentProxyUsernameKey)?.Value;
249249
string proxyPassword = this.Variables.GetValueOrDefault(AgentWebProxySettings.AgentProxyPasswordKey)?.Value;
250250
List<string> proxyBypassHosts = StringUtil.ConvertFromJson<List<string>>(this.Variables.GetValueOrDefault(AgentWebProxySettings.AgentProxyBypassListKey)?.Value ?? "[]");
251+
bool useBasicAuthForProxy = StringUtil.ConvertToBoolean(this.Variables.GetValueOrDefault(AgentWebProxySettings.AgentUseBasicAuthForProxyKey)?.Value);
251252
return new AgentWebProxySettings()
252253
{
253254
ProxyAddress = proxyUrl,
254255
ProxyUsername = proxyUsername,
255256
ProxyPassword = proxyPassword,
256257
ProxyBypassList = proxyBypassHosts,
257-
WebProxy = new AgentWebProxy(proxyUrl, proxyUsername, proxyPassword, proxyBypassHosts)
258+
UseBasicAuthForProxy = useBasicAuthForProxy,
259+
WebProxy = new AgentWebProxy(proxyUrl, proxyUsername, proxyPassword, proxyBypassHosts, useBasicAuthForProxy)
258260
};
259261
}
260262
else

src/Agent.Sdk/TaskPlugin.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public VssConnection InitializeVssConnection()
121121
{
122122
if (!string.IsNullOrEmpty(WebProxySettings.ProxyAddress))
123123
{
124-
VssHttpMessageHandler.DefaultWebProxy = new AgentWebProxy(WebProxySettings.ProxyAddress, WebProxySettings.ProxyUsername, WebProxySettings.ProxyPassword, WebProxySettings.ProxyBypassList);
124+
VssHttpMessageHandler.DefaultWebProxy = new AgentWebProxy(WebProxySettings.ProxyAddress, WebProxySettings.ProxyUsername, WebProxySettings.ProxyPassword, WebProxySettings.ProxyBypassList, WebProxySettings.UseBasicAuthForProxy);
125125
}
126126
}
127127

@@ -330,13 +330,15 @@ public AgentWebProxySettings GetProxyConfiguration()
330330
string proxyUsername = this.Variables.GetValueOrDefault(AgentWebProxySettings.AgentProxyUsernameKey)?.Value;
331331
string proxyPassword = this.Variables.GetValueOrDefault(AgentWebProxySettings.AgentProxyPasswordKey)?.Value;
332332
List<string> proxyBypassHosts = StringUtil.ConvertFromJson<List<string>>(this.Variables.GetValueOrDefault(AgentWebProxySettings.AgentProxyBypassListKey)?.Value ?? "[]");
333+
bool useBasicAuthForProxy = StringUtil.ConvertToBoolean(this.Variables.GetValueOrDefault(AgentWebProxySettings.AgentUseBasicAuthForProxyKey)?.Value);
333334
return new AgentWebProxySettings()
334335
{
335336
ProxyAddress = proxyUrl,
336337
ProxyUsername = proxyUsername,
337338
ProxyPassword = proxyPassword,
338339
ProxyBypassList = proxyBypassHosts,
339-
WebProxy = new AgentWebProxy(proxyUrl, proxyUsername, proxyPassword, proxyBypassHosts)
340+
UseBasicAuthForProxy = useBasicAuthForProxy,
341+
WebProxy = new AgentWebProxy(proxyUrl, proxyUsername, proxyPassword, proxyBypassHosts, useBasicAuthForProxy)
340342
};
341343
}
342344
// back-compat of proxy configuration via environment variables
@@ -346,12 +348,14 @@ public AgentWebProxySettings GetProxyConfiguration()
346348
ProxyUrl = ProxyUrl.Trim();
347349
var ProxyUsername = Environment.GetEnvironmentVariable("VSTS_HTTP_PROXY_USERNAME");
348350
var ProxyPassword = Environment.GetEnvironmentVariable("VSTS_HTTP_PROXY_PASSWORD");
351+
var UseBasicAuthForProxy = StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("VSTS_HTTP_PROXY_BASIC_AUTH"));
349352
return new AgentWebProxySettings()
350353
{
351354
ProxyAddress = ProxyUrl,
352355
ProxyUsername = ProxyUsername,
353356
ProxyPassword = ProxyPassword,
354-
WebProxy = new AgentWebProxy(proxyUrl, ProxyUsername, ProxyPassword, null)
357+
UseBasicAuthForProxy = UseBasicAuthForProxy,
358+
WebProxy = new AgentWebProxy(ProxyUrl, ProxyUsername, ProxyPassword, null, UseBasicAuthForProxy)
355359
};
356360
}
357361
else

0 commit comments

Comments
 (0)