Post 022


The Problem: Bot Sprawl

The Alliance Fleet Discord server was accumulating single-purpose bots. Admiral Ackbar for alerts, BD-1 for knowledge queries, separate webhook endpoints for each automation. Each bot had its own runtime, its own credentials, and its own attack surface.

K-2SO is the consolidation play: a unified observability bot that replaces the sprawl with a single, heavily isolated entry point. It reads everything, queries anything, and can't modify a thing.


Design Principles

  1. Read-only by default - K-2SO can query every system (Wazuh, InfluxDB, Proxmox API, Docker) but cannot modify any of them. No write permissions, no restart capabilities, no config changes.

  2. Double isolation - Docker inside an unprivileged LXC container. Even if the bot is compromised, the attacker is inside a container inside a container with no network path to management interfaces.

  3. HTTPS-only API access - K-2SO reaches backend services exclusively via HTTPS API endpoints. No SSH, no direct database connections, no shell access.

  4. Dedicated network segment - The bot runs on a Bot-Net VLAN with firewall rules that only allow outbound HTTPS to specific service IPs.


Architecture

┌──────────────────────────────────────────────────┐
│                 Proxmox Host (Node-B)            │
│                                                  │
│  ┌─────────────────────────────────────────────┐ │
│  │           Unprivileged LXC Container        │ │
│  │           (Bot-Net VLAN)                    │ │
│  │                                             │ │
│  │  ┌───────────────────────────────────────┐  │ │
│  │  │         Docker Container              │  │ │
│  │  │         (K-2SO Bot Runtime)           │  │ │
│  │  │                                       │  │ │
│  │  │  Node.js + Discord.js                 │  │ │
│  │  │  ├── HTTPS → Wazuh API                │  │ │
│  │  │  ├── HTTPS → InfluxDB API             │  │ │
│  │  │  ├── HTTPS → Proxmox API              │  │ │
│  │  │  ├── HTTPS → Portainer API            │  │ │
│  │  │  └── HTTPS → n8n Webhooks             │  │ │
│  │  └───────────────────────────────────────┘  │ │
│  └─────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘

Why Double Isolation?

Layer 1 - Unprivileged LXC: The container runs with user namespace mapping. Root inside the LXC is mapped to a non-root UID on the host. Even a container escape doesn't grant host-level access.

Layer 2 - Docker inside LXC: The bot process runs in a Docker container inside the LXC. This adds filesystem isolation, network namespace separation, and resource limits.

Combined effect: An attacker who compromises K-2SO's application code would need to escape Docker, then escape the unprivileged LXC, then pivot across VLANs - each with its own isolation boundary.


Network Security

Bot-Net VLAN

K-2SO's LXC container lives on a dedicated VLAN with restrictive firewall rules:

VLAN: Bot-Net
Subnet: 192.168.50.0/24 (example)

Allowed outbound:
  → 192.168.20.30:443   (Wazuh API)
  → 192.168.20.41:8086  (InfluxDB API)
  → 192.168.20.10:9443  (Portainer API)
  → 192.168.1.x:8006    (Proxmox API)
  → Discord API (external HTTPS)

Denied:
  → All SSH (port 22) to any destination
  → All management interfaces not explicitly listed
  → All inter-VLAN traffic not explicitly allowed

Read-Only API Tokens

Every backend API token used by K-2SO is scoped to read-only:

Service Token Scope Can Read Can Write
Wazuh API read ✅ Alerts, agents, CVEs
InfluxDB Read bucket ✅ Metrics queries
Proxmox PVEAuditor role ✅ Node status, VM info
Portainer Read-only user ✅ Container status

AI Routing via n8n

K-2SO routes AI-powered commands through n8n webhooks rather than calling Ollama directly. This adds an authentication layer and centralizes AI workflow logic:

const AI_COMMANDS = {
    '!ask':     { command: 'ask',     description: 'Ask Ollama a question' },
    '!rag':     { command: 'rag',     description: 'Query documents via AnythingLLM' },
    '!imagine': { command: 'imagine', description: 'Generate image via ComfyUI' },
    '!models':  { command: 'models',  description: 'List available Ollama models' },
    '!gpu':     { command: 'gpu',     description: 'Show GPU utilization' },
};

async function handleAICommand(message) {
    const response = await fetch(N8N_WEBHOOK_URL, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-K2SO-Token': K2SO_WEBHOOK_SECRET,
        },
        body: JSON.stringify({
            command: AI_COMMANDS[cmd].command,
            query,
            user: message.author.username,
            channel: message.channel.name,
            timestamp: new Date().toISOString(),
        }),
    });
    // Format and return response to Discord
}

n8n Workflow: AI Router

K-2SO webhook → n8n
    ├── !ask    → Ollama (llama3:8b) → formatted response
    ├── !rag    → AnythingLLM RAG API → context-aware answer
    ├── !imagine → ComfyUI → generated image
    ├── !models → Ollama /api/tags → model list
    └── !gpu    → InfluxDB nvidia_smi query → stats

Conversation Memory (PostgreSQL)

For threaded conversations, K-2SO stores message history in PostgreSQL:

CREATE TABLE k2so_conversations (
    id SERIAL PRIMARY KEY,
    thread_id VARCHAR(64) NOT NULL,
    role VARCHAR(16) NOT NULL,
    content TEXT NOT NULL,
    model VARCHAR(32),
    tokens_used INTEGER,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_thread_recent
    ON k2so_conversations(thread_id, created_at DESC);

-- Auto-cleanup: messages older than 7 days
DELETE FROM k2so_conversations
    WHERE created_at < NOW() - INTERVAL '7 days';

Why This Matters for the Portfolio

The K-2SO architecture demonstrates security thinking beyond "it works":

  • Least privilege - every API token is scoped to minimum required access
  • Defense in depth - two isolation layers plus network segmentation
  • Blast radius containment - compromise of the bot limits damage to read-only API access from an isolated VLAN
  • Separation of concerns - the bot handles interaction, n8n handles orchestration, backend services handle data

These are the same principles behind Azure Managed Identities, AWS IAM roles, and GCP Workload Identity - applied at the homelab level.


Current Status

K-2SO is in design/planning phase. The architecture document is complete. BD-1 currently handles the interactive Discord role; K-2SO would eventually replace it as the unified observability interface.


Related: Post 021 - BD-1 Discord Bot | Post 020 - n8n Automation Platform | Post 024 - Discord as an Ops Console