Try to read the authorized keys file from the configuration

This commit is contained in:
nicolas.dorier 2019-09-20 18:51:14 +09:00
parent a8e2a99faa
commit 9e107b1eb1
No known key found for this signature in database
GPG Key ID: 6618763EF09186FE
6 changed files with 69 additions and 18 deletions

View File

@ -245,6 +245,7 @@ namespace BTCPayServer.Configuration
}
settings.Password = conf.GetOrDefault<string>("sshpassword", "");
settings.KeyFile = conf.GetOrDefault<string>("sshkeyfile", "");
settings.AuthorizedKeysFile = conf.GetOrDefault<string>("sshauthorizedkeys", "");
settings.KeyFilePassword = conf.GetOrDefault<string>("sshkeyfilepassword", "");
return settings;
}

View File

@ -40,6 +40,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("--sshauthorizedkeys", "Path to a authorized_keys file that BTCPayServer can modify from the website (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("--torrcfile", "Path to torrc file containing hidden services directories (default: empty)", CommandOptionType.SingleValue);
app.Option("--socksendpoint", "Socks endpoint to connect to onion urls (default: empty)", CommandOptionType.SingleValue);

View File

@ -474,7 +474,8 @@ namespace BTCPayServer.Controllers
if (appIdsToFetch.Any())
{
var apps = (await _AppService.GetApps(appIdsToFetch.ToArray()))
.ToDictionary(data => data.Id, data => Enum.Parse<AppType>(data.AppType));;
.ToDictionary(data => data.Id, data => Enum.Parse<AppType>(data.AppType));
;
if (!string.IsNullOrEmpty(settings.RootAppId))
{
settings.RootAppType = apps[settings.RootAppId];
@ -504,7 +505,7 @@ namespace BTCPayServer.Controllers
Link = this.Request.GetAbsoluteUriNoPathBase(externalService.Value).AbsoluteUri
});
}
if (_sshState.CanUseSSH)
if (CanShowSSHService())
{
result.OtherExternalServices.Add(new ServicesViewModel.OtherExternalService()
{
@ -835,7 +836,7 @@ namespace BTCPayServer.Controllers
return View(viewModel);
}
var settings = (await _SettingsRepository.GetSettingAsync<DynamicDnsSettings>()) ?? new DynamicDnsSettings();
var i = settings.Services.FindIndex(d => d.Hostname.Equals(hostname, StringComparison.OrdinalIgnoreCase));
if (i == -1)
return NotFound();
@ -900,10 +901,10 @@ namespace BTCPayServer.Controllers
[Route("server/services/ssh")]
public async Task<IActionResult> SSHService()
{
var settings = _Options.SSHSettings;
if (settings == null)
if (!CanShowSSHService())
return NotFound();
var settings = _Options.SSHSettings;
var server = Extensions.IsLocalNetwork(settings.Server) ? this.Request.Host.Host : settings.Server;
SSHServiceViewModel vm = new SSHServiceViewModel();
string port = settings.Port == 22 ? "" : $" -p {settings.Port}";
@ -911,8 +912,19 @@ namespace BTCPayServer.Controllers
vm.Password = settings.Password;
vm.KeyFilePassword = settings.KeyFilePassword;
vm.HasKeyFile = !string.IsNullOrEmpty(settings.KeyFile);
vm.CanConnect = _sshState.CanUseSSH;
if (vm.CanConnect)
// Let's try to just read the authorized key file
if (CanAccessAuthorizedKeyFile())
{
try
{
vm.SSHKeyFileContent = await System.IO.File.ReadAllTextAsync(settings.AuthorizedKeysFile);
}
catch { }
}
// If that fail, just fallback to ssh
if (vm.SSHKeyFileContent == null && _sshState.CanUseSSH)
{
try
{
@ -922,31 +934,68 @@ namespace BTCPayServer.Controllers
vm.SSHKeyFileContent = result.Output;
}
}
catch
{
}
catch { }
}
return View(vm);
}
bool CanShowSSHService()
{
return _Options.SSHSettings != null && (_sshState.CanUseSSH || CanAccessAuthorizedKeyFile());
}
private bool CanAccessAuthorizedKeyFile()
{
return _Options.SSHSettings.AuthorizedKeysFile != null && System.IO.File.Exists(_Options.SSHSettings.AuthorizedKeysFile);
}
[HttpPost]
[Route("server/services/ssh")]
public async Task<IActionResult> SSHService(SSHServiceViewModel viewModel)
{
string newContent = viewModel?.SSHKeyFileContent ?? string.Empty;
newContent = newContent.Replace("\r\n", "\n", StringComparison.OrdinalIgnoreCase);
try
Exception exception = null;
// Let's try to just write the file
if (CanAccessAuthorizedKeyFile())
{
using (var sshClient = await _Options.SSHSettings.ConnectAsync())
try
{
await sshClient.RunBash($"mkdir -p ~/.ssh && echo '{newContent.EscapeSingleQuotes()}' > ~/.ssh/authorized_keys", TimeSpan.FromSeconds(10));
await System.IO.File.WriteAllTextAsync(_Options.SSHSettings.AuthorizedKeysFile, newContent);
StatusMessage = "authorized_keys has been updated";
}
catch (Exception ex)
{
exception = ex;
}
}
// If that fail, fallback to ssh
if (exception != null && _sshState.CanUseSSH)
{
try
{
using (var sshClient = await _Options.SSHSettings.ConnectAsync())
{
await sshClient.RunBash($"mkdir -p ~/.ssh && echo '{newContent.EscapeSingleQuotes()}' > ~/.ssh/authorized_keys", TimeSpan.FromSeconds(10));
}
exception = null;
}
catch (Exception ex)
{
exception = ex;
}
}
if (exception is null)
{
StatusMessage = "authorized_keys has been updated";
}
catch (Exception ex)
else
{
StatusMessage = $"Error: {ex.Message}";
StatusMessage = $"Error: {exception.Message}";
}
return RedirectToAction(nameof(SSHService));
}

View File

@ -11,7 +11,6 @@ namespace BTCPayServer.Models.ServerViewModels
public string Password { get; set; }
public string KeyFilePassword { get; set; }
public bool HasKeyFile { get; set; }
public bool CanConnect { get; set; }
public string SSHKeyFileContent { get; set; }
}
}

View File

@ -13,6 +13,7 @@ namespace BTCPayServer.SSH
public int Port { get; set; } = 22;
public string KeyFile { get; set; }
public string KeyFilePassword { get; set; }
public string AuthorizedKeysFile { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public List<SSHFingerprint> TrustedFingerprints { get; set; } = new List<SSHFingerprint>();

View File

@ -45,7 +45,7 @@
</div>
</div>
@if (Model.CanConnect)
@if (Model.SSHKeyFileContent != null)
{
<h4>Authorized keys</h4>
<div class="row">