-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture Overview
title: "Architecture Overview" category: "architecture" version: "1.0" last_updated: "2024-01-15" standards: ["NIST-SP-800-53", "ISA-IEC-62443", "FSTEC"] related_pages: ["Architecture-Data-Fragmentation", "Architecture-Encryption-Model", "Architecture-Network-Topology", "Architecture-Container-Architecture", "Architecture-Message-Bus", "Security-Principles"] ai_summary: "Complete VaultFlower system architecture. Three-database data fragmentation, HashiCorp Vault as trust anchor, RabbitMQ async messaging, plugin-based MFA, and ISA/IEC 62443 asset hierarchy."
Classical PAM solutions solve privileged access for domain-joined, network-connected assets. VaultFlower solves the harder problem: privileged access for assets that cannot be reached by classical PAM. plus it ads an integrity management workflow for transparency and controll
Classical PAM covers: VaultFlower additionally covers:
✅ Domain-joined Windows servers ✅ Air-gapped OT networks
✅ Network-connected Linux hosts ✅ Offline/isolated assets
✅ Cloud infrastructure ✅ Non-domain ICS/SCADA systems
✅ Legacy industrial controllers
✅ Break-glass accounts
Every architectural decision in VaultFlower derives from these principles. They are non-negotiable and take precedence over convenience, performance, or simplicity.
principles:
- id: P1
name: "Data Fragmentation"
statement: >
No single component ever has access to the complete data picture
without explicit authorization through HashiCorp Vault.
standard: "NIST SP 800-53 AC-3, AC-4"
- id: P2
name: "Zero Persistence of Plaintext"
statement: >
Plaintext passwords exist only in application memory during assembly.
They are shown once in the UI and immediately destroyed.
standard: "NIST SP 800-53 SC-28, IA-5"
- id: P3
name: "Dual Control"
statement: >
Every password access requires two independent MFA authorizations
from two different users. No bypass exists for any role.
standard: "NIST SP 800-53 AC-3(2), FSTEC ОПС.1"
- id: P4
name: "Immutable Audit"
statement: >
Audit log tables have INSERT-only database grants.
UPDATE and DELETE are never permitted on audit schemas.
standard: "NIST SP 800-53 AU-9, AU-10"
- id: P5
name: "Async Messaging"
statement: >
No direct HTTP between internal services.
All inter-service communication goes through RabbitMQ with mTLS.
standard: "Defense in depth"
- id: P6
name: "Task-Gated Access"
statement: >
Any password access requires an active work task approved
by the system owner. No task = no access.
standard: "NIST SP 800-53 AC-2, FSTEC УПД.1"
┌────────────────────────────────────────────────────────────────┐
│ EXTERNAL │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Browser │ │ AD/DNS │ │ PKI/CA │ │
│ │(Operator │ │ DC02 │ │ ICA01 │ │
│ │ Admin │ │.115 │ │ .51 │ │
│ │ Auditor) │ └──────────┘ └──────────┘ │
│ └────┬─────┘ │ │ │
│ │ HTTPS:443 │ Kerberos │ TLS Certs │
└───────│───────────────│───────────────│────────────────────────┘
│ │ │
┌───────│───────────────│───────────────│─────────────────────────┐
│ vfw-core (.210) | │ │
│ │ │ │ │
│ ┌────▼─────┐ │ │ │
│ │ nginx │◄────────┘ │ │
│ │ :443 │ TLS termination │ │
│ └────┬─────┘ │ │
│ │ │ │
│ ┌────▼──────┐ ┌──────────────┐ │ │
│ │ Blazor │ │ .NET 9 │ │ │
│ │ Server │ │ Minimal API │ │ │
│ │ Portal │ │ :7001 │ │ │
│ └────┬──────┘ └──────┬───────┘ │ │
│ │ │ │ │
│ └──────────┬───────┘ │ │
│ │ │ │
│ ┌──────▼──────┐ │ │
│ │ RabbitMQ │◄────────────┘ │
│ │ mTLS :5671 │ mTLS certs from PKI │
│ └──────┬──────┘ │
│ │ │
│ ┌────────────┼────────────┐ │
│ │ │ │ │
│ ┌──▼───┐ ┌───▼───┐ ┌───▼──────────┐ │
│ │Worker│ │Vault │ │ PostgreSQL │ │
│ │Work- │ │:8200 │ │ 3 databases │ │
│ │flow │ │(KMS) │ │ :5432/:5433 │ │
│ └──────┘ └───────┘ │ /:5434 │ │
│ └──────────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Consul │ │ MinIO │ │ OTel │ │ Grafana │ │
│ │ :8500 │ │ :9000 │ │ Collector│ │ :3000 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────┘
│ AMQPS:5671 (mTLS)
┌─────────────│────────────────────────────────────────────────────┐
│ APP01 (.125) │
│ │ │
│ ┌──────────▼──────────────────────────────────────────────┐ │
│ │ VaultFlower.Worker.Rotation (Windows Service + gMSA) │ │
│ │ Executors: WinRM / SSH / LDAP │ │
│ └──────────┬──────────────────────────────────────────────┘ │
└─────────────│────────────────────────────────────────────────────┘
│ WinRM/SSH/LDAP
┌─────────────▼────────────────┐
│ Target Assets │
│ vfw-pki (.51) │
│ + production assets │
└──────────────────────────────┘
| Component | Technology | Responsibility |
|---|---|---|
vfw-dc-nginx |
nginx:alpine | TLS termination, reverse proxy |
vfw-dc-portal |
Blazor Server .NET 9 | Admin UI, operator UI, auditor UI |
vfw-dc-api |
ASP.NET Core Minimal API .NET 9 | REST API, JWT auth, business logic |
vfw-dc-worker-workflow |
.NET 9 Worker | Checkout/checkin state machine, rotation scheduling |
vfw-dc-rabbitmq |
RabbitMQ 4 | Async messaging between all services (mTLS) |
vfw-dc-vault |
HashiCorp Vault OSS | KMS, DEK storage, assembly tokens, Shamir unseal |
vfw-dc-consul |
HashiCorp Consul | Service discovery, active health checks |
vfw-dc-postgres-assets |
PostgreSQL 17 | Assets DB: locations, systems, zones, assets, tasks |
vfw-dc-postgres-secrets |
PostgreSQL 17 | Secrets DB: encrypted credentials, checkouts, history |
vfw-dc-postgres-identity |
PostgreSQL 17 | Identity DB: users, roles, MFA, tenants |
vfw-dc-minio |
MinIO | Object storage for signed task completion forms |
vfw-dc-otel |
OpenTelemetry Collector | Traces, metrics collection + PII masking |
vfw-dc-jaeger |
Jaeger | Distributed tracing UI |
vfw-dc-victoria |
VictoriaMetrics | Metrics storage and querying |
vfw-dc-grafana |
Grafana | Dashboards and alerting |
vfw-dc-registry |
Docker Registry | Private container image registry |
VaultFlower.Worker.Rotation |
.NET 9 Windows Service | Rotation execution: WinRM, SSH, LDAP |
VaultFlower.Agent.Workstation |
.NET 9 Windows Service | Smartcard reader on operator workstations |
VaultFlower organizes managed assets according to the ISA/IEC 62443 Zones and Conduits model:
Tenant (Organization)
└── Location (Physical site / plant)
└── System (Functional system with owner and criticality)
└── Zone (Network segment: OT domain, IT domain, air-gapped)
└── Asset (Server, HMI, PLC, RTU, workstation)
└── Credential (Local admin, domain account, service account)
Per ISA/IEC 62443 and NERC CIP, each system is assigned a criticality level based on potential damage:
| Level | Code | Damage Type | Description |
|---|---|---|---|
| Low | LOW |
Any | Minimal damage, rapid recovery |
| Medium | MEDIUM |
Any | Limited damage, partial production loss |
| High | HIGH |
Any | Significant damage, extended downtime |
| Critical | CRITICAL |
ECONOMIC / ENVIRONMENTAL / HUMAN / COMBINED | Catastrophic damage, threat to life or environment |
Criticality directly affects the authentication model:
-
LOW / MEDIUM / HIGH→ Kerberos SSO + Password MFA -
CRITICAL→ Kerberos SSO + Password MFA + Smartcard + PIN (3FA)
The most critical flow in the system. Every step is audited.
1. Operator creates ACCESS_TASK or ROTATION_TASK
│
▼
2. System emails owner with SSO-gated one-time approval link (TTL: 12h)
│
▼
3. Owner opens link → Kerberos SSO validates identity
Match → approval page
No match → 403 + CRITICAL SIEM event
│
▼
4. Owner approves → task status: ASSIGNED
│
▼
5. Operator opens task → initiates checkout
│
▼
6. Operator passes MFA (adaptive based on criticality)
│
▼
7. Second approver passes MFA → Dual Control complete
│
▼
8. HashiCorp Vault issues time-bound assembly token (TTL = checkout TTL)
│
▼
9. Application assembles data IN MEMORY:
Identity DB → user context, permissions
Assets DB → asset details, task context
Secrets DB → encrypted credential
Vault → DEK to decrypt credential
│
▼
10. Plaintext password shown ONCE in UI
Printed form generated (mandatory)
│
▼
11. Vault assembly token invalidated after use
│
▼
12. Operator executes task → uploads signed form
│
▼
13. Task completed → auto-rotation task created (for ACCESS_TASK)
│
▼
14. All 14 steps logged to audit with CEF format → SIEM
VaultFlower uses adaptive multi-factor authentication — the number of required factors scales with asset criticality:
Factor 1 (always): Kerberos SSO — Windows AD authenticates the user
Factor 2 (always): Password — VaultFlower internal password
Factor 3 (CRITICAL): Smartcard (Mifare) + PIN — physical possession proof
Core MFA (Password) is built-in. Additional factors are delivered as optional Docker containers activated via Service Discovery:
Docker container starts
│
▼
Plugin registers with Consul (health checks every 10s)
│
▼
Plugin publishes to RabbitMQ: vfw.plugins.registry
│
▼
Worker validates license via Vault
│
▼
API activates plugin endpoints automatically
Available plugins: vfw-dc-mfa-totp, vfw-dc-mfa-webauthn, vfw-dc-mfa-smartcard
Password at rest:
plaintext → AES-256-GCM(DEK) → ciphertext stored in Secrets DB
DEK → AES-256(KEK) → encrypted DEK stored in Vault
KEK → Vault master key (Shamir 3-of-5) → never leaves Vault
Password in transit:
All connections TLS 1.3 minimum
RabbitMQ: mTLS (mutual TLS) — client and server certificates
Certificates issued by vfw-pki (ICA01)
Password in memory:
Assembled only during checkout flow
Destroyed immediately after display
Never appears in logs, traces, or metrics (PII masking at OTel level)
Full encryption details → Architecture-Encryption-Model
VaultFlower supports multiple organizations (tenants) on a single installation:
Tenant isolation:
✓ Every table has tenant_id column
✓ API enforces tenant context via URL prefix /{tenant-slug}/
✓ RabbitMQ: dedicated VHost per tenant (/vfw-tenant-{slug})
✓ MinIO: dedicated bucket path per tenant
✓ Audit log: all events tagged with tenant_id
✗ Shared infrastructure (PostgreSQL instances, Vault, RabbitMQ)
— isolation is logical, not physical
For physical tenant isolation (separate DB instances per tenant), see Enterprise edition.
Application → OpenTelemetry SDK
│
▼
vfw-dc-otel (OpenTelemetry Collector)
│
├── PII Masking (before export):
│ User UPN → masked
│ IP addresses → masked
│ Tenant slug → preserved (not PII)
│
├──→ vfw-dc-jaeger (Distributed Tracing)
│ X-Request-ID links traces to audit log entries
│
└──→ vfw-dc-victoria (Metrics)
└──→ vfw-dc-grafana (Dashboards + Alerts)
| Decision | Choice | Alternative Considered | Reason |
|---|---|---|---|
| Inter-service communication | RabbitMQ | HTTP/REST | Durability, async, mTLS |
| Secret management | HashiCorp Vault | AWS KMS | Self-hosted, air-gap compatible |
| Service discovery | Consul | Kubernetes DNS | Active health checks, multi-DC |
| Object storage | MinIO | PostgreSQL bytea | S3-compatible, versioned, encrypted |
| Portal technology | Blazor Server | React/Vue | Logic never leaves server |
| Database per concern | 3x PostgreSQL | Single DB | Data fragmentation security model |
| Message format | CEF | JSON | SIEM compatibility standard |
Full rationale for each decision → ADR section
- Architecture-Data-Fragmentation — detailed B++ model
- Architecture-Encryption-Model — AES-256-GCM, envelope encryption
- Architecture-Network-Topology — network diagram, firewall rules
- Architecture-Container-Architecture — all containers with IPs and ports
- Architecture-Message-Bus — RabbitMQ exchanges, queues, bindings
- Architecture-Plugin-Architecture — plugin lifecycle
- Security-Principles — non-negotiable security rules
- Database-Schema-Overview — three-database schema