Skip to content

Latest commit

 

History

History
619 lines (486 loc) · 12.1 KB

File metadata and controls

619 lines (486 loc) · 12.1 KB

API Documentation

ETC Collector provides a REST API for programmatic security auditing.

Base URL

http://localhost:8443/api/v1

Authentication

The API uses two layers of authentication:

  1. GUI Access Token — protects admin endpoints (config, token creation) and the web GUI
  2. JWT Token (RS256) — protects audit and data endpoints

GUI Access Token

The GUI access token is generated via etc-collector gui-token reset. Only a SHA-256 hash is stored on disk — the plaintext token is shown once and never saved.

Protected endpoints: POST /api/v1/auth/token, /api/v1/admin/*

How to pass the token:

# Via header (recommended)
curl -H "X-GUI-Token: etcsec_gt_..." http://localhost:8443/api/v1/admin/config

# Via query parameter
curl http://localhost:8443/api/v1/admin/config?gui_token=etcsec_gt_...

Verify a GUI token:

Endpoint: POST /api/v1/auth/gui-token/verify

Request:

{
  "token": "etcsec_gt_..."
}

Response:

{
  "valid": true
}

If no GUI token is configured on the server, the response includes "required": false and all requests pass through.

JWT Token (API Authentication)

The server requires an RSA key pair for signing and validating JWT tokens. Generate keys before starting:

mkdir -p keys
openssl genrsa -out keys/private.pem 2048
openssl rsa -in keys/private.pem -pubout -out keys/public.pem

For Docker, mount the keys directory as a volume: -v ./keys:/app/keys:ro

Create Token

Endpoint: POST /api/v1/auth/token

Authentication: Requires GUI access token

Request:

{
  "service": "my-integration",
  "duration": "24h"
}

Response:

{
  "token": "eyJhbGciOiJSUzI1NiIs...",
  "expiresAt": "2024-01-16T10:30:00Z"
}

Example:

curl -X POST http://localhost:8443/api/v1/auth/token \
  -H "Content-Type: application/json" \
  -H "X-GUI-Token: etcsec_gt_..." \
  -d '{"service":"my-app","duration":"24h"}'

Use Token

Include the token in the Authorization header:

curl -H "Authorization: Bearer YOUR_TOKEN" \
  http://localhost:8443/api/v1/audit/ad

Validate Token

Endpoint: POST /api/v1/auth/token/validate

Request:

{
  "token": "eyJhbGciOiJSUzI1NiIs..."
}

Response:

{
  "valid": true,
  "subject": "system",
  "service": "my-app",
  "expiresAt": "2024-01-16T10:30:00Z"
}

Token Info

Endpoint: GET /api/v1/auth/token/info

Authentication: Required

Response:

{
  "subject": "system",
  "service": "my-app",
  "issuedAt": "2024-01-15T10:30:00Z",
  "expiresAt": "2024-01-16T10:30:00Z",
  "jti": "550e8400-e29b-41d4-a716-446655440000"
}

Endpoints

Health Check

Endpoint: GET /health

Response:

{
  "status": "ok",
  "timestamp": "2026-04-05T10:00:00Z",
  "version": "3.0.5",
  "edition": "community"
}

Admin Configuration

These endpoints require the GUI access token.

Get Configuration

Endpoint: GET /api/v1/admin/config

Authentication: GUI token required

Response:

{
  "server": { "host": "0.0.0.0", "port": 8443 },
  "ldap": {
    "configured": true,
    "url": "ldaps://dc.example.com:636",
    "bindDN": "CN=svc-audit,CN=Users,DC=example,DC=com",
    "baseDN": "DC=example,DC=com",
    "tlsVerify": true,
    "connected": true
  },
  "azure": { "configured": false },
  "features": { "networkProbes": false },
  "auth": { "hasKeys": true, "tokenLifetime": "720h0m0s" }
}

Secrets (bindPassword, clientSecret) are never returned.

Update LDAP Configuration

Endpoint: PUT /api/v1/admin/config/ldap

Authentication: GUI token required

Request:

{
  "url": "ldaps://dc.example.com:636",
  "bindDN": "CN=svc-audit,CN=Users,DC=example,DC=com",
  "bindPassword": "P@ssw0rd",
  "baseDN": "DC=example,DC=com",
  "tlsVerify": true
}

Omit bindPassword to keep the existing password.

Response:

{ "success": true, "message": "LDAP configuration updated" }

Example:

curl -X PUT http://localhost:8443/api/v1/admin/config/ldap \
  -H "Content-Type: application/json" \
  -H "X-GUI-Token: etcsec_gt_..." \
  -d '{"url":"ldaps://dc.example.com:636","bindDN":"CN=svc-audit,...","baseDN":"DC=example,DC=com"}'

Test LDAP Connection

Endpoint: POST /api/v1/admin/config/ldap/test

Authentication: GUI token required

Request:

{
  "url": "ldaps://dc.example.com:636",
  "bindDN": "CN=svc-audit,CN=Users,DC=example,DC=com",
  "bindPassword": "P@ssw0rd",
  "baseDN": "DC=example,DC=com",
  "tlsVerify": true
}

Response:

{ "success": true, "message": "Connection successful" }

Does not save the configuration — use PUT /admin/config/ldap to persist.

Remove LDAP Configuration

Endpoint: DELETE /api/v1/admin/config/ldap

Authentication: GUI token required

Response:

{ "success": true, "message": "LDAP configuration removed" }

Providers

Endpoint: GET /api/v1/info/providers

Authentication: Required

Response:

{
  "providers": [
    {
      "type": "ldap",
      "connected": true
    }
  ]
}

Capabilities

Endpoint: GET /api/v1/info/capabilities

Authentication: Required

Response:

{
  "detectorCount": 277,
  "features": {
    "ldap": true,
    "networkProbes": false,
    "sysvol": true
  },
  "version": "3.0.9"
}

Audit Status

Endpoint: GET /api/v1/audit/ad/status

Authentication: Required

Response:

{
  "status": "ready",
  "provider": "ldap"
}

Run Active Directory Audit

Endpoint: POST /api/v1/audit/ad

Authentication: Required

Request:

{
  "includeDetails": false,
  "async": false,
  "networkProbes": false
}

Response:

{
  "success": true,
  "provider": "ad",
  "audit": {
    "summary": {
      "objects": {
        "users": 1234,
        "groups": 567,
        "computers": 890
      },
      "risk": {
        "score": 72.5,
        "rating": "low",
        "findings": {
          "critical": 12,
          "high": 25,
          "medium": 38,
          "low": 12,
          "total": 87
        }
      }
    },
    "accounts": {
      "status": { "findings": [...], "total": 5 },
      "privileged": { "findings": [...], "total": 3 }
    },
    "computers": { "findings": [...], "total": 8 },
    "security": {
      "passwords": { "findings": [...], "total": 10 },
      "kerberos": { "findings": [...], "total": 4 }
    },
    "permissions": { "findings": [...], "total": 15 },
    "domainConfig": {
      "domainInfo": { ... },
      "passwordPolicy": { ... },
      "kerberosPolicy": { ... }
    },
    "metadata": {
      "provider": "ad",
      "domain": {
        "name": "contoso.local",
        "baseDN": "DC=contoso,DC=local"
      },
      "execution": {
        "timestamp": "2024-01-15T10:30:00Z",
        "duration": "2m15s"
      }
    }
  }
}

Each finding in the category arrays has this structure:

{
  "type": "PASSWD_NOTREQD",
  "severity": "critical",
  "category": "accounts",
  "title": "User does not require password",
  "description": "Users with PASSWD_NOTREQD flag set",
  "count": 3,
  "affectedEntities": [
    {
      "name": "Guest",
      "type": "user",
      "details": { ... }
    }
  ]
}

affectedEntities is only included when includeDetails: true in the request.

Example:

TOKEN="your-token-here"

curl -X POST http://localhost:8443/api/v1/audit/ad \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "includeDetails": false,
    "async": false
  }'

Async Audit

For long-running audits, use async mode:

Request:

{
  "async": true
}

Response:

{
  "jobId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "running"
}

List Jobs

Endpoint: GET /api/v1/audit/jobs

Authentication: Required

Response:

{
  "jobs": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "type": "ad",
      "status": "completed",
      "createdAt": "2024-01-15T10:30:00Z",
      "completedAt": "2024-01-15T10:35:23Z"
    }
  ]
}

Get Job

Endpoint: GET /api/v1/audit/jobs/:id

Authentication: Required

Example:

curl -H "Authorization: Bearer $TOKEN" \
  http://localhost:8443/api/v1/audit/jobs/550e8400-e29b-41d4-a716-446655440000

Response:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "type": "ad",
  "status": "completed",
  "createdAt": "2024-01-15T10:30:00Z",
  "completedAt": "2024-01-15T10:35:23Z",
  "result": { ... }
}

Job statuses: pending, running, completed, failed.

Error Handling

Error Response Format:

{
  "error": "error_code",
  "message": "Human-readable error description"
}

HTTP Status Codes:

  • 200 - Success
  • 201 - Created
  • 202 - Accepted (async job started)
  • 400 - Bad Request
  • 401 - Unauthorized
  • 404 - Not Found
  • 500 - Internal Server Error
  • 503 - Service Unavailable

Code Examples

Python

import requests

# Create token
response = requests.post(
    "http://localhost:8443/api/v1/auth/token",
    json={"service": "python-client", "duration": "24h"}
)
token = response.json()["token"]

# Run audit
headers = {"Authorization": f"Bearer {token}"}
response = requests.post(
    "http://localhost:8443/api/v1/audit/ad",
    headers=headers,
    json={"async": False}
)

result = response.json()
risk = result["audit"]["summary"]["risk"]
print(f"Score: {risk['score']} ({risk['rating']})")
print(f"Findings: {risk['findings']['total']}")

JavaScript

const axios = require('axios');

async function runAudit() {
  // Create token
  const tokenResponse = await axios.post(
    'http://localhost:8443/api/v1/auth/token',
    { service: 'nodejs-client', duration: '24h' }
  );
  const token = tokenResponse.data.token;

  // Run audit
  const auditResponse = await axios.post(
    'http://localhost:8443/api/v1/audit/ad',
    { async: false },
    { headers: { Authorization: `Bearer ${token}` } }
  );

  const risk = auditResponse.data.audit.summary.risk;
  console.log(`Score: ${risk.score} (${risk.rating})`);
  console.log(`Findings: ${risk.findings.total}`);
}

runAudit();

PowerShell

# Create token
$tokenBody = @{
    service = "powershell-client"
    duration = "24h"
} | ConvertTo-Json

$tokenResponse = Invoke-RestMethod -Uri "http://localhost:8443/api/v1/auth/token" `
    -Method Post -Body $tokenBody -ContentType "application/json"

$token = $tokenResponse.token

# Run audit
$headers = @{
    "Authorization" = "Bearer $token"
    "Content-Type" = "application/json"
}

$auditBody = @{ async = $false } | ConvertTo-Json

$result = Invoke-RestMethod -Uri "http://localhost:8443/api/v1/audit/ad" `
    -Method Post -Headers $headers -Body $auditBody

$risk = $result.audit.summary.risk
Write-Host "Score: $($risk.score) ($($risk.rating))"
Write-Host "Findings: $($risk.findings.total)"

Finding Severity Levels

Severity Score Description
Critical 9.0-10.0 Immediate exploitation possible
High 7.0-8.9 Significant security weakness
Medium 4.0-6.9 Configuration issues
Low 1.0-3.9 Minor issues
Info 0.0 Informational only

Best Practices

  1. Token Security:

    • Store tokens securely (environment variables, secret managers)
    • Use short expiration times for automated scripts
    • Rotate tokens regularly
  2. Async Mode:

    • Use async mode for large domains (>10,000 objects)
    • Poll job status every 5-10 seconds
    • Implement timeout (5-10 minutes)
  3. Error Handling:

    • Check HTTP status codes
    • Implement retry logic with exponential backoff
    • Log errors with sufficient context
  4. Performance:

    • Use includeDetails: false unless full data is needed
    • Run audits during off-peak hours
    • Consider caching results

Support

For full documentation including CLI reference, installation guides, and vulnerability catalog: