Initial commit: Windmill workspace sync
Scripts, flows, apps, resources and resource types from the Windmill workspace. API token excluded via .gitignore (config/).
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
summary: null
|
||||
display_name: Reporting
|
||||
extra_perms:
|
||||
sebastianserfling@stines.de: true
|
||||
owners:
|
||||
- sebastianserfling@stines.de
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"mysql2": "latest"
|
||||
}
|
||||
}
|
||||
//bun.lock
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"mysql2": "latest",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@types/node": ["@types/node@25.5.2", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg=="],
|
||||
|
||||
"aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="],
|
||||
|
||||
"denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="],
|
||||
|
||||
"generate-function": ["generate-function@2.3.1", "", { "dependencies": { "is-property": "^1.0.2" } }, "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ=="],
|
||||
|
||||
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||
|
||||
"is-property": ["is-property@1.0.2", "", {}, "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="],
|
||||
|
||||
"long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
|
||||
|
||||
"lru.min": ["lru.min@1.1.4", "", {}, "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA=="],
|
||||
|
||||
"mysql2": ["mysql2@3.21.0", "", { "dependencies": { "aws-ssl-profiles": "^1.1.2", "denque": "^2.1.0", "generate-function": "^2.3.1", "iconv-lite": "^0.7.2", "long": "^5.3.2", "lru.min": "^1.1.4", "named-placeholders": "^1.1.6", "sql-escaper": "^1.3.3" }, "peerDependencies": { "@types/node": ">= 8" } }, "sha512-CYNKIuhnalXHTa4gonZ+KhzLESKllvo1qQIDYUVuatpN4NgMk+lsA3WwHYno5AS4PACUiD2qEmiVD9pr3bXWOw=="],
|
||||
|
||||
"named-placeholders": ["named-placeholders@1.1.6", "", { "dependencies": { "lru.min": "^1.1.0" } }, "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w=="],
|
||||
|
||||
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||
|
||||
"sql-escaper": ["sql-escaper@1.3.3", "", {}, "sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw=="],
|
||||
|
||||
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
type Mysql = {
|
||||
host: string;
|
||||
port: number;
|
||||
user: string;
|
||||
password: string;
|
||||
database: string;
|
||||
};
|
||||
|
||||
type LoginRecord = {
|
||||
username: string;
|
||||
lastaccess: string;
|
||||
ipaddress: string;
|
||||
groups: string;
|
||||
};
|
||||
|
||||
export async function main(
|
||||
database: Mysql,
|
||||
record: LoginRecord
|
||||
): Promise<{ inserted: boolean }> {
|
||||
const mysql2 = await import("mysql2/promise");
|
||||
|
||||
const conn = await mysql2.createConnection({
|
||||
host: database.host,
|
||||
port: database.port,
|
||||
user: database.user,
|
||||
password: database.password,
|
||||
database: database.database,
|
||||
});
|
||||
|
||||
try {
|
||||
await conn.execute(
|
||||
"INSERT INTO `bronze.services.reporting` (username, lastaccess, ipaddress, add_date, memberof) VALUES (?, ?, ?, NOW(), ?)",
|
||||
[record.username, record.lastaccess, record.ipaddress, record.groups]
|
||||
);
|
||||
return { inserted: true };
|
||||
} finally {
|
||||
await conn.end();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
summary: RDP Logins Collector
|
||||
description: >
|
||||
Lädt alle aktiven RDS-Server aus bronze.server, gleicht sie mit den
|
||||
verbundenen rport.io Clients ab (per Hostname oder IP), führt auf jedem den
|
||||
PS-Login-Collector aus und schreibt die Ergebnisse in
|
||||
bronze.services.reporting.
|
||||
value:
|
||||
modules:
|
||||
- id: find_rds_clients
|
||||
summary: RDS-Server mit rport.io Clients abgleichen
|
||||
value:
|
||||
type: rawscript
|
||||
content: '!inline rds-server_mit_rport.io_clients_abgleichen.ts'
|
||||
input_transforms:
|
||||
database:
|
||||
type: static
|
||||
value: $res:u/sebastianserfling/fascinating_mysql
|
||||
rportio_api_token:
|
||||
type: static
|
||||
value: $var:f/Reporting/rportio_api_token
|
||||
rportio_base_url:
|
||||
type: static
|
||||
value: $var:f/Reporting/rportio_base_url
|
||||
rportio_username:
|
||||
type: static
|
||||
value: $var:f/Reporting/rportio_username
|
||||
lock: '!inline rds-server_mit_rport.io_clients_abgleichen.lock'
|
||||
language: bun
|
||||
- id: process_servers
|
||||
summary: Pro Server Logins sammeln und speichern
|
||||
value:
|
||||
type: forloopflow
|
||||
modules:
|
||||
- id: execute_ps
|
||||
summary: PowerShell via rport.io ausführen
|
||||
value:
|
||||
type: rawscript
|
||||
content: '!inline powershell_via_rport.io_ausführen.ts'
|
||||
input_transforms:
|
||||
client_id:
|
||||
type: javascript
|
||||
expr: flow_input.iter.value.rport_client_id
|
||||
hours_back:
|
||||
type: javascript
|
||||
expr: flow_input.hours_back ?? 1
|
||||
rportio_api_token:
|
||||
type: static
|
||||
value: $var:f/Reporting/rportio_api_token
|
||||
rportio_base_url:
|
||||
type: static
|
||||
value: $var:f/Reporting/rportio_base_url
|
||||
rportio_username:
|
||||
type: static
|
||||
value: $var:f/Reporting/rportio_username
|
||||
server_ip:
|
||||
type: javascript
|
||||
expr: flow_input.iter.value.ipaddress
|
||||
lock: '!inline powershell_via_rport.io_ausführen.lock'
|
||||
language: bun
|
||||
- id: insert_logins
|
||||
summary: Login-Einträge in MySQL speichern
|
||||
value:
|
||||
type: forloopflow
|
||||
modules:
|
||||
- id: insert_login
|
||||
summary: Einzelnen Login-Eintrag einfügen
|
||||
value:
|
||||
type: rawscript
|
||||
content: '!inline einzelnen_login-eintrag_einfügen.ts'
|
||||
input_transforms:
|
||||
database:
|
||||
type: static
|
||||
value: $res:u/sebastianserfling/fascinating_mysql
|
||||
record:
|
||||
type: javascript
|
||||
expr: flow_input.iter.value
|
||||
lock: '!inline einzelnen_login-eintrag_einfügen.lock'
|
||||
language: bun
|
||||
iterator:
|
||||
type: javascript
|
||||
expr: results.execute_ps
|
||||
parallel: false
|
||||
skip_failures: false
|
||||
iterator:
|
||||
type: javascript
|
||||
expr: results.find_rds_clients
|
||||
parallel: false
|
||||
skip_failures: true
|
||||
schema:
|
||||
$schema: https://json-schema.org/draft/2019-09/schema
|
||||
type: object
|
||||
properties:
|
||||
hours_back:
|
||||
type: integer
|
||||
description: 'Wie viele Stunden zurückschauen (Standard: 1)'
|
||||
default: 1
|
||||
required: []
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {}
|
||||
}
|
||||
//bun.lock
|
||||
<empty>
|
||||
@@ -0,0 +1,133 @@
|
||||
type LoginRecord = {
|
||||
username: string;
|
||||
lastaccess: string;
|
||||
ipaddress: string;
|
||||
groups: string;
|
||||
};
|
||||
|
||||
export async function main(
|
||||
rportio_base_url: string,
|
||||
rportio_username: string,
|
||||
rportio_api_token: string,
|
||||
client_id: string,
|
||||
server_ip: string,
|
||||
hours_back: number = 1
|
||||
): Promise<LoginRecord[]> {
|
||||
const psScript = `
|
||||
$ErrorActionPreference = 'SilentlyContinue'
|
||||
$startTime = (Get-Date).AddHours(-${hours_back})
|
||||
$endTime = Get-Date
|
||||
$filterHashTable = @{
|
||||
LogName = 'Security'
|
||||
Id = 4624
|
||||
StartTime = $startTime
|
||||
EndTime = $endTime
|
||||
}
|
||||
$events = Get-WinEvent -FilterHashtable $filterHashTable -ErrorAction SilentlyContinue
|
||||
$userLogins = @{}
|
||||
if ($events) {
|
||||
foreach ($event in $events) {
|
||||
$eventDetails = [xml]$event.ToXml()
|
||||
$timeCreated = $event.TimeCreated
|
||||
$username = $eventDetails.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetUserName' } | Select-Object -ExpandProperty '#text'
|
||||
$logonType = $eventDetails.Event.EventData.Data | Where-Object { $_.Name -eq 'LogonType' } | Select-Object -ExpandProperty '#text'
|
||||
if ($logonType -ne '10' -or $username -like 'DWM*' -or $username -like '*UMFD*') { continue }
|
||||
$ipaddress = '${server_ip}'
|
||||
$formattedTime = $timeCreated.ToString('yyyy-MM-dd HH:mm:ss')
|
||||
if (-not $userLogins.ContainsKey($username) -or $userLogins[$username]._raw -lt $timeCreated) {
|
||||
$userLogins[$username] = [PSCustomObject]@{
|
||||
lastaccess = $formattedTime
|
||||
username = $username
|
||||
ipaddress = $ipaddress
|
||||
_raw = $timeCreated
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$result = [System.Collections.Generic.List[object]]::new()
|
||||
foreach ($entry in $userLogins.GetEnumerator()) {
|
||||
$u = $entry.Value
|
||||
$adGroups = $null
|
||||
try {
|
||||
$adGroups = @(Get-ADPrincipalGroupMembership -Identity $u.username -ErrorAction Stop | Select-Object -ExpandProperty Name)
|
||||
} catch {}
|
||||
if (-not $adGroups -or $adGroups.Count -eq 0) { $adGroups = @('G-RDP-User') }
|
||||
foreach ($group in $adGroups) {
|
||||
$result.Add([PSCustomObject]@{
|
||||
username = $u.username
|
||||
lastaccess = $u.lastaccess
|
||||
ipaddress = $u.ipaddress
|
||||
groups = $group
|
||||
})
|
||||
}
|
||||
}
|
||||
if ($result.Count -eq 0) { Write-Output '[]' } else { $result | ConvertTo-Json -Depth 3 -Compress }
|
||||
`.trim();
|
||||
|
||||
// rport.io powershell interpreter executes the script directly as PS code
|
||||
const command = psScript;
|
||||
|
||||
// API token is used as Basic Auth password (bypasses 2FA)
|
||||
const auth = Buffer.from(`${rportio_username}:${rportio_api_token}`).toString("base64");
|
||||
const headers: Record<string, string> = {
|
||||
Authorization: `Basic ${auth}`,
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
|
||||
const tlsOpts = { tls: { rejectUnauthorized: false } };
|
||||
|
||||
// Submit command to rport.io
|
||||
// @ts-ignore - Bun-specific TLS option for self-signed certificates
|
||||
const execResp = await fetch(
|
||||
`${rportio_base_url}/api/v1/clients/${client_id}/commands`,
|
||||
{
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify({ command, interpreter: "powershell", timeout_sec: 120 }),
|
||||
...tlsOpts,
|
||||
}
|
||||
);
|
||||
if (!execResp.ok) {
|
||||
const text = await execResp.text();
|
||||
throw new Error(`rport.io execute failed [${execResp.status}]: ${text}`);
|
||||
}
|
||||
const execData = await execResp.json();
|
||||
const jid: string = execData?.data?.jid;
|
||||
if (!jid) throw new Error(`No job ID from rport.io: ${JSON.stringify(execData)}`);
|
||||
|
||||
// Poll until finished (max 120s)
|
||||
let cmdResult: Record<string, unknown> | null = null;
|
||||
for (let i = 0; i < 60; i++) {
|
||||
await new Promise((r) => setTimeout(r, 2000));
|
||||
// @ts-ignore - Bun-specific TLS option
|
||||
const statusResp = await fetch(
|
||||
`${rportio_base_url}/api/v1/clients/${client_id}/commands/${jid}`,
|
||||
{ headers, ...tlsOpts }
|
||||
);
|
||||
if (!statusResp.ok) continue;
|
||||
const statusData = await statusResp.json();
|
||||
const cmd = statusData?.data as Record<string, unknown>;
|
||||
if (cmd?.finished_at) {
|
||||
cmdResult = cmd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!cmdResult) throw new Error("Timeout waiting for rport.io command result");
|
||||
|
||||
const status = cmdResult.status as string;
|
||||
if (status === "failed" || status === "unknown") {
|
||||
const result = cmdResult.result as Record<string, string> ?? {};
|
||||
throw new Error(`PowerShell failed [${status}]: ${cmdResult.error ?? result.stderr ?? ""}`);
|
||||
}
|
||||
|
||||
const result = cmdResult.result as Record<string, string> ?? {};
|
||||
const stdout = (result.stdout ?? "").trim();
|
||||
if (!stdout || stdout === "[]") return [];
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(stdout);
|
||||
return Array.isArray(parsed) ? parsed : [parsed];
|
||||
} catch {
|
||||
throw new Error(`Failed to parse PowerShell JSON output: ${stdout}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"mysql2": "latest"
|
||||
}
|
||||
}
|
||||
//bun.lock
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"mysql2": "latest",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@types/node": ["@types/node@25.5.2", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg=="],
|
||||
|
||||
"aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="],
|
||||
|
||||
"denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="],
|
||||
|
||||
"generate-function": ["generate-function@2.3.1", "", { "dependencies": { "is-property": "^1.0.2" } }, "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ=="],
|
||||
|
||||
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||
|
||||
"is-property": ["is-property@1.0.2", "", {}, "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="],
|
||||
|
||||
"long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
|
||||
|
||||
"lru.min": ["lru.min@1.1.4", "", {}, "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA=="],
|
||||
|
||||
"mysql2": ["mysql2@3.21.0", "", { "dependencies": { "aws-ssl-profiles": "^1.1.2", "denque": "^2.1.0", "generate-function": "^2.3.1", "iconv-lite": "^0.7.2", "long": "^5.3.2", "lru.min": "^1.1.4", "named-placeholders": "^1.1.6", "sql-escaper": "^1.3.3" }, "peerDependencies": { "@types/node": ">= 8" } }, "sha512-CYNKIuhnalXHTa4gonZ+KhzLESKllvo1qQIDYUVuatpN4NgMk+lsA3WwHYno5AS4PACUiD2qEmiVD9pr3bXWOw=="],
|
||||
|
||||
"named-placeholders": ["named-placeholders@1.1.6", "", { "dependencies": { "lru.min": "^1.1.0" } }, "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w=="],
|
||||
|
||||
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
||||
|
||||
"sql-escaper": ["sql-escaper@1.3.3", "", {}, "sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw=="],
|
||||
|
||||
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
type Mysql = {
|
||||
host: string;
|
||||
port: number;
|
||||
user: string;
|
||||
password: string;
|
||||
database: string;
|
||||
};
|
||||
|
||||
type RportClient = {
|
||||
rport_client_id: string;
|
||||
hostname: string;
|
||||
ipaddress: string;
|
||||
};
|
||||
|
||||
export async function main(
|
||||
database: Mysql,
|
||||
rportio_base_url: string,
|
||||
rportio_username: string,
|
||||
rportio_api_token: string
|
||||
): Promise<RportClient[]> {
|
||||
const mysql2 = await import("mysql2/promise");
|
||||
|
||||
// 1. Query MySQL for all active RDS servers
|
||||
const conn = await mysql2.createConnection({
|
||||
host: database.host,
|
||||
port: database.port,
|
||||
user: database.user,
|
||||
password: database.password,
|
||||
database: database.database,
|
||||
});
|
||||
|
||||
const [rows] = await conn.execute(
|
||||
"SELECT hostname, privat_ipaddress FROM `bronze.server` WHERE services LIKE '%RDS%' AND (disable_date IS NULL OR disable_date > NOW())"
|
||||
);
|
||||
await conn.end();
|
||||
|
||||
const dbServers = rows as Array<{ hostname: string; privat_ipaddress: string }>;
|
||||
if (dbServers.length === 0) return [];
|
||||
|
||||
// Build lookup maps for fast matching (hostname → DB row)
|
||||
const byHostname = new Map(dbServers.map((s) => [s.hostname.toLowerCase(), s]));
|
||||
const byIp = new Map(dbServers.map((s) => [s.privat_ipaddress, s]));
|
||||
|
||||
// 2. Query rport.io for all connected clients
|
||||
const auth = Buffer.from(`${rportio_username}:${rportio_api_token}`).toString("base64");
|
||||
const headers = {
|
||||
Authorization: `Basic ${auth}`,
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
|
||||
// @ts-ignore - Bun-specific TLS option to allow self-signed certificates
|
||||
const resp = await fetch(
|
||||
`${rportio_base_url}/api/v1/clients?filter[connection_state]=connected&fields[clients]=id,name,hostname,ipv4&page[limit]=500`,
|
||||
{ headers, tls: { rejectUnauthorized: false } }
|
||||
);
|
||||
|
||||
if (!resp.ok) {
|
||||
const err = await resp.text();
|
||||
throw new Error(`rport.io clients list failed [${resp.status}]: ${err}`);
|
||||
}
|
||||
|
||||
const data = await resp.json();
|
||||
const clients = data?.data ?? [];
|
||||
|
||||
// 3. Match rport.io clients against DB server list (hostname or IP)
|
||||
const matched: RportClient[] = [];
|
||||
for (const client of clients) {
|
||||
const rportHostname = (client.hostname ?? "").toLowerCase();
|
||||
const rportIps: string[] = client.ipv4 ?? [];
|
||||
|
||||
const dbRow = byHostname.get(rportHostname)
|
||||
?? rportIps.map((ip) => byIp.get(ip)).find(Boolean);
|
||||
|
||||
if (dbRow) {
|
||||
matched.push({
|
||||
rport_client_id: client.id,
|
||||
hostname: client.hostname ?? client.name,
|
||||
ipaddress: dbRow.privat_ipaddress, // use IP from DB, not from PS script
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Found ${dbServers.length} RDS servers in DB, ${clients.length} connected rport.io clients, ${matched.length} matched`
|
||||
);
|
||||
|
||||
return matched;
|
||||
}
|
||||
Reference in New Issue
Block a user