Skip to content

Architecture Container Architecture

Maks Zaikin edited this page Jun 28, 2026 · 1 revision

title: "Architecture — Container Architecture" category: "architecture" version: "1.0" last_updated: "2024-01-15" standards: ["NIST-SP-800-53", "ISA-IEC-62443"] related_pages: ["Architecture-Overview", "Architecture-Network-Topology", "Architecture-Message-Bus", "Architecture-Plugin-Architecture", "Operations-Deployment-Guide"] ai_summary: "Complete VaultFlower Docker container inventory. 19 core containers + 3 optional MFA plugin containers. All containers run on vfw-core (..***.210) in Docker network 172.20.0.0/24. Includes image, IP, ports, purpose, dependencies, and environment-specific notes for each container."

🐳 Architecture — Container Architecture

Overview

All VaultFlower application containers run on a single Docker host (vfw-core, ***.***.***.210) within the 172.20.0.0/24 Docker bridge network.

docker_host: vfw-core (***.***.***.210)
docker_network: 172.20.0.0/24
total_core_containers: 19
total_optional_containers: 3 (MFA plugins)
registry: vfw-dc-registry (172.20.0.15:5000)
compose_files:
  - deploy/docker/docker-compose.infra.yml    # Infrastructure
  - deploy/docker/docker-compose.app.yml      # Application
  - deploy/docker/docker-compose.obs.yml      # Observability
  - deploy/docker/docker-compose.dev.yml      # Dev-only extras
  - deploy/docker/docker-compose.plugins.yml  # Optional MFA plugins

Container Groups

Group 1 — Network & Proxy

vfw-dc-nginx

container_name: vfw-dc-nginx
image: nginx:alpine
ip: 172.20.0.2
ports:
  exposed_to_host: 443:443
  internal: 80:80 (HTTP redirect to HTTPS)
