Ensure the ssh connection is trusted

This commit is contained in:
nicolas.dorier 2018-08-12 23:23:26 +09:00
parent ea2dd536b4
commit 322518e9dc
5 changed files with 87 additions and 4 deletions

View File

@ -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>

View File

@ -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;

View File

@ -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();

View File

@ -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();

View File

@ -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();