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>
108 lines
4.1 KiB
C#
108 lines
4.1 KiB
C#
using ITNexusAgent.Models;
|
|
using Newtonsoft.Json;
|
|
using System.Net.Http;
|
|
using System.Text;
|
|
|
|
namespace ITNexusAgent.Services;
|
|
|
|
public class ApiService(string serverUrl, string agentKey)
|
|
{
|
|
private readonly HttpClient _http = new() { Timeout = TimeSpan.FromSeconds(30) };
|
|
private readonly string _baseUrl = serverUrl.TrimEnd('/');
|
|
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)
|
|
{
|
|
var req = new HttpRequestMessage(method, $"{_baseUrl}{path}");
|
|
req.Headers.Add("X-Agent-Key", _agentKey);
|
|
if (body != null)
|
|
{
|
|
var json = JsonConvert.SerializeObject(body);
|
|
req.Content = new StringContent(json, Encoding.UTF8, "application/json");
|
|
}
|
|
return req;
|
|
}
|
|
|
|
public async Task<CheckinResponse> CheckinAsync(CheckinPayload payload)
|
|
{
|
|
var req = BuildRequest(HttpMethod.Post, "/api/monitoring/checkin", payload);
|
|
var resp = await _http.SendAsync(req);
|
|
resp.EnsureSuccessStatusCode();
|
|
var body = await resp.Content.ReadAsStringAsync();
|
|
return JsonConvert.DeserializeObject<CheckinResponse>(body) ?? new CheckinResponse();
|
|
}
|
|
|
|
public async Task<List<Announcement>> PollAnnouncementsAsync(string hostname)
|
|
{
|
|
var req = BuildRequest(HttpMethod.Post, "/api/monitoring/announcements-poll",
|
|
new { hostname });
|
|
var resp = await _http.SendAsync(req);
|
|
resp.EnsureSuccessStatusCode();
|
|
var body = await resp.Content.ReadAsStringAsync();
|
|
var result = JsonConvert.DeserializeAnonymousType(body, new { announcements = new List<Announcement>() });
|
|
return result?.announcements ?? [];
|
|
}
|
|
|
|
public async Task AckAnnouncementAsync(int id, string hostname)
|
|
{
|
|
var req = BuildRequest(HttpMethod.Post, $"/api/announcements/{id}/ack-agent",
|
|
new { hostname });
|
|
var resp = await _http.SendAsync(req);
|
|
resp.EnsureSuccessStatusCode();
|
|
}
|
|
|
|
public async Task ReportCommandResultAsync(CommandResult result)
|
|
{
|
|
var req = BuildRequest(HttpMethod.Post, "/api/patch/commands/result", result);
|
|
var resp = await _http.SendAsync(req);
|
|
resp.EnsureSuccessStatusCode();
|
|
}
|
|
|
|
public async Task<byte[]> DownloadAgentAsync()
|
|
{
|
|
var req = BuildRequest(HttpMethod.Get, "/api/monitoring/agent-script");
|
|
var resp = await _http.SendAsync(req);
|
|
return await resp.Content.ReadAsByteArrayAsync();
|
|
}
|
|
|
|
public async Task<byte[]> DownloadSetupAsync(string hostname)
|
|
{
|
|
var req = BuildRequest(HttpMethod.Get, $"/api/monitoring/agent-setup?hostname={Uri.EscapeDataString(hostname)}");
|
|
var resp = await _http.SendAsync(req);
|
|
resp.EnsureSuccessStatusCode();
|
|
return await resp.Content.ReadAsByteArrayAsync();
|
|
}
|
|
|
|
public async Task SendMessageAsync(string hostname, string message)
|
|
{
|
|
var req = BuildRequest(HttpMethod.Post, "/api/monitoring/message",
|
|
new { hostname, message });
|
|
await _http.SendAsync(req);
|
|
}
|
|
|
|
public async Task SendRebootRequestAsync(string hostname)
|
|
{
|
|
var req = BuildRequest(HttpMethod.Post, "/api/monitoring/reboot-request",
|
|
new { hostname });
|
|
await _http.SendAsync(req);
|
|
}
|
|
}
|