purpose: >
  TLS termination and reverse proxy.
  Single entry point for all browser traffic.
  Routes /portal/* to Blazor Server, /api/* to Minimal API.
depends_on:
  - vfw-dc-api
  - vfw-dc-portal
config_files:
  - deploy/nginx/nginx.conf
  - deploy/nginx/vaultflower.conf
tls_cert: issued by vfw-pki (ICA01.contoso.com)
environment: all
notes: >
  nginx is the ONLY container with a port exposed to the corporate network.
  All other containers are accessible within Docker network only.

Group 2 — Application

vfw-dc-api

container_name: vfw-dc-api
image: vfw-dc-registry:5000/vaultflower/api:latest
ip: 172.20.0.3
ports:
  internal: 7001
  exposed_to_host: none (nginx proxies)
purpose: >
  .NET 9 ASP.NET Core Minimal API.
  Handles all REST API requests.
  JWT authentication and authorization.
  Vault assembly token management.
  RabbitMQ event publishing.
depends_on:
  - vfw-dc-rabbitmq
  - vfw-dc-vault
  - vfw-dc-postgres-assets
  - vfw-dc-postgres-secrets
  - vfw-dc-postgres-identity
  - vfw-dc-consul
connections_outbound:
  - vfw-dc-rabbitmq:5671 (mTLS AMQP)
  - vfw-dc-vault:8200 (Vault API)
  - vfw-dc-postgres-assets:5432
  - vfw-dc-postgres-secrets:5433
  - vfw-dc-postgres-identity:5434
  - vfw-dc-consul:8500 (service discovery)
  - vfw-dc-otel:4317 (traces)
  - DC02:88 (Kerberos)
  - DC02:636 (LDAPS)
  - ICA01:443 (OCSP)
environment: all
health_check: GET /health/ready

vfw-dc-portal

container_name: vfw-dc-portal
image: vfw-dc-registry:5000/vaultflower/portal:latest
ip: 172.20.0.4
ports:
  internal: 7002
  exposed_to_host: none (nginx proxies)
purpose: >
  .NET 9 Blazor Server Admin Portal.
  Server-side rendering + SignalR WebSocket.
  UI for Operators, Admins, and Auditors.
  All UI logic executes on server — never in browser.
depends_on:
  - vfw-dc-api
technology_notes: >
  Blazor Server (not WebAssembly).
  UI state maintained on server via SignalR.
  Browser receives only HTML + minimal JS.
  This is a security decision — business logic never exposed to client.
connections_outbound:
  - vfw-dc-api:7001 (internal API calls)
  - vfw-dc-otel:4317 (traces)
environment: all
health_check: GET /health

vfw-dc-worker-workflow

container_name: vfw-dc-worker-workflow
image: vfw-dc-registry:5000/vaultflower/worker-workflow:latest
ip: 172.20.0.5
ports:
  internal: none (no HTTP server)
  exposed_to_host: none
purpose: >
  .NET 9 Worker Service — Workflow Engine.
  Consumes checkout/checkin events from RabbitMQ.
  Manages checkout state machine.
  Schedules and publishes rotation tasks.
  Handles plugin registration and health tracking.
  Creates automatic rotation tasks after ACCESS_TASK completion.
  Sends email notifications (task assignment, owner approval).
  Manages tenant VHost provisioning in RabbitMQ.
depends_on:
  - vfw-dc-rabbitmq
  - vfw-dc-vault
  - vfw-dc-postgres-assets
  - vfw-dc-minio
connections_outbound:
  - vfw-dc-rabbitmq:5671 (mTLS AMQP — consume and publish)
  - vfw-dc-vault:8200 (license validation, token management)
  - vfw-dc-postgres-assets:5432 (task management)
  - vfw-dc-minio:9000 (document storage)
  - vfw-dc-otel:4317 (traces)
  - SMTP server (email notifications)
environment: all
health_check: internal Consul registration

Group 3 — Infrastructure

vfw-dc-rabbitmq

container_name: vfw-dc-rabbitmq
image: rabbitmq:4-management
ip: 172.20.0.6
ports:
  internal:
    - 5671 (AMQPS — mTLS)
    - 15671 (Management UI — HTTPS)
  exposed_to_host:
    - 5671:5671 (for APP01 Rotation Agent)
    - 15671:15671 (Management UI — dev only)
purpose: >
  Message broker for all async inter-service communication.
  Durable queues ensure rotation tasks survive restarts.
  mTLS enforced — all clients present certificates.
  Separate VHosts per tenant for isolation.
vhosts:
  - /vfw-system (system events, rotation, workflow)
  - /vfw-tenant-{slug} (per-tenant audit, SIEM, alerts)
tls_certs: issued by vfw-pki
data_volume: /mnt/vfw-data/rabbitmq
environment: all
health_check: rabbitmq-diagnostics ping
notes: >
  Management UI (15671) exposed in dev only.
  In production, management UI is internal only.
  APP01 connects to 5671 from ***.***.***.125.
  Firewall restricts 5671 to APP01 IP only.

vfw-dc-vault

container_name: vfw-dc-vault
image: hashicorp/vault
ip: 172.20.0.7
ports:
  internal: 8200
  exposed_to_host: none
purpose: >
  HashiCorp Vault OSS — Key Management System.
  Stores and manages all encryption keys (DEK, KEK).
  Issues time-bound assembly tokens for checkout.
  Stores MFA secrets (TOTP, WebAuthn, Smartcard refs).
  Stores database connection strings.
  Stores RabbitMQ and MinIO credentials.
  Manages Shamir Secret Sharing for unseal.
unseal: Shamir Secret Sharing (3-of-5 administrators + Smartcard + PIN)
data_volume: /mnt/vfw-data/vault
environment: all
health_check: vault status
critical_notes: >
  Vault port (8200) is NEVER exposed to the host network.
  Accessible only within Docker network.
  Vault must be manually unsealed after every restart.
  Sealed Vault = entire VaultFlower system unavailable.
  See Operations-Vault-Init for unseal procedures.

vfw-dc-consul

container_name: vfw-dc-consul
image: hashicorp/consul
ip: 172.20.0.18
ports:
  internal: 8500
  exposed_to_host: none
purpose: >
  HashiCorp Consul — Service Discovery and Health Checks.
  Active health checks for all containers (every 10 seconds).
  MFA plugin registration and discovery.
  Multi-datacenter ready for future production scale-out.
used_for:
  - Active health checks (GET /health every 10s per service)
  - MFA plugin lifecycle management
  - Service-to-service discovery by name
not_used_for:
  - Key-Value store (Vault handles this)
  - Service Mesh / mTLS (PKI handles this)
data_volume: /mnt/vfw-data/consul
environment: all
health_check: consul info

vfw-dc-minio

container_name: vfw-dc-minio
image: minio/minio
ip: 172.20.0.19
ports:
  internal:
    - 9000 (S3 API)
    - 9001 (MinIO Console UI)
  exposed_to_host: none
purpose: >
  S3-compatible object storage for signed task completion forms.
  Every completed task (ROTATION_TASK, ACCESS_TASK) requires
  a signed physical form uploaded as PDF/JPG/PNG.
  Forms are encrypted at rest using Vault-managed keys.
bucket_structure: vfw-forms/{tenant-slug}/{year}/{month}/
encryption: SSE-KMS via HashiCorp Vault
data_volume: /mnt/vfw-data/minio
environment: all
health_check: mc ready
notes: >
  MinIO Console (9001) for admin access is not exposed to host.
  Access via Docker exec or SSH tunnel for administration.

Group 4 — Databases

vfw-dc-postgres-assets

container_name: vfw-dc-postgres-assets
image: postgres:17
ip: 172.20.0.8
ports:
  internal: 5432
  exposed_to_host: none
purpose: >
  Assets database — stores 
     - location/system/zone/asset hierarchy,
   maintenance tasks and schedules, password policies,
  approval tokens, and task documents metadata.
schemas:
  - assets (operational data)
  - assets_audit (append-only audit log — INSERT only)
database_user_vfw_app: SELECT, INSERT, UPDATE, DELETE on assets schema
database_user_vfw_app: INSERT ONLY on assets_audit schema
database_user_vfw_auditor: SELECT ONLY on assets_audit schema
data_volume: /mnt/vfw-data/postgres-assets
environment: all
health_check: pg_isready

vfw-dc-postgres-secrets

container_name: vfw-dc-postgres-secrets
image: postgres:17
ip: 172.20.0.9
ports:
  internal: 5433
  exposed_to_host: none
purpose: >
  Secrets database — stores encrypted credentials, checkout records,
  and password history. Contains AES-256-GCM ciphertext only.
  No asset hostnames, no user names — UUID references only.
schemas:
  - secrets (encrypted credentials, checkouts, history)
  - secrets_audit (append-only audit log — INSERT only)
critical_notes: >
  This database contains encrypted credential data.
  DEK for decryption is in HashiCorp Vault — not in this DB.
  Compromise of this DB alone yields only unreadable ciphertext.
data_volume: /mnt/vfw-data/postgres-secrets
environment: all
health_check: pg_isready

vfw-dc-postgres-identity

container_name: vfw-dc-postgres-identity
image: postgres:17
ip: 172.20.0.10
ports:
  internal: 5434
  exposed_to_host: none
purpose: >
  Identity database — stores tenants, users, roles, MFA methods,
  access scopes, and plugin registry.
  Authentication secrets (TOTP keys, WebAuthn keys) stored as
  Vault path references — never the secrets themselves.
schemas:
  - identity (operational data)
  - identity_audit (append-only audit log — INSERT only)
data_volume: /mnt/vfw-data/postgres-identity
environment: all
health_check: pg_isready

Group 5 — Observability

vfw-dc-otel

container_name: vfw-dc-otel
image: otel/opentelemetry-collector
ip: 172.20.0.11
ports:
  internal:
    - 4317 (gRPC OTLP receiver)
    - 4318 (HTTP OTLP receiver)
  exposed_to_host: none
purpose: >
  OpenTelemetry Collector.
  Receives traces and metrics from all application services.
  Applies PII masking before export to backends.
  Routes traces to Jaeger, metrics to VictoriaMetrics.
pii_masking:
  masked_before_export:
    - user.upn → "***@***.***"
    - net.peer.ip → "192.168.***.***"
    - asset.hostname → "***"
  preserved:
    - tenant.slug (not PII)
    - request.id (UUID, not PII)
    - event.type (structured, not PII)
exporters:
  traces: vfw-dc-jaeger:14250
  metrics: vfw-dc-victoria:8428
environment: all

vfw-dc-jaeger

container_name: vfw-dc-jaeger
image: jaegertracing/all-in-one
ip: 172.20.0.12
ports:
  internal:
    - 16686 (Jaeger UI)
    - 14250 (gRPC collector)
  exposed_to_host: none
purpose: >
  Distributed tracing UI.
  Stores and visualizes request traces across all services.
  X-Request-ID links traces to audit log entries.
  All PII already masked by OTel Collector before arrival.
environment: all
notes: >
  In production, consider Jaeger with external storage (Cassandra/ES).
  All-in-one image suitable for development and small installations.

vfw-dc-victoria

container_name: vfw-dc-victoria
image: victoriametrics/victoria-metrics
ip: 172.20.0.13
ports:
  internal: 8428
  exposed_to_host: none
purpose: >
  Time-series metrics storage.
  Stores application and infrastructure metrics from OTel Collector.
  Queried by Grafana for dashboards.
  Stores: request rates, error rates, checkout counts,
          rotation success/failure rates, queue depths.
data_volume: /mnt/vfw-data/victoria
environment: all

vfw-dc-grafana

container_name: vfw-dc-grafana
image: grafana/grafana
ip: 172.20.0.14
ports:
  internal: 3000
  exposed_to_host: none
purpose: >
  Monitoring dashboards and alerting.
  Data source: VictoriaMetrics.
  Pre-built dashboards:
    - System health (all containers)
    - Checkout activity (daily/weekly/monthly)
    - Rotation success rates
    - Failed authentication attempts
    - SIEM event rates by severity
    - Queue depth monitoring
data_volume: /mnt/vfw-data/grafana
environment: all
notes: >
  Not exposed to host network.
  Access via SSH tunnel or VPN in production.
  In development, can expose port for convenience.

Group 6 — Registry

vfw-dc-registry

container_name: vfw-dc-registry
image: registry:2
ip: 172.20.0.15
ports:
  internal: 5000
  exposed_to_host: 5000:5000
purpose: >
  Private Docker image registry.
  Stores all VaultFlower application images in isolated network.
  No internet required for image pulls after initial setup.
  Images pushed here from CI/CD pipeline.
repositories:
  - vaultflower/api
  - vaultflower/portal
  - vaultflower/worker-workflow
  - vaultflower/mfa-totp (plugin)
  - vaultflower/mfa-webauthn (plugin)
  - vaultflower/mfa-smartcard (plugin)
data_volume: /mnt/vfw-data/registry
environment: all

vfw-dc-registry-ui

container_name: vfw-dc-registry-ui
image: joxit/docker-registry-ui
ip: 172.20.0.17
ports:
  internal: 8081
  exposed_to_host: none
purpose: Web UI for browsing Docker Registry contents
environment: development ONLY
production_note: >
  This container is NOT included in docker-compose.prod.yml.
  Remove or comment out before production deployment.

Group 7 — Development Tools

vfw-dc-pgadmin

container_name: vfw-dc-pgadmin
image: dpage/pgadmin4
ip: 172.20.0.16
ports:
  internal: 8080
  exposed_to_host: none
purpose: Web UI for PostgreSQL database administration
environment: development ONLY
production_note: >
  This container is NOT included in docker-compose.prod.yml.
  Direct database access in production only via audited CLI sessions.
  pgAdmin in production is an unnecessary attack surface.

Group 8 — MFA Plugins (Optional, Licensed)

MFA plugins are not started by default. They are activated by adding the relevant service to the Docker Compose plugin file and providing a valid license key.

vfw-dc-mfa-totp

container_name: vfw-dc-mfa-totp
image: vfw-dc-registry:5000/vaultflower/mfa-totp:latest
ip: 172.20.0.20
ports:
  internal: 8301
  exposed_to_host: none
purpose: TOTP MFA plugin (Google/Microsoft Authenticator compatible)
license: required
activation:
  1. Start container
  2. Plugin registers with Consul
  3. Plugin publishes to RabbitMQ vfw.plugins.registry
  4. Worker validates license via Vault
  5. API activates /mfa/totp/* endpoints automatically
health_check: GET /health (Consul monitors every 10s)
environment: optional (enterprise)

vfw-dc-mfa-webauthn

container_name: vfw-dc-mfa-webauthn
image: vfw-dc-registry:5000/vaultflower/mfa-webauthn:latest
ip: 172.20.0.21
ports:
  internal: 8302
  exposed_to_host: none
purpose: WebAuthn MFA plugin (YubiKey, Touch ID, Windows Hello)
license: required
activation: same as vfw-dc-mfa-totp
health_check: GET /health
environment: optional (enterprise)

vfw-dc-mfa-smartcard

container_name: vfw-dc-mfa-smartcard
image: vfw-dc-registry:5000/vaultflower/mfa-smartcard:latest
ip: 172.20.0.22
ports:
  internal: 8303
  exposed_to_host: none
purpose: >
  Mifare Smartcard + PIN MFA plugin.
  Required for CRITICAL asset checkout (3rd authentication factor).
  Works with VaultFlower.Agent.Workstation on user workstations.
license: required
activation: same as vfw-dc-mfa-totp
health_check: GET /health
environment: optional (enterprise)
notes: >
  Requires VaultFlower.Agent.Workstation installed on user workstations.
  Agent reads Mifare UID via NFC reader and sends to browser via
  localhost WebSocket (port 7777, loopback only).

Windows Services (Not Containers)

These components run as Windows Services on existing servers, not as Docker containers.

VaultFlower.Worker.Rotation

service_name: VaultFlower.Worker.Rotation
host: APP01 (***.***.***.125)
os: Windows Server 2022
account: gMSA (vfw-rotation$) from Active Directory
purpose: >
  Password rotation execution agent.
  Consumes rotation tasks from RabbitMQ.
  Executes: WinRM (Windows local accounts)
             SSH (Linux accounts)
             LDAP/ADSI (Active Directory domain accounts)
  Publishes results back to RabbitMQ.
  Never accesses databases or Vault directly.
  All communication via RabbitMQ only.
connection: vfw-core:5671 (AMQPS/mTLS)
certificate: gMSA certificate from vfw-pki
deploy: manual installation or GPO deployment

VaultFlower.Agent.Workstation

service_name: VaultFlower.Agent.Workstation
host: Operator workstations
os: Windows (any version supported by .NET 9)
account: Local System or domain user
purpose: >
  Lightweight smartcard reader agent.
  Reads Mifare card UID from NFC/RFID reader connected to workstation.
  Exposes UID via localhost WebSocket (port 7777, loopback ONLY).
  Browser communicates with agent to pass UID to VaultFlower API.
  Required only when Smartcard MFA plugin is active.
connection: localhost:7777 (WebSocket, loopback only — never network)
deploy: GPO software deployment across workstations

Container Startup Order


Phase 1 — Infrastructure (must be healthy before Phase 2):
  vfw-dc-postgres-assets
  vfw-dc-postgres-secrets
  vfw-dc-postgres-identity
  vfw-dc-rabbitmq
  vfw-dc-vault  ← must be unsealed manually after start
  vfw-dc-consul
  vfw-dc-minio

Phase 2 — Application (depends on Phase 1):
  vfw-dc-worker-workflow
  vfw-dc-api
  vfw-dc-portal

Phase 3 — Proxy (depends on Phase 2):
  vfw-dc-nginx

Phase 4 — Observability (independent, can start any time):
  vfw-dc-otel
  vfw-dc-jaeger
  vfw-dc-victoria
  vfw-dc-grafana

Phase 5 — Registry (independent):
  vfw-dc-registry
  vfw-dc-registry-ui (dev only)
  vfw-dc-pgadmin (dev only)

Phase 6 — Plugins (optional, after Phase 2):
  vfw-dc-mfa-totp
  vfw-dc-mfa-webauthn
  vfw-dc-mfa-smartcard

Resource Estimates

Minimum resources for vfw-core:

Resource Minimum Recommended
CPU 4 cores 6 cores
RAM 20 GB 24 GB
OS Disk 40 GB 40 GB
Data Disk 100 GB 200 GB

RAM breakdown:


vfw-dc-postgres-assets:    ~1.5 GB
vfw-dc-postgres-secrets:   ~1.5 GB
vfw-dc-postgres-identity:  ~1.5 GB
vfw-dc-rabbitmq:           ~1.0 GB
vfw-dc-vault:              ~0.5 GB
vfw-dc-consul:             ~0.3 GB
vfw-dc-minio:              ~0.5 GB
vfw-dc-api:                ~0.5 GB
vfw-dc-portal:             ~0.5 GB
vfw-dc-worker-workflow:    ~0.5 GB
vfw-dc-nginx:              ~0.1 GB
vfw-dc-otel:               ~0.3 GB
vfw-dc-jaeger:             ~0.5 GB
vfw-dc-victoria:           ~1.0 GB
vfw-dc-grafana:            ~0.3 GB
vfw-dc-registry:           ~0.2 GB
OS + Docker overhead:      ~4.0 GB
Buffer:                    ~3.8 GB
─────────────────────────────────
Total:                    ~18.0 GB (minimum 20 GB recommended)

Related Pages

Clone this wiki locally