mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2024-11-19 09:54:30 +01:00
Parse torrc file to know virtual port of hidden services
This commit is contained in:
parent
4f582a6712
commit
ea02d77e69
@ -146,6 +146,46 @@ namespace BTCPayServer.Tests
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
[Trait("Fast", "Fast")]
|
||||
public void CanParseTorrc()
|
||||
{
|
||||
var nl = "\n";
|
||||
var input = "# For the hidden service BTCPayServer" + nl +
|
||||
"HiddenServiceDir /var/lib/tor/hidden_services/BTCPayServer" + nl +
|
||||
"# Redirecting to nginx" + nl +
|
||||
"HiddenServicePort 80 172.19.0.10:81";
|
||||
nl = Environment.NewLine;
|
||||
var expected = "HiddenServiceDir /var/lib/tor/hidden_services/BTCPayServer" + nl +
|
||||
"HiddenServicePort 80 172.19.0.10:81" + nl;
|
||||
Assert.True(Torrc.TryParse(input, out var torrc));
|
||||
Assert.Equal(expected, torrc.ToString());
|
||||
nl = "\r\n";
|
||||
input = "# For the hidden service BTCPayServer" + nl +
|
||||
"HiddenServiceDir /var/lib/tor/hidden_services/BTCPayServer" + nl +
|
||||
"# Redirecting to nginx" + nl +
|
||||
"HiddenServicePort 80 172.19.0.10:81";
|
||||
|
||||
Assert.True(Torrc.TryParse(input, out torrc));
|
||||
Assert.Equal(expected, torrc.ToString());
|
||||
|
||||
input = "# For the hidden service BTCPayServer" + nl +
|
||||
"HiddenServiceDir /var/lib/tor/hidden_services/BTCPayServer" + nl +
|
||||
"# Redirecting to nginx" + nl +
|
||||
"HiddenServicePort 80 172.19.0.10:80" + nl +
|
||||
"HiddenServiceDir /var/lib/tor/hidden_services/Woocommerce" + nl +
|
||||
"# Redirecting to nginx" + nl +
|
||||
"HiddenServicePort 80 172.19.0.11:80";
|
||||
nl = Environment.NewLine;
|
||||
expected = "HiddenServiceDir /var/lib/tor/hidden_services/BTCPayServer" + nl +
|
||||
"HiddenServicePort 80 172.19.0.10:80" + nl +
|
||||
"HiddenServiceDir /var/lib/tor/hidden_services/Woocommerce" + nl +
|
||||
"HiddenServicePort 80 172.19.0.11:80" + nl;
|
||||
Assert.True(Torrc.TryParse(input, out torrc));
|
||||
Assert.Equal(expected, torrc.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Fast", "Fast")]
|
||||
public void CanCalculateCryptoDue()
|
||||
@ -881,7 +921,7 @@ namespace BTCPayServer.Tests
|
||||
using (var tester = ServerTester.Create())
|
||||
{
|
||||
tester.Start();
|
||||
foreach(var req in new[]
|
||||
foreach (var req in new[]
|
||||
{
|
||||
"invoices/",
|
||||
"invoices",
|
||||
|
@ -149,7 +149,7 @@ namespace BTCPayServer.Configuration
|
||||
PostgresConnectionString = conf.GetOrDefault<string>("postgres", null);
|
||||
MySQLConnectionString = conf.GetOrDefault<string>("mysql", null);
|
||||
BundleJsCss = conf.GetOrDefault<bool>("bundlejscss", true);
|
||||
TorHiddenServicesDirectory = conf.GetOrDefault<string>("torservices", null);
|
||||
TorrcFile = conf.GetOrDefault<string>("torrcfile", null);
|
||||
|
||||
var sshSettings = ParseSSHConfiguration(conf);
|
||||
if ((!string.IsNullOrEmpty(sshSettings.Password) || !string.IsNullOrEmpty(sshSettings.KeyFile)) && !string.IsNullOrEmpty(sshSettings.Server))
|
||||
@ -274,6 +274,6 @@ namespace BTCPayServer.Configuration
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public string TorHiddenServicesDirectory { get; set; }
|
||||
public string TorrcFile { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ namespace BTCPayServer.Configuration
|
||||
app.Option("--sshkeyfile", "SSH private key file to manage BTCPay (default: empty)", CommandOptionType.SingleValue);
|
||||
app.Option("--sshkeyfilepassword", "Password of the SSH keyfile (default: empty)", CommandOptionType.SingleValue);
|
||||
app.Option("--sshtrustedfingerprints", "SSH Host public key fingerprint or sha256 (default: empty, it will allow untrusted connections)", CommandOptionType.SingleValue);
|
||||
app.Option("--torservices", "Path to folder containing hidden services directories (default: empty)", CommandOptionType.SingleValue);
|
||||
app.Option("--torrcfile", "Path to torrc file containing hidden services directories (default: empty)", CommandOptionType.SingleValue);
|
||||
app.Option("--debuglog", "A rolling log file for debug messages.", CommandOptionType.SingleValue);
|
||||
app.Option("--debugloglevel", "The severity you log (default:information)", CommandOptionType.SingleValue);
|
||||
app.Option("--disable-registration", "Disables new user registrations (default:true)", CommandOptionType.SingleValue);
|
||||
|
@ -466,7 +466,17 @@ namespace BTCPayServer.Controllers
|
||||
Link = this.Url.Action(nameof(SSHService))
|
||||
});
|
||||
}
|
||||
result.TorServices = await _torServices.GetServices();
|
||||
foreach(var torService in await _torServices.GetServices())
|
||||
{
|
||||
if (torService.VirtualPort == 80)
|
||||
{
|
||||
result.OtherExternalServices.Add(new ServicesViewModel.OtherExternalService()
|
||||
{
|
||||
Name = torService.Name,
|
||||
Link = $"http://{torService.OnionHost}"
|
||||
});
|
||||
}
|
||||
}
|
||||
return View(result);
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,6 @@ namespace BTCPayServer.Models.ServerViewModels
|
||||
|
||||
public List<ExternalService> ExternalServices { get; set; } = new List<ExternalService>();
|
||||
public List<OtherExternalService> OtherExternalServices { get; set; } = new List<OtherExternalService>();
|
||||
public TorService[] TorServices { get; set; } = Array.Empty<TorService>();
|
||||
public List<OtherExternalService> TorServices { get; set; } = new List<OtherExternalService>();
|
||||
}
|
||||
}
|
||||
|
@ -17,20 +17,31 @@ namespace BTCPayServer.Services
|
||||
|
||||
public async Task<TorService[]> GetServices()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_Options.TorHiddenServicesDirectory) || !Directory.Exists(_Options.TorHiddenServicesDirectory))
|
||||
if (string.IsNullOrEmpty(_Options.TorrcFile) || !File.Exists(_Options.TorrcFile))
|
||||
return Array.Empty<TorService>();
|
||||
List<TorService> result = new List<TorService>();
|
||||
var servicesDirs = Directory.GetDirectories(_Options.TorHiddenServicesDirectory);
|
||||
var services = servicesDirs
|
||||
.Select(d => new DirectoryInfo(d))
|
||||
.Select(d => (ServiceName: d.Name, ReadingLines: System.IO.File.ReadAllLinesAsync(Path.Combine(d.FullName, "hostname"))))
|
||||
try
|
||||
{
|
||||
var torrcContent = await File.ReadAllTextAsync(_Options.TorrcFile);
|
||||
if (!Torrc.TryParse(torrcContent, out var torrc))
|
||||
return Array.Empty<TorService>();
|
||||
|
||||
var services = torrc.ServiceDirectories.SelectMany(d => d.ServicePorts.Select(p => (Directory: new DirectoryInfo(d.DirectoryPath), VirtualPort: p.VirtualPort)))
|
||||
.Select(d => (ServiceName: d.Directory.Name,
|
||||
ReadingLines: System.IO.File.ReadAllLinesAsync(Path.Combine(d.Directory.FullName, "hostname")),
|
||||
VirtualPort: d.VirtualPort))
|
||||
.ToArray();
|
||||
foreach (var service in services)
|
||||
{
|
||||
try
|
||||
{
|
||||
var onionHost = (await service.ReadingLines)[0].Trim();
|
||||
var torService = new TorService() { Name = service.ServiceName, OnionHost = onionHost };
|
||||
var torService = new TorService()
|
||||
{
|
||||
Name = service.ServiceName,
|
||||
OnionHost = onionHost,
|
||||
VirtualPort = service.VirtualPort
|
||||
};
|
||||
if (service.ServiceName.Equals("BTCPayServer", StringComparison.OrdinalIgnoreCase))
|
||||
torService.ServiceType = TorServiceType.BTCPayServer;
|
||||
result.Add(torService);
|
||||
@ -40,6 +51,10 @@ namespace BTCPayServer.Services
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
return result.ToArray();
|
||||
}
|
||||
}
|
||||
@ -49,6 +64,7 @@ namespace BTCPayServer.Services
|
||||
public TorServiceType ServiceType { get; set; } = TorServiceType.Other;
|
||||
public string Name { get; set; }
|
||||
public string OnionHost { get; set; }
|
||||
public int VirtualPort { get; set; }
|
||||
}
|
||||
|
||||
public enum TorServiceType
|
||||
|
100
BTCPayServer/Services/Torrc.cs
Normal file
100
BTCPayServer/Services/Torrc.cs
Normal file
@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Services
|
||||
{
|
||||
public class Torrc
|
||||
{
|
||||
public static bool TryParse(string str, out Torrc value)
|
||||
{
|
||||
value = null;
|
||||
List<HiddenServiceDir> serviceDirectories = new List<HiddenServiceDir>();
|
||||
var lines = str.Split(new char[] { '\n' });
|
||||
HiddenServiceDir currentDirectory = null;
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (HiddenServiceDir.TryParse(line, out var dir))
|
||||
{
|
||||
serviceDirectories.Add(dir);
|
||||
currentDirectory = dir;
|
||||
}
|
||||
else if (HiddenServicePortDefinition.TryParse(line, out var portDef) && currentDirectory != null)
|
||||
{
|
||||
currentDirectory.ServicePorts.Add(portDef);
|
||||
}
|
||||
}
|
||||
value = new Torrc() { ServiceDirectories = serviceDirectories };
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<HiddenServiceDir> ServiceDirectories { get; set; } = new List<HiddenServiceDir>();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
foreach(var serviceDir in ServiceDirectories)
|
||||
{
|
||||
builder.AppendLine(serviceDir.ToString());
|
||||
foreach (var port in serviceDir.ServicePorts)
|
||||
builder.AppendLine(port.ToString());
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public class HiddenServiceDir
|
||||
{
|
||||
public static bool TryParse(string str, out HiddenServiceDir serviceDir)
|
||||
{
|
||||
serviceDir = null;
|
||||
if (!str.Trim().StartsWith("HiddenServiceDir ", StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
var parts = str.Split(new char[] { ' ', '\t' }, StringSplitOptions.None);
|
||||
if (parts.Length != 2)
|
||||
return false;
|
||||
serviceDir = new HiddenServiceDir() { DirectoryPath = parts[1].Trim() };
|
||||
return true;
|
||||
}
|
||||
|
||||
public string DirectoryPath { get; set; }
|
||||
public List<HiddenServicePortDefinition> ServicePorts { get; set; } = new List<HiddenServicePortDefinition>();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"HiddenServiceDir {DirectoryPath}";
|
||||
}
|
||||
}
|
||||
public class HiddenServicePortDefinition
|
||||
{
|
||||
public static bool TryParse(string str, out HiddenServicePortDefinition portDefinition)
|
||||
{
|
||||
portDefinition = null;
|
||||
if (!str.Trim().StartsWith("HiddenServicePort ", StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
var parts = str.Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length != 3)
|
||||
return false;
|
||||
if (!int.TryParse(parts[1].Trim(), out int virtualPort))
|
||||
return false;
|
||||
var addressPort = parts[2].Trim().Split(':', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (addressPort.Length != 2)
|
||||
return false;
|
||||
if (!int.TryParse(addressPort[1].Trim(), out int port))
|
||||
return false;
|
||||
if (!IPAddress.TryParse(addressPort[0].Trim(), out IPAddress address))
|
||||
return false;
|
||||
portDefinition = new HiddenServicePortDefinition() { VirtualPort = virtualPort, Endpoint = new IPEndPoint(address, port) };
|
||||
return true;
|
||||
}
|
||||
public int VirtualPort { get; set; }
|
||||
public IPEndPoint Endpoint { get; set; }
|
||||
public override string ToString()
|
||||
{
|
||||
return $"HiddenServicePort {VirtualPort} {Endpoint}";
|
||||
}
|
||||
}
|
||||
}
|
@ -85,7 +85,7 @@
|
||||
<div class="col-md-8">
|
||||
<h4>TOR hidden services</h4>
|
||||
<div class="form-group">
|
||||
<span>TOR services hosted on this server, services using auto service registration (like lightning services) will not appear here.</span>
|
||||
<span>TOR services hosted on this server, only http servers are listed here.</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<table class="table table-sm table-responsive-md">
|
||||
@ -100,7 +100,7 @@
|
||||
{
|
||||
<tr>
|
||||
<td>@s.Name</td>
|
||||
<td style="text-align:right">@s.OnionHost</td>
|
||||
<a href="@s.Link" target="_blank">See information</a>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
|
Loading…
Reference in New Issue
Block a user