Skip to content

Architecture

Architecture Overview

Solti-Ensemble provides shared infrastructure services following a layered services architecture. Services are organized by dependency level and function.

Service Layers

Layer 1: Foundation (Base System)

Purpose: Essential system hardening and base utilities

Components: - SSHD Hardening - Secure remote access - Fail2ban Config - Intrusion prevention - Podman - Container runtime

Characteristics: - No dependencies on other ensemble services - Applied early in host lifecycle - Security-focused - Cross-platform (Debian, Rocky, Ubuntu)

Deployment Order: First

Layer 2: Infrastructure Services

Purpose: Shared resources consumed by applications

Components: - MariaDB - Relational database - NFS Client - Network storage - WireGuard - VPN connectivity

Characteristics: - May depend on Layer 1 (e.g., Podman for containerized MariaDB) - Provide services to Layer 3 applications - Production-critical - Require careful lifecycle management

Deployment Order: Second

Layer 3: Application Services

Purpose: End-user applications and platforms

Components: - Gitea - Git hosting (requires MariaDB or SQLite) - Ghost - Content management (requires database) - ISPConfig Utilities - Server management tools

Characteristics: - Consume Layer 2 infrastructure services - User-facing functionality - Complex configuration - Integration with external systems

Deployment Order: Third

Layer 4: Development Tools

Purpose: Developer productivity and workflows

Components: - VS Code - IDE installation - Claude SecTest - Security auditing

Characteristics: - Independent of other layers - Development and analysis focus - Not production-critical - Can be deployed anytime

Deployment Order: As needed

Dependency Graph

Layer 4 (Dev Tools)
  ├─ VS Code (standalone)
  └─ Claude SecTest (standalone)

Layer 3 (Applications)
  ├─ Gitea
  │   ├─ MariaDB (Layer 2) or SQLite
  │   └─ Podman (Layer 1, optional)
  ├─ Ghost
  │   ├─ MariaDB (Layer 2)
  │   └─ NFS Client (Layer 2, optional)
  └─ ISPConfig Utilities
      └─ MariaDB (Layer 2)

Layer 2 (Infrastructure)
  ├─ MariaDB
  │   └─ Podman (Layer 1, optional)
  ├─ NFS Client (standalone)
  └─ WireGuard (standalone)

Layer 1 (Foundation)
  ├─ SSHD Hardening (standalone)
  ├─ Fail2ban Config (standalone)
  └─ Podman (standalone)

Service Interaction Patterns

Pattern 1: Database Service

Example: Application requires MariaDB

# Step 1: Deploy MariaDB
- hosts: db_servers
  roles:
    - jackaltx.solti_ensemble.mariadb
  vars:
    mariadb_mysql_root_password: "{{ vault_root_pass }}"
    mariadb_bind_address: "0.0.0.0"

# Step 2: Deploy application
- hosts: app_servers
  roles:
    - jackaltx.solti_ensemble.gitea
  vars:
    gitea_db_host: "db.example.com"
    gitea_db_password: "{{ vault_gitea_db_pass }}"

Pattern 2: VPN Connectivity

Example: Remote monitoring via WireGuard

# Step 1: Deploy WireGuard on hub
- hosts: monitor_hub
  roles:
    - jackaltx.solti_ensemble.wireguard
  vars:
    wireguard_mode: server
    wireguard_listen_port: 51820

# Step 2: Deploy WireGuard on spoke
- hosts: remote_hosts
  roles:
    - jackaltx.solti_ensemble.wireguard
  vars:
    wireguard_mode: client
    wireguard_endpoint: "hub.example.com:51820"

Pattern 3: Storage Integration

Example: Application data on NFS

# Step 1: Mount NFS
- hosts: app_servers
  roles:
    - jackaltx.solti_ensemble.nfs_client
  vars:
    nfs_shares:
      - server: "nas.example.com"
        remote_path: "/export/app_data"
        local_path: "/mnt/nfs/app_data"

# Step 2: Configure application to use NFS path
- hosts: app_servers
  roles:
    - jackaltx.solti_ensemble.ghost
  vars:
    ghost_data_path: "/mnt/nfs/app_data/ghost"

State Management

All ensemble roles follow consistent state management:

Installation State

# Install service
role_state: present

Actions: - Install packages - Generate configuration - Start services - Enable on boot

Removal State

# Remove service
role_state: absent
role_remove_data: false  # Keep data

Actions: - Stop services - Disable from boot - Remove packages - Optionally remove data (if role_remove_data: true)

Idempotency

All roles are idempotent: - Safe to re-run - Only changes what needs changing - Reports changed/unchanged status

Security Architecture

Credential Management

Ansible Vault Pattern:

# vars/vault.yml (encrypted)
vault_mariadb_root: "secure_password"
vault_gitea_db_pass: "another_secure_password"
vault_wireguard_psk: "pre_shared_key"

