Skip to content

Commit

Permalink
improved outbound test (cert details), removed default swagger ui, fi…
Browse files Browse the repository at this point in the history
…xed readme
  • Loading branch information
vjirovsky committed Jul 4, 2022
1 parent 7e91120 commit 332922d
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 66 deletions.
5 changes: 2 additions & 3 deletions AzFappDebugger.csproj
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DnsClient" Version="1.6.1" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.OpenApi" Version="1.0.0" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.0.1" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.1.1" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
Expand Down
41 changes: 41 additions & 0 deletions HtmlBrandingHelper.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Authentication;
using System.Text;
using System.Threading.Tasks;

Expand Down Expand Up @@ -80,7 +81,47 @@ internal static string GetBootstrapWhatItMeans(string uniqueId, string text, boo
$"<div class='" + (isActive ? "" : "collapse") + $"' id='whatItMeans{uniqueId}'><div class='callout callout-info'>{text}</div></div>";
}

internal static string NormalizeLength(string value, int maxLength)
{
return value.Length <= maxLength ? value : value.Substring(0, maxLength) + "...";
}

internal static string NiceTlsProtocol(SslProtocols protocol)
{
switch (protocol)
{
default:
return protocol.ToString();
break;

case SslProtocols.None:
return "<<none>>";
break;

case SslProtocols.Ssl2:
return "SSL 2.0";
break;
case SslProtocols.Ssl3:
return "SSL 3.0";
break;
case SslProtocols.Tls:
return "TLS 1.0";
break;
case SslProtocols.Default:
return "SSL 3.0/TLS 1.0";
break;
case SslProtocols.Tls11:
return "TLS 1.1";
break;
case SslProtocols.Tls12:
return "TLS 1.2";
break;
case SslProtocols.Tls13:
return "TLS 1.3";
break;

}
}

}
}
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ The tool is released under the MIT License, see LICENSE file.

## Usage

Just install this codebase as package to given Azure FunctionApp.
Just install this codebase as package to given Azure FunctionApp and visit `<<url>>/RunTests`

To control tool set Configuration variables:<br>
`TEST_DNS_RESOLVE_DOMAINS` = <em>`<<comma-delimited list of domains>>`</em> - performs resolutions of given domains via all available DNS servers<br>
For tool configuration set following Configuration variables:<br>
- `TEST_DNS_RESOLVE_DOMAINS` = <em>`<<comma-delimited list of domains>>`</em> - performs resolutions of given domains via all available DNS servers<br>
<em>e.g. `TEST_DNS_RESOLVE_DOMAINS` = `azure.com,someonpremresource.contoso.internal,vjirovsky.cz` </em>

`TEST_HTTPCLIENT_GET_URL` = <em>`<<url to test>>`</em> - performs HTTP request to given URL via same outbound connectivity configuration as the real application will use<br>
- `TEST_HTTPCLIENT_GET_URL` = <em>`<<url to test>>`</em> - performs HTTP request to given URL via same outbound connectivity configuration as the real application will use<br>
<em> e.g. `TEST_DNS_RESOLVE_DOMAINS` = `https://vjirovsky.cz` </em>


Expand Down
15 changes: 7 additions & 8 deletions RunTestsFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,11 @@ namespace AzFappDebugger
{
public static class RunTestsFunction
{
private static HttpClient _httpClient = null;
private static HttpClient _defaultHttpClient = null;
private static HttpClientHandler _defaultHttpClientHandler = null;

private static IDictionary<string, string> _environmentVariablesDictionary = new Dictionary<string, string>();



static RunTestsFunction()
{

Expand All @@ -36,12 +35,12 @@ static RunTestsFunction()
}



// ignore SSL cert errors for this debugger (e.g. DPI, proxy, etc.)
var httpClientHandler = new HttpClientHandler();
_defaultHttpClientHandler = new HttpClientHandler();

_defaultHttpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) => {return true;};

httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) =>{return true;};
_httpClient = new HttpClient(httpClientHandler);
_defaultHttpClient = new HttpClient(_defaultHttpClientHandler);

}

