Security: Pro-Geräte Agent-Keys statt geteiltem AGENT_API_KEY (v2.8.0)

Agent v2.8.0 tauscht beim Start automatisch den geteilten Bootstrap-Key
gegen einen individuellen Per-Device-Key (POST /api/monitoring/enroll,
idempotent). Checkin/Announcements-Poll/Setup-Download/WS-Agent-Verbindungen
validieren den Key jetzt gegen den jeweiligen Hostname — ein gestohlener
Key kann sich nicht mehr als anderer Agent ausgeben (manuell verifiziert).

Alte Agents mit dem geteilten Key funktionieren während der Übergangsphase
weiter (validateAgentKey() akzeptiert beides), damit der Rollout die Fleet
nicht abrupt bricht — Migration läuft über den bestehenden Staged-Rollout
(Test → Pilot → Produktion).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-26 13:50:02 +02:00
parent d513992de7
commit f3bd8d0910
11 changed files with 110 additions and 21 deletions

View File

@@ -9,7 +9,25 @@ public class ApiService(string serverUrl, string agentKey)
{
private readonly HttpClient _http = new() { Timeout = TimeSpan.FromSeconds(30) };
private readonly string _baseUrl = serverUrl.TrimEnd('/');
private readonly string _agentKey = agentKey;
private string _agentKey = agentKey;
// Nach erfolgreichem Enrollment wird der geteilte Bootstrap-Key durch den individuellen
// Per-Device-Key ersetzt — alle nachfolgenden Requests dieser Instanz nutzen ab dann den neuen Key.
public void UpdateKey(string newKey) => _agentKey = newKey;
public async Task<string?> EnrollAsync(string hostname)
{
try
{
var req = BuildRequest(HttpMethod.Post, "/api/monitoring/enroll", new { hostname });
var resp = await _http.SendAsync(req);
if (!resp.IsSuccessStatusCode) return null;
var body = await resp.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeAnonymousType(body, new { status = "", data = new { agent_key = "" } });
return result?.data?.agent_key;
}
catch { return null; }
}
private HttpRequestMessage BuildRequest(HttpMethod method, string path, object? body = null)
{