Stufe 2 (#764): Einmal-Anmeldung fuer die Huelle ueber den Manager-Socket
Der Manager hoert zusaetzlich auf einem lokalen Socket (0600 bzw. Pipe nur fuer den eigenen Benutzer). Dort gibt es nur /api/shell/*, ueber TCP gibt es diese Pfade nicht. Ein Einmal-Code (256 Bit, einmal, hoechstens 60 s) wird in der WebUI aus dem Fragment gegen das Token getauscht und sofort aus der Adresse entfernt. Tests: zweimal, zu spaet, ueber TCP - alle scheitern. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
c3927292fd
commit
775c4dbc8e
@@ -121,7 +121,7 @@ public static class InstanceEndpoint
|
||||
ClearStaleFile(McpSocketPath(instanceDirectory));
|
||||
}
|
||||
|
||||
private static void ClearStaleFile(string path)
|
||||
internal static void ClearStaleFile(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -152,7 +152,7 @@ public static class InstanceEndpoint
|
||||
RestrictFile(McpSocketPath(instanceDirectory));
|
||||
}
|
||||
|
||||
private static void RestrictFile(string path)
|
||||
internal static void RestrictFile(string path)
|
||||
{
|
||||
if (OperatingSystem.IsWindows()) return;
|
||||
|
||||
@@ -185,7 +185,7 @@ public static class InstanceEndpoint
|
||||
public static HttpMessageHandler CreateHandler(string instanceDirectory)
|
||||
=> CreateHandler(SocketPath(instanceDirectory), PipeName(instanceDirectory));
|
||||
|
||||
private static HttpMessageHandler CreateHandler(string socketPath, string pipeName)
|
||||
internal static HttpMessageHandler CreateHandler(string socketPath, string pipeName)
|
||||
{
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
@@ -239,7 +239,7 @@ public static class InstanceEndpoint
|
||||
/// müssen denselben Pipenamen ergeben. Unter Linux sind es zwei Verzeichnisse und
|
||||
/// dürfen es nicht.</para>
|
||||
/// </summary>
|
||||
private static string Fingerprint(string instanceDirectory)
|
||||
internal static string Fingerprint(string instanceDirectory)
|
||||
{
|
||||
var full = Path.GetFullPath(instanceDirectory).TrimEnd(
|
||||
Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
namespace ClawdDotNet.Core.Instances;
|
||||
|
||||
/// <summary>
|
||||
/// #764: The instance manager's local socket — the way the shell (ClawdDotNet.Shell) talks to
|
||||
/// it without the web access token.
|
||||
///
|
||||
/// <para><b>Why a socket and not the web port.</b> The shell opens the web interface signed in,
|
||||
/// through a one-time code. Asking for that code must prove that the asker is the user who runs
|
||||
/// the manager on this machine — not anybody who reaches the port. The file system says that:
|
||||
/// the socket is 0600 (Linux) or a pipe for the current user only (Windows), exactly like the
|
||||
/// instance socket (<see cref="InstanceEndpoint"/>). Over TCP the shell endpoints do not exist.</para>
|
||||
///
|
||||
/// <para>It lives next to <c>manager.json</c>: the shell finds the manager the same way it finds
|
||||
/// its port.</para>
|
||||
/// </summary>
|
||||
public static class ManagerEndpoint
|
||||
{
|
||||
public const string SocketFileName = "manager-shell.sock";
|
||||
|
||||
public static string SocketPath(string configDirectory)
|
||||
=> Path.Combine(Path.GetFullPath(configDirectory), SocketFileName);
|
||||
|
||||
/// <summary>Pipe names are machine-wide; a fingerprint of the folder keeps two installations apart.</summary>
|
||||
public static string PipeName(string configDirectory)
|
||||
=> "clawd-manager-" + InstanceEndpoint.Fingerprint(configDirectory);
|
||||
|
||||
public static HttpClient CreateClient(string configDirectory)
|
||||
=> new(InstanceEndpoint.CreateHandler(SocketPath(configDirectory), PipeName(configDirectory)))
|
||||
{
|
||||
BaseAddress = new Uri("http://manager")
|
||||
};
|
||||
|
||||
/// <summary>A socket file left by a hard end would block the next bind (see <see cref="InstanceEndpoint.ClearStale"/>).</summary>
|
||||
public static void ClearStale(string configDirectory)
|
||||
{
|
||||
if (OperatingSystem.IsWindows()) return;
|
||||
InstanceEndpoint.ClearStaleFile(SocketPath(configDirectory));
|
||||
}
|
||||
|
||||
/// <summary>0600 after binding; on Windows the pipe's CurrentUserOnly carries the same promise.</summary>
|
||||
public static void Restrict(string configDirectory)
|
||||
{
|
||||
if (OperatingSystem.IsWindows()) return;
|
||||
InstanceEndpoint.RestrictFile(SocketPath(configDirectory));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user