Expand All @@ -58,7 +57,7 @@ public static async Task<IActionResult> Run(


var dnsTestsProvider = new DnsTests(_environmentVariablesDictionary);
var outboundConnectivityTestsProvider = new OutboundConnectivityTests(_environmentVariablesDictionary, _httpClient);
var outboundConnectivityTestsProvider = new OutboundConnectivityTests(_environmentVariablesDictionary, _defaultHttpClient);
var overviewTestsProvider = new OverviewTests(_environmentVariablesDictionary);
var contentStorageAccessTestsProvider = new ContentStorageAccessTests(_environmentVariablesDictionary);

Expand Down
142 changes: 91 additions & 51 deletions Tests/OutboundConnectivityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using System.Web;
Expand All @@ -13,15 +16,14 @@ namespace AzFappDebugger.Tests
{
internal class OutboundConnectivityTests
{
private static IDictionary<string, string> _environmentVariablesDictionary = null;
private IDictionary<string, string> _environmentVariablesDictionary = null;
private HttpClient _defaultHttpClient;

private HttpClient _httpClient = null;

internal OutboundConnectivityTests(IDictionary<string, string> environmentVariablesDictionary, HttpClient httpClient)
internal OutboundConnectivityTests(IDictionary<string, string> environmentVariablesDictionary, HttpClient defaultHttpClient)
{

_environmentVariablesDictionary = environmentVariablesDictionary;
_httpClient = httpClient;
_defaultHttpClient = defaultHttpClient;
}


Expand All @@ -44,9 +46,10 @@ internal async Task<string> RunAllTestsAsHtmlOutputAsync()
{
output += HtmlBrandingHelper.GetStandardTableRow("Integrated vNET", $"<code>{configItemValue}</code>");
}
else {
else
{
output += HtmlBrandingHelper.GetStandardTableRow("Integrated vNET", $"<span class=\"badge text-bg-danger\">No vNET integration</span><br>" +
$"The application is not integrated with any vNET.");
$"The application is not integrated with any vNET.");
}


Expand All @@ -62,71 +65,108 @@ internal async Task<string> RunAllTestsAsHtmlOutputAsync()

output += $"<h3>Connectivity tests</h3>";

if (_httpClient != null)
{
string httpClientUrlToResolve = string.Empty;

string httpClientUrlToResolve = string.Empty;

output += $"<h4>HttpClient test</h4> <small>";
output += HtmlBrandingHelper.GetBootstrapWhatItMeans("OutboundConnectivityHttpClient",
$"<p>This test performs HTTP request via outbound connectivity of the application to hostname specified in <code>{Constants.TEST_HTTPCLIENT_RESOLVE_URL_VARIABLE}</code> configuration variable.</p>", false, false, "Test description");
output += "</small>";
output += $"<h4>HttpClient test</h4> <small>";
output += HtmlBrandingHelper.GetBootstrapWhatItMeans("OutboundConnectivityHttpClient",
$"<p>This test performs HTTP request via outbound connectivity of the application to hostname specified in <code>{Constants.TEST_HTTPCLIENT_RESOLVE_URL_VARIABLE}</code> configuration variable.</p>", false, false, "Test description");
output += "</small>";


_environmentVariablesDictionary.TryGetValue(Constants.TEST_HTTPCLIENT_RESOLVE_URL_VARIABLE, out httpClientUrlToResolve);
_environmentVariablesDictionary.TryGetValue(Constants.TEST_HTTPCLIENT_RESOLVE_URL_VARIABLE, out httpClientUrlToResolve);

if (!string.IsNullOrWhiteSpace(httpClientUrlToResolve))
if (!string.IsNullOrWhiteSpace(httpClientUrlToResolve) && _defaultHttpClient != null)
{
output += "<table class=\"table\">";
string testResult = "";
try
{
output += "<table class=\"table\">";
string testResult = "";
try
{
var content = await _httpClient.GetStringAsync(httpClientUrlToResolve);
testResult = $"<span class=\"badge text-bg-success\">OK</span><br><code>{HttpUtility.HtmlEncode(content)}</code>";
}
catch (Exception e)
{
testResult = "<span class=\"badge text-bg-danger\">QUERY FAILED</span><br> " + e.Message +"<br>" + (e.InnerException != null ? e.InnerException.Message : "");
}
finally
{
output += HtmlBrandingHelper.GetStandardTableRow($"{httpClientUrlToResolve}", testResult);
}
var httpClientUrlToResolveUri = new Uri(httpClientUrlToResolve);
var content = await _defaultHttpClient.GetAsync(httpClientUrlToResolveUri);
content.EnsureSuccessStatusCode();
string text = await content.Content.ReadAsStringAsync();

output += "</table>";
}

testResult = $"<span class=\"badge text-bg-success\">OK</span>&nbsp; {(int)content.StatusCode} {content.StatusCode}<br><br>";

if (!string.IsNullOrWhiteSpace(Constants.MY_IP_ADDRESS_EXTERNAL_SERVICE_URL))
{
output += $"<h4>My outbound IP address</h4> <small>";
output += HtmlBrandingHelper.GetBootstrapWhatItMeans("OutboundConnectivityExternalIp",
$"<p>This test performs a HTTP request to external service, which returns a IP address of HTTP request.</p>", false, false, "Test description");
output += "</small>";

output += "<table class=\"table\">";
string testResult = "";
testResult += $"<strong>Connection details</strong><br>" +
$"Protocol: <code>HTTP {content.Version}</code><br>";
try
{
var content = await _httpClient.GetStringAsync(Constants.MY_IP_ADDRESS_EXTERNAL_SERVICE_URL);
testResult = $"<code>{HttpUtility.HtmlEncode(content)}</code>";
}
catch (Exception e)
{
testResult = "<span class=\"badge text-bg-danger\">QUERY FAILED</span><br> " + e.InnerException.Message;
RemoteCertificateValidationCallback certCallback = (_, _, _, _) => true;
using var client = new TcpClient(httpClientUrlToResolveUri.Host, httpClientUrlToResolveUri.Port);
using var sslStream = new SslStream(client.GetStream(), true, certCallback);
await sslStream.AuthenticateAsClientAsync(httpClientUrlToResolveUri.Host);
var serverCertificate = sslStream.RemoteCertificate;
var certificate = new X509Certificate2(serverCertificate);

if (certificate != null)
{
testResult += $"Security protocol: <code>{HtmlBrandingHelper.NiceTlsProtocol(sslStream.SslProtocol)}</code><br>" +
$"Negotiated cipher: <code>{sslStream.NegotiatedCipherSuite}</code><br><br>";

testResult += $"<strong>Certificate</strong><br>" +
$"Subject: <code>{certificate.Subject}</code><br>" +
$"Issuer: <code>{certificate.Issuer}</code><br>" +
$"Valid from: <code>{certificate.GetEffectiveDateString()}</code><br>" +
$"Expires on: <code>{certificate.GetExpirationDateString()}</code><br>" +
$"Thumbprint: <code>{certificate.Thumbprint}</code><br><br>"
;
}
}
finally
catch (Exception ee)
{
output += HtmlBrandingHelper.GetStandardTableRow($"My IP address", testResult);
testResult += $"<i>Connection is not secured</i><br><br>";

}

output += "</table>";
testResult += $"<strong>Body:</strong><br>" +
$"<code>{HttpUtility.HtmlEncode(HtmlBrandingHelper.NormalizeLength(text, 1000))}</code>";
}
catch (Exception e)
{
testResult = "<span class=\"badge text-bg-danger\">QUERY FAILED</span><br> " + e.Message + "<br>" + (e.InnerException != null ? e.InnerException.Message : "");
}
finally
{
output += HtmlBrandingHelper.GetStandardTableRow($"{httpClientUrlToResolve}", testResult);
}


output += "</table>";
}
else
{
output += $"<div class='callout callout-warning'>No valid <code>{Constants.TEST_HTTPCLIENT_RESOLVE_URL_VARIABLE}</code> variable defined, test has been skipped.</div>";
}


if (!string.IsNullOrWhiteSpace(Constants.MY_IP_ADDRESS_EXTERNAL_SERVICE_URL) && _defaultHttpClient != null)
{
output += $"<h4>My outbound IP address</h4> <small>";
output += HtmlBrandingHelper.GetBootstrapWhatItMeans("OutboundConnectivityExternalIp",
$"<p>This test performs a HTTP request to external service, which returns a IP address of HTTP request.</p>", false, false, "Test description");
output += "</small>";

output += "<table class=\"table\">";
string testResult = "";
try
{
var content = await _defaultHttpClient.GetStringAsync(Constants.MY_IP_ADDRESS_EXTERNAL_SERVICE_URL);
testResult = $"<code>{HttpUtility.HtmlEncode(content)}</code>";
}
catch (Exception e)
{
testResult = "<span class=\"badge text-bg-danger\">QUERY FAILED</span><br> " + e.InnerException.Message;
}
finally
{
output += HtmlBrandingHelper.GetStandardTableRow($"My IP address", testResult);
}

output += "</table>";
}

return output;
}
Expand Down

0 comments on commit 332922d

Please sign in to comment.