Switched to using a single common certificate implementation
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user