Skip to content

Commit

Permalink
#115 refactor: better system abstraction
Browse files Browse the repository at this point in the history
  • Loading branch information
joseantmazonsb committed Feb 27, 2022
1 parent 121c78e commit fadc76c
Show file tree
Hide file tree
Showing 29 changed files with 205 additions and 189 deletions.
2 changes: 1 addition & 1 deletion Linguard/Cli/CliStartup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public void ConfigureServices(IServiceCollection services) {
services.AddTransient<IWorkingDirectory, WorkingDirectory>();
services.AddSingleton<IConfigurationSerializer>(DefaultYamlConfigurationSerializer.Instance);
services.AddTransient<ILogger, NLogLogger>();
services.AddTransient<ICommandRunner, CommandRunner>();
services.AddTransient<ISystemWrapper, Core.OS.SystemWrapper>();
services.AddTransient<IWireguardService, WireguardService>();
services.AddTransient<IInterfaceGenerator, DefaultInterfaceGenerator>();
services.AddTransient<IClientGenerator, DefaultClientGenerator>();
Expand Down
4 changes: 1 addition & 3 deletions Linguard/Core.Test/ClientShould.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -14,8 +13,7 @@ namespace Core.Test;
public class ClientShould {
private static readonly Mock<IConfigurationManager> 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() {

Expand Down
4 changes: 1 addition & 3 deletions Linguard/Core.Test/InterfaceShould.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -16,8 +15,7 @@ namespace Core.Test;
public class InterfaceShould {
private static readonly Mock<IConfigurationManager> 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() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
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<IConfigurationManager> 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();
}

[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();
}

[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();
Expand Down
18 changes: 9 additions & 9 deletions Linguard/Core/Managers/ConfigurationManagerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down Expand Up @@ -44,12 +44,12 @@ private void LoadTrafficDefaults() {
}
private void LoadWireguardDefaults() {
Configuration.Wireguard.Interfaces = new HashSet<Interface>();
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);
Expand Down
4 changes: 2 additions & 2 deletions Linguard/Core/Managers/DefaultFileConfigurationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion Linguard/Core/Managers/FileConfigurationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions Linguard/Core/Managers/YamlConfigurationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
}
}
14 changes: 0 additions & 14 deletions Linguard/Core/OS/CommandRunner.cs

This file was deleted.

5 changes: 0 additions & 5 deletions Linguard/Core/OS/ICommandRunner.cs

This file was deleted.

16 changes: 16 additions & 0 deletions Linguard/Core/OS/ISystemWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Net.NetworkInformation;
using Linguard.Core.Models.Wireguard;

namespace Linguard.Core.OS;

/// <summary>
/// An abstraction of the system running the app.
/// </summary>
public interface ISystemWrapper {
IEnumerable<NetworkInterface> NetworkInterfaces { get; }
ICommandResult RunCommand(string command);
void AddNetworkInterface(Interface iface);
void RemoveNetworkInterface(Interface iface);
bool IsInterfaceUp(Interface iface);
bool IsInterfaceDown(Interface iface);
}
Original file line number Diff line number Diff line change
@@ -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<NetworkInterface> 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);
Expand All @@ -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);
}
}
13 changes: 0 additions & 13 deletions Linguard/Core/Services/IInterfaceService.cs

This file was deleted.

5 changes: 5 additions & 0 deletions Linguard/Core/Services/IWireguardService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
59 changes: 42 additions & 17 deletions Linguard/Core/Services/WireguardService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand All @@ -85,13 +110,13 @@ public IEnumerable<TrafficData> 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<TrafficData> 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<TrafficData>()
Expand Down
Loading

0 comments on commit fadc76c

Please sign in to comment.