From b7c4dadb6f808aebb25fe878fa22d57a71e67a6d Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Mon, 10 Jul 2023 10:44:35 +0200 Subject: [PATCH] AutoWire detected proxies during setup (#59) --- .../Elastic.Ephemeral.Example.csproj | 6 +- examples/Elastic.Ephemeral.Example/Program.cs | 19 ++++++ examples/ScratchPad/Program.cs | 2 +- examples/ScratchPad/ValidateCombinations.cs | 2 +- .../EphemeralCluster.cs | 10 +++- .../EphemeralClusterConfiguration.cs | 10 +++- .../Tasks/IClusterComposeTask.cs | 58 ++++++++++--------- .../InstallationTasks/PrintConfiguration.cs | 2 +- .../ClusterBase.cs | 17 ++++++ .../Configuration/ClusterConfiguration.cs | 1 + .../DetectedProxySoftware.cs | 12 ++++ 11 files changed, 102 insertions(+), 37 deletions(-) create mode 100644 src/Elastic.Elasticsearch.Managed/DetectedProxySoftware.cs diff --git a/examples/Elastic.Ephemeral.Example/Elastic.Ephemeral.Example.csproj b/examples/Elastic.Ephemeral.Example/Elastic.Ephemeral.Example.csproj index 3745a2f..bc13e1f 100644 --- a/examples/Elastic.Ephemeral.Example/Elastic.Ephemeral.Example.csproj +++ b/examples/Elastic.Ephemeral.Example/Elastic.Ephemeral.Example.csproj @@ -9,7 +9,11 @@ - + + + + + diff --git a/examples/Elastic.Ephemeral.Example/Program.cs b/examples/Elastic.Ephemeral.Example/Program.cs index 5d03b9b..efd2487 100644 --- a/examples/Elastic.Ephemeral.Example/Program.cs +++ b/examples/Elastic.Ephemeral.Example/Program.cs @@ -3,7 +3,12 @@ // See the LICENSE file in the project root for more information using Elastic.Elasticsearch.Ephemeral; +using Elastic.Elasticsearch.Managed; +using Elastic.Transport; +using Elastic.Transport.Products.Elasticsearch; +using static Elastic.Elasticsearch.Ephemeral.ClusterAuthentication; using static Elastic.Elasticsearch.Ephemeral.ClusterFeatures; +using HttpMethod = Elastic.Transport.HttpMethod; var config = new EphemeralClusterConfiguration("8.7.0", XPack | Security | SSL); @@ -16,4 +21,18 @@ exitEvent.Set(); }; using var started = cluster.Start(); + +var pool = new StaticNodePool(cluster.NodesUris()); +var transportConfig = new TransportConfiguration(pool, productRegistration: ElasticsearchProductRegistration.Default) + .Authentication(new BasicAuthentication(Admin.Username, Admin.Password)) + .ServerCertificateValidationCallback(CertificateValidations.AllowAll); +if (cluster.DetectedProxy != DetectedProxySoftware.None) + transportConfig = transportConfig.Proxy(new Uri("http://localhost:8080"), null!, null!); + +var transport = new DefaultHttpTransport(transportConfig); + +var response = await transport.RequestAsync(HttpMethod.GET, "/"); +Console.WriteLine(response); + + exitEvent.WaitOne(); diff --git a/examples/ScratchPad/Program.cs b/examples/ScratchPad/Program.cs index aa5b2e4..5ff29df 100644 --- a/examples/ScratchPad/Program.cs +++ b/examples/ScratchPad/Program.cs @@ -39,7 +39,7 @@ private static void ManualConfigRun() var features = Security | XPack | SSL; var config = new EphemeralClusterConfiguration(version, features, plugins, 1) { - HttpFiddlerAware = true, + AutoWireKnownProxies = true, ShowElasticsearchOutputAfterStarted = true, CacheEsHomeInstallation = false, TrialMode = XPackTrialMode.Trial, diff --git a/examples/ScratchPad/ValidateCombinations.cs b/examples/ScratchPad/ValidateCombinations.cs index d29ed92..acb3c78 100644 --- a/examples/ScratchPad/ValidateCombinations.cs +++ b/examples/ScratchPad/ValidateCombinations.cs @@ -38,7 +38,7 @@ public static void Run() Console.WriteLine($"{v} {f}"); Console.ForegroundColor = reset; - var config = new EphemeralClusterConfiguration(v, f, plugins, 1) {HttpFiddlerAware = true,}; + var config = new EphemeralClusterConfiguration(v, f, plugins, 1) {AutoWireKnownProxies = true,}; using (var cluster = new EphemeralCluster(config)) try diff --git a/src/Elastic.Elasticsearch.Ephemeral/EphemeralCluster.cs b/src/Elastic.Elasticsearch.Ephemeral/EphemeralCluster.cs index 5a84f5d..4f64e16 100644 --- a/src/Elastic.Elasticsearch.Ephemeral/EphemeralCluster.cs +++ b/src/Elastic.Elasticsearch.Ephemeral/EphemeralCluster.cs @@ -37,9 +37,13 @@ protected EphemeralCluster(TConfiguration clusterConfiguration) : base(clusterCo public virtual ICollection NodesUris(string hostName = null) { - hostName = hostName ?? (ClusterConfiguration.HttpFiddlerAware && Process.GetProcessesByName("fiddler").Any() - ? "ipv4.fiddler" - : "localhost"); + hostName ??= "localhost"; + if (hostName == "localhost" && ClusterConfiguration.AutoWireKnownProxies) + { + if (DetectedProxy == DetectedProxySoftware.Fiddler) + hostName = "ipv4.fiddler"; //magic reverse proxy address for fiddler + } + var ssl = ClusterConfiguration.EnableSsl ? "s" : ""; return Nodes .Select(n => $"http{ssl}://{hostName}:{n.Port ?? 9200}") diff --git a/src/Elastic.Elasticsearch.Ephemeral/EphemeralClusterConfiguration.cs b/src/Elastic.Elasticsearch.Ephemeral/EphemeralClusterConfiguration.cs index 87d15cb..a81dabe 100644 --- a/src/Elastic.Elasticsearch.Ephemeral/EphemeralClusterConfiguration.cs +++ b/src/Elastic.Elasticsearch.Ephemeral/EphemeralClusterConfiguration.cs @@ -89,8 +89,14 @@ public EphemeralClusterConfiguration(ElasticVersion version, ClusterFeatures fea /// public XPackTrialMode TrialMode { get; set; } - /// Bootstrapping HTTP calls should attempt to auto route traffic through fiddler if its running - public bool HttpFiddlerAware { get; set; } + /// + /// Bootstrapping HTTP calls should attempt to auto route traffic through known proxy software if they are running + /// + /// Fiddler, typically on Windows + /// mitmproxy, typically on non Windows OS's + /// + /// + public bool AutoWireKnownProxies { get; set; } protected virtual string NodePrefix => "ephemeral"; diff --git a/src/Elastic.Elasticsearch.Ephemeral/Tasks/IClusterComposeTask.cs b/src/Elastic.Elasticsearch.Ephemeral/Tasks/IClusterComposeTask.cs index ebfdbf6..536c22d 100644 --- a/src/Elastic.Elasticsearch.Ephemeral/Tasks/IClusterComposeTask.cs +++ b/src/Elastic.Elasticsearch.Ephemeral/Tasks/IClusterComposeTask.cs @@ -16,6 +16,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Elastic.Elasticsearch.Managed; using Elastic.Elasticsearch.Managed.ConsoleWriters; using ICSharpCode.SharpZipLib.GZip; using ICSharpCode.SharpZipLib.Tar; @@ -87,6 +88,9 @@ private HttpResponseMessage Call( AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip | DecompressionMethods.None, }; + if (cluster.DetectedProxy != DetectedProxySoftware.None) + handler.Proxy = new WebProxy { Address = new Uri("http://localhost:8080") }; + cluster.Writer.WriteDiagnostic( $"{{{nameof(Call)}}} [{statusUrl}] SSL: {cluster.ClusterConfiguration.EnableSsl} Security: {cluster.ClusterConfiguration.EnableSecurity}"); if (cluster.ClusterConfiguration.EnableSsl) @@ -98,39 +102,37 @@ private HttpResponseMessage Call( #endif } - using (var client = new HttpClient(handler) {Timeout = TimeSpan.FromSeconds(20)}) + using var client = new HttpClient(handler) {Timeout = TimeSpan.FromSeconds(20)}; + if (cluster.ClusterConfiguration.EnableSecurity) { - if (cluster.ClusterConfiguration.EnableSecurity) - { - var byteArray = - Encoding.ASCII.GetBytes( - $"{ClusterAuthentication.Admin.Username}:{ClusterAuthentication.Admin.Password}"); - client.DefaultRequestHeaders.Authorization = - new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray)); - } + var byteArray = + Encoding.ASCII.GetBytes( + $"{ClusterAuthentication.Admin.Username}:{ClusterAuthentication.Admin.Password}"); + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray)); + } - try - { - var response = verb(client, statusUrl, tokenSource.Token).ConfigureAwait(false).GetAwaiter() - .GetResult(); - if (response.StatusCode == HttpStatusCode.OK) return response; - cluster.Writer.WriteDiagnostic( - $"{{{nameof(Call)}}} [{statusUrl}] Bad status code: [{(int) response.StatusCode}]"); - var body = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); - foreach (var l in (body ?? string.Empty).Split('\n', '\r')) - cluster.Writer.WriteDiagnostic($"{{{nameof(Call)}}} [{statusUrl}] returned [{l}]"); - } - catch (Exception e) - { - cluster.Writer.WriteError($"{{{nameof(Call)}}} [{statusUrl}] exception: {e}"); - // ignored - } - finally - { + try + { + var response = verb(client, statusUrl, tokenSource.Token).ConfigureAwait(false).GetAwaiter() + .GetResult(); + if (response.StatusCode == HttpStatusCode.OK) return response; + cluster.Writer.WriteDiagnostic( + $"{{{nameof(Call)}}} [{statusUrl}] Bad status code: [{(int) response.StatusCode}]"); + var body = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + foreach (var l in (body ?? string.Empty).Split('\n', '\r')) + cluster.Writer.WriteDiagnostic($"{{{nameof(Call)}}} [{statusUrl}] returned [{l}]"); + } + catch (Exception e) + { + cluster.Writer.WriteError($"{{{nameof(Call)}}} [{statusUrl}] exception: {e}"); + // ignored + } + finally + { #if !NETSTANDARD ServicePointManager.ServerCertificateValidationCallback -= ServerCertificateValidationCallback; #endif - } } return null; diff --git a/src/Elastic.Elasticsearch.Ephemeral/Tasks/InstallationTasks/PrintConfiguration.cs b/src/Elastic.Elasticsearch.Ephemeral/Tasks/InstallationTasks/PrintConfiguration.cs index f15d9c5..d088ce4 100644 --- a/src/Elastic.Elasticsearch.Ephemeral/Tasks/InstallationTasks/PrintConfiguration.cs +++ b/src/Elastic.Elasticsearch.Ephemeral/Tasks/InstallationTasks/PrintConfiguration.cs @@ -50,7 +50,7 @@ string F(ClusterFeatures feature) cluster.Writer?.WriteDiagnostic( $"{{{nameof(PrintConfiguration)}}} {{{nameof(c.SkipBuiltInAfterStartTasks)}}} [{c.SkipBuiltInAfterStartTasks}]"); cluster.Writer?.WriteDiagnostic( - $"{{{nameof(PrintConfiguration)}}} {{{nameof(c.HttpFiddlerAware)}}} [{c.HttpFiddlerAware}]"); + $"{{{nameof(PrintConfiguration)}}} {{{nameof(c.AutoWireKnownProxies)}}} [{c.AutoWireKnownProxies}]"); cluster.Writer?.WriteDiagnostic( $"{{{nameof(PrintConfiguration)}}} {{{nameof(c.NoCleanupAfterNodeStopped)}}} [{c.NoCleanupAfterNodeStopped}]"); } diff --git a/src/Elastic.Elasticsearch.Managed/ClusterBase.cs b/src/Elastic.Elasticsearch.Managed/ClusterBase.cs index 502e5b8..8f8f28c 100644 --- a/src/Elastic.Elasticsearch.Managed/ClusterBase.cs +++ b/src/Elastic.Elasticsearch.Managed/ClusterBase.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; @@ -12,6 +13,7 @@ using Elastic.Elasticsearch.Managed.ConsoleWriters; using Elastic.Elasticsearch.Managed.FileSystem; using ProcNet.Std; +using static Elastic.Elasticsearch.Managed.DetectedProxySoftware; namespace Elastic.Elasticsearch.Managed { @@ -30,6 +32,11 @@ public interface ICluster : IDisposable IDisposable Start(TimeSpan waitForStarted); IDisposable Start(IConsoleLineHandler writer, TimeSpan waitForStarted); + + /// + /// Whether known proxies were detected as running during startup + /// + DetectedProxySoftware DetectedProxy { get; } } @@ -75,8 +82,18 @@ NodeConfiguration Modify(NodeConfiguration n, int p) node.NodeConfiguration.InitialMasterNodes(initialMasterNodes); Nodes = new ReadOnlyCollection(nodes); + + if (Process.GetProcessesByName("fiddler").Any()) DetectedProxy = Fiddler; + else if (Process.GetProcessesByName("mitmproxy").Any()) DetectedProxy = MitmProxy; + else DetectedProxy = None; } + /// + /// Whether known proxies were detected as running during startup + /// + public DetectedProxySoftware DetectedProxy { get; } + + /// /// A short name to identify the cluster defaults to the subclass name with Cluster /// removed diff --git a/src/Elastic.Elasticsearch.Managed/Configuration/ClusterConfiguration.cs b/src/Elastic.Elasticsearch.Managed/Configuration/ClusterConfiguration.cs index c5cf01f..ddc4a97 100644 --- a/src/Elastic.Elasticsearch.Managed/Configuration/ClusterConfiguration.cs +++ b/src/Elastic.Elasticsearch.Managed/Configuration/ClusterConfiguration.cs @@ -80,6 +80,7 @@ public ClusterConfiguration(ElasticVersion version, Func