mirror of
https://github.com/actions/runner.git
synced 2025-12-10 12:36:23 +00:00
Add Proxy Support for self-hosted runner. (#206)
This commit is contained in:
@@ -9,9 +9,6 @@ varCheckList=(
|
||||
'GRADLE_HOME'
|
||||
'NVM_BIN'
|
||||
'NVM_PATH'
|
||||
'VSTS_HTTP_PROXY'
|
||||
'VSTS_HTTP_PROXY_USERNAME'
|
||||
'VSTS_HTTP_PROXY_PASSWORD'
|
||||
'LD_LIBRARY_PATH'
|
||||
'PERL5LIB'
|
||||
)
|
||||
|
||||
@@ -29,9 +29,6 @@ namespace GitHub.Runner.Common
|
||||
Service,
|
||||
CredentialStore,
|
||||
Certificates,
|
||||
Proxy,
|
||||
ProxyCredentials,
|
||||
ProxyBypass,
|
||||
Options,
|
||||
}
|
||||
|
||||
@@ -97,8 +94,6 @@ namespace GitHub.Runner.Common
|
||||
public static readonly string Auth = "auth";
|
||||
public static readonly string MonitorSocketAddress = "monitorsocketaddress";
|
||||
public static readonly string Pool = "pool";
|
||||
public static readonly string ProxyUrl = "proxyurl";
|
||||
public static readonly string ProxyUserName = "proxyusername";
|
||||
public static readonly string SslCACert = "sslcacert";
|
||||
public static readonly string SslClientCert = "sslclientcert";
|
||||
public static readonly string SslClientCertKey = "sslclientcertkey";
|
||||
@@ -111,14 +106,12 @@ namespace GitHub.Runner.Common
|
||||
|
||||
// Secret args. Must be added to the "Secrets" getter as well.
|
||||
public static readonly string Password = "password";
|
||||
public static readonly string ProxyPassword = "proxypassword";
|
||||
public static readonly string SslClientCertPassword = "sslclientcertpassword";
|
||||
public static readonly string Token = "token";
|
||||
public static readonly string WindowsLogonPassword = "windowslogonpassword";
|
||||
public static string[] Secrets => new[]
|
||||
{
|
||||
Password,
|
||||
ProxyPassword,
|
||||
SslClientCertPassword,
|
||||
Token,
|
||||
WindowsLogonPassword,
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace GitHub.Runner.Common
|
||||
ShutdownReason RunnerShutdownReason { get; }
|
||||
ISecretMasker SecretMasker { get; }
|
||||
ProductInfoHeaderValue UserAgent { get; }
|
||||
RunnerWebProxy WebProxy { get; }
|
||||
string GetDirectory(WellKnownDirectory directory);
|
||||
string GetConfigFile(WellKnownConfigFile configFile);
|
||||
Tracing GetTrace(string name);
|
||||
@@ -67,12 +68,14 @@ namespace GitHub.Runner.Common
|
||||
private IDisposable _diagListenerSubscription;
|
||||
private StartupType _startupType;
|
||||
private string _perfFile;
|
||||
private RunnerWebProxy _webProxy = new RunnerWebProxy();
|
||||
|
||||
public event EventHandler Unloading;
|
||||
public CancellationToken RunnerShutdownToken => _runnerShutdownTokenSource.Token;
|
||||
public ShutdownReason RunnerShutdownReason { get; private set; }
|
||||
public ISecretMasker SecretMasker => _secretMasker;
|
||||
public ProductInfoHeaderValue UserAgent => _userAgent;
|
||||
public RunnerWebProxy WebProxy => _webProxy;
|
||||
public HostContext(string hostType, string logFile = null)
|
||||
{
|
||||
// Validate args.
|
||||
@@ -147,6 +150,48 @@ namespace GitHub.Runner.Common
|
||||
_trace.Error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Check and trace proxy info
|
||||
if (!string.IsNullOrEmpty(WebProxy.HttpProxyAddress))
|
||||
{
|
||||
if (string.IsNullOrEmpty(WebProxy.HttpProxyUsername) && string.IsNullOrEmpty(WebProxy.HttpProxyPassword))
|
||||
{
|
||||
_trace.Info($"Configuring anonymous proxy {WebProxy.HttpProxyAddress} for all HTTP requests.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Register proxy password as secret
|
||||
if (!string.IsNullOrEmpty(WebProxy.HttpProxyPassword))
|
||||
{
|
||||
this.SecretMasker.AddValue(WebProxy.HttpProxyPassword);
|
||||
}
|
||||
|
||||
_trace.Info($"Configuring authenticated proxy {WebProxy.HttpProxyAddress} for all HTTP requests.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(WebProxy.HttpsProxyAddress))
|
||||
{
|
||||
if (string.IsNullOrEmpty(WebProxy.HttpsProxyUsername) && string.IsNullOrEmpty(WebProxy.HttpsProxyPassword))
|
||||
{
|
||||
_trace.Info($"Configuring anonymous proxy {WebProxy.HttpsProxyAddress} for all HTTPS requests.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Register proxy password as secret
|
||||
if (!string.IsNullOrEmpty(WebProxy.HttpsProxyPassword))
|
||||
{
|
||||
this.SecretMasker.AddValue(WebProxy.HttpsProxyPassword);
|
||||
}
|
||||
|
||||
_trace.Info($"Configuring authenticated proxy {WebProxy.HttpsProxyAddress} for all HTTPS requests.");
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(WebProxy.HttpProxyAddress) && string.IsNullOrEmpty(WebProxy.HttpsProxyAddress))
|
||||
{
|
||||
_trace.Info($"No proxy settings were found based on environmental variables (http_proxy/https_proxy/HTTP_PROXY/HTTPS_PROXY)");
|
||||
}
|
||||
}
|
||||
|
||||
public RunMode RunMode
|
||||
@@ -282,24 +327,6 @@ namespace GitHub.Runner.Common
|
||||
".certificates");
|
||||
break;
|
||||
|
||||
case WellKnownConfigFile.Proxy:
|
||||
path = Path.Combine(
|
||||
GetDirectory(WellKnownDirectory.Root),
|
||||
".proxy");
|
||||
break;
|
||||
|
||||
case WellKnownConfigFile.ProxyCredentials:
|
||||
path = Path.Combine(
|
||||
GetDirectory(WellKnownDirectory.Root),
|
||||
".proxycredentials");
|
||||
break;
|
||||
|
||||
case WellKnownConfigFile.ProxyBypass:
|
||||
path = Path.Combine(
|
||||
GetDirectory(WellKnownDirectory.Root),
|
||||
".proxybypass");
|
||||
break;
|
||||
|
||||
case WellKnownConfigFile.Options:
|
||||
path = Path.Combine(
|
||||
GetDirectory(WellKnownDirectory.Root),
|
||||
@@ -580,8 +607,7 @@ namespace GitHub.Runner.Common
|
||||
public static HttpClientHandler CreateHttpClientHandler(this IHostContext context)
|
||||
{
|
||||
HttpClientHandler clientHandler = new HttpClientHandler();
|
||||
var runnerWebProxy = context.GetService<IRunnerWebProxy>();
|
||||
clientHandler.Proxy = runnerWebProxy.WebProxy;
|
||||
clientHandler.Proxy = context.WebProxy;
|
||||
return clientHandler;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,196 +0,0 @@
|
||||
using GitHub.Runner.Common.Util;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using GitHub.Runner.Sdk;
|
||||
|
||||
namespace GitHub.Runner.Common
|
||||
{
|
||||
[ServiceLocator(Default = typeof(RunnerWebProxy))]
|
||||
public interface IRunnerWebProxy : IRunnerService
|
||||
{
|
||||
string ProxyAddress { get; }
|
||||
string ProxyUsername { get; }
|
||||
string ProxyPassword { get; }
|
||||
List<string> ProxyBypassList { get; }
|
||||
IWebProxy WebProxy { get; }
|
||||
}
|
||||
|
||||
public class RunnerWebProxy : RunnerService, IRunnerWebProxy
|
||||
{
|
||||
private readonly List<Regex> _regExBypassList = new List<Regex>();
|
||||
private readonly List<string> _bypassList = new List<string>();
|
||||
private RunnerWebProxyCore _runnerWebProxy = new RunnerWebProxyCore();
|
||||
|
||||
public string ProxyAddress { get; private set; }
|
||||
public string ProxyUsername { get; private set; }
|
||||
public string ProxyPassword { get; private set; }
|
||||
public List<string> ProxyBypassList => _bypassList;
|
||||
public IWebProxy WebProxy => _runnerWebProxy;
|
||||
|
||||
public override void Initialize(IHostContext context)
|
||||
{
|
||||
base.Initialize(context);
|
||||
LoadProxySetting();
|
||||
}
|
||||
|
||||
// This should only be called from config
|
||||
public void SetupProxy(string proxyAddress, string proxyUsername, string proxyPassword)
|
||||
{
|
||||
ArgUtil.NotNullOrEmpty(proxyAddress, nameof(proxyAddress));
|
||||
Trace.Info($"Update proxy setting from '{ProxyAddress ?? string.Empty}' to'{proxyAddress}'");
|
||||
ProxyAddress = proxyAddress;
|
||||
ProxyUsername = proxyUsername;
|
||||
ProxyPassword = proxyPassword;
|
||||
|
||||
if (string.IsNullOrEmpty(ProxyUsername) || string.IsNullOrEmpty(ProxyPassword))
|
||||
{
|
||||
Trace.Info($"Config proxy use DefaultNetworkCredentials.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Trace.Info($"Config authentication proxy as: {ProxyUsername}.");
|
||||
}
|
||||
|
||||
_runnerWebProxy.Update(ProxyAddress, ProxyUsername, ProxyPassword, ProxyBypassList);
|
||||
}
|
||||
|
||||
// This should only be called from config
|
||||
public void SaveProxySetting()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ProxyAddress))
|
||||
{
|
||||
string proxyConfigFile = HostContext.GetConfigFile(WellKnownConfigFile.Proxy);
|
||||
IOUtil.DeleteFile(proxyConfigFile);
|
||||
Trace.Info($"Store proxy configuration to '{proxyConfigFile}' for proxy '{ProxyAddress}'");
|
||||
File.WriteAllText(proxyConfigFile, ProxyAddress);
|
||||
File.SetAttributes(proxyConfigFile, File.GetAttributes(proxyConfigFile) | FileAttributes.Hidden);
|
||||
|
||||
string proxyCredFile = HostContext.GetConfigFile(WellKnownConfigFile.ProxyCredentials);
|
||||
IOUtil.DeleteFile(proxyCredFile);
|
||||
if (!string.IsNullOrEmpty(ProxyUsername) && !string.IsNullOrEmpty(ProxyPassword))
|
||||
{
|
||||
string lookupKey = Guid.NewGuid().ToString("D").ToUpperInvariant();
|
||||
Trace.Info($"Store proxy credential lookup key '{lookupKey}' to '{proxyCredFile}'");
|
||||
File.WriteAllText(proxyCredFile, lookupKey);
|
||||
File.SetAttributes(proxyCredFile, File.GetAttributes(proxyCredFile) | FileAttributes.Hidden);
|
||||
|
||||
var credStore = HostContext.GetService<IRunnerCredentialStore>();
|
||||
credStore.Write($"GITHUB_ACTIONS_RUNNER_PROXY_{lookupKey}", ProxyUsername, ProxyPassword);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Trace.Info("No proxy configuration exist.");
|
||||
}
|
||||
}
|
||||
|
||||
// This should only be called from unconfig
|
||||
public void DeleteProxySetting()
|
||||
{
|
||||
string proxyCredFile = HostContext.GetConfigFile(WellKnownConfigFile.ProxyCredentials);
|
||||
if (File.Exists(proxyCredFile))
|
||||
{
|
||||
Trace.Info("Delete proxy credential from credential store.");
|
||||
string lookupKey = File.ReadAllLines(proxyCredFile).FirstOrDefault();
|
||||
if (!string.IsNullOrEmpty(lookupKey))
|
||||
{
|
||||
var credStore = HostContext.GetService<IRunnerCredentialStore>();
|
||||
credStore.Delete($"GITHUB_ACTIONS_RUNNER_PROXY_{lookupKey}");
|
||||
}
|
||||
|
||||
Trace.Info($"Delete .proxycredentials file: {proxyCredFile}");
|
||||
IOUtil.DeleteFile(proxyCredFile);
|
||||
}
|
||||
|
||||
string proxyBypassFile = HostContext.GetConfigFile(WellKnownConfigFile.ProxyBypass);
|
||||
if (File.Exists(proxyBypassFile))
|
||||
{
|
||||
Trace.Info($"Delete .proxybypass file: {proxyBypassFile}");
|
||||
IOUtil.DeleteFile(proxyBypassFile);
|
||||
}
|
||||
|
||||
string proxyConfigFile = HostContext.GetConfigFile(WellKnownConfigFile.Proxy);
|
||||
Trace.Info($"Delete .proxy file: {proxyConfigFile}");
|
||||
IOUtil.DeleteFile(proxyConfigFile);
|
||||
}
|
||||
|
||||
private void LoadProxySetting()
|
||||
{
|
||||
string proxyConfigFile = HostContext.GetConfigFile(WellKnownConfigFile.Proxy);
|
||||
if (File.Exists(proxyConfigFile))
|
||||
{
|
||||
// we expect the first line of the file is the proxy url
|
||||
Trace.Verbose($"Try read proxy setting from file: {proxyConfigFile}.");
|
||||
ProxyAddress = File.ReadLines(proxyConfigFile).FirstOrDefault() ?? string.Empty;
|
||||
ProxyAddress = ProxyAddress.Trim();
|
||||
Trace.Verbose($"{ProxyAddress}");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ProxyAddress) && !Uri.IsWellFormedUriString(ProxyAddress, UriKind.Absolute))
|
||||
{
|
||||
Trace.Info($"The proxy url is not a well formed absolute uri string: {ProxyAddress}.");
|
||||
ProxyAddress = string.Empty;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ProxyAddress))
|
||||
{
|
||||
Trace.Info($"Config proxy at: {ProxyAddress}.");
|
||||
|
||||
string proxyCredFile = HostContext.GetConfigFile(WellKnownConfigFile.ProxyCredentials);
|
||||
if (File.Exists(proxyCredFile))
|
||||
{
|
||||
string lookupKey = File.ReadAllLines(proxyCredFile).FirstOrDefault();
|
||||
if (!string.IsNullOrEmpty(lookupKey))
|
||||
{
|
||||
var credStore = HostContext.GetService<IRunnerCredentialStore>();
|
||||
var proxyCred = credStore.Read($"GITHUB_ACTIONS_RUNNER_PROXY_{lookupKey}");
|
||||
ProxyUsername = proxyCred.UserName;
|
||||
ProxyPassword = proxyCred.Password;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ProxyPassword))
|
||||
{
|
||||
HostContext.SecretMasker.AddValue(ProxyPassword);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(ProxyUsername) || string.IsNullOrEmpty(ProxyPassword))
|
||||
{
|
||||
Trace.Info($"Config proxy use DefaultNetworkCredentials.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Trace.Info($"Config authentication proxy as: {ProxyUsername}.");
|
||||
}
|
||||
|
||||
string proxyBypassFile = HostContext.GetConfigFile(WellKnownConfigFile.ProxyBypass);
|
||||
if (File.Exists(proxyBypassFile))
|
||||
{
|
||||
Trace.Verbose($"Try read proxy bypass list from file: {proxyBypassFile}.");
|
||||
foreach (string bypass in File.ReadAllLines(proxyBypassFile))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(bypass))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
Trace.Info($"Bypass proxy for: {bypass}.");
|
||||
ProxyBypassList.Add(bypass.Trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_runnerWebProxy.Update(ProxyAddress, ProxyUsername, ProxyPassword, ProxyBypassList);
|
||||
}
|
||||
else
|
||||
{
|
||||
Trace.Info($"No proxy setting found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,9 +37,8 @@ namespace GitHub.Runner.Listener
|
||||
{
|
||||
try
|
||||
{
|
||||
var runnerWebProxy = HostContext.GetService<IRunnerWebProxy>();
|
||||
var runnerCertManager = HostContext.GetService<IRunnerCertificateManager>();
|
||||
VssUtil.InitializeVssClientSettings(HostContext.UserAgent, runnerWebProxy.WebProxy, runnerCertManager.VssClientCertificateManager);
|
||||
VssUtil.InitializeVssClientSettings(HostContext.UserAgent, HostContext.WebProxy, runnerCertManager.VssClientCertificateManager);
|
||||
|
||||
_inConfigStage = true;
|
||||
_completedCommand.Reset();
|
||||
|
||||
@@ -47,9 +47,6 @@ namespace GitHub.Runner.Listener
|
||||
Constants.Runner.CommandLine.Args.MonitorSocketAddress,
|
||||
Constants.Runner.CommandLine.Args.Password,
|
||||
Constants.Runner.CommandLine.Args.Pool,
|
||||
Constants.Runner.CommandLine.Args.ProxyPassword,
|
||||
Constants.Runner.CommandLine.Args.ProxyUrl,
|
||||
Constants.Runner.CommandLine.Args.ProxyUserName,
|
||||
Constants.Runner.CommandLine.Args.SslCACert,
|
||||
Constants.Runner.CommandLine.Args.SslClientCert,
|
||||
Constants.Runner.CommandLine.Args.SslClientCertKey,
|
||||
@@ -290,21 +287,6 @@ namespace GitHub.Runner.Listener
|
||||
return GetArg(Constants.Runner.CommandLine.Args.StartupType);
|
||||
}
|
||||
|
||||
public string GetProxyUrl()
|
||||
{
|
||||
return GetArg(Constants.Runner.CommandLine.Args.ProxyUrl);
|
||||
}
|
||||
|
||||
public string GetProxyUserName()
|
||||
{
|
||||
return GetArg(Constants.Runner.CommandLine.Args.ProxyUserName);
|
||||
}
|
||||
|
||||
public string GetProxyPassword()
|
||||
{
|
||||
return GetArg(Constants.Runner.CommandLine.Args.ProxyPassword);
|
||||
}
|
||||
|
||||
public bool GetSkipCertificateValidation()
|
||||
{
|
||||
return TestFlag(Constants.Runner.CommandLine.Flags.SslSkipCertValidation);
|
||||
|
||||
@@ -86,24 +86,6 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
throw new InvalidOperationException("Cannot configure the runner because it is already configured. To reconfigure the runner, run 'config.cmd remove' or './config.sh remove' first.");
|
||||
}
|
||||
|
||||
// Populate proxy setting from commandline args
|
||||
var runnerProxy = HostContext.GetService<IRunnerWebProxy>();
|
||||
bool saveProxySetting = false;
|
||||
string proxyUrl = command.GetProxyUrl();
|
||||
if (!string.IsNullOrEmpty(proxyUrl))
|
||||
{
|
||||
if (!Uri.IsWellFormedUriString(proxyUrl, UriKind.Absolute))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(proxyUrl));
|
||||
}
|
||||
|
||||
Trace.Info("Reset proxy base on commandline args.");
|
||||
string proxyUserName = command.GetProxyUserName();
|
||||
string proxyPassword = command.GetProxyPassword();
|
||||
(runnerProxy as RunnerWebProxy).SetupProxy(proxyUrl, proxyUserName, proxyPassword);
|
||||
saveProxySetting = true;
|
||||
}
|
||||
|
||||
// Populate cert setting from commandline args
|
||||
var runnerCertManager = HostContext.GetService<IRunnerCertificateManager>();
|
||||
bool saveCertSetting = false;
|
||||
@@ -371,12 +353,6 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
|
||||
_store.SaveSettings(runnerSettings);
|
||||
|
||||
if (saveProxySetting)
|
||||
{
|
||||
Trace.Info("Save proxy setting to disk.");
|
||||
(runnerProxy as RunnerWebProxy).SaveProxySetting();
|
||||
}
|
||||
|
||||
if (saveCertSetting)
|
||||
{
|
||||
Trace.Info("Save agent cert setting to disk.");
|
||||
@@ -515,8 +491,6 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
currentAction = "Removing .runner";
|
||||
if (isConfigured)
|
||||
{
|
||||
// delete proxy setting
|
||||
(HostContext.GetService<IRunnerWebProxy>() as RunnerWebProxy).DeleteProxySetting();
|
||||
|
||||
// delete agent cert setting
|
||||
(HostContext.GetService<IRunnerCertificateManager>() as RunnerCertificateManager).DeleteCertificateSetting();
|
||||
|
||||
@@ -79,8 +79,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_0
|
||||
{
|
||||
// Validate args.
|
||||
ArgUtil.NotNull(executionContext, nameof(executionContext));
|
||||
Uri proxyUrlWithCred = null;
|
||||
string proxyUrlWithCredString = null;
|
||||
bool useSelfSignedCACert = false;
|
||||
bool useClientCert = false;
|
||||
string clientCertPrivateKeyAskPassFile = null;
|
||||
@@ -164,25 +162,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_0
|
||||
// 3. git version greater than 2.14.2 if use SChannel for SSL backend (Windows only)
|
||||
RequirementCheck(executionContext, gitCommandManager, gitLfsSupport);
|
||||
|
||||
// prepare credentail embedded urls
|
||||
var runnerProxy = executionContext.GetProxyConfiguration();
|
||||
if (runnerProxy != null && !string.IsNullOrEmpty(runnerProxy.ProxyAddress) && !runnerProxy.WebProxy.IsBypassed(repositoryUrl))
|
||||
{
|
||||
proxyUrlWithCred = UrlUtil.GetCredentialEmbeddedUrl(new Uri(runnerProxy.ProxyAddress), runnerProxy.ProxyUsername, runnerProxy.ProxyPassword);
|
||||
|
||||
// uri.absoluteuri will not contains port info if the scheme is http/https and the port is 80/443
|
||||
// however, git.exe always require you provide port info, if nothing passed in, it will use 1080 as default
|
||||
// as result, we need prefer the uri.originalstring when it's different than uri.absoluteuri.
|
||||
if (string.Equals(proxyUrlWithCred.AbsoluteUri, proxyUrlWithCred.OriginalString, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
proxyUrlWithCredString = proxyUrlWithCred.AbsoluteUri;
|
||||
}
|
||||
else
|
||||
{
|
||||
proxyUrlWithCredString = proxyUrlWithCred.OriginalString;
|
||||
}
|
||||
}
|
||||
|
||||
// prepare askpass for client cert private key, if the repository's endpoint url match the runner config url
|
||||
var systemConnection = executionContext.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
||||
if (runnerCert != null && Uri.Compare(repositoryUrl, systemConnection.Url, UriComponents.SchemeAndServer, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) == 0)
|
||||
@@ -373,13 +352,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_0
|
||||
await RemoveGitConfig(executionContext, gitCommandManager, targetPath, $"http.{repositoryUrl.AbsoluteUri}.extraheader", string.Empty);
|
||||
}
|
||||
|
||||
// always remove any possible left proxy setting from git config, the proxy setting may contains credential
|
||||
if (await gitCommandManager.GitConfigExist(executionContext, targetPath, $"http.proxy"))
|
||||
{
|
||||
executionContext.Debug("Remove any proxy setting from git config.");
|
||||
await RemoveGitConfig(executionContext, gitCommandManager, targetPath, $"http.proxy", string.Empty);
|
||||
}
|
||||
|
||||
List<string> additionalFetchArgs = new List<string>();
|
||||
List<string> additionalLfsFetchArgs = new List<string>();
|
||||
|
||||
@@ -389,15 +361,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_0
|
||||
additionalFetchArgs.Add($"-c http.extraheader=\"AUTHORIZATION: {GenerateBasicAuthHeader(executionContext, accessToken)}\"");
|
||||
}
|
||||
|
||||
// Prepare proxy config for fetch.
|
||||
if (runnerProxy != null && !string.IsNullOrEmpty(runnerProxy.ProxyAddress) && !runnerProxy.WebProxy.IsBypassed(repositoryUrl))
|
||||
{
|
||||
executionContext.Debug($"Config proxy server '{runnerProxy.ProxyAddress}' for git fetch.");
|
||||
ArgUtil.NotNullOrEmpty(proxyUrlWithCredString, nameof(proxyUrlWithCredString));
|
||||
additionalFetchArgs.Add($"-c http.proxy=\"{proxyUrlWithCredString}\"");
|
||||
additionalLfsFetchArgs.Add($"-c http.proxy=\"{proxyUrlWithCredString}\"");
|
||||
}
|
||||
|
||||
// Prepare ignore ssl cert error config for fetch.
|
||||
if (acceptUntrustedCerts)
|
||||
{
|
||||
@@ -539,14 +502,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_0
|
||||
additionalSubmoduleUpdateArgs.Add($"-c http.{authorityUrl}.extraheader=\"AUTHORIZATION: {GenerateBasicAuthHeader(executionContext, accessToken)}\"");
|
||||
}
|
||||
|
||||
// Prepare proxy config for submodule update.
|
||||
if (runnerProxy != null && !string.IsNullOrEmpty(runnerProxy.ProxyAddress) && !runnerProxy.WebProxy.IsBypassed(repositoryUrl))
|
||||
{
|
||||
executionContext.Debug($"Config proxy server '{runnerProxy.ProxyAddress}' for git submodule update.");
|
||||
ArgUtil.NotNullOrEmpty(proxyUrlWithCredString, nameof(proxyUrlWithCredString));
|
||||
additionalSubmoduleUpdateArgs.Add($"-c http.proxy=\"{proxyUrlWithCredString}\"");
|
||||
}
|
||||
|
||||
// Prepare ignore ssl cert error config for fetch.
|
||||
if (acceptUntrustedCerts)
|
||||
{
|
||||
@@ -637,7 +592,7 @@ namespace GitHub.Runner.Plugins.Repository.v1_0
|
||||
int exitCode_configUnset = await gitCommandManager.GitConfigUnset(executionContext, targetPath, configKey);
|
||||
if (exitCode_configUnset != 0)
|
||||
{
|
||||
// if unable to use git.exe unset http.extraheader, http.proxy or core.askpass, modify git config file on disk. make sure we don't left credential.
|
||||
// if unable to use git.exe unset http.extraheader or core.askpass, modify git config file on disk. make sure we don't left credential.
|
||||
if (!string.IsNullOrEmpty(configValue))
|
||||
{
|
||||
executionContext.Warning("An unsuccessful attempt was made using git command line to remove \"http.extraheader\" from the git config. Attempting to modify the git config file directly to remove the credential.");
|
||||
@@ -650,9 +605,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_0
|
||||
string setting = $"extraheader = {configValue}";
|
||||
gitConfigContent = Regex.Replace(gitConfigContent, setting, string.Empty, RegexOptions.IgnoreCase);
|
||||
|
||||
setting = $"proxy = {configValue}";
|
||||
gitConfigContent = Regex.Replace(gitConfigContent, setting, string.Empty, RegexOptions.IgnoreCase);
|
||||
|
||||
setting = $"askpass = {configValue}";
|
||||
gitConfigContent = Regex.Replace(gitConfigContent, setting, string.Empty, RegexOptions.IgnoreCase);
|
||||
|
||||
|
||||
@@ -65,8 +65,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_1
|
||||
// Validate args.
|
||||
ArgUtil.NotNull(executionContext, nameof(executionContext));
|
||||
Dictionary<string, string> configModifications = new Dictionary<string, string>();
|
||||
Uri proxyUrlWithCred = null;
|
||||
string proxyUrlWithCredString = null;
|
||||
bool useSelfSignedCACert = false;
|
||||
bool useClientCert = false;
|
||||
string clientCertPrivateKeyAskPassFile = null;
|
||||
@@ -153,18 +151,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_1
|
||||
// 3. git version greater than 2.14.2 if use SChannel for SSL backend (Windows only)
|
||||
RequirementCheck(executionContext, gitCommandManager, gitLfsSupport);
|
||||
|
||||
// prepare credentail embedded urls
|
||||
var runnerProxy = executionContext.GetProxyConfiguration();
|
||||
if (runnerProxy != null && !string.IsNullOrEmpty(runnerProxy.ProxyAddress) && !runnerProxy.WebProxy.IsBypassed(repositoryUrl))
|
||||
{
|
||||
proxyUrlWithCred = UrlUtil.GetCredentialEmbeddedUrl(new Uri(runnerProxy.ProxyAddress), runnerProxy.ProxyUsername, runnerProxy.ProxyPassword);
|
||||
|
||||
// uri.absoluteuri will not contains port info if the scheme is http/https and the port is 80/443
|
||||
// however, git.exe always require you provide port info, if nothing passed in, it will use 1080 as default
|
||||
// as result, we need prefer the uri.originalstring over uri.absoluteuri.
|
||||
proxyUrlWithCredString = proxyUrlWithCred.OriginalString;
|
||||
}
|
||||
|
||||
// prepare askpass for client cert private key, if the repository's endpoint url match the runner config url
|
||||
var systemConnection = executionContext.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
||||
if (runnerCert != null && Uri.Compare(repositoryUrl, systemConnection.Url, UriComponents.SchemeAndServer, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) == 0)
|
||||
@@ -355,13 +341,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_1
|
||||
await RemoveGitConfig(executionContext, gitCommandManager, targetPath, $"http.{repositoryUrl.AbsoluteUri}.extraheader", string.Empty);
|
||||
}
|
||||
|
||||
// always remove any possible left proxy setting from git config, the proxy setting may contains credential
|
||||
if (await gitCommandManager.GitConfigExist(executionContext, targetPath, $"http.proxy"))
|
||||
{
|
||||
executionContext.Debug("Remove any proxy setting from git config.");
|
||||
await RemoveGitConfig(executionContext, gitCommandManager, targetPath, $"http.proxy", string.Empty);
|
||||
}
|
||||
|
||||
List<string> additionalFetchArgs = new List<string>();
|
||||
List<string> additionalLfsFetchArgs = new List<string>();
|
||||
|
||||
@@ -376,15 +355,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_1
|
||||
throw new InvalidOperationException($"Git config failed with exit code: {exitCode_config}");
|
||||
}
|
||||
|
||||
// Prepare proxy config for fetch.
|
||||
if (runnerProxy != null && !string.IsNullOrEmpty(runnerProxy.ProxyAddress) && !runnerProxy.WebProxy.IsBypassed(repositoryUrl))
|
||||
{
|
||||
executionContext.Debug($"Config proxy server '{runnerProxy.ProxyAddress}' for git fetch.");
|
||||
ArgUtil.NotNullOrEmpty(proxyUrlWithCredString, nameof(proxyUrlWithCredString));
|
||||
additionalFetchArgs.Add($"-c http.proxy=\"{proxyUrlWithCredString}\"");
|
||||
additionalLfsFetchArgs.Add($"-c http.proxy=\"{proxyUrlWithCredString}\"");
|
||||
}
|
||||
|
||||
// Prepare ignore ssl cert error config for fetch.
|
||||
if (acceptUntrustedCerts)
|
||||
{
|
||||
@@ -514,14 +484,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_1
|
||||
|
||||
List<string> additionalSubmoduleUpdateArgs = new List<string>();
|
||||
|
||||
// Prepare proxy config for submodule update.
|
||||
if (runnerProxy != null && !string.IsNullOrEmpty(runnerProxy.ProxyAddress) && !runnerProxy.WebProxy.IsBypassed(repositoryUrl))
|
||||
{
|
||||
executionContext.Debug($"Config proxy server '{runnerProxy.ProxyAddress}' for git submodule update.");
|
||||
ArgUtil.NotNullOrEmpty(proxyUrlWithCredString, nameof(proxyUrlWithCredString));
|
||||
additionalSubmoduleUpdateArgs.Add($"-c http.proxy=\"{proxyUrlWithCredString}\"");
|
||||
}
|
||||
|
||||
// Prepare ignore ssl cert error config for fetch.
|
||||
if (acceptUntrustedCerts)
|
||||
{
|
||||
@@ -592,7 +554,7 @@ namespace GitHub.Runner.Plugins.Repository.v1_1
|
||||
GitCliManager gitCommandManager = new GitCliManager();
|
||||
await gitCommandManager.LoadGitExecutionInfo(executionContext);
|
||||
|
||||
executionContext.Debug("Remove any extraheader and proxy setting from git config.");
|
||||
executionContext.Debug("Remove any extraheader setting from git config.");
|
||||
var configKeys = JsonUtility.FromString<List<string>>(Environment.GetEnvironmentVariable("STATE_modifiedgitconfig"));
|
||||
if (configKeys?.Count > 0)
|
||||
{
|
||||
@@ -677,7 +639,7 @@ namespace GitHub.Runner.Plugins.Repository.v1_1
|
||||
int exitCode_configUnset = await gitCommandManager.GitConfigUnset(executionContext, targetPath, configKey);
|
||||
if (exitCode_configUnset != 0)
|
||||
{
|
||||
// if unable to use git.exe unset http.extraheader, http.proxy or core.askpass, modify git config file on disk. make sure we don't left credential.
|
||||
// if unable to use git.exe unset http.extraheader or core.askpass, modify git config file on disk. make sure we don't left credential.
|
||||
if (!string.IsNullOrEmpty(configValue))
|
||||
{
|
||||
executionContext.Warning("An unsuccessful attempt was made using git command line to remove \"http.extraheader\" from the git config. Attempting to modify the git config file directly to remove the credential.");
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace GitHub.Runner.Sdk
|
||||
{
|
||||
private readonly string DebugEnvironmentalVariable = "ACTIONS_STEP_DEBUG";
|
||||
private VssConnection _connection;
|
||||
private RunnerWebProxy _webProxy;
|
||||
private readonly object _stdoutLock = new object();
|
||||
private readonly ITraceWriter _trace; // for unit tests
|
||||
|
||||
@@ -57,6 +58,19 @@ namespace GitHub.Runner.Sdk
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public RunnerWebProxy WebProxy
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_webProxy == null)
|
||||
{
|
||||
_webProxy = new RunnerWebProxy();
|
||||
}
|
||||
return _webProxy;
|
||||
}
|
||||
}
|
||||
|
||||
public VssConnection InitializeVssConnection()
|
||||
{
|
||||
var headerValues = new List<ProductInfoHeaderValue>();
|
||||
@@ -84,15 +98,7 @@ namespace GitHub.Runner.Sdk
|
||||
}
|
||||
}
|
||||
|
||||
var proxySetting = GetProxyConfiguration();
|
||||
if (proxySetting != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(proxySetting.ProxyAddress))
|
||||
{
|
||||
VssHttpMessageHandler.DefaultWebProxy = new RunnerWebProxyCore(proxySetting.ProxyAddress, proxySetting.ProxyUsername, proxySetting.ProxyPassword, proxySetting.ProxyBypassList);
|
||||
}
|
||||
}
|
||||
|
||||
VssHttpMessageHandler.DefaultWebProxy = this.WebProxy;
|
||||
ServiceEndpoint systemConnection = this.Endpoints.FirstOrDefault(e => string.Equals(e.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
||||
ArgUtil.NotNull(systemConnection, nameof(systemConnection));
|
||||
ArgUtil.NotNull(systemConnection.Url, nameof(systemConnection.Url));
|
||||
@@ -255,29 +261,6 @@ namespace GitHub.Runner.Sdk
|
||||
}
|
||||
}
|
||||
|
||||
public RunnerWebProxySettings GetProxyConfiguration()
|
||||
{
|
||||
string proxyUrl = GetRunnerContext("ProxyUrl");
|
||||
if (!string.IsNullOrEmpty(proxyUrl))
|
||||
{
|
||||
string proxyUsername = GetRunnerContext("ProxyUsername");
|
||||
string proxyPassword = GetRunnerContext("ProxyPassword");
|
||||
List<string> proxyBypassHosts = StringUtil.ConvertFromJson<List<string>>(GetRunnerContext("ProxyBypassList") ?? "[]");
|
||||
return new RunnerWebProxySettings()
|
||||
{
|
||||
ProxyAddress = proxyUrl,
|
||||
ProxyUsername = proxyUsername,
|
||||
ProxyPassword = proxyPassword,
|
||||
ProxyBypassList = proxyBypassHosts,
|
||||
WebProxy = new RunnerWebProxyCore(proxyUrl, proxyUsername, proxyPassword, proxyBypassHosts)
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private string Escape(string input)
|
||||
{
|
||||
foreach (var mapping in _commandEscapeMappings)
|
||||
|
||||
224
src/Runner.Sdk/RunnerWebProxy.cs
Normal file
224
src/Runner.Sdk/RunnerWebProxy.cs
Normal file
@@ -0,0 +1,224 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace GitHub.Runner.Sdk
|
||||
{
|
||||
public struct ByPassInfo
|
||||
{
|
||||
public string Host { get; set; }
|
||||
|
||||
public string Port { get; set; }
|
||||
};
|
||||
|
||||
public class RunnerWebProxy : IWebProxy
|
||||
{
|
||||
private string _httpProxyAddress;
|
||||
private string _httpProxyUsername;
|
||||
private string _httpProxyPassword;
|
||||
|
||||
private string _httpsProxyAddress;
|
||||
private string _httpsProxyUsername;
|
||||
private string _httpsProxyPassword;
|
||||
|
||||
private readonly List<ByPassInfo> _noProxyList = new List<ByPassInfo>();
|
||||
private readonly HashSet<string> _noProxyUnique = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly Regex _validIpRegex = new Regex("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$", RegexOptions.Compiled);
|
||||
|
||||
public string HttpProxyAddress => _httpProxyAddress;
|
||||
public string HttpProxyUsername => _httpProxyUsername;
|
||||
public string HttpProxyPassword => _httpProxyPassword;
|
||||
|
||||
public string HttpsProxyAddress => _httpsProxyAddress;
|
||||
public string HttpsProxyUsername => _httpsProxyUsername;
|
||||
public string HttpsProxyPassword => _httpsProxyPassword;
|
||||
|
||||
public List<ByPassInfo> NoProxyList => _noProxyList;
|
||||
|
||||
public ICredentials Credentials { get; set; }
|
||||
|
||||
public RunnerWebProxy()
|
||||
{
|
||||
Credentials = new CredentialCache();
|
||||
|
||||
var httpProxyAddress = Environment.GetEnvironmentVariable("http_proxy");
|
||||
if (string.IsNullOrEmpty(httpProxyAddress))
|
||||
{
|
||||
httpProxyAddress = Environment.GetEnvironmentVariable("HTTP_PROXY");
|
||||
}
|
||||
httpProxyAddress = httpProxyAddress?.Trim();
|
||||
|
||||
var httpsProxyAddress = Environment.GetEnvironmentVariable("https_proxy");
|
||||
if (string.IsNullOrEmpty(httpsProxyAddress))
|
||||
{
|
||||
httpsProxyAddress = Environment.GetEnvironmentVariable("HTTPS_PROXY");
|
||||
}
|
||||
httpsProxyAddress = httpsProxyAddress?.Trim();
|
||||
|
||||
var noProxyList = Environment.GetEnvironmentVariable("no_proxy");
|
||||
if (string.IsNullOrEmpty(noProxyList))
|
||||
{
|
||||
noProxyList = Environment.GetEnvironmentVariable("NO_PROXY");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(httpProxyAddress) && string.IsNullOrEmpty(httpsProxyAddress))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(httpProxyAddress) && Uri.TryCreate(httpProxyAddress, UriKind.Absolute, out var proxyHttpUri))
|
||||
{
|
||||
_httpProxyAddress = proxyHttpUri.AbsoluteUri;
|
||||
|
||||
// the proxy url looks like http://[user:pass@]127.0.0.1:8888
|
||||
var userInfo = Uri.UnescapeDataString(proxyHttpUri.UserInfo).Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (userInfo.Length == 2)
|
||||
{
|
||||
_httpProxyUsername = userInfo[0];
|
||||
_httpProxyPassword = userInfo[1];
|
||||
}
|
||||
else if (userInfo.Length == 1)
|
||||
{
|
||||
_httpProxyUsername = userInfo[0];
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_httpProxyUsername) || !string.IsNullOrEmpty(_httpProxyPassword))
|
||||
{
|
||||
var credentials = new NetworkCredential(_httpProxyUsername, _httpProxyPassword);
|
||||
|
||||
// Replace the entry in the credential cache if it exists
|
||||
(Credentials as CredentialCache).Remove(proxyHttpUri, "Basic");
|
||||
(Credentials as CredentialCache).Add(proxyHttpUri, "Basic", credentials);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(httpsProxyAddress) && Uri.TryCreate(httpsProxyAddress, UriKind.Absolute, out var proxyHttpsUri))
|
||||
{
|
||||
_httpsProxyAddress = proxyHttpsUri.AbsoluteUri;
|
||||
|
||||
// the proxy url looks like http://[user:pass@]127.0.0.1:8888
|
||||
var userInfo = Uri.UnescapeDataString(proxyHttpsUri.UserInfo).Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (userInfo.Length == 2)
|
||||
{
|
||||
_httpsProxyUsername = userInfo[0];
|
||||
_httpsProxyPassword = userInfo[1];
|
||||
}
|
||||
else if (userInfo.Length == 1)
|
||||
{
|
||||
_httpsProxyUsername = userInfo[0];
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_httpsProxyUsername) || !string.IsNullOrEmpty(_httpsProxyPassword))
|
||||
{
|
||||
var credentials = new NetworkCredential(_httpsProxyUsername, _httpsProxyPassword);
|
||||
|
||||
// Replace the entry in the credential cache if it exists
|
||||
(Credentials as CredentialCache).Remove(proxyHttpsUri, "Basic");
|
||||
(Credentials as CredentialCache).Add(proxyHttpsUri, "Basic", credentials);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(noProxyList))
|
||||
{
|
||||
var noProxyListSplit = noProxyList.Split(',', StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (string noProxy in noProxyListSplit)
|
||||
{
|
||||
var noProxyTrim = noProxy.Trim();
|
||||
if (string.IsNullOrEmpty(noProxyTrim))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (_noProxyUnique.Add(noProxyTrim))
|
||||
{
|
||||
var noProxyInfo = new ByPassInfo();
|
||||
var noProxyHostPort = noProxyTrim.Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (noProxyHostPort.Length == 1)
|
||||
{
|
||||
noProxyInfo.Host = noProxyHostPort[0];
|
||||
}
|
||||
else if (noProxyHostPort.Length == 2)
|
||||
{
|
||||
noProxyInfo.Host = noProxyHostPort[0];
|
||||
noProxyInfo.Port = noProxyHostPort[1];
|
||||
}
|
||||
|
||||
// We don't support IP address for no_proxy
|
||||
if (_validIpRegex.IsMatch(noProxyInfo.Host))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_noProxyList.Add(noProxyInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Uri GetProxy(Uri destination)
|
||||
{
|
||||
if (IsBypassed(destination))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (destination.Scheme == Uri.UriSchemeHttps)
|
||||
{
|
||||
return new Uri(_httpsProxyAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Uri(_httpProxyAddress);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsBypassed(Uri uri)
|
||||
{
|
||||
if (uri.Scheme == Uri.UriSchemeHttps && string.IsNullOrEmpty(_httpsProxyAddress))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (uri.Scheme == Uri.UriSchemeHttp && string.IsNullOrEmpty(_httpProxyAddress))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return uri.IsLoopback || IsUriInBypassList(uri);
|
||||
}
|
||||
|
||||
private bool IsUriInBypassList(Uri input)
|
||||
{
|
||||
foreach (var noProxy in _noProxyList)
|
||||
{
|
||||
var matchHost = false;
|
||||
var matchPort = false;
|
||||
|
||||
if (string.IsNullOrEmpty(noProxy.Port))
|
||||
{
|
||||
matchPort = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
matchPort = string.Equals(noProxy.Port, input.Port.ToString());
|
||||
}
|
||||
|
||||
if (noProxy.Host.StartsWith('.'))
|
||||
{
|
||||
matchHost = input.Host.EndsWith(noProxy.Host, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
else
|
||||
{
|
||||
matchHost = string.Equals(input.Host, noProxy.Host, StringComparison.OrdinalIgnoreCase) || input.Host.EndsWith($".{noProxy.Host}", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
if (matchHost && matchPort)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace GitHub.Runner.Sdk
|
||||
{
|
||||
public class RunnerWebProxySettings
|
||||
{
|
||||
public string ProxyAddress { get; set; }
|
||||
public string ProxyUsername { get; set; }
|
||||
public string ProxyPassword { get; set; }
|
||||
public List<string> ProxyBypassList { get; set; }
|
||||
public IWebProxy WebProxy { get; set; }
|
||||
}
|
||||
|
||||
public class RunnerWebProxyCore : IWebProxy
|
||||
{
|
||||
private string _proxyAddress;
|
||||
private readonly List<Regex> _regExBypassList = new List<Regex>();
|
||||
|
||||
public ICredentials Credentials { get; set; }
|
||||
|
||||
public RunnerWebProxyCore()
|
||||
{
|
||||
}
|
||||
|
||||
public RunnerWebProxyCore(string proxyAddress, string proxyUsername, string proxyPassword, List<string> proxyBypassList)
|
||||
{
|
||||
Update(proxyAddress, proxyUsername, proxyPassword, proxyBypassList);
|
||||
}
|
||||
|
||||
public void Update(string proxyAddress, string proxyUsername, string proxyPassword, List<string> proxyBypassList)
|
||||
{
|
||||
_proxyAddress = proxyAddress?.Trim();
|
||||
|
||||
if (string.IsNullOrEmpty(proxyUsername) || string.IsNullOrEmpty(proxyPassword))
|
||||
{
|
||||
Credentials = CredentialCache.DefaultNetworkCredentials;
|
||||
}
|
||||
else
|
||||
{
|
||||
Credentials = new NetworkCredential(proxyUsername, proxyPassword);
|
||||
}
|
||||
|
||||
if (proxyBypassList != null)
|
||||
{
|
||||
foreach (string bypass in proxyBypassList)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(bypass))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
Regex bypassRegex = new Regex(bypass.Trim(), RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.ECMAScript);
|
||||
_regExBypassList.Add(bypassRegex);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// eat all exceptions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Uri GetProxy(Uri destination)
|
||||
{
|
||||
if (IsBypassed(destination))
|
||||
{
|
||||
return destination;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Uri(_proxyAddress);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsBypassed(Uri uri)
|
||||
{
|
||||
return string.IsNullOrEmpty(_proxyAddress) || uri.IsLoopback || IsMatchInBypassList(uri);
|
||||
}
|
||||
|
||||
private bool IsMatchInBypassList(Uri input)
|
||||
{
|
||||
string matchUriString = input.IsDefaultPort ?
|
||||
input.Scheme + "://" + input.Host :
|
||||
input.Scheme + "://" + input.Host + ":" + input.Port.ToString();
|
||||
|
||||
foreach (Regex r in _regExBypassList)
|
||||
{
|
||||
if (r.IsMatch(matchUriString))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -616,29 +616,6 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
// PostJobSteps for job ExecutionContext
|
||||
PostJobSteps = new Stack<IStep>();
|
||||
// Proxy variables
|
||||
// var agentWebProxy = HostContext.GetService<IRunnerWebProxy>();
|
||||
// if (!string.IsNullOrEmpty(agentWebProxy.ProxyAddress))
|
||||
// {
|
||||
// SetRunnerContext("proxyurl", agentWebProxy.ProxyAddress);
|
||||
|
||||
// if (!string.IsNullOrEmpty(agentWebProxy.ProxyUsername))
|
||||
// {
|
||||
// SetRunnerContext("proxyusername", agentWebProxy.ProxyUsername);
|
||||
// }
|
||||
|
||||
// if (!string.IsNullOrEmpty(agentWebProxy.ProxyPassword))
|
||||
// {
|
||||
// HostContext.SecretMasker.AddValue(agentWebProxy.ProxyPassword);
|
||||
// SetRunnerContext("proxypassword", agentWebProxy.ProxyPassword);
|
||||
// }
|
||||
|
||||
// if (agentWebProxy.ProxyBypassList.Count > 0)
|
||||
// {
|
||||
// SetRunnerContext("proxybypasslist", JsonUtility.ToString(agentWebProxy.ProxyBypassList));
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Certificate variables
|
||||
// var agentCert = HostContext.GetService<IRunnerCertificateManager>();
|
||||
// if (agentCert.SkipServerCertificateValidation)
|
||||
|
||||
@@ -55,10 +55,13 @@ namespace GitHub.Runner.Worker
|
||||
context.Debug($"Primary repository: {repoFullName}");
|
||||
|
||||
// Print proxy setting information for better diagnostic experience
|
||||
var runnerWebProxy = HostContext.GetService<IRunnerWebProxy>();
|
||||
if (!string.IsNullOrEmpty(runnerWebProxy.ProxyAddress))
|
||||
if (!string.IsNullOrEmpty(HostContext.WebProxy.HttpProxyAddress))
|
||||
{
|
||||
context.Output($"Runner is running behind proxy server: '{runnerWebProxy.ProxyAddress}'");
|
||||
context.Output($"Runner is running behind proxy server '{HostContext.WebProxy.HttpProxyAddress}' for all HTTP requests.");
|
||||
}
|
||||
if (!string.IsNullOrEmpty(HostContext.WebProxy.HttpsProxyAddress))
|
||||
{
|
||||
context.Output($"Runner is running behind proxy server '{HostContext.WebProxy.HttpsProxyAddress}' for all HTTPS requests.");
|
||||
}
|
||||
|
||||
// Prepare the workflow directory
|
||||
|
||||
@@ -40,9 +40,8 @@ namespace GitHub.Runner.Worker
|
||||
// Validate args.
|
||||
ArgUtil.NotNullOrEmpty(pipeIn, nameof(pipeIn));
|
||||
ArgUtil.NotNullOrEmpty(pipeOut, nameof(pipeOut));
|
||||
var runnerWebProxy = HostContext.GetService<IRunnerWebProxy>();
|
||||
var runnerCertManager = HostContext.GetService<IRunnerCertificateManager>();
|
||||
VssUtil.InitializeVssClientSettings(HostContext.UserAgent, runnerWebProxy.WebProxy, runnerCertManager.VssClientCertificateManager);
|
||||
VssUtil.InitializeVssClientSettings(HostContext.UserAgent, HostContext.WebProxy, runnerCertManager.VssClientCertificateManager);
|
||||
var jobRunner = HostContext.CreateService<IJobRunner>();
|
||||
|
||||
using (var channel = HostContext.CreateService<IProcessChannel>())
|
||||
|
||||
@@ -107,6 +107,35 @@ namespace GitHub.Runner.Common.Tests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Common")]
|
||||
public void SecretMaskerForProxy()
|
||||
{
|
||||
try
|
||||
{
|
||||
Environment.SetEnvironmentVariable("http_proxy", "http://user:password123@127.0.0.1:8888");
|
||||
|
||||
// Arrange.
|
||||
Setup();
|
||||
|
||||
// Assert.
|
||||
var logFile = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), $"trace_{nameof(HostContextL0)}_{nameof(SecretMaskerForProxy)}.log");
|
||||
var tempFile = Path.GetTempFileName();
|
||||
File.Delete(tempFile);
|
||||
File.Copy(logFile, tempFile);
|
||||
var content = File.ReadAllText(tempFile);
|
||||
Assert.DoesNotContain("password123", content);
|
||||
Assert.Contains("http://user:***@127.0.0.1:8888", content);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Environment.SetEnvironmentVariable("http_proxy", null);
|
||||
// Cleanup.
|
||||
Teardown();
|
||||
}
|
||||
}
|
||||
|
||||
private void Setup([CallerMemberName] string testName = "")
|
||||
{
|
||||
_tokenSource = new CancellationTokenSource();
|
||||
|
||||
@@ -26,8 +26,6 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
||||
private Mock<IPromptManager> _promptManager;
|
||||
private Mock<IConfigurationStore> _store;
|
||||
private Mock<IExtensionManager> _extnMgr;
|
||||
// private Mock<IDeploymentGroupServer> _machineGroupServer;
|
||||
private Mock<IRunnerWebProxy> _runnerWebProxy;
|
||||
private Mock<IRunnerCertificateManager> _cert;
|
||||
|
||||
#if OS_WINDOWS
|
||||
@@ -59,8 +57,6 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
||||
_store = new Mock<IConfigurationStore>();
|
||||
_extnMgr = new Mock<IExtensionManager>();
|
||||
_rsaKeyManager = new Mock<IRSAKeyManager>();
|
||||
// _machineGroupServer = new Mock<IDeploymentGroupServer>();
|
||||
_runnerWebProxy = new Mock<IRunnerWebProxy>();
|
||||
_cert = new Mock<IRunnerCertificateManager>();
|
||||
|
||||
#if OS_WINDOWS
|
||||
@@ -134,7 +130,6 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
||||
tc.SetSingleton<IExtensionManager>(_extnMgr.Object);
|
||||
tc.SetSingleton<IRunnerServer>(_agentServer.Object);
|
||||
tc.SetSingleton<ILocationServer>(_locationServer.Object);
|
||||
tc.SetSingleton<IRunnerWebProxy>(_runnerWebProxy.Object);
|
||||
tc.SetSingleton<IRunnerCertificateManager>(_cert.Object);
|
||||
|
||||
#if OS_WINDOWS
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
using GitHub.Runner.Common.Util;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Xunit;
|
||||
using System;
|
||||
using GitHub.Runner.Sdk;
|
||||
|
||||
namespace GitHub.Runner.Common.Tests
|
||||
{
|
||||
public sealed class ProxyConfigL0
|
||||
{
|
||||
private static readonly Regex NewHttpClientHandlerRegex = new Regex("New\\s+HttpClientHandler\\s*\\(", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex NewHttpClientRegex = new Regex("New\\s+HttpClient\\s*\\(\\s*\\)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly List<string> SkippedFiles = new List<string>()
|
||||
{
|
||||
"Runner.Common\\HostContext.cs",
|
||||
"Runner.Common/HostContext.cs"
|
||||
};
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Common")]
|
||||
public void IsNotUseRawHttpClientHandler()
|
||||
{
|
||||
List<string> sourceFiles = Directory.GetFiles(
|
||||
TestUtil.GetProjectPath("Runner.Common"),
|
||||
"*.cs",
|
||||
SearchOption.AllDirectories).ToList();
|
||||
sourceFiles.AddRange(Directory.GetFiles(
|
||||
TestUtil.GetProjectPath("Runner.Listener"),
|
||||
"*.cs",
|
||||
SearchOption.AllDirectories));
|
||||
sourceFiles.AddRange(Directory.GetFiles(
|
||||
TestUtil.GetProjectPath("Runner.Worker"),
|
||||
"*.cs",
|
||||
SearchOption.AllDirectories));
|
||||
|
||||
List<string> badCode = new List<string>();
|
||||
foreach (string sourceFile in sourceFiles)
|
||||
{
|
||||
// Skip skipped files.
|
||||
if (SkippedFiles.Any(s => sourceFile.Contains(s)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip files in the obj directory.
|
||||
if (sourceFile.Contains(StringUtil.Format("{0}obj{0}", Path.DirectorySeparatorChar)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int lineCount = 0;
|
||||
foreach (string line in File.ReadAllLines(sourceFile))
|
||||
{
|
||||
lineCount++;
|
||||
if (NewHttpClientHandlerRegex.IsMatch(line))
|
||||
{
|
||||
badCode.Add($"{sourceFile} (line {lineCount})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Assert.True(badCode.Count == 0, $"The following code is using Raw HttpClientHandler() which will not follow the proxy setting agent have. Please use HostContext.CreateHttpClientHandler() instead.\n {string.Join("\n", badCode)}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Common")]
|
||||
public void IsNotUseRawHttpClient()
|
||||
{
|
||||
List<string> sourceFiles = Directory.GetFiles(
|
||||
TestUtil.GetProjectPath("Runner.Common"),
|
||||
"*.cs",
|
||||
SearchOption.AllDirectories).ToList();
|
||||
sourceFiles.AddRange(Directory.GetFiles(
|
||||
TestUtil.GetProjectPath("Runner.Listener"),
|
||||
"*.cs",
|
||||
SearchOption.AllDirectories));
|
||||
sourceFiles.AddRange(Directory.GetFiles(
|
||||
TestUtil.GetProjectPath("Runner.Worker"),
|
||||
"*.cs",
|
||||
SearchOption.AllDirectories));
|
||||
|
||||
List<string> badCode = new List<string>();
|
||||
foreach (string sourceFile in sourceFiles)
|
||||
{
|
||||
// Skip skipped files.
|
||||
if (SkippedFiles.Any(s => sourceFile.Contains(s)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip files in the obj directory.
|
||||
if (sourceFile.Contains(StringUtil.Format("{0}obj{0}", Path.DirectorySeparatorChar)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int lineCount = 0;
|
||||
foreach (string line in File.ReadAllLines(sourceFile))
|
||||
{
|
||||
lineCount++;
|
||||
if (NewHttpClientRegex.IsMatch(line))
|
||||
{
|
||||
badCode.Add($"{sourceFile} (line {lineCount})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Assert.True(badCode.Count == 0, $"The following code is using Raw HttpClient() which will not follow the proxy setting agent have. Please use New HttpClient(HostContext.CreateHttpClientHandler()) instead.\n {string.Join("\n", badCode)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
416
src/Test/L0/RunnerWebProxyL0.cs
Normal file
416
src/Test/L0/RunnerWebProxyL0.cs
Normal file
@@ -0,0 +1,416 @@
|
||||
using GitHub.Runner.Common.Util;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Xunit;
|
||||
using System;
|
||||
using GitHub.Runner.Sdk;
|
||||
|
||||
namespace GitHub.Runner.Common.Tests
|
||||
{
|
||||
public sealed class RunnerWebProxyL0
|
||||
{
|
||||
private static readonly Regex NewHttpClientHandlerRegex = new Regex("New\\s+HttpClientHandler\\s*\\(", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex NewHttpClientRegex = new Regex("New\\s+HttpClient\\s*\\(\\s*\\)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly List<string> SkippedFiles = new List<string>()
|
||||
{
|
||||
"Runner.Common\\HostContext.cs",
|
||||
"Runner.Common/HostContext.cs"
|
||||
};
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Common")]
|
||||
public void IsNotUseRawHttpClientHandler()
|
||||
{
|
||||
List<string> sourceFiles = Directory.GetFiles(
|
||||
TestUtil.GetProjectPath("Runner.Common"),
|
||||
"*.cs",
|
||||
SearchOption.AllDirectories).ToList();
|
||||
sourceFiles.AddRange(Directory.GetFiles(
|
||||
TestUtil.GetProjectPath("Runner.Listener"),
|
||||
"*.cs",
|
||||
SearchOption.AllDirectories));
|
||||
sourceFiles.AddRange(Directory.GetFiles(
|
||||
TestUtil.GetProjectPath("Runner.Worker"),
|
||||
"*.cs",
|
||||
SearchOption.AllDirectories));
|
||||
|
||||
List<string> badCode = new List<string>();
|
||||
foreach (string sourceFile in sourceFiles)
|
||||
{
|
||||
// Skip skipped files.
|
||||
if (SkippedFiles.Any(s => sourceFile.Contains(s)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip files in the obj directory.
|
||||
if (sourceFile.Contains(StringUtil.Format("{0}obj{0}", Path.DirectorySeparatorChar)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int lineCount = 0;
|
||||
foreach (string line in File.ReadAllLines(sourceFile))
|
||||
{
|
||||
lineCount++;
|
||||
if (NewHttpClientHandlerRegex.IsMatch(line))
|
||||
{
|
||||
badCode.Add($"{sourceFile} (line {lineCount})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Assert.True(badCode.Count == 0, $"The following code is using Raw HttpClientHandler() which will not follow the proxy setting agent have. Please use HostContext.CreateHttpClientHandler() instead.\n {string.Join("\n", badCode)}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Common")]
|
||||
public void IsNotUseRawHttpClient()
|
||||
{
|
||||
List<string> sourceFiles = Directory.GetFiles(
|
||||
TestUtil.GetProjectPath("Runner.Common"),
|
||||
"*.cs",
|
||||
SearchOption.AllDirectories).ToList();
|
||||
sourceFiles.AddRange(Directory.GetFiles(
|
||||
TestUtil.GetProjectPath("Runner.Listener"),
|
||||
"*.cs",
|
||||
SearchOption.AllDirectories));
|
||||
sourceFiles.AddRange(Directory.GetFiles(
|
||||
TestUtil.GetProjectPath("Runner.Worker"),
|
||||
"*.cs",
|
||||
SearchOption.AllDirectories));
|
||||
|
||||
List<string> badCode = new List<string>();
|
||||
foreach (string sourceFile in sourceFiles)
|
||||
{
|
||||
// Skip skipped files.
|
||||
if (SkippedFiles.Any(s => sourceFile.Contains(s)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip files in the obj directory.
|
||||
if (sourceFile.Contains(StringUtil.Format("{0}obj{0}", Path.DirectorySeparatorChar)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int lineCount = 0;
|
||||
foreach (string line in File.ReadAllLines(sourceFile))
|
||||
{
|
||||
lineCount++;
|
||||
if (NewHttpClientRegex.IsMatch(line))
|
||||
{
|
||||
badCode.Add($"{sourceFile} (line {lineCount})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Assert.True(badCode.Count == 0, $"The following code is using Raw HttpClient() which will not follow the proxy setting agent have. Please use New HttpClient(HostContext.CreateHttpClientHandler()) instead.\n {string.Join("\n", badCode)}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Common")]
|
||||
public void WebProxyFromEnvironmentVariables()
|
||||
{
|
||||
try
|
||||
{
|
||||
Environment.SetEnvironmentVariable("http_proxy", "http://127.0.0.1:8888");
|
||||
Environment.SetEnvironmentVariable("https_proxy", "http://user:pass@127.0.0.1:9999");
|
||||
Environment.SetEnvironmentVariable("no_proxy", "github.com, google.com,");
|
||||
var proxy = new RunnerWebProxy();
|
||||
|
||||
Assert.Equal("http://127.0.0.1:8888/", proxy.HttpProxyAddress);
|
||||
Assert.Null(proxy.HttpProxyUsername);
|
||||
Assert.Null(proxy.HttpProxyPassword);
|
||||
|
||||
Assert.Equal("http://user:pass@127.0.0.1:9999/", proxy.HttpsProxyAddress);
|
||||
Assert.Equal("user", proxy.HttpsProxyUsername);
|
||||
Assert.Equal("pass", proxy.HttpsProxyPassword);
|
||||
|
||||
Assert.Equal(2, proxy.NoProxyList.Count);
|
||||
Assert.Equal("github.com", proxy.NoProxyList[0].Host);
|
||||
Assert.Equal("google.com", proxy.NoProxyList[1].Host);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanProxyEnv();
|
||||
}
|
||||
}
|
||||
|
||||
#if !OS_WINDOWS
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Common")]
|
||||
public void WebProxyFromEnvironmentVariablesPreferLowerCase()
|
||||
{
|
||||
try
|
||||
{
|
||||
Environment.SetEnvironmentVariable("http_proxy", "http://127.0.0.1:7777");
|
||||
Environment.SetEnvironmentVariable("HTTP_PROXY", "http://127.0.0.1:8888");
|
||||
Environment.SetEnvironmentVariable("https_proxy", "http://user:pass@127.0.0.1:8888");
|
||||
Environment.SetEnvironmentVariable("HTTPS_PROXY", "http://user:pass@127.0.0.1:9999");
|
||||
Environment.SetEnvironmentVariable("no_proxy", "github.com, github.com ");
|
||||
Environment.SetEnvironmentVariable("NO_PROXY", "github.com, google.com,");
|
||||
var proxy = new RunnerWebProxy();
|
||||
|
||||
Assert.Equal("http://127.0.0.1:7777/", proxy.HttpProxyAddress);
|
||||
Assert.Null(proxy.HttpProxyUsername);
|
||||
Assert.Null(proxy.HttpProxyPassword);
|
||||
|
||||
Assert.Equal("http://user:pass@127.0.0.1:8888/", proxy.HttpsProxyAddress);
|
||||
Assert.Equal("user", proxy.HttpsProxyUsername);
|
||||
Assert.Equal("pass", proxy.HttpsProxyPassword);
|
||||
|
||||
Assert.Equal(1, proxy.NoProxyList.Count);
|
||||
Assert.Equal("github.com", proxy.NoProxyList[0].Host);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanProxyEnv();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Common")]
|
||||
public void WebProxyFromEnvironmentVariablesInvalidString()
|
||||
{
|
||||
try
|
||||
{
|
||||
Environment.SetEnvironmentVariable("http_proxy", "127.0.0.1:7777");
|
||||
Environment.SetEnvironmentVariable("https_proxy", "127.0.0.1");
|
||||
var proxy = new RunnerWebProxy();
|
||||
|
||||
Assert.Null(proxy.HttpProxyAddress);
|
||||
Assert.Null(proxy.HttpProxyUsername);
|
||||
Assert.Null(proxy.HttpProxyPassword);
|
||||
|
||||
Assert.Null(proxy.HttpsProxyAddress);
|
||||
Assert.Null(proxy.HttpsProxyUsername);
|
||||
Assert.Null(proxy.HttpsProxyPassword);
|
||||
|
||||
Assert.Equal(0, proxy.NoProxyList.Count);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanProxyEnv();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Common")]
|
||||
public void WebProxyFromEnvironmentVariablesProxyCredentials()
|
||||
{
|
||||
try
|
||||
{
|
||||
Environment.SetEnvironmentVariable("http_proxy", "http://user1@127.0.0.1:8888");
|
||||
Environment.SetEnvironmentVariable("https_proxy", "http://user2:pass@127.0.0.1:9999");
|
||||
Environment.SetEnvironmentVariable("no_proxy", "github.com, google.com,");
|
||||
var proxy = new RunnerWebProxy();
|
||||
|
||||
Assert.Equal("http://user1@127.0.0.1:8888/", proxy.HttpProxyAddress);
|
||||
Assert.Equal("user1", proxy.HttpProxyUsername);
|
||||
Assert.Null(proxy.HttpProxyPassword);
|
||||
|
||||
var cred = proxy.Credentials.GetCredential(new Uri("http://user1@127.0.0.1:8888/"), "Basic");
|
||||
Assert.Equal("user1", cred.UserName);
|
||||
Assert.Equal(string.Empty, cred.Password);
|
||||
|
||||
Assert.Equal("http://user2:pass@127.0.0.1:9999/", proxy.HttpsProxyAddress);
|
||||
Assert.Equal("user2", proxy.HttpsProxyUsername);
|
||||
Assert.Equal("pass", proxy.HttpsProxyPassword);
|
||||
|
||||
cred = proxy.Credentials.GetCredential(new Uri("http://user2:pass@127.0.0.1:9999/"), "Basic");
|
||||
Assert.Equal("user2", cred.UserName);
|
||||
Assert.Equal("pass", cred.Password);
|
||||
|
||||
Assert.Equal(2, proxy.NoProxyList.Count);
|
||||
Assert.Equal("github.com", proxy.NoProxyList[0].Host);
|
||||
Assert.Equal("google.com", proxy.NoProxyList[1].Host);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanProxyEnv();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Common")]
|
||||
public void WebProxyFromEnvironmentVariablesProxyCredentialsEncoding()
|
||||
{
|
||||
try
|
||||
{
|
||||
Environment.SetEnvironmentVariable("http_proxy", "http://user1:pass1%40@127.0.0.1:8888");
|
||||
Environment.SetEnvironmentVariable("https_proxy", "http://user2:pass2%40@127.0.0.1:9999");
|
||||
Environment.SetEnvironmentVariable("no_proxy", "github.com, google.com,");
|
||||
var proxy = new RunnerWebProxy();
|
||||
|
||||
Assert.Equal("http://user1:pass1%40@127.0.0.1:8888/", proxy.HttpProxyAddress);
|
||||
Assert.Equal("user1", proxy.HttpProxyUsername);
|
||||
Assert.Equal("pass1@", proxy.HttpProxyPassword);
|
||||
|
||||
var cred = proxy.Credentials.GetCredential(new Uri("http://user1:pass1%40@127.0.0.1:8888/"), "Basic");
|
||||
Assert.Equal("user1", cred.UserName);
|
||||
Assert.Equal("pass1@", cred.Password);
|
||||
|
||||
Assert.Equal("http://user2:pass2%40@127.0.0.1:9999/", proxy.HttpsProxyAddress);
|
||||
Assert.Equal("user2", proxy.HttpsProxyUsername);
|
||||
Assert.Equal("pass2@", proxy.HttpsProxyPassword);
|
||||
|
||||
cred = proxy.Credentials.GetCredential(new Uri("http://user2:pass2%40@127.0.0.1:9999/"), "Basic");
|
||||
Assert.Equal("user2", cred.UserName);
|
||||
Assert.Equal("pass2@", cred.Password);
|
||||
|
||||
Assert.Equal(2, proxy.NoProxyList.Count);
|
||||
Assert.Equal("github.com", proxy.NoProxyList[0].Host);
|
||||
Assert.Equal("google.com", proxy.NoProxyList[1].Host);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanProxyEnv();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Common")]
|
||||
public void WebProxyFromEnvironmentVariablesByPassEmptyProxy()
|
||||
{
|
||||
var proxy = new RunnerWebProxy();
|
||||
Assert.True(proxy.IsBypassed(new Uri("https://github.com")));
|
||||
Assert.True(proxy.IsBypassed(new Uri("https://github.com")));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Common")]
|
||||
public void WebProxyFromEnvironmentVariablesGetProxyEmptyHttpProxy()
|
||||
{
|
||||
try
|
||||
{
|
||||
Environment.SetEnvironmentVariable("https_proxy", "http://user2:pass2%40@127.0.0.1:9999");
|
||||
var proxy = new RunnerWebProxy();
|
||||
|
||||
Assert.Null(proxy.GetProxy(new Uri("http://github.com")));
|
||||
Assert.Null(proxy.GetProxy(new Uri("http://example.com:444")));
|
||||
|
||||
Assert.Equal("http://user2:pass2%40@127.0.0.1:9999/", proxy.GetProxy(new Uri("https://something.com")).AbsoluteUri);
|
||||
Assert.Equal("http://user2:pass2%40@127.0.0.1:9999/", proxy.GetProxy(new Uri("https://www.something2.com")).AbsoluteUri);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanProxyEnv();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Common")]
|
||||
public void WebProxyFromEnvironmentVariablesGetProxyEmptyHttpsProxy()
|
||||
{
|
||||
try
|
||||
{
|
||||
Environment.SetEnvironmentVariable("http_proxy", "http://user1:pass1%40@127.0.0.1:8888");
|
||||
var proxy = new RunnerWebProxy();
|
||||
|
||||
Assert.Null(proxy.GetProxy(new Uri("https://github.com/owner/repo")));
|
||||
Assert.Null(proxy.GetProxy(new Uri("https://mails.google.com")));
|
||||
|
||||
Assert.Equal("http://user1:pass1%40@127.0.0.1:8888/", proxy.GetProxy(new Uri("http://something.com")).AbsoluteUri);
|
||||
Assert.Equal("http://user1:pass1%40@127.0.0.1:8888/", proxy.GetProxy(new Uri("http://www.something2.com")).AbsoluteUri);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanProxyEnv();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Common")]
|
||||
public void WebProxyFromEnvironmentVariablesNoProxy()
|
||||
{
|
||||
try
|
||||
{
|
||||
Environment.SetEnvironmentVariable("http_proxy", "http://user1:pass1%40@127.0.0.1:8888");
|
||||
Environment.SetEnvironmentVariable("https_proxy", "http://user2:pass2%40@127.0.0.1:9999");
|
||||
Environment.SetEnvironmentVariable("no_proxy", "github.com, .google.com, example.com:444, 192.168.0.123:123, 192.168.1.123");
|
||||
var proxy = new RunnerWebProxy();
|
||||
|
||||
Assert.False(proxy.IsBypassed(new Uri("https://actions.com")));
|
||||
Assert.False(proxy.IsBypassed(new Uri("https://ggithub.com")));
|
||||
Assert.False(proxy.IsBypassed(new Uri("https://github.comm")));
|
||||
Assert.False(proxy.IsBypassed(new Uri("https://google.com")));
|
||||
Assert.False(proxy.IsBypassed(new Uri("https://example.com")));
|
||||
Assert.False(proxy.IsBypassed(new Uri("http://example.com:333")));
|
||||
Assert.False(proxy.IsBypassed(new Uri("http://192.168.0.123:123")));
|
||||
Assert.False(proxy.IsBypassed(new Uri("http://192.168.1.123/home")));
|
||||
|
||||
Assert.True(proxy.IsBypassed(new Uri("https://github.com")));
|
||||
Assert.True(proxy.IsBypassed(new Uri("https://GITHUB.COM")));
|
||||
Assert.True(proxy.IsBypassed(new Uri("https://github.com/owner/repo")));
|
||||
Assert.True(proxy.IsBypassed(new Uri("https://actions.github.com")));
|
||||
Assert.True(proxy.IsBypassed(new Uri("https://mails.google.com")));
|
||||
Assert.True(proxy.IsBypassed(new Uri("https://MAILS.GOOGLE.com")));
|
||||
Assert.True(proxy.IsBypassed(new Uri("https://mails.v2.google.com")));
|
||||
Assert.True(proxy.IsBypassed(new Uri("http://mails.v2.v3.google.com/inbox")));
|
||||
Assert.True(proxy.IsBypassed(new Uri("https://example.com:444")));
|
||||
Assert.True(proxy.IsBypassed(new Uri("http://example.com:444")));
|
||||
Assert.True(proxy.IsBypassed(new Uri("http://example.COM:444")));
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanProxyEnv();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Common")]
|
||||
public void WebProxyFromEnvironmentVariablesGetProxy()
|
||||
{
|
||||
try
|
||||
{
|
||||
Environment.SetEnvironmentVariable("http_proxy", "http://user1:pass1%40@127.0.0.1:8888");
|
||||
Environment.SetEnvironmentVariable("https_proxy", "http://user2:pass2%40@127.0.0.1:9999");
|
||||
Environment.SetEnvironmentVariable("no_proxy", "github.com, .google.com, example.com:444");
|
||||
var proxy = new RunnerWebProxy();
|
||||
|
||||
Assert.Null(proxy.GetProxy(new Uri("http://github.com")));
|
||||
Assert.Null(proxy.GetProxy(new Uri("https://github.com/owner/repo")));
|
||||
Assert.Null(proxy.GetProxy(new Uri("https://mails.google.com")));
|
||||
Assert.Null(proxy.GetProxy(new Uri("http://example.com:444")));
|
||||
|
||||
|
||||
Assert.Equal("http://user1:pass1%40@127.0.0.1:8888/", proxy.GetProxy(new Uri("http://something.com")).AbsoluteUri);
|
||||
Assert.Equal("http://user1:pass1%40@127.0.0.1:8888/", proxy.GetProxy(new Uri("http://www.something2.com")).AbsoluteUri);
|
||||
|
||||
Assert.Equal("http://user2:pass2%40@127.0.0.1:9999/", proxy.GetProxy(new Uri("https://something.com")).AbsoluteUri);
|
||||
Assert.Equal("http://user2:pass2%40@127.0.0.1:9999/", proxy.GetProxy(new Uri("https://www.something2.com")).AbsoluteUri);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CleanProxyEnv();
|
||||
}
|
||||
}
|
||||
|
||||
private void CleanProxyEnv()
|
||||
{
|
||||
Environment.SetEnvironmentVariable("http_proxy", null);
|
||||
Environment.SetEnvironmentVariable("https_proxy", null);
|
||||
Environment.SetEnvironmentVariable("HTTP_PROXY", null);
|
||||
Environment.SetEnvironmentVariable("HTTPS_PROXY", null);
|
||||
Environment.SetEnvironmentVariable("no_proxy", null);
|
||||
Environment.SetEnvironmentVariable("NO_PROXY", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,6 +90,8 @@ namespace GitHub.Runner.Common.Tests
|
||||
|
||||
public ProductInfoHeaderValue UserAgent => new ProductInfoHeaderValue("L0Test", "0.0");
|
||||
|
||||
public RunnerWebProxy WebProxy => new RunnerWebProxy();
|
||||
|
||||
public async Task Delay(TimeSpan delay, CancellationToken token)
|
||||
{
|
||||
await Task.Delay(TimeSpan.Zero);
|
||||
@@ -274,24 +276,6 @@ namespace GitHub.Runner.Common.Tests
|
||||
".certificates");
|
||||
break;
|
||||
|
||||
case WellKnownConfigFile.Proxy:
|
||||
path = Path.Combine(
|
||||
GetDirectory(WellKnownDirectory.Root),
|
||||
".proxy");
|
||||
break;
|
||||
|
||||
case WellKnownConfigFile.ProxyCredentials:
|
||||
path = Path.Combine(
|
||||
GetDirectory(WellKnownDirectory.Root),
|
||||
".proxycredentials");
|
||||
break;
|
||||
|
||||
case WellKnownConfigFile.ProxyBypass:
|
||||
path = Path.Combine(
|
||||
GetDirectory(WellKnownDirectory.Root),
|
||||
".proxybypass");
|
||||
break;
|
||||
|
||||
case WellKnownConfigFile.Options:
|
||||
path = Path.Combine(
|
||||
GetDirectory(WellKnownDirectory.Root),
|
||||
|
||||
@@ -1688,10 +1688,6 @@ runs:
|
||||
_hc.SetSingleton<IRunnerPluginManager>(_pluginManager.Object);
|
||||
_hc.SetSingleton<IActionManifestManager>(actionManifest);
|
||||
|
||||
var proxy = new RunnerWebProxy();
|
||||
proxy.Initialize(_hc);
|
||||
_hc.SetSingleton<IRunnerWebProxy>(proxy);
|
||||
|
||||
_configurationStore = new Mock<IConfigurationStore>();
|
||||
_configurationStore
|
||||
.Setup(x => x.GetSettings())
|
||||
|
||||
@@ -256,10 +256,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
configurationStore.Setup(x => x.GetSettings()).Returns(new RunnerSettings());
|
||||
hc.SetSingleton(configurationStore.Object);
|
||||
|
||||
// Arrange: Setup the proxy configation.
|
||||
var proxy = new Mock<IRunnerWebProxy>();
|
||||
hc.SetSingleton(proxy.Object);
|
||||
|
||||
// Arrange: Setup the cert configation.
|
||||
var cert = new Mock<IRunnerCertificateManager>();
|
||||
hc.SetSingleton(cert.Object);
|
||||
|
||||
@@ -22,7 +22,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
private CancellationTokenSource _tokenSource;
|
||||
private Mock<IJobServer> _jobServer;
|
||||
private Mock<IJobServerQueue> _jobServerQueue;
|
||||
private Mock<IRunnerWebProxy> _proxyConfig;
|
||||
private Mock<IRunnerCertificateManager> _cert;
|
||||
private Mock<IConfigurationStore> _config;
|
||||
private Mock<IExtensionManager> _extensions;
|
||||
@@ -43,7 +42,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_jobExtension = new Mock<IJobExtension>();
|
||||
_jobServer = new Mock<IJobServer>();
|
||||
_jobServerQueue = new Mock<IJobServerQueue>();
|
||||
_proxyConfig = new Mock<IRunnerWebProxy>();
|
||||
_cert = new Mock<IRunnerCertificateManager>();
|
||||
_stepRunner = new Mock<IStepsRunner>();
|
||||
_logger = new Mock<IPagingLogger>();
|
||||
@@ -95,9 +93,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_jobExtension.Setup(x => x.InitializeJob(It.IsAny<IExecutionContext>(), It.IsAny<Pipelines.AgentJobRequestMessage>())).
|
||||
Returns(Task.FromResult(_initResult));
|
||||
|
||||
_proxyConfig.Setup(x => x.ProxyAddress)
|
||||
.Returns(string.Empty);
|
||||
|
||||
var settings = new RunnerSettings
|
||||
{
|
||||
AgentId = 1,
|
||||
@@ -114,7 +109,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
hc.SetSingleton(_config.Object);
|
||||
hc.SetSingleton(_jobServer.Object);
|
||||
hc.SetSingleton(_jobServerQueue.Object);
|
||||
hc.SetSingleton(_proxyConfig.Object);
|
||||
hc.SetSingleton(_cert.Object);
|
||||
hc.SetSingleton(_stepRunner.Object);
|
||||
hc.SetSingleton(_extensions.Object);
|
||||
|
||||
@@ -16,14 +16,12 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
private Mock<IProcessChannel> _processChannel;
|
||||
private Mock<IJobRunner> _jobRunner;
|
||||
private Mock<IRunnerWebProxy> _proxy;
|
||||
private Mock<IRunnerCertificateManager> _cert;
|
||||
|
||||
public WorkerL0()
|
||||
{
|
||||
_processChannel = new Mock<IProcessChannel>();
|
||||
_jobRunner = new Mock<IJobRunner>();
|
||||
_proxy = new Mock<IRunnerWebProxy>();
|
||||
_cert = new Mock<IRunnerCertificateManager>();
|
||||
}
|
||||
|
||||
@@ -92,7 +90,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var worker = new GitHub.Runner.Worker.Worker();
|
||||
hc.EnqueueInstance<IProcessChannel>(_processChannel.Object);
|
||||
hc.EnqueueInstance<IJobRunner>(_jobRunner.Object);
|
||||
hc.SetSingleton<IRunnerWebProxy>(_proxy.Object);
|
||||
hc.SetSingleton<IRunnerCertificateManager>(_cert.Object);
|
||||
worker.Initialize(hc);
|
||||
var jobMessage = CreateJobRequestMessage("job1");
|
||||
@@ -145,7 +142,6 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
var worker = new GitHub.Runner.Worker.Worker();
|
||||
hc.EnqueueInstance<IProcessChannel>(_processChannel.Object);
|
||||
hc.EnqueueInstance<IJobRunner>(_jobRunner.Object);
|
||||
hc.SetSingleton<IRunnerWebProxy>(_proxy.Object);
|
||||
hc.SetSingleton<IRunnerCertificateManager>(_cert.Object);
|
||||
worker.Initialize(hc);
|
||||
var jobMessage = CreateJobRequestMessage("job1");
|
||||
|
||||
Reference in New Issue
Block a user