using System.Net; using DodoSSH.Api.Features.Relay; using DodoSSH.Api.Setup; namespace DodoSSH.Api.Tests; /// /// ADR 0004 ยง3, address by address. /// /// /// The refused list is tested against the most permissive configuration this server accepts โ€” every /// network allowed, private ranges on โ€” because "no configuration escape" is a claim about exactly that /// case. A test that ran it only under the defaults would pass for a policy an operator could open up. /// public sealed class RelayAddressPolicyTests { [Theory] [InlineData("127.0.0.1")] [InlineData("127.255.255.254")] [InlineData("0.0.0.0")] [InlineData("0.1.2.3")] [InlineData("169.254.169.254")] [InlineData("169.254.0.1")] [InlineData("224.0.0.251")] [InlineData("255.255.255.255")] [InlineData("100.100.100.200")] [InlineData("::")] [InlineData("::1")] [InlineData("fe80::1")] [InlineData("ff02::1")] [InlineData("fd00:ec2::254")] // The same addresses, spelled as IPv6 โ€” the bypass ADR 0004 names, and its NAT64 cousin. [InlineData("::ffff:127.0.0.1")] [InlineData("::ffff:169.254.169.254")] [InlineData("64:ff9b::a9fe:a9fe")] [InlineData("64:ff9b::7f00:1")] [InlineData("::127.0.0.1")] public void TheRefusedList_HasNoConfigurationEscape(string address) { var wideOpen = new RelayOptions { AllowPrivateNetworks = true }; wideOpen.AllowedNetworks.Add("0.0.0.0/0"); wideOpen.AllowedNetworks.Add("::/0"); new RelayAddressPolicy(wideOpen).Permits(IPAddress.Parse(address)).ShouldBeFalse(); new RelayAddressPolicy(new RelayOptions()).Permits(IPAddress.Parse(address)).ShouldBeFalse(); } [Fact] public void ALinkLocalAddressWithAScope_IsStillLinkLocal() { var scoped = IPAddress.Parse("fe80::1%7"); new RelayAddressPolicy(new RelayOptions()).Permits(scoped).ShouldBeFalse(); } [Theory] [InlineData("10.0.0.5")] [InlineData("172.16.4.4")] [InlineData("192.168.1.10")] [InlineData("100.101.102.103")] // A Tailnet address: carrier-grade NAT, beside the refused metadata one. [InlineData("fd12:3456::1")] [InlineData("::ffff:10.0.0.5")] public void PrivateNetworks_ArePermittedByDefault_AndRefusedWhenSwitchedOff(string address) { new RelayAddressPolicy(new RelayOptions()).Permits(IPAddress.Parse(address)).ShouldBeTrue(); new RelayAddressPolicy(new RelayOptions { AllowPrivateNetworks = false }) .Permits(IPAddress.Parse(address)) .ShouldBeFalse(); } [Theory] [InlineData("203.0.113.10")] [InlineData("2001:db8::10")] public void PublicAddresses_ArePermitted(string address) => new RelayAddressPolicy(new RelayOptions { AllowPrivateNetworks = false }) .Permits(IPAddress.Parse(address)) .ShouldBeTrue(); [Fact] public void AnAllowList_IsTheWholeAnswer_EvenWithPrivateNetworksOff() { var options = new RelayOptions { AllowPrivateNetworks = false }; options.AllowedNetworks.Add("10.20.0.0/16"); var policy = new RelayAddressPolicy(options); policy.Permits(IPAddress.Parse("10.20.3.4")).ShouldBeTrue(); policy.Permits(IPAddress.Parse("10.21.3.4")).ShouldBeFalse(); policy.Permits(IPAddress.Parse("203.0.113.10")).ShouldBeFalse(); } [Fact] public void ADenial_IsAHoleInAnAllowance() { var options = new RelayOptions(); options.AllowedNetworks.Add("10.20.0.0/16"); options.DeniedNetworks.Add("10.20.5.0/24"); var policy = new RelayAddressPolicy(options); policy.Permits(IPAddress.Parse("10.20.4.1")).ShouldBeTrue(); policy.Permits(IPAddress.Parse("10.20.5.1")).ShouldBeFalse(); } [Theory] [InlineData(25, false)] [InlineData(465, false)] [InlineData(587, false)] [InlineData(0, false)] [InlineData(65536, false)] [InlineData(22, true)] [InlineData(5432, true)] public void Ports_RefuseSmtpAndNothingElse(int port, bool permitted) => RelayAddressPolicy.PermitsPort(port).ShouldBe(permitted); [Theory] [InlineData("10.0.0.0/8", true)] [InlineData("fd00::/8", true)] [InlineData("10.0.0.1/8", false)] [InlineData("10.0.0.0", false)] [InlineData("ten dot oh", false)] public void NetworksAreReadStrictly(string entry, bool readable) => RelayAddressPolicy.AreNetworks([entry]).ShouldBe(readable); }