NoAuthentication option added for "LocalForward" in config (only) #77 (#97)

* NoAuthentication option added for "LocalForward" in config (only) #77
This commit is contained in:
Clemens Vasters 2024-08-30 16:25:42 +02:00 коммит произвёл GitHub
Родитель 43708190c0
Коммит 81cf5b623e
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
10 изменённых файлов: 295 добавлений и 103 удалений

Просмотреть файл

@ -345,6 +345,7 @@ and multiple entries are permitted.
* **RelayName** - name of the Azure Relay name to bind to
* **ConnectionString** - optional Azure Relay connection string to use just for this forwarder, overriding the global **AzureRelayConnectionString** property.
* **NoAuthentication** - optional, if set to true, the connection is made without authentication with the assumption that the hybrid connection is configured to not require it.
For a single port binding on the Relay name, the following properties can be
used on the same entry. For multiple bindings they can be used to form a list.
@ -359,20 +360,32 @@ Examples:
- Single listener binding:
``` YAML
- RelayName: myrelay
BindAddress: 127.0.8.1
BindPort: 8888
LocalForward:
- RelayName: myrelay
BindAddress: 127.0.8.1
BindPort: 8888
```
- Single listener binding (no client authentication):
``` YAML
LocalForward:
- RelayName: myrelay
BindAddress: 127.0.8.1
BindPort: 8888
NoAuthentication: true
```
- Multiple listener binding:
``` YAML
- RelayName: myrelay
Bindings:
- BindAddress: 127.0.8.1
BindPort: 5671
PortName: amqps
- BindAddress: 127.0.8.1
BindPort: 5672
PortName: amqp
LocalForward:
- RelayName: myrelay
Bindings:
- BindAddress: 127.0.8.1
BindPort: 5671
PortName: amqps
- BindAddress: 127.0.8.1
BindPort: 5672
PortName: amqp
```
@ -410,12 +423,15 @@ Examples:
- Single listener binding:
``` YAML
RemoteForward:
- RelayName: myrelay
Host: localhost
HostPort: 8888
```
- Multiple listener binding:
``` YAML
RemoteForward:
- RelayName: myrelay
Bindings:
- Host: broker.corp.example.com

Просмотреть файл

@ -219,5 +219,31 @@ namespace Microsoft.Azure.Relay.Bridge.Configuration
}
}
}
public bool NoAuthentication
{
get
{
if (bindings.Count == 1)
{
return bindings[0].NoAuthentication;
}
else
{
return false;
}
}
set
{
if (bindings.Count == 0)
{
bindings.Add(new LocalForwardBinding { NoAuthentication = value });
}
else
{
bindings[0].NoAuthentication = value;
}
}
}
}
}

Просмотреть файл

@ -12,6 +12,7 @@
private string bindAddress;
private string hostName;
private int bindPort;
private bool noAuthentication = false;
private string bindLocalSocket = null;
string portName;
@ -190,5 +191,11 @@
bindLocalSocket = val;
}
}
public bool NoAuthentication
{
get => noAuthentication;
set => noAuthentication = value;
}
}
}

Просмотреть файл

@ -72,7 +72,7 @@ namespace Microsoft.Azure.Relay.Bridge
try
{
{
socketListenerBridge = SocketLocalForwardBridge.FromConnectionString(this.config, rcbs, binding.PortName);
socketListenerBridge = SocketLocalForwardBridge.FromConnectionString(this.config, rcbs, binding.PortName, binding.NoAuthentication);
socketListenerBridge.Run(binding.BindLocalSocket);
this.socketListenerBridges.Add(socketListenerBridge);
@ -115,7 +115,7 @@ namespace Microsoft.Azure.Relay.Bridge
if (bindToAddress != null)
{
tcpListenerBridge =
TcpLocalForwardBridge.FromConnectionString(this.config, rcbs, binding.PortName);
TcpLocalForwardBridge.FromConnectionString(this.config, rcbs, binding.PortName, binding.NoAuthentication);
tcpListenerBridge.Run(new IPEndPoint(bindToAddress, binding.BindPort));
this.listenerBridges.Add(tcpListenerBridge);
@ -156,7 +156,7 @@ namespace Microsoft.Azure.Relay.Bridge
if (bindToAddress != null)
{
udpListenerBridge =
UdpLocalForwardBridge.FromConnectionString(this.config, rcbs, binding.PortName);
UdpLocalForwardBridge.FromConnectionString(this.config, rcbs, binding.PortName, binding.NoAuthentication);
udpListenerBridge.Run(new IPEndPoint(bindToAddress, -binding.BindPort));
this.udpBridges.Add(udpListenerBridge);

Просмотреть файл

@ -27,11 +27,15 @@ namespace Microsoft.Azure.Relay.Bridge
Socket socketListener;
string localEndpoint;
public SocketLocalForwardBridge(Config config, RelayConnectionStringBuilder connectionString, string portName)
public SocketLocalForwardBridge(Config config, RelayConnectionStringBuilder connectionString, string portName, bool noAuth)
{
PortName = portName;
this.config = config;
if (connectionString.SharedAccessKeyName == null && connectionString.SharedAccessSignature == null)
if (noAuth)
{
this.hybridConnectionClient = new HybridConnectionClient(new Uri(connectionString.Endpoint, connectionString.EntityPath));
}
else if (string.IsNullOrEmpty(connectionString.SharedAccessKeyName) && string.IsNullOrEmpty(connectionString.SharedAccessSignature))
{
this.hybridConnectionClient = new HybridConnectionClient(new Uri(connectionString.Endpoint, connectionString.EntityPath), Host.DefaultAzureCredentialTokenProvider);
}
@ -52,9 +56,9 @@ namespace Microsoft.Azure.Relay.Bridge
public HybridConnectionClient HybridConnectionClient => hybridConnectionClient;
public static SocketLocalForwardBridge FromConnectionString(Config config,
RelayConnectionStringBuilder connectionString, string bindingPortName)
RelayConnectionStringBuilder connectionString, string bindingPortName, bool noAuth)
{
return new SocketLocalForwardBridge(config, connectionString, bindingPortName);
return new SocketLocalForwardBridge(config, connectionString, bindingPortName, noAuth);
}
public void Close()

Просмотреть файл

@ -15,7 +15,7 @@ namespace Microsoft.Azure.Relay.Bridge
sealed class TcpLocalForwardBridge : IDisposable
{
public string PortName { get; }
private readonly Config config;
readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
@ -26,11 +26,15 @@ namespace Microsoft.Azure.Relay.Bridge
TcpListener tcpListener;
string localEndpoint;
public TcpLocalForwardBridge(Config config, RelayConnectionStringBuilder connectionString, string portName)
public TcpLocalForwardBridge(Config config, RelayConnectionStringBuilder connectionString, string portName, bool noAuth)
{
PortName = portName;
this.config = config;
if (connectionString.SharedAccessKeyName == null && connectionString.SharedAccessSignature == null)
if (noAuth)
{
this.hybridConnectionClient = new HybridConnectionClient(new Uri(connectionString.Endpoint, connectionString.EntityPath));
}
else if (string.IsNullOrEmpty(connectionString.SharedAccessKeyName) && string.IsNullOrEmpty(connectionString.SharedAccessSignature))
{
this.hybridConnectionClient = new HybridConnectionClient(new Uri(connectionString.Endpoint, connectionString.EntityPath), Host.DefaultAzureCredentialTokenProvider);
}
@ -51,9 +55,9 @@ namespace Microsoft.Azure.Relay.Bridge
public HybridConnectionClient HybridConnectionClient => hybridConnectionClient;
public static TcpLocalForwardBridge FromConnectionString(Config config,
RelayConnectionStringBuilder connectionString, string portName)
RelayConnectionStringBuilder connectionString, string portName, bool noAuth)
{
return new TcpLocalForwardBridge(config, connectionString, portName);
return new TcpLocalForwardBridge(config, connectionString, portName, noAuth);
}
public void Close()

Просмотреть файл

@ -30,11 +30,15 @@ namespace Microsoft.Azure.Relay.Bridge
UdpClient udpClient;
string localEndpoint;
public UdpLocalForwardBridge(Config config, RelayConnectionStringBuilder connectionString, string portName)
public UdpLocalForwardBridge(Config config, RelayConnectionStringBuilder connectionString, string portName, bool noAuth)
{
PortName = portName;
this.config = config;
if (connectionString.SharedAccessKeyName == null && connectionString.SharedAccessSignature == null)
if (noAuth)
{
this.hybridConnectionClient = new HybridConnectionClient(new Uri(connectionString.Endpoint, connectionString.EntityPath));
}
else if (string.IsNullOrEmpty(connectionString.SharedAccessKeyName) && string.IsNullOrEmpty(connectionString.SharedAccessSignature))
{
this.hybridConnectionClient = new HybridConnectionClient(new Uri(connectionString.Endpoint, connectionString.EntityPath), Host.DefaultAzureCredentialTokenProvider);
}
@ -55,9 +59,9 @@ namespace Microsoft.Azure.Relay.Bridge
public HybridConnectionClient HybridConnectionClient => hybridConnectionClient;
public static UdpLocalForwardBridge FromConnectionString(Config config,
RelayConnectionStringBuilder connectionString, string portName)
RelayConnectionStringBuilder connectionString, string portName, bool noAuth)
{
return new UdpLocalForwardBridge(config, connectionString, portName);
return new UdpLocalForwardBridge(config, connectionString, portName, noAuth);
}
public void Close()

Просмотреть файл

@ -4,43 +4,131 @@
namespace Microsoft.Azure.Relay.Bridge.Test
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Relay.Bridge.Configuration;
using Microsoft.Azure.Relay.Bridge.Tests;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestPlatform.Utilities;
using Xunit;
using Xunit.Abstractions;
public class BridgeTest : IClassFixture<LaunchSettingsFixture>
{
#if _WINDOWS
private const string relayA1 = "a1.win";
private const string relayA2 = "a2.win";
private const string relayA3 = "a3.win";
private const string relayHttp = "http.win";
#elif _LINUX
private const string relayA1 = "a1.linux";
private const string relayA2 = "a2.linux";
private const string relayA3 = "a3.linux";
private const string relayHttp = "http.linux";
#elif _OSX
private const string relayA1 = "a1.osx";
private const string relayA2 = "a2.osx";
private const string relayA3 = "a3.osx";
private const string relayHttp = "http.osx";
#endif
readonly LaunchSettingsFixture launchSettingsFixture;
readonly ITestOutputHelper _output;
public BridgeTest(LaunchSettingsFixture launchSettingsFixture)
class SubscriberObserver : IObserver<DiagnosticListener>
{
private ITestOutputHelper logger;
public SubscriberObserver(ITestOutputHelper logger)
{
this.logger = logger;
}
public void OnCompleted()
{
}
public void OnError(Exception error)
{
}
public void OnNext(DiagnosticListener value)
{
if (value.Name == "Microsoft.Azure.Relay.Bridge")
{
value.Subscribe(new TraceObserver(logger));
}
}
}
class TraceObserver : IObserver<KeyValuePair<string, object>>
{
private ITestOutputHelper logger;
public TraceObserver(ITestOutputHelper logger)
{
this.logger = logger;
}
public void OnCompleted()
{
}
public void OnError(Exception error)
{
}
public void OnNext(KeyValuePair<string, object> value)
{
DiagnosticsRecord record = (DiagnosticsRecord)value.Value;
string message = $"[{DateTime.UtcNow}], {value.Key}, {record.Activity}, {record.Info}";
logger.WriteLine(message);
}
}
public BridgeTest(LaunchSettingsFixture launchSettingsFixture, ITestOutputHelper testOutputHelper)
{
this.launchSettingsFixture = launchSettingsFixture;
this._output = testOutputHelper;
}
[Fact]
public void TcpBridge()
public async Task RunScenarios()
{
DiagnosticListener.AllListeners.Subscribe(new SubscriberObserver(_output));
_output.WriteLine("Starting BridgeTest.RunScenarios");
_output.WriteLine("OS: " + Environment.OSVersion.Platform);
_output.WriteLine("OS Version: " + Environment.OSVersion.VersionString);
_output.WriteLine("Starting TcpBridge");
TcpBridge();
_output.WriteLine("Starting TcpBridgeNoAuth");
TcpBridgeNoAuth();
_output.WriteLine("Starting UdpBridge");
UdpBridge();
_output.WriteLine("Starting HttpBridgeAsync");
await HttpBridgeAsync();
}
internal void TcpBridge()
{
_output.WriteLine("------- Starting TCP Bridge -------------");
// set up the bridge first
Config cfg = new Config
{
@ -66,17 +154,22 @@ namespace Microsoft.Azure.Relay.Bridge.Test
try
{
// now try to use it
var l = new TcpListener(IPAddress.Parse("127.0.97.2"), 29877);
_output.WriteLine($"TcpBridge: Starting TCP Listener, at {l.LocalEndpoint}");
l.Start();
l.AcceptTcpClientAsync().ContinueWith((t) =>
{
var c = t.Result;
_output.WriteLine($"TcpBridge: Accepted TCP client {c.Client.RemoteEndPoint}");
var stream = c.GetStream();
using (var b = new StreamReader(stream))
{
var text = b.ReadLine();
_output.WriteLine($"TcpBridge: Read from client stream: {text}");
using (var w = new StreamWriter(stream))
{
_output.WriteLine("TcpBridge: Writing back to client stream: " + text);
w.WriteLine(text);
w.Flush();
}
@ -85,14 +178,95 @@ namespace Microsoft.Azure.Relay.Bridge.Test
using (var s = new TcpClient())
{
_output.WriteLine("TcpBridge: Connecting to TCP server");
s.Connect("127.0.97.1", 29876);
var sstream = s.GetStream();
using (var w = new StreamWriter(sstream))
{
var text = "Hello!";
_output.WriteLine("TcpBridge: Writing to stream " + text);
w.WriteLine(text);
w.Flush();
Thread.Sleep(1000);
using (var b = new StreamReader(sstream))
{
text = b.ReadLine();
_output.WriteLine($"TcpBridge: Read from stream: {text}");
Assert.Equal("Hello!", text);
}
}
}
l.Stop();
}
finally
{
host.Stop();
}
}
internal void TcpBridgeNoAuth()
{
_output.WriteLine("------- Starting TcpBridgeNoAuth() -------------");
// set up the bridge first
Config cfg = new Config
{
AzureRelayConnectionString = Utilities.GetConnectionString()
};
cfg.LocalForward.Add(new LocalForward
{
BindAddress = "127.0.97.3",
BindPort = 29876,
PortName = "test",
RelayName = relayA3,
NoAuthentication = true
});
cfg.RemoteForward.Add(new RemoteForward
{
Host = "127.0.97.4",
HostPort = 29877,
PortName = "test",
RelayName = relayA3
});
Host host = new Host(cfg);
host.Start();
try
{
// now try to use it
var l = new TcpListener(IPAddress.Parse("127.0.97.4"), 29877);
_output.WriteLine($"TcpBridgeNoAuth: Starting TCP Listener, at {l.LocalEndpoint}");
l.Start();
l.AcceptTcpClientAsync().ContinueWith((t) =>
{
var c = t.Result;
var stream = c.GetStream();
using (var b = new StreamReader(stream))
{
var text = b.ReadLine();
_output.WriteLine($"TcpBridgeNoAuth: Read from client stream: {text}");
using (var w = new StreamWriter(stream))
{
_output.WriteLine("TcpBridgeNoAuth: Writing back to client stream: " + text);
w.WriteLine(text);
w.Flush();
}
}
});
using (var s = new TcpClient())
{
_output.WriteLine("TcpBridgeNoAuth: Connecting to TCP server");
s.Connect("127.0.97.3", 29876);
var sstream = s.GetStream();
using (var w = new StreamWriter(sstream))
{
_output.WriteLine("TcpBridgeNoAuth: Writing to stream Hello!");
w.WriteLine("Hello!");
w.Flush();
using (var b = new StreamReader(sstream))
{
_output.WriteLine("TcpBridgeNoAuth: Reading from stream");
Assert.Equal("Hello!", b.ReadLine());
}
}
@ -106,8 +280,7 @@ namespace Microsoft.Azure.Relay.Bridge.Test
}
}
[Fact]
public void UdpBridge()
internal void UdpBridge()
{
// set up the bridge first
Config cfg = new Config
@ -134,10 +307,12 @@ namespace Microsoft.Azure.Relay.Bridge.Test
try
{
// now try to use it
_output.WriteLine("UdpBridge: Starting UDP Bridge");
using (var l = new UdpClient(new IPEndPoint(IPAddress.Parse("127.0.97.2"), 29877)))
{
l.ReceiveAsync().ContinueWith(async (t) =>
{
_output.WriteLine("UdpBridge: Read UDP message");
var c = t.Result;
var stream = c.Buffer;
using (var mr = new MemoryStream(stream))
@ -149,6 +324,7 @@ namespace Microsoft.Azure.Relay.Bridge.Test
{
using (var w = new StreamWriter(mw))
{
_output.WriteLine("UdpBridge: Writing back to sender: " + text);
w.WriteLine(text);
w.Flush();
await l.SendAsync(mw.GetBuffer(), (int)mw.Length, c.RemoteEndPoint);
@ -160,6 +336,7 @@ namespace Microsoft.Azure.Relay.Bridge.Test
using (var s = new UdpClient())
{
_output.WriteLine("UdpBridge: Sending UDP message");
s.Connect("127.0.97.1", 29876);
using (MemoryStream mw = new MemoryStream())
{
@ -175,6 +352,7 @@ namespace Microsoft.Azure.Relay.Bridge.Test
{
using (var b = new StreamReader(mr))
{
_output.WriteLine("UdpBridge: Reading from sender");
Assert.Equal("Hello!", b.ReadLine());
}
}
@ -189,9 +367,8 @@ namespace Microsoft.Azure.Relay.Bridge.Test
}
}
#if _SYSTEMD
[Fact(Skip="Unreliable")]
public void SocketBridge()
#if _LINUX
internal void SocketBridge()
{
// not yet supported on Windows.
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
@ -270,67 +447,8 @@ namespace Microsoft.Azure.Relay.Bridge.Test
}
#endif
[Fact(Skip = "Unreliable")]
public void TcpBridgeBadListener()
{
// set up the bridge first
Config cfg = new Config
{
AzureRelayConnectionString = Utilities.GetConnectionString()
};
cfg.LocalForward.Add(new LocalForward
{
BindAddress = "127.0.97.1",
BindPort = 29876,
RelayName = relayA1
});
cfg.RemoteForward.Add(new RemoteForward
{
Host = "127.0.97.2",
HostPort = 29877,
RelayName = relayA1
});
Host host = new Host(cfg);
host.Start();
try
{
// now try to use it
var l = new TcpListener(IPAddress.Parse("127.0.97.2"), 29877);
l.Start();
l.AcceptTcpClientAsync().ContinueWith((t) =>
{
t.Result.Client.Close(0);
l.Stop();
});
using (var s = new TcpClient())
{
s.Connect("127.0.97.1", 29876);
s.NoDelay = true;
s.Client.Blocking = true;
using (var w = s.GetStream())
{
byte[] bytes = new byte[1024 * 1024];
Assert.Throws<IOException>(() =>
{
for (int i = 0; i < 5; i++)
{
w.Write(bytes, 0, bytes.Length);
}
});
}
}
}
finally
{
host.Stop();
}
}
[Fact]
public async Task HttpBridge()
internal async Task HttpBridgeAsync()
{
// set up the bridge first
Config cfg = new Config

Просмотреть файл

@ -79,6 +79,11 @@ namespace Microsoft.Azure.Relay.Bridge.Test
" BindPort : 8008" + textWriter.NewLine +
" RelayName : bar" + textWriter.NewLine +
" HostName : bar.example.com" + textWriter.NewLine +
" - BindAddress : 127.0.100.3" + textWriter.NewLine +
" BindPort : 8008" + textWriter.NewLine +
" RelayName : bam" + textWriter.NewLine +
" HostName : bam.example.com" + textWriter.NewLine +
" NoAuthentication: true" + textWriter.NewLine +
#if !NETFRAMEWORK
" - BindLocalSocket : test" + textWriter.NewLine +
" RelayName : baz" + textWriter.NewLine +
@ -786,27 +791,33 @@ namespace Microsoft.Azure.Relay.Bridge.Test
Assert.True(config.ExitOnForwardFailure);
#if !NETFRAMEWORK
Assert.Equal(3, config.LocalForward.Count);
Assert.Equal(4, config.LocalForward.Count);
#else
Assert.Equal(2, config.LocalForward.Count);
Assert.Equal(3, config.LocalForward.Count);
#endif
Assert.Equal("127.0.100.1", config.LocalForward[0].BindAddress);
Assert.Equal(8008, config.LocalForward[0].BindPort);
Assert.Equal("foo", config.LocalForward[0].RelayName);
Assert.Equal("foo.example.com", config.LocalForward[0].HostName);
Assert.False(config.LocalForward[0].NoAuthentication);
Assert.Equal("127.0.100.2", config.LocalForward[1].BindAddress);
Assert.Equal(8008, config.LocalForward[1].BindPort);
Assert.Equal("bar", config.LocalForward[1].RelayName);
Assert.Equal("bar.example.com", config.LocalForward[1].HostName);
Assert.False(config.LocalForward[1].NoAuthentication);
Assert.Equal("127.0.100.3", config.LocalForward[2].BindAddress);
Assert.Equal(8008, config.LocalForward[2].BindPort);
Assert.Equal("bam", config.LocalForward[2].RelayName);
Assert.True(config.LocalForward[2].NoAuthentication);
#if !NETFRAMEWORK
Assert.Equal("test", config.LocalForward[2].BindLocalSocket);
Assert.Equal("baz", config.LocalForward[2].RelayName);
Assert.Equal("test", config.LocalForward[3].BindLocalSocket);
Assert.Equal("baz", config.LocalForward[3].RelayName);
#endif
#if !NETFRAMEWORK
Assert.Equal(3, config.RemoteForward.Count);
#else
Assert.Equal(2, config.RemoteForward.Count);
Assert.Equal(4, config.RemoteForward.Count);
#endif
Assert.Equal("foo", config.RemoteForward[0].RelayName);
Assert.Equal(123, config.RemoteForward[0].HostPort);
@ -830,7 +841,7 @@ namespace Microsoft.Azure.Relay.Bridge.Test
Config config = Config.LoadConfig(settings);
config.SaveConfigFile(configFileName, false);
config = Config.LoadConfigFile(configFileName);
CheckMaxConfig(config);

Просмотреть файл

@ -1,2 +1,4 @@
sudo ifconfig lo0 alias 127.0.97.1 up
sudo ifconfig lo0 alias 127.0.97.2 up
sudo ifconfig lo0 alias 127.0.97.2 up
sudo ifconfig lo0 alias 127.0.97.3 up
sudo ifconfig lo0 alias 127.0.97.4 up