From c4c5f4a819232ea8ef896ab65db52f85a873ed23 Mon Sep 17 00:00:00 2001 From: Micah Morrison Date: Sat, 5 Feb 2022 10:49:52 -0500 Subject: [PATCH 01/12] Show adapter addresses in Internet Sharing dialog --- .../Models/InternetSharingPrerequisite.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/WireGuardServerForWindows/Models/InternetSharingPrerequisite.cs b/WireGuardServerForWindows/Models/InternetSharingPrerequisite.cs index 5e14fe1..ae153a3 100644 --- a/WireGuardServerForWindows/Models/InternetSharingPrerequisite.cs +++ b/WireGuardServerForWindows/Models/InternetSharingPrerequisite.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Management; +using System.Net.NetworkInformation; using System.Windows.Input; using Humanizer; using NETCONLib; @@ -104,10 +105,18 @@ public void Resolve(string networkToShare) // string.Join(' ', ...) will put it back together like "Media Disconnected" string status = string.Join(' ', netSharingManager.NetConnectionProps[connection].Status.Humanize().Transform(To.LowerCase, To.TitleCase).Split().Skip(1)); + // Get the IP address assigned to the adapter, if any. Prefer IPv4. + string address = default; + if (NetworkInterface.GetAllNetworkInterfaces().FirstOrDefault(i => i.Id == netSharingManager.NetConnectionProps[connection].Guid)?.GetIPProperties() is { } ipProperties) + { + address = (ipProperties.UnicastAddresses.FirstOrDefault(a => a.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) + ?? ipProperties.UnicastAddresses.FirstOrDefault(a => a.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6))?.Address.ToString(); + } + selectionWindowModel.Items.Add(new SelectionItem { DisplayText = $"{netSharingManager.NetConnectionProps[connection].Name} ({status})", - Description = netSharingManager.NetConnectionProps[connection].DeviceName, + Description = $"{netSharingManager.NetConnectionProps[connection].DeviceName}{(string.IsNullOrEmpty(address) ? string.Empty : $" ({address})")}", BackingObject = connection }); } From 8404756871ce11171357f331f39f88f602036624 Mon Sep 17 00:00:00 2001 From: Micah Morrison Date: Sat, 5 Feb 2022 12:05:09 -0500 Subject: [PATCH 02/12] For #17: Integrate Persistent Internet Sharing workaround into application --- .../Utilities.cs | 22 +++++++++++++++ .../PersistentInternetSharingPrerequisite.cs | 27 ++++++++++++++++++- .../Properties/Resources.Designer.cs | 4 +-- .../Properties/Resources.resx | 2 +- .../WireGuardServerForWindows.csproj | 1 + 5 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 WireGuardServerForWindows.Cli.Options/Utilities.cs diff --git a/WireGuardServerForWindows.Cli.Options/Utilities.cs b/WireGuardServerForWindows.Cli.Options/Utilities.cs new file mode 100644 index 0000000..9a921a6 --- /dev/null +++ b/WireGuardServerForWindows.Cli.Options/Utilities.cs @@ -0,0 +1,22 @@ +using System; +using System.Linq; +using CommandLine; + +namespace WireGuardServerForWindows.Cli.Options +{ + /// + /// Static utilities related to the CLI options + /// + public static class Utilities + { + /// + /// If the given has the , returns the value. + /// Otherwise, returns null. + /// + /// + public static string GetVerb(this Type verbType) + { + return verbType.GetCustomAttributes(inherit: true).OfType().FirstOrDefault()?.Name; + } + } +} diff --git a/WireGuardServerForWindows/Models/PersistentInternetSharingPrerequisite.cs b/WireGuardServerForWindows/Models/PersistentInternetSharingPrerequisite.cs index cf19570..b2d60ad 100644 --- a/WireGuardServerForWindows/Models/PersistentInternetSharingPrerequisite.cs +++ b/WireGuardServerForWindows/Models/PersistentInternetSharingPrerequisite.cs @@ -1,8 +1,11 @@ using System; +using System.IO; using System.Linq; using System.Management; using System.Windows.Input; using Microsoft.Win32; +using Microsoft.Win32.TaskScheduler; +using WireGuardServerForWindows.Cli.Options; using WireGuardServerForWindows.Properties; namespace WireGuardServerForWindows.Models @@ -38,7 +41,12 @@ public PersistentInternetSharingPrerequisite() : base // If good, result is true if (GetRegistryKeyValue() is {} value && value == 1) { - result = true; + // Finally, verify that the task exists and that all of the parameters are correct. + result = TaskService.Instance.FindTask(RestartInternetSharingTaskUniqueName) is { Enabled: true } task + && task.Definition.Triggers.FirstOrDefault() is BootTrigger + && task.Definition.Actions.FirstOrDefault() is ExecAction action + && action.Path == Path.Combine(AppContext.BaseDirectory, "ws4w.exe") + && action.Arguments == typeof(RestartInternetSharingCommand).GetVerb(); } } } @@ -60,6 +68,12 @@ public override void Resolve() SetRegistryKeyValue(1); + // Create/update a Scheduled Task that disables/enables internet sharing on boot. + TaskDefinition td = TaskService.Instance.NewTask(); + td.Actions.Add(new ExecAction(Path.Combine(AppContext.BaseDirectory, "ws4w.exe"), typeof(RestartInternetSharingCommand).GetVerb())); + td.Triggers.Add(new BootTrigger()); + TaskService.Instance.RootFolder.RegisterTaskDefinition(RestartInternetSharingTaskUniqueName, td, TaskCreation.CreateOrUpdate, "SYSTEM", null, TaskLogonType.ServiceAccount); + Refresh(); Mouse.OverrideCursor = null; @@ -78,6 +92,11 @@ public override void Configure() SetRegistryKeyValue(0); + if (TaskService.Instance.FindTask(RestartInternetSharingTaskUniqueName) is { } task) + { + task.Enabled = false; + } + Refresh(); Mouse.OverrideCursor = null; @@ -106,5 +125,11 @@ private void SetRegistryKeyValue(int value) } #endregion + + #region Private fields + + private readonly string RestartInternetSharingTaskUniqueName = "WS4W Restart Internet Sharing (b17f2530-acc7-42d6-ad05-ab57b923356f)"; + + #endregion } } diff --git a/WireGuardServerForWindows/Properties/Resources.Designer.cs b/WireGuardServerForWindows/Properties/Resources.Designer.cs index 43ff440..a3066bb 100644 --- a/WireGuardServerForWindows/Properties/Resources.Designer.cs +++ b/WireGuardServerForWindows/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace WireGuardServerForWindows.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class Resources { @@ -532,7 +532,7 @@ public static string PersistentInternetSharingResolve { } /// - /// Looks up a localized string similar to Persistent internet sharing has been enabled. Note that this is not a perfect solution, and occasionally internet sharing must be re-enabled after reboot.. + /// Looks up a localized string similar to Persistent internet sharing has been enabled, and a Scheduled Task has been created to re-enable sharing upon boot.. /// public static string PersistentInternetSharingSucecss { get { diff --git a/WireGuardServerForWindows/Properties/Resources.resx b/WireGuardServerForWindows/Properties/Resources.resx index 69c5c3e..858b564 100644 --- a/WireGuardServerForWindows/Properties/Resources.resx +++ b/WireGuardServerForWindows/Properties/Resources.resx @@ -343,7 +343,7 @@ Current value: {1} Persistent Internet Sharing - Persistent internet sharing has been enabled. Note that this is not a perfect solution, and occasionally internet sharing must be re-enabled after reboot. + Persistent internet sharing has been enabled, and a Scheduled Task has been created to re-enable sharing upon boot. Persistent internet sharing is not enabled. The WireGuard tunnel may lose network access after a reboot. diff --git a/WireGuardServerForWindows/WireGuardServerForWindows.csproj b/WireGuardServerForWindows/WireGuardServerForWindows.csproj index a5c3878..749ff77 100644 --- a/WireGuardServerForWindows/WireGuardServerForWindows.csproj +++ b/WireGuardServerForWindows/WireGuardServerForWindows.csproj @@ -40,6 +40,7 @@ + From e71189bfcb9db894aa0679cb098685a419af8ab8 Mon Sep 17 00:00:00 2001 From: Micah Morrison Date: Sat, 5 Feb 2022 12:24:49 -0500 Subject: [PATCH 03/12] Update wiki to reflect latest changes --- README.md | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 1ff2731..24badcc 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Below are the tasks that can be performed automatically using this application. ## Before ![BeforeScreenshot](https://i.imgur.com/Mlyd0TS.png) -### Download and Install WireGuard +### WireGuard.exe This step downloads and runs the latest version of WireGuard for Windows from https://download.wireguard.com/windows-client/wireguard-installer.exe. Once installed, it can be uninstalled directly from WS4W, too. ### Server Configuration @@ -42,7 +42,7 @@ You should set the Endpoint property to your public IPv4, IPv6, or domain addres ### Client Configuration ![ClientConfiguration](https://i.imgur.com/frxdJ7S.png) -Here you can configure the client(s). The Address can be entered manually or calculated based on the server's network range. For example, if the server's network is `10.253.0.0/24`, the client config can determine that `10.253.0.2` is a valid address. Note that the first address in the range (in this case, `10.253.0.1`) is reserved for the server. DNS is optional, but recommended. Lastly, the Private Key and Public Keys are again generated using `wg genkey` and `wg pubkey [private key]`. However, the Preshared Key must match the server's. If it has already been generated in the server config, it can be automatically copied to the client config. +Here you can configure the client(s). The Address can be entered manually or calculated based on the server's network range. For example, if the server's network is `10.253.0.0/24`, the client config can determine that `10.253.0.2` is a valid address. Note that the first address in the range (in this example, `10.253.0.1`) is reserved for the server. DNS is optional, but recommended. Lastly, the Private Key and Public Keys are again generated using `wg genkey` and `wg pubkey [private key]`. However, the Preshared Key must match the server's. If it has already been generated in the server config, it can be automatically copied to the client config. Once configured, it's easy to import the configuration into your client app of choice via QR code or by exporting the `.conf` file. @@ -51,7 +51,7 @@ Once configured, it's easy to import the configuration into your client app of c ### Tunnnel Service Once the server and client(s) are configured, you may install the tunnel service, which creates a new network interface for WireGuard using the `wireguard /installtunnelservice` command. After installation, the tunnel may be also removed directly within WS4W. This uses the `wireguard /uninstalltunnelservice` command. -Installing the tunnel service should be sufficient to perform a WireGuard handshake. +After completing this step, WireGuard clients should be able to get as far as performing a successful handshake with the server. > **Note:** If the server configuration is edited after the tunnel service is installed, the tunnel service will automatically be updated via the `wg syncconf` command (if the newly saved server configuration is valid). This is also true of the client configurations, updates to which often cause the server configuration to be updated (e.g., if a new client is added, the server configuration must be aware of this new peer). @@ -65,18 +65,18 @@ Even after the tunnel service is installed, some protocols may be blocked. It is Perhaps most importantly, internet sharing must be enabled in order to provide a real network connection to the WireGuard interface. In Windows, this is accomplished using Internet Connection Sharing, which serves as NAT router between the system's public network and the devices connected to the WireGuard interface. -When configuring this option, you may select any of your network adapters to share. Note that it will likely only work for adapters whose status is `Connected`, and it will only be useful for adapters which provide internet or LAN access. +When configuring this option, you may select any of your network adapters to share. Note that it will likely only work for adapters whose status is `Connected`, and it will only be useful for adapters which provide internet or LAN access. When choosing the adapter to share, hover over the menu item to get more details, including the adapter's assigned IP address, to determine if it's the one you want to share. > **Note:** When performing internet sharing, the WireGuard adapter is assigned an IP from the `ScopeAddress` registry value (under `HKLM\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters`). This value is automatically set when updating the Address property of the server configuration. See more [here](#server-configuration). ### Persistent Internet Sharing -There is a known bug in Windows that causes Internet Sharing to become disabled after a reboot. If the WireGuard server is intended to be left unattended, it is recommended to enable Persistent Internet Sharing so that no interaction is required after rebooting. +There are issues in Windows that cause Internet Sharing to become disabled after a reboot. If the WireGuard server is intended to be left unattended, it is recommended to enable Persistent Internet Sharing so that no interaction is required after rebooting. -When enabling this feature, two steps are performed. +When enabling this feature, two actions are performed in Windows: 1. The `Internet Connection Sharing` service startup mode is changed from `Manual` to `Automatic`. 2. The value of the `EnableRebootPersistConnection` regstry value in `HKLM\Software\Microsoft\Windows\CurrentVersion\SharedAccess` is set to `1` (it is created if not found). -**Warning**: This feature is currently unreliable due to Windows bugs, and may not consistently preserve internet sharing through reboots. To ensure that Internet Sharing is enabled after a reboot, see [Internet Sharing Workaround](#internet-sharing-workaround). +Note that even with these workarounds, Internet Sharing can become disabled after a reboot. Therefore, one more action is performed. A Scheduled Task is created that disables and re-enables Internet Sharing using the WS4W CLI upon system boot. This should be sufficient to guarantee that sharing stays persistent. ### View Server Status ![ServerStatus](https://i.imgur.com/dcSJXKU.png) @@ -84,10 +84,10 @@ When enabling this feature, two steps are performed. Once the tunnel is installed, the status of the WireGuard interface may be viewed. This is accomplished via the `wg show` command. It will be continually updated as long as `Update Live` is checked. ## After -![AfterScreenshot](https://i.imgur.com/Ck5yfvj.png) +![AfterScreenshot](https://user-images.githubusercontent.com/7417301/152651829-e31d2c1d-666e-426b-be55-dd73a6f3c913.png) ## CLI -There is also a CLI bundled in the portable download called `ws4w.exe` which can be invoked from a terminal or included in a script. In addition to messages written to standard out, the CLI will also set the exit code based on the success of executing the given command. In PowerShell, for example, the exit code can be printed with `echo $lastexitcode`. +There is also a CLI bundled in the portable download called `ws4w.exe` which can be invoked from a terminal or called from a script. In addition to messages written to standard out, the CLI will also set the exit code based on the success of executing the given command. In PowerShell, for example, the exit code can be printed with `echo $lastexitcode`. > **Note**: The CLI must also be run as an Administrator for the same reasons as above. @@ -102,23 +102,13 @@ The CLI uses verbs, or top-level commands, each of which has its own set of opti * If multiple networks are already shared, it is not possible to tell which one is shared with the WireGuard network, so the `--network` option must be passed to specify. * If Internet Sharing is not already enabled, the `--network` option must be passed, otherwise there is no way to know which network to share. * The exit code will be 0 if the requested or previously shared network was successfully reshared. + > This command is used by the Scheduled Task that is created when Persistent Internet Sharing is enabled. * ```ws4w.exe setpath``` - * This will tell WS4W to add the current executing directory to the system's `PATH` environment variable. It is mainly intended to be invoked by the installer but may be called manually after the fact. + * This will tell WS4W to add the current executing directory to the system's `PATH` environment variable. * This verb has no options. + > This command is used by the installer when the "Add CLI to PATH" option is selected. # Known Issues -Even following the steps in Henry's guide, the Persistent Internet Sharing feature is unreliable. A reboot may still cause the the internet sharing to fail, even though the `Internet Connection Sharing` service is running, and the network interface indicates that it is sharing in Control Panel. Only unsharing and resharing can fix this. - -### Internet Sharing Workaround -Fortunately, the CLI makes the process of unsharing and resharing easy to automate. Following is an example using the Windows Task Scheduler. - -1. Create a task which runs whether or not the user is logged in. -![image](https://user-images.githubusercontent.com/7417301/116771243-c457f300-aa17-11eb-9373-1b26dedfb52b.png) -2. Set the task to be triggered by system startup. -![image](https://user-images.githubusercontent.com/7417301/116771266-f0737400-aa17-11eb-99ec-7aa2ef9116a4.png) -3. Add an action that starts `ws4w.exe` with the `restartinternetsharing` verb. -![image](https://user-images.githubusercontent.com/7417301/116771293-23b60300-aa18-11eb-9070-1f2c2c0bb21d.png) -![image](https://user-images.githubusercontent.com/7417301/116771300-36c8d300-aa18-11eb-825d-28f8a74078f7.png) ### Inability to Enable Internet Sharing If you experience the following error message when enabling Internet Sharing, please perform the following manual steps. @@ -133,6 +123,3 @@ If you experience the following error message when enabling Internet Sharing, pl - Close and reopen WS4W. It should now show Internet Sharing enabled, and subsequent attempts to disable/re-enable should be sucessful going forward. > Note: This issue is often triggered after creating a new virtual switch for a VM. The manual workaround should only be needed once after that and does not affect the virtual switch. - -# Goals -One of the more lofty goals of this project was to run a VPN behind NAT without port forwarding. I am interested by Jordan Whited's post, [WireGuard Endpoint Discovery and NAT Traversal using DNS-SD](https://www.jordanwhited.com/posts/wireguard-endpoint-discovery-nat-traversal/) and hope to investigate the possibility of integrating it into this application at some point. From c90e2f7c7118c4ac3f72130696c6e0a231c5889e Mon Sep 17 00:00:00 2001 From: Micah Morrison Date: Sat, 5 Feb 2022 12:27:06 -0500 Subject: [PATCH 04/12] A couple more README tweaks --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 24badcc..b25d0a0 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ When enabling this feature, two actions are performed in Windows: 1. The `Internet Connection Sharing` service startup mode is changed from `Manual` to `Automatic`. 2. The value of the `EnableRebootPersistConnection` regstry value in `HKLM\Software\Microsoft\Windows\CurrentVersion\SharedAccess` is set to `1` (it is created if not found). -Note that even with these workarounds, Internet Sharing can become disabled after a reboot. Therefore, one more action is performed. A Scheduled Task is created that disables and re-enables Internet Sharing using the WS4W CLI upon system boot. This should be sufficient to guarantee that sharing stays persistent. +Even with these workarounds, Internet Sharing can become disabled after a reboot. Therefore, one more action is performed. A Scheduled Task is created that disables and re-enables Internet Sharing using the WS4W CLI upon system boot. This should be sufficient to guarantee that sharing remains enabled. ### View Server Status ![ServerStatus](https://i.imgur.com/dcSJXKU.png) From 68541d915789b7396b0a41e85eb8a0a64fd15b2f Mon Sep 17 00:00:00 2001 From: Micah Morrison Date: Wed, 9 Mar 2022 16:27:06 -0500 Subject: [PATCH 05/12] For #18: Start adding the New-NetNat functionality --- WireGuardAPI/Commands/WireGuardCommand.cs | 4 +- WireGuardAPI/WireGuardExe.cs | 18 ++- .../Controls/PrerequisiteItemControl.xaml | 83 +++++++--- .../Controls/PrerequisiteItemControl.xaml.cs | 20 ++- WireGuardServerForWindows/MainWindow.xaml | 2 +- WireGuardServerForWindows/MainWindow.xaml.cs | 44 +++++- .../Models/InternetSharingPrerequisite.cs | 2 + .../Models/NatPrerequisiteGroup.cs | 15 ++ .../Models/NewNetNatPrerequisite.cs | 144 ++++++++++++++++++ .../PersistentInternetSharingPrerequisite.cs | 2 + .../Models/PrerequisiteItem.cs | 24 ++- .../Models/ServerConfiguration.cs | 25 +++ .../Properties/Resources.Designer.cs | 63 ++++++++ .../Properties/Resources.resx | 21 +++ 14 files changed, 416 insertions(+), 51 deletions(-) create mode 100644 WireGuardServerForWindows/Models/NatPrerequisiteGroup.cs create mode 100644 WireGuardServerForWindows/Models/NewNetNatPrerequisite.cs diff --git a/WireGuardAPI/Commands/WireGuardCommand.cs b/WireGuardAPI/Commands/WireGuardCommand.cs index d434680..20adb41 100644 --- a/WireGuardAPI/Commands/WireGuardCommand.cs +++ b/WireGuardAPI/Commands/WireGuardCommand.cs @@ -1,8 +1,8 @@ namespace WireGuardAPI { - public abstract class WireGuardCommand + public class WireGuardCommand { - protected WireGuardCommand(string @switch, WhichExe whichExe, params string[] args) + public WireGuardCommand(string @switch, WhichExe whichExe, params string[] args) { Switch = @switch; WhichExe = whichExe; diff --git a/WireGuardAPI/WireGuardExe.cs b/WireGuardAPI/WireGuardExe.cs index 969a151..296d617 100644 --- a/WireGuardAPI/WireGuardExe.cs +++ b/WireGuardAPI/WireGuardExe.cs @@ -62,8 +62,14 @@ private string GetPath(WhichExe whichExe) #region Public methods public string ExecuteCommand(WireGuardCommand command) + { + return ExecuteCommand(command, out _); + } + + public string ExecuteCommand(WireGuardCommand command, out int exitCode) { string result = default; + exitCode = 1; switch (command.WhichExe) { @@ -76,16 +82,20 @@ public string ExecuteCommand(WireGuardCommand command) // For some reason, awaiting this can hang, so this method must do everything synchronously. var bufferedResult = cmd.ExecuteBufferedAsync().Task.Result; result = bufferedResult.StandardOutput.Trim(); + exitCode = bufferedResult.ExitCode; break; case WhichExe.Custom: - Process.Start(new ProcessStartInfo + Process process = Process.Start(new ProcessStartInfo { FileName = command.Args[0], Arguments = string.Join(' ', command.Args.Skip(1)), - Verb = "runas", - UseShellExecute = true, - })?.WaitForExit(); + CreateNoWindow = true, + RedirectStandardOutput = true + }); + process?.WaitForExit(); + result = process?.StandardOutput.ReadToEnd(); + exitCode = process?.ExitCode ?? 1; break; } diff --git a/WireGuardServerForWindows/Controls/PrerequisiteItemControl.xaml b/WireGuardServerForWindows/Controls/PrerequisiteItemControl.xaml index 37f3f11..6e47a6b 100644 --- a/WireGuardServerForWindows/Controls/PrerequisiteItemControl.xaml +++ b/WireGuardServerForWindows/Controls/PrerequisiteItemControl.xaml @@ -5,46 +5,83 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:models="clr-namespace:WireGuardServerForWindows.Models" xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding" + xmlns:controls="clr-namespace:WireGuardServerForWindows.Controls" mc:Ignorable="d" - d:DataContext="{d:DesignInstance Type={x:Type models:PrerequisiteItem}}" d:DesignWidth="800"> + d:DataContext="{d:DesignInstance Type={x:Type models:PrerequisiteItem}}" d:DesignWidth="800" + x:Name="This"> - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - + -