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