# playbook.yml
- hosts: servers
  vars_files:
    - vars/vault.yml
  vars:
    mariadb_mysql_root_password: "{{ vault_mariadb_root }}"

Network Security

Default Policies: - Services bind to localhost by default - Explicit configuration required for external access - WireGuard for secure remote connectivity - Fail2ban for intrusion prevention

Example Hardened Deployment:

- hosts: servers
  roles:
    # Layer 1: Foundation
    - role: jackaltx.solti_ensemble.sshd_harden
    - role: jackaltx.solti_ensemble.fail2ban_config

    # Layer 2: Infrastructure
    - role: jackaltx.solti_ensemble.wireguard
      vars:
        wireguard_mode: client

    - role: jackaltx.solti_ensemble.mariadb
      vars:
        mariadb_bind_address: "127.0.0.1"  # Localhost only
        mariadb_security: true

Integration with SOLTI Ecosystem

Consumed by solti-monitoring

# monitoring server uses ensemble services
- hosts: monitor_hub
  roles:
    # Ensemble provides VPN
    - jackaltx.solti_ensemble.wireguard

    # Ensemble provides storage
    - jackaltx.solti_ensemble.nfs_client

    # Monitoring uses storage for InfluxDB data
    - jackaltx.solti_monitoring.influxdb
      vars:
        influxdb_data_path: "/mnt/nfs/influxdb"

Integration with Custom Applications

# custom app consumes ensemble database
- hosts: app_servers
  roles:
    - jackaltx.solti_ensemble.mariadb
    - custom.app.deployment
  vars:
    app_database_host: "localhost"
    app_database_port: 3306

Cross-Platform Support

Supported Distributions: - Debian 12 - Rocky Linux 9 - Ubuntu 24.04

Platform-Specific Behavior:

Role Debian Rocky Ubuntu Notes
MariaDB Different package names handled
WireGuard Kernel module vs userspace
SSHD Hardening Config paths differ
NFS Client Package names differ
Podman Repository setup differs
Gitea Systemd service consistent

Testing Architecture

Molecule Scenarios

Each role includes molecule testing:

roles/mariadb/molecule/
├── default/          # Podman container test
├── proxmox/         # Proxmox VM test
└── combined/        # Integration with other roles

Test Platforms

Podman (Development): - Fast feedback loop - Isolated environments - Local testing

Proxmox (Pre-Release): - Production-like VMs - Multi-distro validation - Full integration testing

GitHub Actions (CI): - Automated on PR - Custom testing containers - Regression prevention

Verification Pattern

All roles include verification tasks:

# molecule/default/verify.yml
- name: Verify MariaDB
  hosts: all
  tasks:
    - name: Check service running
      systemd:
        name: mariadb
        state: started
      check_mode: yes

    - name: Test database connection
      mysql_db:
        name: test_db
        state: present
        login_user: root
        login_password: "{{ mariadb_mysql_root_password }}"

Deployment Patterns

Single Host (All-in-One)

Deploy all services on one host:

- hosts: standalone_server
  roles:
    - jackaltx.solti_ensemble.sshd_harden
    - jackaltx.solti_ensemble.mariadb
    - jackaltx.solti_ensemble.gitea
    - jackaltx.solti_ensemble.nfs_client

Distributed Services

Separate services across hosts:

# Database server
- hosts: db_servers
  roles:
    - jackaltx.solti_ensemble.mariadb

# Application servers
- hosts: app_servers
  roles:
    - jackaltx.solti_ensemble.gitea
  vars:
    gitea_db_host: "db.example.com"

Hub-and-Spoke (with solti-monitoring)

Ensemble provides connectivity:

# Hub (central services)
- hosts: service_hub
  roles:
    - jackaltx.solti_ensemble.wireguard
    - jackaltx.solti_ensemble.mariadb
    - jackaltx.solti_ensemble.nfs_client

# Spokes (remote hosts)
- hosts: remote_hosts
  roles:
    - jackaltx.solti_ensemble.wireguard
    - jackaltx.solti_monitoring.telegraf
    - jackaltx.solti_monitoring.alloy

Resource Requirements

Minimal Deployment

Single host with MariaDB + Gitea: - CPU: 2 cores - RAM: 2GB - Disk: 20GB - Network: 100 Mbps

Production Deployment

Distributed services: - Database server: 2 cores, 4GB RAM, 100GB disk - Application servers: 2 cores, 2GB RAM, 50GB disk - VPN hub: 1 core, 1GB RAM, 10GB disk

Next Steps

  • Getting Started - Installation and first deployment
  • Infrastructure Services - Detailed role documentation
  • Security Tools - Hardening and auditing guides
  • Testing Framework - Validation and CI/CD