From 072efe0625dcf33a57fef4233d589fec96cfa944 Mon Sep 17 00:00:00 2001 From: Kenneth Skovhede Date: Fri, 25 Oct 2024 11:17:30 +0200 Subject: [PATCH] Implemented explict support for IPv6 listener in the webserver. Using any/all/* will now listen on both IPv4 and IPv6 if available. To force IPv4-any, use `0.0.0.0` and similar `[::]` for IPv6. The loopback value listens on IPv4 and the new ipv6loopback value selects IPv6 loopback. It is not possible to get both loopback values at the same time. --- Duplicati/Library/RestAPI/Strings.cs | 2 +- Duplicati/Server/WebServerLoader.cs | 2 ++ Duplicati/WebserverCore/DuplicatiWebserver.cs | 20 +++++++++++++++---- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Duplicati/Library/RestAPI/Strings.cs b/Duplicati/Library/RestAPI/Strings.cs index 8115953df..0210afdf0 100644 --- a/Duplicati/Library/RestAPI/Strings.cs +++ b/Duplicati/Library/RestAPI/Strings.cs @@ -32,7 +32,7 @@ Error message: {0}", error); } public static string WebserverPortDescription { get { return LC.L(@"The port the webserver listens on. Multiple values may be supplied with a comma in between."); } } public static string WebserverCertificateFileDescription { get { return LC.L(@"The certificate and key file in PKCS #12 format the webserver use for SSL. Only RSA/DSA keys are supported."); } } public static string WebserverCertificatePasswordDescription { get { return LC.L(@"The password for decryption of certificate PKCS #12 file."); } } - public static string WebserverInterfaceDescription { get { return LC.L(@"The interface the webserver listens on. The special values ""*"" and ""any"" means any interface. The special value ""loopback"" means the loopback adapter."); } } + public static string WebserverInterfaceDescription { get { return LC.L(@"The interface the webserver listens on. The special values ""*"" and ""any"" means any interface. The special value ""loopback"" means the loopback adapter and ""ipv6loopback"" means IPv6 loopback."); } } public static string WebserverPasswordDescription { get { return LC.L(@"The password required to access the webserver. This option is saved so you do not need to set it on each run. Setting an empty value disables the password."); } } public static string WebserverAllowedhostnamesDescription { get { return LC.L(@"The hostnames that are accepted, separated with semicolons. If any of the hostnames are ""*"", all hostnames are allowed and the hostname checking is disabled."); } } public static string PingpongkeepaliveLong { get { return LC.L(@"When running as a server, the service daemon must verify that the process is responding. If this option is enabled, the server reads stdin and writes a reply to each line read."); } } diff --git a/Duplicati/Server/WebServerLoader.cs b/Duplicati/Server/WebServerLoader.cs index 43724d688..6be89523d 100644 --- a/Duplicati/Server/WebServerLoader.cs +++ b/Duplicati/Server/WebServerLoader.cs @@ -173,6 +173,8 @@ public static class WebServerLoader interfacestring = interfacestring.Trim(); if (new[] { "*", "all", "any" }.Any(x => x.Equals(interfacestring, StringComparison.OrdinalIgnoreCase))) listenInterface = System.Net.IPAddress.Any; + else if (interfacestring == "ipv6loopback") + listenInterface = System.Net.IPAddress.IPv6Loopback; else if (interfacestring != "loopback") listenInterface = System.Net.IPAddress.Parse(interfacestring); diff --git a/Duplicati/WebserverCore/DuplicatiWebserver.cs b/Duplicati/WebserverCore/DuplicatiWebserver.cs index e48706ffe..c66b6aa3a 100644 --- a/Duplicati/WebserverCore/DuplicatiWebserver.cs +++ b/Duplicati/WebserverCore/DuplicatiWebserver.cs @@ -98,11 +98,23 @@ public partial class DuplicatiWebserver builder.WebHost.ConfigureKestrel(options => { - options.Listen(settings.Interface, settings.Port, listenOptions => + // Handle IPv6 addresses + if (settings.Interface == System.Net.IPAddress.Any) { - if (settings.Certificate != null) - listenOptions.UseHttps(settings.Certificate); - }); + options.ListenAnyIP(settings.Port, listenOptions => + { + if (settings.Certificate != null) + listenOptions.UseHttps(settings.Certificate); + }); + } + else + { + options.Listen(settings.Interface, settings.Port, listenOptions => + { + if (settings.Certificate != null) + listenOptions.UseHttps(settings.Certificate); + }); + } }); builder.Services.Configure(opt =>