Switched to using a single common certificate implementation

This commit is contained in:
Kenneth Skovhede
2024-09-20 09:42:21 +02:00
parent eae1988d0b
commit 550397cd70
7 changed files with 21 additions and 52 deletions
+3 -8
View File
@@ -127,7 +127,7 @@ public static class Program
KeepRemoteConnection.Start(
settings.ServerUrl,
settings.JWT,
ConvertCertificates(settings.ServerCertificates),
settings.ServerCertificates,
cts.Token,
ReKey,
(m) => OnMessage(m, agentConfig)
@@ -159,7 +159,7 @@ public static class Program
if (!string.IsNullOrWhiteSpace(keydata.JWT) && settings.JWT != keydata.JWT)
settings = settings with { JWT = keydata.JWT };
if (keydata.ServerCertificates.Any())
settings = settings with { ServerCertificates = ConvertCertificates(keydata.ServerCertificates).Concat(settings.ServerCertificates).DistinctBy(x => x.MachineId).ToArray() };
settings = settings with { ServerCertificates = (keydata.ServerCertificates ?? []).Concat(settings.ServerCertificates).DistinctBy(x => x.Identifier).ToArray() };
if (!string.IsNullOrWhiteSpace(keydata.LocalEncryptionKey) && settings.SettingsEncryptionKey != keydata.LocalEncryptionKey)
{
@@ -173,11 +173,6 @@ public static class Program
return Task.CompletedTask;
}
private static IEnumerable<ServerCertificate> ConvertCertificates(IEnumerable<Settings.ServerCertificate> certificates)
=> (certificates ?? []).Select(cert => new ServerCertificate(cert.MachineId, cert.PublicKey, cert.Obtained, cert.Expiry)).ToArray();
private static IEnumerable<Settings.ServerCertificate> ConvertCertificates(IEnumerable<ServerCertificate> certificates)
=> (certificates ?? []).Select(cert => new Settings.ServerCertificate(cert.MachineId, cert.PublicKey, cert.Obtained, cert.Expiry)).ToArray();
private static async Task RunServer(string[] args, CancellationToken cancellationToken)
{
cancellationToken.Register(() =>
@@ -234,7 +229,7 @@ public static class Program
JWT = claimedClientData.JWT,
ServerUrl = claimedClientData.ServerUrl,
SettingsEncryptionKey = claimedClientData.LocalEncryptionKey,
ServerCertificates = ConvertCertificates(claimedClientData.ServerCertificates)
ServerCertificates = claimedClientData.ServerCertificates
};
settings.Save();
+3 -17
View File
@@ -23,6 +23,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using Duplicati.Library.Encryption;
using Duplicati.Library.Main;
using Duplicati.Library.RemoteControl;
namespace Duplicati.Agent;
@@ -39,24 +40,9 @@ public sealed record Settings(
string JWT,
string ServerUrl,
string? SettingsEncryptionKey,
IEnumerable<Settings.ServerCertificate> ServerCertificates
IEnumerable<MiniServerCertificate> ServerCertificates
)
{
/// <summary>
/// The persisted version of a server certificate.
/// This is the data that is serialized to the file.
/// </summary>
/// <param name="MachineId">The machine the key belongs to</param>
/// <param name="PublicKey">The public key</param>
/// <param name="Obtained">The date the key was obtained</param>
/// <param name="Expiry">The expiry date of the key</param>
public sealed record ServerCertificate(
string MachineId,
string PublicKey,
DateTimeOffset Obtained,
DateTimeOffset Expiry
);
/// <summary>
/// Gets the default settings file path
/// </summary>
@@ -80,7 +66,7 @@ public sealed record Settings(
{
path ??= DefaultSettingsFile;
var tmp = JsonSerializer.Deserialize<Settings>(File.ReadAllText(path))
?? new Settings(path, string.Empty, string.Empty, string.Empty, Array.Empty<ServerCertificate>());
?? new Settings(path, string.Empty, string.Empty, string.Empty, Array.Empty<MiniServerCertificate>());
return tmp with
{
@@ -102,7 +102,7 @@ public class KeepRemoteConnection : IDisposable
/// <param name="JWT">The JWT token to use</param>
/// <param name="serverKey">The server key to use</param>
/// <param name="cancellationToken">The token to cancel the connection</param>
private KeepRemoteConnection(string serverUrl, string JWT, IEnumerable<ServerCertificate> serverKeys, CancellationToken cancellationToken, Func<ClaimedClientData, Task> onReKey, Func<CommandMessage, Task> onMessage)
private KeepRemoteConnection(string serverUrl, string JWT, IEnumerable<MiniServerCertificate> serverKeys, CancellationToken cancellationToken, Func<ClaimedClientData, Task> onReKey, Func<CommandMessage, Task> onMessage)
{
_client = new Websocket.Client.WebsocketClient(new Uri(serverUrl));
_client.ReconnectTimeout = TimeSpan.FromSeconds(30);
@@ -126,7 +126,7 @@ public class KeepRemoteConnection : IDisposable
try
{
var envelope = EnvelopedMessage.ForceParse(msg.Text);
var machineKey = serverKeys.FirstOrDefault(x => x.MachineId == envelope.From && x.Expiry > DateTimeOffset.Now)?.PublicKey;
var machineKey = serverKeys.FirstOrDefault(x => x.Identifier == envelope.From && x.Expiry > DateTimeOffset.Now)?.PublicKey;
envelope.ValidateSignature(machineKey);
if (_state == ConnectionState.Connected)
@@ -214,7 +214,7 @@ public class KeepRemoteConnection : IDisposable
/// <param name="onReKey">The callback to call when rekeying</param>
/// <param name="onMessage">The callback to call when a message is received</param>
/// <returns></returns>
public static Task Start(string serverUrl, string JWT, IEnumerable<ServerCertificate> serverKeys, CancellationToken cancellationToken, Func<ClaimedClientData, Task> onReKey, Func<CommandMessage, Task> onMessage)
public static Task Start(string serverUrl, string JWT, IEnumerable<MiniServerCertificate> serverKeys, CancellationToken cancellationToken, Func<ClaimedClientData, Task> onReKey, Func<CommandMessage, Task> onMessage)
=> Task.Run(async () =>
{
using var connection = new KeepRemoteConnection(serverUrl, JWT, serverKeys, cancellationToken, onReKey, onMessage);
@@ -290,7 +290,7 @@ public class KeepRemoteConnection : IDisposable
/// <param name="onMessage">The callback to call when a message is received</param>
/// <param name="cancellationToken">The token to cancel the connection</param>
/// <returns>The connection object</returns>
public static KeepRemoteConnection CreateRemoteListener(string serverUrl, string JWT, IEnumerable<ServerCertificate> serverKeys, CancellationToken cancellationToken, Func<ClaimedClientData, Task> onReKey, Func<CommandMessage, Task> onMessage)
public static KeepRemoteConnection CreateRemoteListener(string serverUrl, string JWT, IEnumerable<MiniServerCertificate> serverKeys, CancellationToken cancellationToken, Func<ClaimedClientData, Task> onReKey, Func<CommandMessage, Task> onMessage)
=> new KeepRemoteConnection(serverUrl, JWT, serverKeys, cancellationToken, onReKey, onMessage);
/// <summary>
@@ -47,21 +47,21 @@ public sealed record RegisterClientData(
public sealed record ClaimedClientData(
string JWT,
string ServerUrl,
IEnumerable<ServerCertificate> ServerCertificates,
IEnumerable<MiniServerCertificate> ServerCertificates,
string? LocalEncryptionKey
);
// TODO: Replace with a standard certificate type
/// <summary>
/// The server certificate for a machine
/// The server certificate for a machine.
/// This data is serialized to various files, and chosen instead of X509 certificates.
/// If we find a non-complex certificate format, we should switch to that.
/// </summary>
/// <param name="MachineId">The machine identifier the key is valid for</param>
/// <param name="Identifier">The machine identifier the key is valid for</param>
/// <param name="PublicKey">The certificate public key</param>
/// <param name="Obtained">The date the certificate was obtained</param>
/// <param name="Expiry">The expiry date of the certificate</param>
public sealed record ServerCertificate(
string MachineId,
public sealed record MiniServerCertificate(
string Identifier,
string PublicKey,
DateTimeOffset Obtained,
DateTimeOffset Expiry
@@ -1,5 +1,3 @@
using System.Text.Json;
using Duplicati.Library.Encryption;
using Duplicati.Library.RestAPI;
using Duplicati.Server.Database;
using Duplicati.WebserverCore.Abstractions;
@@ -43,16 +43,7 @@ public sealed record RemoteControlConfig
/// <summary>
/// The trusted server certificates.
/// </summary>
public required IEnumerable<CertificateKey> ServerCertificates { get; init; }
/// <summary>
/// A server certificate.
/// </summary>
/// <param name="MachineId">The machine identifier</param>
/// <param name="PublicKey">The machine public key</param>
/// <param name="Expiry">The expiry date of the certificate</param>
/// <param name="Obtained">The date the certificate was obtained</param>
public sealed record CertificateKey(string MachineId, string PublicKey, DateTimeOffset Expiry, DateTimeOffset Obtained);
public required IEnumerable<MiniServerCertificate> ServerCertificates { get; init; }
}
/// <summary>
@@ -116,8 +107,7 @@ public class RemoteControllerRegistrationService(Connection connection, IHttpCli
connection.ApplicationSettings.RemoteControlConfig = JsonSerializer.Serialize(new RemoteControlConfig
{
Token = claimData.JWT,
ServerCertificates = (claimData.ServerCertificates ?? Enumerable.Empty<ServerCertificate>())
.Select(x => new RemoteControlConfig.CertificateKey(x.MachineId, x.PublicKey, x.Expiry, x.Obtained)),
ServerCertificates = claimData.ServerCertificates,
ServerUrl = claimData.ServerUrl
});
@@ -79,7 +79,7 @@ public class RemoteControllerService(Connection connection, IHttpClientFactory h
_keepRemoteConnection = KeepRemoteConnection.CreateRemoteListener(
config.ServerUrl,
config.Token,
config.ServerCertificates.Select(x => new ServerCertificate(x.MachineId, x.PublicKey, x.Expiry, x.Obtained)).ToArray(),
config.ServerCertificates,
CancellationToken.None,
ReKey,
OnMessage
@@ -96,7 +96,7 @@ public class RemoteControllerService(Connection connection, IHttpClientFactory h
connection.ApplicationSettings.RemoteControlConfig = JsonConvert.SerializeObject(new RemoteControlConfig
{
Token = data.JWT,
ServerCertificates = data.ServerCertificates.Select(x => new RemoteControlConfig.CertificateKey(x.MachineId, x.PublicKey, x.Expiry, x.Obtained)),
ServerCertificates = data.ServerCertificates,
ServerUrl = data.ServerUrl
});