From 8f14466cbbc58f1b2e5520374e418fd85834dcb0 Mon Sep 17 00:00:00 2001 From: Tingluo Huang Date: Sat, 30 Jan 2021 22:35:45 -0500 Subject: [PATCH] Add http POST to --check. (#949) * Add http POST to --check. * feedback. --- docs/checks/actions.md | 1 + docs/checks/network.md | 3 + src/Runner.Listener/Checks/ActionsCheck.cs | 9 ++- src/Runner.Listener/Checks/CheckUtil.cs | 82 +++++++++++++++++++-- src/Runner.Listener/Checks/InternetCheck.cs | 2 +- 5 files changed, 85 insertions(+), 12 deletions(-) diff --git a/docs/checks/actions.md b/docs/checks/actions.md index a855bac1e..a67e41883 100644 --- a/docs/checks/actions.md +++ b/docs/checks/actions.md @@ -27,6 +27,7 @@ Make sure the runner has access to actions service for GitHub.com or GitHub Ente - DNS lookup for pipelines.actions.githubusercontent.com using dotnet - Ping pipelines.actions.githubusercontent.com using dotnet - Make HTTP GET to https://pipelines.actions.githubusercontent.com/_apis/health or https://myGHES.com/_services/pipelines/_apis/health using dotnet, check response headers contains `x-vss-e2eid` +- Make HTTP POST to https://pipelines.actions.githubusercontent.com/_apis/health or https://myGHES.com/_services/pipelines/_apis/health using dotnet, check response headers contains `x-vss-e2eid` ## How to fix the issue? diff --git a/docs/checks/network.md b/docs/checks/network.md index 49738a37f..1cf74b378 100644 --- a/docs/checks/network.md +++ b/docs/checks/network.md @@ -10,6 +10,8 @@ - Proxy try to decrypt and exam HTTPS traffic for security purpose but cause the actions-runner to fail to finish SSL handshake due to the lack of trusting proxy's CA. +- Proxy try to modify the HTTPS request (like add or change some http headers) and causes the request become incompatible with the Actions Service (ASP.NetCore), Ex: [Nginx](https://github.com/dotnet/aspnetcore/issues/17081) + - Firewall rules that block action runner from accessing certain hosts, ex: `*.github.com`, `*.actions.githubusercontent.com`, etc. @@ -21,6 +23,7 @@ Use a 3rd party tool to make the same requests as the runner did would be a good - Use `nslookup` to check DNS - Use `ping` to check Ping +- Use `traceroute`, `tracepath`, or `tracert` to check the network route between the runner and the Actions service - Use `curl -v` to check the network stack, good for verifying default certificate/proxy settings. - Use `Invoke-WebRequest` from `pwsh` (`PowerShell Core`) to check the dotnet network stack, good for verifying bugs in the dotnet framework. diff --git a/src/Runner.Listener/Checks/ActionsCheck.cs b/src/Runner.Listener/Checks/ActionsCheck.cs index 931b7f2e9..d489d7ccf 100644 --- a/src/Runner.Listener/Checks/ActionsCheck.cs +++ b/src/Runner.Listener/Checks/ActionsCheck.cs @@ -61,17 +61,20 @@ namespace GitHub.Runner.Listener.Check // check github api checkTasks.Add(CheckUtil.CheckDns(githubApiUrl)); checkTasks.Add(CheckUtil.CheckPing(githubApiUrl)); - checkTasks.Add(HostContext.CheckHttpsRequests(githubApiUrl, pat, expectedHeader: "X-GitHub-Request-Id")); + checkTasks.Add(HostContext.CheckHttpsGetRequests(githubApiUrl, pat, expectedHeader: "X-GitHub-Request-Id")); // check actions token service checkTasks.Add(CheckUtil.CheckDns(actionsTokenServiceUrl)); checkTasks.Add(CheckUtil.CheckPing(actionsTokenServiceUrl)); - checkTasks.Add(HostContext.CheckHttpsRequests(actionsTokenServiceUrl, pat, expectedHeader: "x-vss-e2eid")); + checkTasks.Add(HostContext.CheckHttpsGetRequests(actionsTokenServiceUrl, pat, expectedHeader: "x-vss-e2eid")); // check actions pipelines service checkTasks.Add(CheckUtil.CheckDns(actionsPipelinesServiceUrl)); checkTasks.Add(CheckUtil.CheckPing(actionsPipelinesServiceUrl)); - checkTasks.Add(HostContext.CheckHttpsRequests(actionsPipelinesServiceUrl, pat, expectedHeader: "x-vss-e2eid")); + checkTasks.Add(HostContext.CheckHttpsGetRequests(actionsPipelinesServiceUrl, pat, expectedHeader: "x-vss-e2eid")); + + // check HTTP POST to actions pipelines service + checkTasks.Add(HostContext.CheckHttpsPostRequests(actionsPipelinesServiceUrl, pat, expectedHeader: "x-vss-e2eid")); var result = true; while (checkTasks.Count > 0) diff --git a/src/Runner.Listener/Checks/CheckUtil.cs b/src/Runner.Listener/Checks/CheckUtil.cs index 8b4f1a564..ee2e824f3 100644 --- a/src/Runner.Listener/Checks/CheckUtil.cs +++ b/src/Runner.Listener/Checks/CheckUtil.cs @@ -117,14 +117,14 @@ namespace GitHub.Runner.Listener.Check return result; } - public static async Task CheckHttpsRequests(this IHostContext hostContext, string url, string pat, string expectedHeader) + public static async Task CheckHttpsGetRequests(this IHostContext hostContext, string url, string pat, string expectedHeader) { var result = new CheckResult(); try { result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************"); result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** ****"); - result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** Send HTTPS Request to {url} "); + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** Send HTTPS Request (GET) to {url} "); result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** ****"); result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************"); using (var _ = new HttpEventSourceListener(result.Logs)) @@ -159,7 +159,7 @@ namespace GitHub.Runner.Listener.Check { result.Pass = false; result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************"); - result.Logs.Add($"{DateTime.UtcNow.ToString("O")} Http request 'GET' to {url} succeed but doesn't have expected HTTP Header."); + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} Http request 'GET' to {url} succeed but doesn't have expected HTTP response Header '{expectedHeader}'."); result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************"); result.Logs.Add($"{DateTime.UtcNow.ToString("O")} "); result.Logs.Add($"{DateTime.UtcNow.ToString("O")} "); @@ -189,6 +189,67 @@ namespace GitHub.Runner.Listener.Check return result; } + public static async Task CheckHttpsPostRequests(this IHostContext hostContext, string url, string pat, string expectedHeader) + { + var result = new CheckResult(); + try + { + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************"); + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** ****"); + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** Send HTTPS Request (POST) to {url} "); + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** ****"); + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************"); + using (var _ = new HttpEventSourceListener(result.Logs)) + using (var httpClientHandler = hostContext.CreateHttpClientHandler()) + using (var httpClient = new HttpClient(httpClientHandler)) + { + httpClient.DefaultRequestHeaders.UserAgent.AddRange(hostContext.UserAgents); + if (!string.IsNullOrEmpty(pat)) + { + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("token", pat); + } + + // Send empty JSON '{}' to service + var response = await httpClient.PostAsJsonAsync>(url, new Dictionary()); + + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} Http status code: {response.StatusCode}"); + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} Http response headers: {response.Headers}"); + + var responseContent = await response.Content.ReadAsStringAsync(); + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} Http response body: {responseContent}"); + if (response.Headers.Contains(expectedHeader)) + { + result.Pass = true; + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************"); + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} Http request 'POST' to {url} has expected HTTP response header"); + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************"); + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} "); + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} "); + } + else + { + result.Pass = false; + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************"); + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} Http request 'POST' to {url} doesn't have expected HTTP response Header '{expectedHeader}'."); + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************"); + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} "); + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} "); + } + } + } + catch (Exception ex) + { + result.Pass = false; + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************"); + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** ****"); + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** Https request 'POST' to {url} failed with error: {ex}"); + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} **** ****"); + result.Logs.Add($"{DateTime.UtcNow.ToString("O")} ***************************************************************************************************************"); + } + + return result; + } + public static async Task DownloadExtraCA(this IHostContext hostContext, string url, string pat) { var result = new CheckResult(); @@ -289,18 +350,23 @@ namespace GitHub.Runner.Listener.Check private readonly Dictionary> _ignoredEvent = new Dictionary> { { - "Private.InternalDiagnostics.System.Net.Http", + "Microsoft-System-Net-Http", new HashSet { "Info", - "Associate" + "Associate", + "Enter", + "Exit" } }, { - "Private.InternalDiagnostics.System.Net.Security", + "Microsoft-System-Net-Security", new HashSet { + "Enter", + "Exit", "Info", + "DumpBuffer", "SslStreamCtor", "SecureChannelCtor", "NoDelegateNoClientCert", @@ -324,8 +390,8 @@ namespace GitHub.Runner.Listener.Check { base.OnEventSourceCreated(eventSource); - if (eventSource.Name == "Private.InternalDiagnostics.System.Net.Http" || - eventSource.Name == "Private.InternalDiagnostics.System.Net.Security") + if (eventSource.Name == "Microsoft-System-Net-Http" || + eventSource.Name == "Microsoft-System-Net-Security") { EnableEvents(eventSource, EventLevel.Verbose, EventKeywords.All); } diff --git a/src/Runner.Listener/Checks/InternetCheck.cs b/src/Runner.Listener/Checks/InternetCheck.cs index e9e98c51b..7933a8b37 100644 --- a/src/Runner.Listener/Checks/InternetCheck.cs +++ b/src/Runner.Listener/Checks/InternetCheck.cs @@ -40,7 +40,7 @@ namespace GitHub.Runner.Listener.Check checkTasks.Add(CheckUtil.CheckPing("https://api.github.com")); // We don't need to pass a PAT since it might be a token for GHES. - checkTasks.Add(HostContext.CheckHttpsRequests("https://api.github.com", pat: null, expectedHeader: "X-GitHub-Request-Id")); + checkTasks.Add(HostContext.CheckHttpsGetRequests("https://api.github.com", pat: null, expectedHeader: "X-GitHub-Request-Id")); var result = true; while (checkTasks.Count > 0)