Skip to content

Security architecture

This page describes the components of PKIFactor, the trust boundaries between them, the data flows, and where secrets and private keys are kept. It describes the single-node appliance; the high-availability layout adds database replication between nodes (High availability).

Components

flowchart TB
    C["Browsers · API clients · ACME / EST / SCEP / CMP clients"]
    N["nginx<br/>TLS termination · routing · rate limiting"]
    F["Frontend<br/>(web interface)"]
    B["Backend API<br/>(business logic, access control, encryption)"]
    P["Protocol gateways<br/>ACME · EST · SCEP · CMP"]
    W["Background worker<br/>renewals · deployment · verification · scans · notifications"]
    D[("PostgreSQL 16<br/>single node or Patroni cluster")]
    CA["Certification authorities<br/>Vault · AD CS · EJBCA · ZetaCA · DigiCert · Sectigo · ACME · CMP"]
    DEV["Devices (deployment)<br/>F5 · FortiWeb · PAN-OS · Check Point · Kemp · IIS · SSH"]
    PL["Platforms<br/>KMS / HSM · directories · SSO · ServiceNow · SIEM · SMTP"]

    C --> N
    N --> F
    N --> B
    N --> P
    F --> B
    P --> B
    P --> D
    B --> D
    B --> CA
    B --> PL
    W --> D
    W --> CA
    W --> DEV
    W --> PL

Overview. The backend issues directly when a request does not need approval; the worker runs everything that happens later (renewals, deployments, checks). The detailed view below shows the containers, ports and data stores.

PKIFactor appliance: architecture

Editable source: pkifactor-architecture.drawio (open it in draw.io / diagrams.net; two pages: architecture, and data and connectors).

Component Role Runs as
nginx The only container with published ports (80 redirects to 443). Terminates TLS for the interface, the API and the gateways; checks optional client certificates; rate limiting Root in the appliance Compose file, to read the TLS keys written by the backend; the image itself runs as a non-root user (used on OpenShift)
Frontend Renders the screens; holds no data; calls the backend API Non-root user appuser
Backend API Business logic, access control, encryption, connectors to authorities and devices Starts as root to fix volume ownership, then drops to appuser
Worker Same image as the backend, one process: renewals, deployments, revocation monitoring, scheduled backups, e-mail queue, SIEM replay Same as the backend
ACME, EST, SCEP, CMP gateways Separate applications, each enabled on its own; they speak the enrolment protocol and ask the backend to issue Non-root user appuser
PostgreSQL 16 One database; the ACME and EST gateways have their own schema Not published on the host
KMS or HSM Holds the key-encryption key (Vault Transit or a PKCS#11 HSM) External to the appliance

In single-node mode the CMP and SCEP containers also listen on 127.0.0.1 of the host (8003, 5683/udp, 8004), never on an external address; clients reach them through nginx.

Trust boundaries

Boundary What crosses it Protection
Network to nginx Every inbound request TLS 1.2 or 1.3 with ECDHE and AES-GCM suites; optional client certificate checked against the configured CA chain; per-address request limits
nginx to the applications Proxied requests, and the result of the client certificate check in X-SSL-Client-* headers Plain HTTP on the internal Docker network. nginx adds a shared secret (MTLS_PROXY_SECRET, EST_PROXY_SECRET for EST) that the interface, the backend and the EST gateway check before trusting the X-SSL-Client-* and X-Real-IP headers; the interface relays them to the backend only with that proof. Without the secret, the backend falls back to the trusted proxy networks (TRUSTED_PROXY_NETWORKS; nginx has a fixed address on that network)
Gateways to the backend Issuance and revocation requests Each gateway authenticates with its own API key, held by a protected service account; the backend applies the profile's allowed domains (when the filter is enabled) whatever the entry point
Applications to PostgreSQL All data Application role clm_app (see below); the database port is not published
Applications to the KMS or HSM Wrap and unwrap of data keys only Vault: HTTPS with the Vault CA, AppRole credentials; HSM: vendor PKCS#11 library and slot PIN
Backend to authorities, devices, directories Certificate requests, deployments, directory reads TLS with certificate verification by default; credentials encrypted at rest
Between HA nodes Database replication, etcd, administrator actions forwarded to the other nodes etcd with mutual TLS; forwarded administrator actions are refused unless HA_PEER_CA is set (Hardening)

The database role

The PostgreSQL container creates an administrator account (DB_USER, clm_user on the appliance), used only inside the database container and by pkifactor-ctl for dumps and restores. The applications connect as clm_app, created with NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION: it owns the PKIFactor database and its schemas, nothing more. An SQL injection or a leaked connection string therefore does not give the rights of a PostgreSQL superuser (reading server files, running commands).

The backend builds its connection from DB_USER and DB_PASSWORD; the appliance Compose file sets both to the application role for the backend and the worker.

Data flows

Flow Path Notes
Sign-in and screens Browser, nginx, frontend, backend The backend issues the session token; the frontend forwards it on each call
REST API Client, nginx (/api/ and /api/v1/), backend Every route requires authentication, except the health probes (REST API)
Enrolment Client, nginx, gateway, backend, authority The gateway never talks to the authority itself
Deployment Worker, device (F5, FortiWeb, Kemp, SSH...) The private key is decrypted in memory only for the push
Audit Backend, audit.log, SIEM See Logging and incident handling
Licence None Verification is local; PKIFactor sends no licence call and no telemetry

The complete list of ports and outbound connections per connector is in Network flow matrix.

Where secrets and private keys live

Configuration objects and where data lives

Item Location Protection
Platform secrets (data key, token keys, KMS credentials) /opt/pkifactor/secrets/, mounted as files into the services that need them Directory mode 750, owner root:pkifactor
Data keys (DEKs) data/keys/dek_store.json Stored wrapped by the KMS or HSM key
Key-encryption key (KEK) Vault Transit or the HSM slot Never leaves the KMS or HSM
Connector credentials, API keys, gateway secrets Database Encrypted with the data keys
Private keys generated by PKIFactor Database, in the certificate record Encrypted with the data keys; turned into a PKCS#12 only on download
Passwords of local accounts Database bcrypt-SHA256 hash
Database passwords /opt/pkifactor/.env File mode 640
TLS keys of the platform nginx/ssl/ Key files mode 600
Licence signing key Not on the appliance The appliance holds only the public key

Details: Secrets and key management.

Isolation between organizations

All organizations share one database. Isolation is enforced by the backend on every request, not by separate schemas:

  • every certificate belongs to one organization, taken from its profile;
  • an operator or a user acts only on certificates of the organizations they belong to, through their teams; what an operator may do there is set per person by their operator permissions;
  • a validator sees the queues of the organizations where they approve, and a substitute sees them only during an active delegation;
  • administrators have a global view; auditors too, unless their audit scope is limited to their own organizations;
  • a membership taken from a directory stops counting when that team has not been synchronized for too long: an access decision based on stale data is refused.

See Organizations and teams and Users and roles.