feat: bind each node to the binary it runs at its first handshake; refuse changes that are not in a CI-signed manifest [CI]
ci/woodpecker/push/main-amd64-build Pipeline failed
ci/woodpecker/push/main-amd64-build Pipeline failed
This commit is contained in:
1 parent
117d34a2af
commit
4841f7b15b
40 files changed
+1011
-21
No files matched your search
@@ -0,0 +1,43 @@
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Fleet.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 节点握手时自述的「我跑的是哪个二进制」:正在运行的可执行文件的 SHA-256,以及(容器节点)宿主机更新器验过的 CI 签名清单和签名。
|
||||
/// 服务端在节点第一次握手成功的那一刻把哈希绑定到这个节点,之后哈希对不上就不让它继续运行。
|
||||
/// </summary>
|
||||
public sealed record BinaryAttestation(string Sha256, string? Manifest, string? Signature);
|
||||
|
||||
/// <summary>
|
||||
/// 正在运行的二进制的哈希。Linux 上读 <c>/proc/self/exe</c>:它指向进程启动时映射的那个映像,磁盘上的文件之后被换掉也不影响,
|
||||
/// 所以「运行期间被替换、重启后换了别的」的情况下一次握手就会对不上。算一次,之后缓存。
|
||||
/// </summary>
|
||||
public static class NodeBinary
|
||||
{
|
||||
private static readonly Lazy<string?> s_hash = new(Compute);
|
||||
|
||||
/// <summary>小写十六进制;读不到可执行文件时为 null(这样的节点不做绑定)。</summary>
|
||||
public static string? Sha256Hex => s_hash.Value;
|
||||
|
||||
/// <summary>在后台先算好,免得第一次握手等它。</summary>
|
||||
public static void Prime() => _ = Task.Run(() => s_hash.Value);
|
||||
|
||||
private static string? Compute()
|
||||
{
|
||||
try
|
||||
{
|
||||
string? path = OperatingSystem.IsLinux() && File.Exists("/proc/self/exe") ? "/proc/self/exe" : Environment.ProcessPath;
|
||||
if (path is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete, 1 << 16);
|
||||
return Convert.ToHexStringLower(SHA256.HashData(stream));
|
||||
}
|
||||
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,4 +27,6 @@ public static class FleetErrors
|
||||
public static readonly ErrorCode JoinTokenExpired = new("fleet_join_token_expired");
|
||||
public static readonly ErrorCode ProofInvalid = new("fleet_proof_invalid");
|
||||
public static readonly ErrorCode FrameTooLarge = new("fleet_frame_too_large");
|
||||
/// <summary>节点现在跑的二进制和它第一次握手时绑定的不一致,也不在 CI 签名的更新清单里。</summary>
|
||||
public static readonly ErrorCode BinaryMismatch = new("fleet_binary_mismatch");
|
||||
}
|
||||
Reference in new issue
Block a user