System Architecture¶
Overview¶
graph TB
subgraph "Management Layer"
WebUI[Web UI<br/>EJS Templates]
API[Node.js API Server]
DB[(PostgreSQL)]
LDAP[LDAP Gateway<br/>NodeJS]
end
subgraph "Network Services"
DNS[DNSMasq<br/>DHCP + DNS]
NGINX[NGINX<br/>Reverse Proxy]
end
subgraph "External Services"
PushNotif[Push Notification Service<br/>2FA Authentication]
end
subgraph PVE[Proxmox Cluster]
LXC[LXC Container]
end
WebUI --> API
API --> DB
API --> PVE
API --> DNS
API --> NGINX
API --> PushNotif
LDAP --> DB
LDAP --> PushNotif
LXC -.-> LDAP
DNS --> LXC
NGINX --> LXC
classDef management fill:#e1f5ff,stroke:#01579b
classDef network fill:#f3e5f5,stroke:#4a148c
classDef compute fill:#e8f5e9,stroke:#1b5e20
classDef external fill:#fff3e0,stroke:#e65100
class WebUI,API,DB,LDAP management
class DNS,NGINX network
class LXC compute
class PushNotif external
Components¶
| Component | Role |
|---|---|
| Proxmox VE 13+ | Hypervisor — manages LXC containers via REST API. Nodes are registered Proxmox servers. |
| DNSMasq | DHCP + DNS. Auto-assigns IPs to containers, provides internal name resolution (container.cluster.internal). |
| NGINX | Reverse proxy — L7 (HTTP/HTTPS with auto TLS via ACME) and L4 (TCP port mapping). Config auto-generated from container services. |
| LDAP Gateway | Node.js LDAP server (source). Reads users/groups from the DB; containers authenticate via PAM/SSSD. |
| Push Notification Service | 2FA via push notifications (source). Configured in Settings. Used by LDAP gateway when AUTH_BACKENDS includes notification. |
| Database | PostgreSQL via Sequelize ORM. Stores users, groups, sites, nodes, containers, and service config. |
Data Flow¶
Container Creation¶
sequenceDiagram
participant User
participant API
participant Proxmox
participant LXC
participant DNSMasq
participant DB
participant NGINX
User->>API: Create Container Request
API->>Proxmox: Create LXC via API
Proxmox->>LXC: Create
LXC->>DNSMasq: Retrieve IP via DHCP
DNSMasq-->>LXC: IP assigned
API->>DNSMasq: Retrieve IP for new LXC
DNSMasq-->>API: LXC IP address
API->>DB: Store LXC + IP information
API-->>User: Container ready
NGINX->>API: Retrieve updated config
API-->>NGINX: Updated routing config
User Authentication¶
sequenceDiagram
participant User
participant Container
participant LDAP
participant DB
participant PushNotif as Push Notification<br/>Service
User->>Container: SSH login attempt
Container->>LDAP: LDAP bind request
LDAP->>DB: Verify credentials
DB-->>LDAP: Password validated
alt 2FA Enabled
LDAP->>PushNotif: Send notification (username)
PushNotif->>User: Push notification to device
User->>PushNotif: Approve/Reject
PushNotif-->>LDAP: User decision
alt Approved
LDAP-->>Container: Authentication success
Container-->>User: Login granted
else Rejected or Timeout
LDAP-->>Container: Authentication failed
Container-->>User: Access denied
end
else 2FA Disabled
LDAP-->>Container: Authentication success
Container-->>User: Login granted
end
HTTP Service Exposure¶
sequenceDiagram
participant Client
participant NGINX
participant ACME
participant DNSMasq
participant Container
Note over NGINX: Initial setup
NGINX->>ACME: Request certificate
ACME-->>NGINX: Certificate issued
Note over Client: User request
Client->>NGINX: HTTPS request (app.example.com)
NGINX->>DNSMasq: Resolve container IP
DNSMasq-->>NGINX: Container IP address
NGINX->>Container: Forward request
Container-->>NGINX: Response
NGINX-->>Client: HTTPS response
Authenticated HTTP Services¶
When authRequired is enabled on an HTTP service, NGINX uses the auth_request module to authenticate requests before proxying. The domain's authServer must be configured (see External Domains).
sequenceDiagram
participant Client
participant NGINX
participant AuthServer as Auth Server
participant Container
Client->>NGINX: GET app.example.com/page
NGINX->>AuthServer: Subrequest: GET /verify
alt 2xx (authenticated)
AuthServer-->>NGINX: 200 + X-User-* headers
NGINX->>Container: Proxied request + identity headers
Container-->>NGINX: Response
NGINX-->>Client: Response
else 401 (unauthenticated)
AuthServer-->>NGINX: 401
NGINX-->>Client: 302 → /login?redirect=original_url
end
NGINX captures identity headers from the auth server subrequest (X-User-ID, X-Username, X-User-First-Name, X-User-Last-Name, X-Email, X-Groups) and forwards them to the backend container via proxy_set_header.
If authRequired is enabled but no authServer is configured on the domain, NGINX serves a 503 error page.