diff --git a/Linguard/Cli/CliStartup.cs b/Linguard/Cli/CliStartup.cs index 0a87371..8b062d7 100644 --- a/Linguard/Cli/CliStartup.cs +++ b/Linguard/Cli/CliStartup.cs @@ -23,7 +23,7 @@ public void ConfigureServices(IServiceCollection services) { services.AddTransient(); services.AddSingleton(DefaultYamlConfigurationSerializer.Instance); services.AddTransient(); - services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/Linguard/Core.Test/ClientShould.cs b/Linguard/Core.Test/ClientShould.cs index 50c0305..544a963 100644 --- a/Linguard/Core.Test/ClientShould.cs +++ b/Linguard/Core.Test/ClientShould.cs @@ -4,7 +4,6 @@ using Linguard.Core; using Linguard.Core.Managers; using Linguard.Core.Models.Wireguard; -using Linguard.Core.OS; using Linguard.Core.Services; using Moq; using Xunit; @@ -14,8 +13,7 @@ namespace Core.Test; public class ClientShould { private static readonly Mock ConfigurationManagerMock = new DefaultConfigurationManager(); private static IWireguardService WireguardService => - new WireguardService(ConfigurationManagerMock.Object, new CommandRunner(), - new InterfaceService(ConfigurationManagerMock.Object, new CommandRunner())); + new WireguardService(ConfigurationManagerMock.Object, new Linguard.Core.OS.SystemWrapper(ConfigurationManagerMock.Object)); [Fact] public void CreateValidWireguardConfig() { diff --git a/Linguard/Core.Test/InterfaceShould.cs b/Linguard/Core.Test/InterfaceShould.cs index 7e5fb63..ee51a93 100644 --- a/Linguard/Core.Test/InterfaceShould.cs +++ b/Linguard/Core.Test/InterfaceShould.cs @@ -7,7 +7,6 @@ using Linguard.Core.Managers; using Xunit; using Linguard.Core.Models.Wireguard; -using Linguard.Core.OS; using Linguard.Core.Services; using Moq; @@ -16,8 +15,7 @@ namespace Core.Test; public class InterfaceShould { private static readonly Mock ConfigurationManagerMock = new DefaultConfigurationManager(); private static IWireguardService WireguardService => - new WireguardService(ConfigurationManagerMock.Object, new CommandRunner(), - new InterfaceService(ConfigurationManagerMock.Object, new CommandRunner())); + new WireguardService(ConfigurationManagerMock.Object, new Linguard.Core.OS.SystemWrapper(ConfigurationManagerMock.Object)); [Fact] public void CreateValidWireguardConfiguration() { diff --git a/Linguard/Core.Test/CommandRunnerShould.cs b/Linguard/Core.Test/SystemShould.cs similarity index 52% rename from Linguard/Core.Test/CommandRunnerShould.cs rename to Linguard/Core.Test/SystemShould.cs index 7598774..1d13e3e 100644 --- a/Linguard/Core.Test/CommandRunnerShould.cs +++ b/Linguard/Core.Test/SystemShould.cs @@ -1,13 +1,20 @@ -using FluentAssertions; +using Core.Test.Mocks; +using FluentAssertions; +using Linguard.Core.Managers; using Linguard.Core.OS; +using Moq; using Xunit; namespace Core.Test; -public class CommandRunnerShould { +public class SystemShould { + + private static readonly Mock ConfigurationManagerMock = new DefaultConfigurationManager(); + private readonly ISystemWrapper _systemWrapper = new Linguard.Core.OS.SystemWrapper(ConfigurationManagerMock.Object); + [Fact] public void RunSingleCommand() { - var result = new CommandRunner().Run("gci"); + var result = _systemWrapper.RunCommand("gci"); result.Success.Should().BeTrue(); result.Stdout.Should().NotBeEmpty(); result.Stderr.Should().BeEmpty(); @@ -15,7 +22,7 @@ public void RunSingleCommand() { [Fact] public void RunPipedCommands() { - var result = new CommandRunner().Run("gci | sort-object -Property Name"); + var result = _systemWrapper.RunCommand("gci | sort-object -Property Name"); result.Success.Should().BeTrue(); result.Stdout.Should().NotBeEmpty(); result.Stderr.Should().BeEmpty(); @@ -23,7 +30,7 @@ public void RunPipedCommands() { [Fact] public void RunCommandsWithoutPiping() { - var result = new CommandRunner().Run("gci; get-process"); + var result = _systemWrapper.RunCommand("gci; get-process"); result.Success.Should().BeTrue(); result.Stdout.Should().NotBeEmpty(); result.Stderr.Should().BeEmpty(); diff --git a/Linguard/Core/Managers/ConfigurationManagerBase.cs b/Linguard/Core/Managers/ConfigurationManagerBase.cs index db25e51..4163a61 100644 --- a/Linguard/Core/Managers/ConfigurationManagerBase.cs +++ b/Linguard/Core/Managers/ConfigurationManagerBase.cs @@ -10,13 +10,13 @@ namespace Linguard.Core.Managers; public abstract class ConfigurationManagerBase : IConfigurationManager { - private readonly ICommandRunner _commandRunner; + private readonly ISystemWrapper _systemWrapper; protected ConfigurationManagerBase(IConfiguration configuration, IWorkingDirectory workingDirectory, - ICommandRunner commandRunner) { + ISystemWrapper systemWrapper) { Configuration = configuration; WorkingDirectory = workingDirectory; - _commandRunner = commandRunner; + _systemWrapper = systemWrapper; } public IConfiguration Configuration { get; set; } @@ -44,12 +44,12 @@ private void LoadTrafficDefaults() { } private void LoadWireguardDefaults() { Configuration.Wireguard.Interfaces = new HashSet(); - Configuration.Wireguard.IptablesBin = _commandRunner - .Run("whereis iptables | tr ' ' '\n' | grep bin").Stdout; - Configuration.Wireguard.WireguardBin = _commandRunner - .Run("whereis wg | tr ' ' '\n' | grep bin").Stdout; - Configuration.Wireguard.WireguardQuickBin = _commandRunner - .Run("whereis wg-quick | tr ' ' '\n' | grep bin").Stdout; + Configuration.Wireguard.IptablesBin = _systemWrapper + .RunCommand("whereis iptables | tr ' ' '\n' | grep bin").Stdout; + Configuration.Wireguard.WireguardBin = _systemWrapper + .RunCommand("whereis wg | tr ' ' '\n' | grep bin").Stdout; + Configuration.Wireguard.WireguardQuickBin = _systemWrapper + .RunCommand("whereis wg-quick | tr ' ' '\n' | grep bin").Stdout; Configuration.Wireguard.Interfaces = new(); Configuration.Wireguard.PrimaryDns = new("8.8.8.8", UriKind.RelativeOrAbsolute); Configuration.Wireguard.SecondaryDns = new("8.8.4.4", UriKind.RelativeOrAbsolute); diff --git a/Linguard/Core/Managers/DefaultFileConfigurationManager.cs b/Linguard/Core/Managers/DefaultFileConfigurationManager.cs index e19479c..4ba22b7 100644 --- a/Linguard/Core/Managers/DefaultFileConfigurationManager.cs +++ b/Linguard/Core/Managers/DefaultFileConfigurationManager.cs @@ -8,8 +8,8 @@ namespace Linguard.Core.Managers; public abstract class DefaultFileConfigurationManager : FileConfigurationManager { protected DefaultFileConfigurationManager(IConfiguration configuration, IWorkingDirectory workingDirectory, - ICommandRunner commandRunner, IConfigurationSerializer serializer) - : base(configuration, workingDirectory, commandRunner, serializer) { + ISystemWrapper systemWrapper, IConfigurationSerializer serializer) + : base(configuration, workingDirectory, systemWrapper, serializer) { } private FileInfo? _configurationFile; diff --git a/Linguard/Core/Managers/FileConfigurationManager.cs b/Linguard/Core/Managers/FileConfigurationManager.cs index efb2e9a..4b368fd 100644 --- a/Linguard/Core/Managers/FileConfigurationManager.cs +++ b/Linguard/Core/Managers/FileConfigurationManager.cs @@ -8,7 +8,7 @@ namespace Linguard.Core.Managers; public abstract class FileConfigurationManager : ConfigurationManagerBase { protected FileConfigurationManager(IConfiguration configuration, IWorkingDirectory workingDirectory, - ICommandRunner commandRunner, IConfigurationSerializer serializer) : base(configuration, workingDirectory, commandRunner) { + ISystemWrapper systemWrapper, IConfigurationSerializer serializer) : base(configuration, workingDirectory, systemWrapper) { Configuration = configuration; WorkingDirectory = workingDirectory; Serializer = serializer; diff --git a/Linguard/Core/Managers/YamlConfigurationManager.cs b/Linguard/Core/Managers/YamlConfigurationManager.cs index 407ac1a..27c2731 100644 --- a/Linguard/Core/Managers/YamlConfigurationManager.cs +++ b/Linguard/Core/Managers/YamlConfigurationManager.cs @@ -11,7 +11,7 @@ public class YamlConfigurationManager : DefaultFileConfigurationManager { }; public YamlConfigurationManager(IConfiguration configuration, IWorkingDirectory workingDirectory, - ICommandRunner commandRunner, IConfigurationSerializer serializer) - : base(configuration, workingDirectory, commandRunner, serializer) { + ISystemWrapper systemWrapper, IConfigurationSerializer serializer) + : base(configuration, workingDirectory, systemWrapper, serializer) { } } \ No newline at end of file diff --git a/Linguard/Core/OS/CommandRunner.cs b/Linguard/Core/OS/CommandRunner.cs deleted file mode 100644 index 95e6c61..0000000 --- a/Linguard/Core/OS/CommandRunner.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Management.Automation; - -namespace Linguard.Core.OS; - -public class CommandRunner : ICommandRunner { - - public ICommandResult Run(string command) { - var ps = PowerShell.Create(); - var results = ps.AddScript(command).Invoke(); - var stdout = string.Join(Environment.NewLine, results); - var stderr = string.Join(Environment.NewLine, ps.Streams.Error); - return new CommandResult(stdout, stderr, !ps.HadErrors); - } -} \ No newline at end of file diff --git a/Linguard/Core/OS/ICommandRunner.cs b/Linguard/Core/OS/ICommandRunner.cs deleted file mode 100644 index 6ddd7a5..0000000 --- a/Linguard/Core/OS/ICommandRunner.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Linguard.Core.OS; - -public interface ICommandRunner { - ICommandResult Run(string command); -} \ No newline at end of file diff --git a/Linguard/Core/OS/ISystemWrapper.cs b/Linguard/Core/OS/ISystemWrapper.cs new file mode 100644 index 0000000..7b22b83 --- /dev/null +++ b/Linguard/Core/OS/ISystemWrapper.cs @@ -0,0 +1,16 @@ +using System.Net.NetworkInformation; +using Linguard.Core.Models.Wireguard; + +namespace Linguard.Core.OS; + +/// +/// An abstraction of the system running the app. +/// +public interface ISystemWrapper { + IEnumerable NetworkInterfaces { get; } + ICommandResult RunCommand(string command); + void AddNetworkInterface(Interface iface); + void RemoveNetworkInterface(Interface iface); + bool IsInterfaceUp(Interface iface); + bool IsInterfaceDown(Interface iface); +} \ No newline at end of file diff --git a/Linguard/Core/Services/InterfaceService.cs b/Linguard/Core/OS/SystemWrapper.cs similarity index 51% rename from Linguard/Core/Services/InterfaceService.cs rename to Linguard/Core/OS/SystemWrapper.cs index 309ff58..43e8722 100644 --- a/Linguard/Core/Services/InterfaceService.cs +++ b/Linguard/Core/OS/SystemWrapper.cs @@ -1,29 +1,41 @@ -using System.Net.NetworkInformation; +using System.Management.Automation; +using System.Net.NetworkInformation; using Linguard.Core.Configuration; using Linguard.Core.Managers; using Linguard.Core.Models.Wireguard; -using Linguard.Core.OS; using Linguard.Core.Services.Exceptions; -namespace Linguard.Core.Services; +namespace Linguard.Core.OS; -public class InterfaceService : IInterfaceService { - +public class SystemWrapper : ISystemWrapper { private readonly IConfigurationManager _configurationManager; - private readonly ICommandRunner _commandRunner; - - public InterfaceService(IConfigurationManager configurationManager, ICommandRunner commandRunner) { + + public SystemWrapper(IConfigurationManager configurationManager) { _configurationManager = configurationManager; - _commandRunner = commandRunner; } private IWireguardConfiguration Configuration => _configurationManager.Configuration.Wireguard; - + public IEnumerable NetworkInterfaces => NetworkInterface.GetAllNetworkInterfaces(); + + public ICommandResult RunCommand(string command) { + var ps = PowerShell.Create(); + var results = ps.AddScript(command).Invoke(); + var stdout = string.Join(Environment.NewLine, results); + var stderr = string.Join(Environment.NewLine, ps.Streams.Error); + return new CommandResult(stdout, stderr, !ps.HadErrors); + } - public Interface? GetInterface(Client client) => Configuration.Interfaces - .SingleOrDefault(i => i.Clients.Contains(client)); + public void AddNetworkInterface(Interface iface) { + var result = RunCommand($"sudo {Configuration.WireguardQuickBin} up {iface.Name}"); + if (!result.Success) throw new WireguardException(result.Stderr); + } + public void RemoveNetworkInterface(Interface iface) { + var result = RunCommand($"sudo {Configuration.WireguardQuickBin} up {iface.Name}"); + if (!result.Success) throw new WireguardException(result.Stderr); + } + public bool IsInterfaceUp(Interface iface) { return NetworkInterfaces .Any(i => i.Name.Equals(iface.Name) && i.OperationalStatus == OperationalStatus.Up); @@ -32,16 +44,4 @@ public bool IsInterfaceUp(Interface iface) { public bool IsInterfaceDown(Interface iface) { return !IsInterfaceUp(iface); } - - public void StartInterface(Interface @interface) { - var result = _commandRunner - .Run($"sudo {Configuration.WireguardQuickBin} up {@interface.Name}"); - if (!result.Success) throw new WireguardException(result.Stderr); - } - - public void StopInterface(Interface @interface) { - var result = _commandRunner - .Run($"sudo {Configuration.WireguardQuickBin} down {@interface.Name}"); - if (!result.Success) throw new WireguardException(result.Stderr); - } } \ No newline at end of file diff --git a/Linguard/Core/Services/IInterfaceService.cs b/Linguard/Core/Services/IInterfaceService.cs deleted file mode 100644 index c15917c..0000000 --- a/Linguard/Core/Services/IInterfaceService.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Net.NetworkInformation; -using Linguard.Core.Models.Wireguard; - -namespace Linguard.Core.Services; - -public interface IInterfaceService { - IEnumerable NetworkInterfaces { get; } - Interface? GetInterface(Client client); - bool IsInterfaceUp(Interface iface); - bool IsInterfaceDown(Interface iface); - void StartInterface(Interface @interface); - void StopInterface(Interface @interface); -} \ No newline at end of file diff --git a/Linguard/Core/Services/IWireguardService.cs b/Linguard/Core/Services/IWireguardService.cs index 8f137d9..aa81c29 100644 --- a/Linguard/Core/Services/IWireguardService.cs +++ b/Linguard/Core/Services/IWireguardService.cs @@ -5,6 +5,11 @@ namespace Linguard.Core.Services; public interface IWireguardService { + Interface? GetInterface(Client client); + void StartInterface(Interface iface); + void StopInterface(Interface iface); + void AddClient(Interface iface, Client client); + void RemoveClient(Interface iface, Client client); string? GenerateWireguardPrivateKey(); string? GenerateWireguardPublicKey(string privateKey); string[] GenerateOnUpRules(string interfaceName, NetworkInterface gateway); diff --git a/Linguard/Core/Services/WireguardService.cs b/Linguard/Core/Services/WireguardService.cs index 46f06d3..ffbb937 100644 --- a/Linguard/Core/Services/WireguardService.cs +++ b/Linguard/Core/Services/WireguardService.cs @@ -12,28 +12,53 @@ namespace Linguard.Core.Services; public class WireguardService : IWireguardService { private readonly IConfigurationManager _configurationManager; - private readonly ICommandRunner _commandRunner; - private readonly IInterfaceService _interfaceService; + private readonly ISystemWrapper _systemWrapper; - public WireguardService(IConfigurationManager configurationManager, ICommandRunner commandRunner, - IInterfaceService interfaceService) { + public WireguardService(IConfigurationManager configurationManager, ISystemWrapper systemWrapper) { _configurationManager = configurationManager; - _commandRunner = commandRunner; - _interfaceService = interfaceService; + _systemWrapper = systemWrapper; } private IWireguardConfiguration Configuration => _configurationManager.Configuration.Wireguard; - public string? GenerateWireguardPrivateKey() { - var result = _commandRunner - .Run($"sudo {Configuration.WireguardBin} genkey"); + public Interface? GetInterface(Client client) => Configuration.Interfaces + .SingleOrDefault(i => i.Clients.Contains(client)); + + public void StartInterface(Interface @interface) { + var result = _systemWrapper + .RunCommand($"sudo {Configuration.WireguardQuickBin} up {@interface.Name}"); + if (!result.Success) throw new WireguardException(result.Stderr); + } + + public void StopInterface(Interface @interface) { + var result = _systemWrapper + .RunCommand($"sudo {Configuration.WireguardQuickBin} down {@interface.Name}"); + if (!result.Success) throw new WireguardException(result.Stderr); + } + + public void AddClient(Interface iface, Client client) { + var cmd = $"sudo {Configuration.WireguardQuickBin} set {iface.Name} peer {client.PublicKey} " + + $"allowed-ips {string.Join(",", client.AllowedIPs)}"; + var result = _systemWrapper.RunCommand(cmd); + if (!result.Success) throw new WireguardException(result.Stderr); + } + + public void RemoveClient(Interface iface, Client client) { + var cmd = $"sudo {Configuration.WireguardQuickBin} set {iface.Name} peer {client.PublicKey} remove"; + var result = _systemWrapper.RunCommand(cmd); + if (!result.Success) throw new WireguardException(result.Stderr); + } + + public string GenerateWireguardPrivateKey() { + var result = _systemWrapper + .RunCommand($"sudo {Configuration.WireguardBin} genkey"); if (!result.Success) throw new WireguardException(result.Stderr); return result.Stdout; } - public string? GenerateWireguardPublicKey(string privateKey) { - var result = _commandRunner - .Run($"echo {privateKey} | sudo {Configuration.WireguardBin} pubkey"); + public string GenerateWireguardPublicKey(string privateKey) { + var result = _systemWrapper + .RunCommand($"echo {privateKey} | sudo {Configuration.WireguardBin} pubkey"); if (!result.Success) throw new WireguardException(result.Stderr); return result.Stdout; } @@ -65,8 +90,8 @@ public string GenerateWireguardConfiguration(IWireguardPeer peer) { } public DateTime GetLastHandshake(Client client) { - var rawData = _commandRunner - .Run($"{Configuration.WireguardBin} show {_interfaceService.GetInterface(client).Name} dump") + var rawData = _systemWrapper + .RunCommand($"{Configuration.WireguardBin} show {GetInterface(client).Name} dump") .Stdout; try { return WireguardDumpParser.GetLastHandshake(rawData, client); @@ -85,13 +110,13 @@ public IEnumerable GetTrafficData() { } public TrafficData? GetTrafficData(Client client) { - var data = GetTrafficData(_interfaceService.GetInterface(client)); + var data = GetTrafficData(GetInterface(client)); return data.SingleOrDefault(e => e.Peer.Equals(client)); } public IEnumerable GetTrafficData(Interface iface) { - var rawData = _commandRunner - .Run($"{Configuration.WireguardBin} show {iface.Name} dump") + var rawData = _systemWrapper + .RunCommand($"{Configuration.WireguardBin} show {iface.Name} dump") .Stdout; return string.IsNullOrEmpty(rawData) ? Enumerable.Empty() diff --git a/Linguard/Web/Pages/EditInterface.razor b/Linguard/Web/Pages/EditInterface.razor index 91e313c..ed6d449 100644 --- a/Linguard/Web/Pages/EditInterface.razor +++ b/Linguard/Web/Pages/EditInterface.razor @@ -5,6 +5,7 @@ @using Linguard.Core.Managers @using Linguard.Core.Configuration @using FluentValidation +@using Linguard.Core.OS @using Linguard.Web.Services @inject IConfigurationManager _configurationManager @@ -15,7 +16,7 @@ @inject NavigationManager _navigationManager @inject IJSRuntime _js @inject AbstractValidator _validator -@inject IInterfaceService _interfaceService +@inject ISystemWrapper _systemWrapper @code { const string Title = "Interface"; @@ -37,7 +38,7 @@

@Title - @if (_interfaceService.IsInterfaceUp(Iface)) { + @if (_systemWrapper.IsInterfaceUp(Iface)) { } else { diff --git a/Linguard/Web/Pages/Network.razor b/Linguard/Web/Pages/Network.razor index 21168e1..a523144 100644 --- a/Linguard/Web/Pages/Network.razor +++ b/Linguard/Web/Pages/Network.razor @@ -2,11 +2,7 @@ @using Linguard.Core.Utils @using System.Net.NetworkInformation @using System.Net.Sockets -@using Linguard.Core.Services - -@code { - const string Title = "Network"; -} +@using Linguard.Core.OS @($"{AssemblyInfo.Product} | {Title}") @@ -35,8 +31,9 @@ -@inject IInterfaceService _interfaceService +@inject ISystemWrapper _systemWrapper @code { + const string Title = "Network"; class NetworkInterfaceViewModel { public string Name { get; set; } @@ -51,7 +48,7 @@ protected override void OnInitialized() { _interfaces = new List(); - var interfaces = _interfaceService.NetworkInterfaces + var interfaces = _systemWrapper.NetworkInterfaces .OrderBy(i => i.Name) .ToList(); foreach (var iface in interfaces) { diff --git a/Linguard/Web/Pages/Wireguard.razor b/Linguard/Web/Pages/Wireguard.razor index ec82a59..51402bc 100644 --- a/Linguard/Web/Pages/Wireguard.razor +++ b/Linguard/Web/Pages/Wireguard.razor @@ -4,17 +4,10 @@ @using Linguard.Core.Managers @using System.Net.NetworkInformation @using Linguard.Core.Configuration +@using Linguard.Core.OS @using Linguard.Core.Services @using Linguard.Web.Services -@inject IConfigurationManager _configurationManager -@inject IInterfaceService _interfaceService -@inject IWebService _webService -@inject NotificationService _notificationService -@inject DialogService _dialogService -@inject NavigationManager _navigationManager -@inject IJSRuntime _js - @($"{AssemblyInfo.Product} | {Title}") @@ -39,7 +32,7 @@