diff --git a/src/Tgstation.Server.Host/Controllers/SwarmController.cs b/src/Tgstation.Server.Host/Controllers/SwarmController.cs
index be171b19d8..cdaa834d46 100644
--- a/src/Tgstation.Server.Host/Controllers/SwarmController.cs
+++ b/src/Tgstation.Server.Host/Controllers/SwarmController.cs
@@ -96,9 +96,9 @@ namespace Tgstation.Server.Host.Controllers
return StatusCode((int)HttpStatusCode.UpgradeRequired);
var registrationResult = await swarmOperations.RegisterNode(registrationRequest, RequestRegistrationId, cancellationToken);
- if (!registrationResult)
+ if (registrationResult == null)
return Conflict();
- return NoContent();
+ return Json(registrationResult);
}
///
diff --git a/src/Tgstation.Server.Host/Swarm/ISwarmOperations.cs b/src/Tgstation.Server.Host/Swarm/ISwarmOperations.cs
index 5a0889b7e9..c8ae31031a 100644
--- a/src/Tgstation.Server.Host/Swarm/ISwarmOperations.cs
+++ b/src/Tgstation.Server.Host/Swarm/ISwarmOperations.cs
@@ -39,8 +39,8 @@ namespace Tgstation.Server.Host.Swarm
/// The that is registering.
/// The registration .
/// The for the operation.
- /// A resulting in if the registration was successful, otherwise.
- ValueTask RegisterNode(SwarmServer node, Guid registrationId, CancellationToken cancellationToken);
+ /// A resulting in a if the registration was successful, otherwise.
+ ValueTask RegisterNode(SwarmServer node, Guid registrationId, CancellationToken cancellationToken);
///
/// Attempt to unregister a node with a given with the controller.
diff --git a/src/Tgstation.Server.Host/Swarm/SwarmRegistrationResponse.cs b/src/Tgstation.Server.Host/Swarm/SwarmRegistrationResponse.cs
new file mode 100644
index 0000000000..a31858fa29
--- /dev/null
+++ b/src/Tgstation.Server.Host/Swarm/SwarmRegistrationResponse.cs
@@ -0,0 +1,13 @@
+namespace Tgstation.Server.Host.Swarm
+{
+ ///
+ /// Response for a .
+ ///
+ public sealed class SwarmRegistrationResponse
+ {
+ ///
+ /// The base64 encoded token signing key.
+ ///
+ public required string TokenSigningKeyBase64 { get; init; }
+ }
+}
diff --git a/src/Tgstation.Server.Host/Swarm/SwarmRegistrationResult.cs b/src/Tgstation.Server.Host/Swarm/SwarmRegistrationResult.cs
index 87508af508..a754aa52cd 100644
--- a/src/Tgstation.Server.Host/Swarm/SwarmRegistrationResult.cs
+++ b/src/Tgstation.Server.Host/Swarm/SwarmRegistrationResult.cs
@@ -24,5 +24,10 @@
/// A communication error occurred.
///
CommunicationFailure,
+
+ ///
+ /// Response could not be deserialized.
+ ///
+ PayloadFailure,
}
}
diff --git a/src/Tgstation.Server.Host/Swarm/SwarmService.cs b/src/Tgstation.Server.Host/Swarm/SwarmService.cs
index ea77de6a3c..c62148b4be 100644
--- a/src/Tgstation.Server.Host/Swarm/SwarmService.cs
+++ b/src/Tgstation.Server.Host/Swarm/SwarmService.cs
@@ -24,6 +24,7 @@ using Tgstation.Server.Host.Configuration;
using Tgstation.Server.Host.Core;
using Tgstation.Server.Host.Database;
using Tgstation.Server.Host.IO;
+using Tgstation.Server.Host.Security;
using Tgstation.Server.Host.System;
using Tgstation.Server.Host.Transfer;
using Tgstation.Server.Host.Utils;
@@ -89,6 +90,11 @@ namespace Tgstation.Server.Host.Swarm
///
readonly IFileTransferTicketProvider transferService;
+ ///
+ /// The for the .
+ ///
+ readonly ITokenFactory tokenFactory;
+
///
/// The for the .
///
@@ -159,6 +165,7 @@ namespace Tgstation.Server.Host.Swarm
/// The value of .
/// The value of .
/// The value of .
+ /// The value of .
/// The containing the value of .
/// The value of .
public SwarmService(
@@ -169,6 +176,7 @@ namespace Tgstation.Server.Host.Swarm
IAsyncDelayer asyncDelayer,
IServerUpdater serverUpdater,
IFileTransferTicketProvider transferService,
+ ITokenFactory tokenFactory,
IOptions swarmConfigurationOptions,
ILogger logger)
{
@@ -179,6 +187,7 @@ namespace Tgstation.Server.Host.Swarm
this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
this.serverUpdater = serverUpdater ?? throw new ArgumentNullException(nameof(serverUpdater));
this.transferService = transferService ?? throw new ArgumentNullException(nameof(transferService));
+ this.tokenFactory = tokenFactory ?? throw new ArgumentNullException(nameof(tokenFactory));
swarmConfiguration = swarmConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(swarmConfigurationOptions));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
@@ -540,7 +549,7 @@ namespace Tgstation.Server.Host.Swarm
}
///
- public async ValueTask RegisterNode(SwarmServer node, Guid registrationId, CancellationToken cancellationToken)
+ public async ValueTask RegisterNode(SwarmServer node, Guid registrationId, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(node);
@@ -560,6 +569,11 @@ namespace Tgstation.Server.Host.Swarm
await AbortUpdate();
+ SwarmRegistrationResponse CreateResponse() => new()
+ {
+ TokenSigningKeyBase64 = Convert.ToBase64String(tokenFactory.SigningKey),
+ };
+
var registrationIdsAndTimes = this.registrationIdsAndTimes!;
lock (swarmServers)
{
@@ -569,7 +583,7 @@ namespace Tgstation.Server.Host.Swarm
if (preExistingRegistrationKvp.Key == node.Identifier)
{
logger.LogWarning("Node {nodeId} has already registered!", node.Identifier);
- return true;
+ return CreateResponse();
}
logger.LogWarning(
@@ -577,7 +591,7 @@ namespace Tgstation.Server.Host.Swarm
node.Identifier,
preExistingRegistrationKvp.Key,
registrationId);
- return false;
+ return null;
}
if (registrationIdsAndTimes.TryGetValue(node.Identifier, out var oldRegistration))
@@ -599,7 +613,7 @@ namespace Tgstation.Server.Host.Swarm
logger.LogInformation("Registered node {nodeId} ({nodeIP}) with ID {registrationId}", node.Identifier, node.Address, registrationId);
MarkServersDirty();
- return true;
+ return CreateResponse();
}
///
@@ -1281,6 +1295,36 @@ namespace Tgstation.Server.Host.Swarm
using var response = await httpClient.SendAsync(registrationRequest, HttpCompletionOption.ResponseContentRead, cancellationToken);
if (response.IsSuccessStatusCode)
{
+ try
+ {
+ var json = await response.Content.ReadAsStringAsync(cancellationToken);
+ if (json == null)
+ {
+ logger.LogDebug("Error reading registration response content stream! Text was null!");
+ return SwarmRegistrationResult.PayloadFailure;
+ }
+
+ var registrationResponse = JsonConvert.DeserializeObject(json);
+ if (registrationResponse == null)
+ {
+ logger.LogDebug("Error reading registration response content stream! Payload was null!");
+ return SwarmRegistrationResult.PayloadFailure;
+ }
+
+ if (registrationResponse.TokenSigningKeyBase64 == null)
+ {
+ logger.LogDebug("Error reading registration response content stream! SigningKey was null!");
+ return SwarmRegistrationResult.PayloadFailure;
+ }
+
+ tokenFactory.SigningKey = Convert.FromBase64String(registrationResponse.TokenSigningKeyBase64);
+ }
+ catch (Exception ex)
+ {
+ logger.LogDebug(ex, "Error reading registration response content stream!");
+ return SwarmRegistrationResult.PayloadFailure;
+ }
+
logger.LogInformation("Sucessfully registered with ID {registrationId}", requestedRegistrationId);
controllerRegistration = requestedRegistrationId;
lastControllerHealthCheck = DateTimeOffset.UtcNow;