-
Notifications
You must be signed in to change notification settings - Fork 0
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."
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 pluginscontainer_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.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/readycontainer_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 /healthcontainer_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 registrationcontainer_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.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.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 infocontainer_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.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_isreadycontainer_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_isreadycontainer_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_isreadycontainer_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: allcontainer_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.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: allcontainer_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.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: allcontainer_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.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.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.
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)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)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).These components run as Windows Services on existing servers, not as Docker containers.
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 deploymentservice_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
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
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)
- Architecture-Overview — system context
- Architecture-Network-Topology — IP addresses and firewall
- Architecture-Message-Bus — RabbitMQ configuration
- Architecture-Plugin-Architecture — plugin lifecycle
- Operations-Deployment-Guide — deployment instructions