mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-01-19 05:33:31 +01:00
Ensure the ssh connection is trusted
This commit is contained in:
parent
ea2dd536b4
commit
322518e9dc
@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<Version>1.0.2.88</Version>
|
||||
<Version>1.0.2.89</Version>
|
||||
<NoWarn>NU1701,CA1816,CA1308,CA1810,CA2208</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
|
@ -12,6 +12,7 @@ using Microsoft.Extensions.Configuration;
|
||||
using NBXplorer;
|
||||
using BTCPayServer.Payments.Lightning;
|
||||
using Renci.SshNet;
|
||||
using NBitcoin.DataEncoders;
|
||||
|
||||
namespace BTCPayServer.Configuration
|
||||
{
|
||||
@ -72,7 +73,7 @@ namespace BTCPayServer.Configuration
|
||||
settings.Username = "root";
|
||||
}
|
||||
}
|
||||
else if(externalUrl != null)
|
||||
else if (externalUrl != null)
|
||||
{
|
||||
settings.Port = 22;
|
||||
settings.Username = "root";
|
||||
@ -182,7 +183,7 @@ namespace BTCPayServer.Configuration
|
||||
ExternalUrl = conf.GetOrDefault<Uri>("externalurl", null);
|
||||
|
||||
var sshSettings = SSHSettings.ParseConfiguration(conf);
|
||||
if (!string.IsNullOrEmpty(sshSettings.Password) || !string.IsNullOrEmpty(sshSettings.KeyFile))
|
||||
if ((!string.IsNullOrEmpty(sshSettings.Password) || !string.IsNullOrEmpty(sshSettings.KeyFile)) && !string.IsNullOrEmpty(sshSettings.Server))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(sshSettings.KeyFile) && !File.Exists(sshSettings.KeyFile))
|
||||
throw new ConfigException($"sshkeyfile does not exist");
|
||||
@ -199,10 +200,19 @@ namespace BTCPayServer.Configuration
|
||||
{
|
||||
throw new ConfigException($"sshkeyfilepassword is invalid");
|
||||
}
|
||||
|
||||
SSHSettings = sshSettings;
|
||||
}
|
||||
|
||||
var fingerPrints = conf.GetOrDefault<string>("sshtrustedfingerprints", "");
|
||||
if (!string.IsNullOrEmpty(fingerPrints))
|
||||
{
|
||||
foreach (var fingerprint in fingerPrints.Split(';', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(str => str.Replace(":", "", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
TrustedFingerprints.Add(DecodeFingerprint(fingerprint));
|
||||
}
|
||||
}
|
||||
|
||||
RootPath = conf.GetOrDefault<string>("rootpath", "/");
|
||||
if (!RootPath.StartsWith("/", StringComparison.InvariantCultureIgnoreCase))
|
||||
RootPath = "/" + RootPath;
|
||||
@ -210,6 +220,45 @@ namespace BTCPayServer.Configuration
|
||||
if (old != null)
|
||||
throw new ConfigException($"internallightningnode should not be used anymore, use btclightning instead");
|
||||
}
|
||||
|
||||
private static byte[] DecodeFingerprint(string fingerprint)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Encoders.Hex.DecodeData(fingerprint.Trim());
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
var localFingerprint = fingerprint;
|
||||
if (localFingerprint.StartsWith("SHA256", StringComparison.OrdinalIgnoreCase))
|
||||
localFingerprint = localFingerprint.Substring("SHA256".Length).Trim();
|
||||
try
|
||||
{
|
||||
return Encoders.Base64.DecodeData(localFingerprint);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
if (!localFingerprint.EndsWith('='))
|
||||
localFingerprint = localFingerprint + "=";
|
||||
try
|
||||
{
|
||||
return Encoders.Base64.DecodeData(localFingerprint);
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new ConfigException($"sshtrustedfingerprints is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsTrustedFingerprint(byte[] fingerPrint)
|
||||
{
|
||||
return TrustedFingerprints.Any(f => Utils.ArrayEqual(f, fingerPrint));
|
||||
}
|
||||
|
||||
public string RootPath { get; set; }
|
||||
public Dictionary<string, LightningConnectionString> InternalLightningByCryptoCode { get; set; } = new Dictionary<string, LightningConnectionString>();
|
||||
public ExternalServices ExternalServicesByCryptoCode { get; set; } = new ExternalServices();
|
||||
@ -230,6 +279,7 @@ namespace BTCPayServer.Configuration
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public List<byte[]> TrustedFingerprints { get; set; } = new List<byte[]>();
|
||||
public SSHSettings SSHSettings
|
||||
{
|
||||
get;
|
||||
|
@ -38,6 +38,7 @@ namespace BTCPayServer.Configuration
|
||||
app.Option("--sshpassword", "SSH password to manage BTCPay (default: empty)", CommandOptionType.SingleValue);
|
||||
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 SHA256 rsa fingerprint in base64 or hex (default: empty, it will allow untrusted connections)", CommandOptionType.SingleValue);
|
||||
foreach (var network in provider.GetAll())
|
||||
{
|
||||
var crypto = network.CryptoCode.ToLowerInvariant();
|
||||
|
@ -260,6 +260,29 @@ namespace BTCPayServer.Controllers
|
||||
ssh = $"sudo bash -c '. /etc/profile.d/btcpay-env.sh && nohup {ssh} > /dev/null 2>&1 & disown'";
|
||||
var sshClient = _Options.SSHSettings == null ? vm.CreateSSHClient(this.Request.Host.Host)
|
||||
: new SshClient(_Options.SSHSettings.CreateConnectionInfo());
|
||||
|
||||
if (_Options.TrustedFingerprints.Count != 0)
|
||||
{
|
||||
sshClient.HostKeyReceived += (object sender, Renci.SshNet.Common.HostKeyEventArgs e) =>
|
||||
{
|
||||
if (_Options.TrustedFingerprints.Count == 0)
|
||||
{
|
||||
Logs.Configuration.LogWarning($"SSH host fingerprint for {e.HostKey} is untrusted, start BTCPay with -sshtrustedfingerprints \"{Encoders.Hex.EncodeData(e.FingerPrint)}\"");
|
||||
e.CanTrust = true; // Not a typo, we want the connection to succeed with a warning
|
||||
}
|
||||
else
|
||||
{
|
||||
e.CanTrust = _Options.IsTrustedFingerprint(e.FingerPrint);
|
||||
if(!e.CanTrust)
|
||||
Logs.Configuration.LogError($"SSH host fingerprint for {e.HostKey} is untrusted, start BTCPay with -sshtrustedfingerprints \"{Encoders.Hex.EncodeData(e.FingerPrint)}\"");
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
sshClient.Connect();
|
||||
|
@ -10,6 +10,7 @@ using Microsoft.Extensions.Hosting;
|
||||
using System.Threading;
|
||||
using BTCPayServer.Configuration;
|
||||
using BTCPayServer.Logging;
|
||||
using NBitcoin.DataEncoders;
|
||||
|
||||
namespace BTCPayServer.HostedServices
|
||||
{
|
||||
@ -30,6 +31,14 @@ namespace BTCPayServer.HostedServices
|
||||
{
|
||||
Logs.Configuration.LogInformation($"SSH settings detected, testing connection to {_options.SSHSettings.Username}@{_options.SSHSettings.Server} on port {_options.SSHSettings.Port} ...");
|
||||
var connection = new Renci.SshNet.SshClient(_options.SSHSettings.CreateConnectionInfo());
|
||||
connection.HostKeyReceived += (object sender, Renci.SshNet.Common.HostKeyEventArgs e) =>
|
||||
{
|
||||
e.CanTrust = true;
|
||||
if (!_options.IsTrustedFingerprint(e.FingerPrint))
|
||||
{
|
||||
Logs.Configuration.LogWarning($"SSH host fingerprint for {e.HostKey} is untrusted, start BTCPay with -sshtrustedfingerprints \"{Encoders.Hex.EncodeData(e.FingerPrint)}\"");
|
||||
}
|
||||
};
|
||||
try
|
||||
{
|
||||
connection.Connect();
|
||||
|
Loading…
Reference in New Issue
Block a user