Threat Model¶
Scope and Methodology¶
- Scope: Cloud-Native DCS platform deployed on Kubernetes for pharmaceutical batch manufacturing
- Methodology: STRIDE (per-element analysis) with MITRE ATT&CK for ICS mapping
- Alignment: IEC 62443-3-2 (security risk assessment for IACS), IEC 62443-3-3 (system security requirements)
- Last updated: 2026-03-24
- Review cadence: Annually, or after major architecture changes, new component additions, or significant security incidents
This threat model covers the Cloud-Native DCS application layer. Kubernetes control plane security, underlying OS hardening, and physical site security are out of scope but noted as assumptions.
BPCS-layer protection — not a safety-rated system
Cloud-Native DCS is a Basic Process Control System. It is not a Safety Instrumented System under IEC 61508 / IEC 61511 and is not rated for SIL 1, SIL 2, SIL 3, or SIL 4 functions. Security hardening protects the process-control plane against cyber threats. It does not turn it into a safety-rated shutdown system. Its interlocks and alarms are BPCS-layer protection functions. Whether a given hazard (emergency shutdown, overpressure trip, burner management, LEL/gas trip) requires an independent SIS is a process-hazard-analysis (PHA/LOPA) decision, and any function assigned to an SIS lives on independent, separately certified safety-PLC or hardwired-trip hardware that this software does not control.
System Description¶
Cloud-Native DCS is an ISA-88 compliant Distributed Control System that runs on Kubernetes. It manages pharmaceutical batch manufacturing processes through a hierarchy of CRDs representing physical equipment, procedural sequences, recipes, and batch executions. Operators reconcile CRDs into runtime state, unit runtimes execute control logic on edge devices, and a centralized gateway provides API and UI access for plant operators and engineers.
Component Inventory¶
| Component | Function | Runs On | Exposed Interfaces |
|---|---|---|---|
| Gateway | REST API, Web UI | Control plane node | HTTP :8090 |
| Physical Operator | Manages equipment hierarchy CRDs | Control plane node | Metrics :8080, Health :8081 |
| Procedural Operator | Manages procedural sequence CRDs | Control plane node | Metrics :8080, Health :8081 |
| Batch Operator | Manages batch execution and recipe CRDs | Control plane node | Metrics :8080, Health :8081 |
| Control Operator | Deploys control programs to unit runtimes | Control plane node | Metrics :8080, Health :8081 |
| MQTT Broker | Pub/sub for telemetry, commands, events | Control plane node | MQTTS :8883 (prod), WS :9443 |
| Unit Runtime | Executes FB networks, drives I/O on edge devices | Edge device node (hostNetwork: true) |
HTTP :61152 bound on the host network; hostPath volume for FB-state persistence |
| IO-Probe | Per-Controller I/O connectivity health check | Edge device node | None (reports via CRD status) |
| Historian | MQTT subscriber, writes telemetry to PostgreSQL | Control plane node | HTTP :8092 |
| Audit Archiver | CronJob: archives AuditRecords to PostgreSQL | Control plane node | None (batch job) |
Deployment Topology¶
A typical pharmaceutical deployment consists of a Kubernetes cluster (e.g.,
k3s) with control plane nodes running operators, gateway, MQTT broker, and
historian. Edge device worker nodes join the cluster at the deployment
layer (e.g., Talos machine config, per ADR 0004), are registered as
Controller resources, and run unit runtimes pinned via nodeSelector
with dcs.io/device=<unit-name> labels.
Edge devices communicate with field I/O modules (e.g., WAGO 750-series couplers, Turck TBEN modules) over Modbus TCP, EtherNet/IP, or OPC UA on a dedicated field bus network segment. Network segmentation follows IEC 62443 zone/conduit principles with Kubernetes NetworkPolicies enforcing boundaries between Control Zone, Field Zone, External Zone, and Field Bus Zone.
Key configuration surface areas are centralized in the Helm chart
(Helm values.yaml), including security toggles
for mTLS (mtls.enabled), NetworkPolicies (networkPolicies.enabled),
MQTT security profile (mqtt.securityProfile), OIDC authentication
(gateway.auth), and rate limiting (gateway.rateLimit).
Existing Security Controls¶
The following controls are implemented and referenced as mitigations throughout this threat model:
| Control | Scope | Configuration |
|---|---|---|
| mTLS (cert-manager) | All inter-component HTTP | mtls.enabled in Helm values |
| MQTT TLS + auth + ACLs | Broker connections | mqtt.securityProfile: production |
| OIDC authentication | Gateway API/UI | gateway.auth.oidc in Helm values |
| Kubernetes RBAC | Operator CRD access | Generated RBAC ClusterRoles |
| NetworkPolicies | Zone/conduit boundaries | networkPolicies.enabled |
| Pod hardening | All containers, including unit-runtime (which keeps hostNetwork + hostPath by design — see UR-E-01) | Distroless, non-root, no caps |
| Immutable audit trail | AuditRecord CRD | Webhook + RBAC create-only |
| Electronic signatures | Recipe approval, batch | HMAC-SHA256 |
| Rate limiting | Gateway API | gateway.rateLimit in Helm values |
| Image signing + SBOM | Release pipeline | Cosign + syft in CI |
| Vulnerability scanning | Release pipeline | Trivy + govulncheck in CI |
Data Flow Diagram¶
flowchart TD
Users[Operators<br/>Engineers, QA] -->|HTTPS / OIDC Bearer<br/>TB1| Ingress
Ingress --> GW[Gateway<br/>REST :8090]
GW -->|TB2| K8s[(k8s API / CRDs)]
GW -->|MQTTS :8883<br/>TB3| MQTT[MQTT Broker]
K8s --> Ops[Physical / Procedural / Batch / Control Operators]
Ops -->|pub/sub| MQTT
MQTT -->|sub only| Hist[Historian]
Hist --> PG[(PostgreSQL / TimescaleDB)]
MQTT -->|TB4| RT[Unit Runtime<br/>:61152]
RT -->|Modbus / EtherNet/IP / OPC UA<br/>TB5| IO[I/O Modules<br/>field]
Legend: TB = Trust Boundary (dashed lines), arrows = data flow direction.
Trust Boundaries¶
| ID | Boundary | Side A | Side B | Protocol | Protection |
|---|---|---|---|---|---|
| TB1 | External Network / Gateway | End users (browser, CLI) | Gateway pod | HTTPS | TLS termination at ingress, OIDC JWT authentication |
| TB2 | Gateway / Kubernetes API Server | Gateway pod | kube-apiserver | HTTPS | ServiceAccount RBAC, least-privilege ClusterRole |
| TB3 | Control Zone / MQTT Broker | Operators, Gateway | MQTT Broker | MQTTS :8883 | TLS + per-role credentials + topic ACLs |
| TB4 | MQTT Broker / Field Zone | MQTT Broker | Unit Runtimes | MQTTS :8883 | TLS + dcs-runtime credentials + restricted ACLs |
| TB5 | Unit Runtime / Field Bus | Unit Runtime pod | I/O Modules (Modbus/EIP) | Modbus TCP, EtherNet/IP | Protocol-native; physical network isolation |
| TB6 | Historian / PostgreSQL | Historian pod | TimescaleDB (CNPG) | PostgreSQL wire protocol | Database credentials via Kubernetes Secret |
Legacy Plaintext Field Protocols (TB5 Compensating Controls)¶
Modbus TCP (and EtherNet/IP) carry no authentication and no encryption.
The protocols themselves define none, so the drivers
(pkg/driver/modbus/) have none to offer. Any host with network reachability to an I/O
module can read coils and write registers. For IEC 62443-3-3 SR 3.1
(communication integrity) the conduit between the unit-runtime node and
the I/O modules therefore relies entirely on compensating controls,
all of which are required in production:
- Layer-2 segmentation of the field bus. I/O modules live on a dedicated OT VLAN or physically separate switch fabric reachable only from the unit-runtime device nodes. No route exists from the IT network, the cluster pod network, or the internet to the field bus segment. See the reference architectures for the wiring this assumes.
- NetworkPolicy scope.
networkPolicies.fieldBusCIDRsrestricts which workloads may egress to the field bus CIDR: only unit-runtime pods (hostNetwork: trueon the device nodes) and the node-pinned io-probe pods at the zone edge. - Physical security of the conduit. Switches, cabling, and I/O modules are inside locked cabinets/rooms. An attacker with physical access to the field bus defeats segmentation (see Residual Risks).
Zone/conduit summary: the field bus is a separate IEC 62443 zone whose only conduit is the unit-runtime node itself. Isolation provides that zone's security level, and the protocol provides none of it.
Prefer OPC UA where the hardware supports it. The OPC UA driver is
secure-by-design here: sessions default to Sign and can be raised to
SignAndEncrypt per Unit serviceBinding (or per IOModule), and
gateway.opcua.allowInsecure stays false. Plaintext Modbus/EtherNet/IP
should be reserved for devices that offer nothing better.
STRIDE Analysis¶
Gateway¶
| ID | Cat | Description | Impact | Likelihood | Mitigation | Status |
|---|---|---|---|---|---|---|
| GW-S-01 | S | Spoofed user identity via stolen or forged OIDC token | Major | Unlikely | OIDC JWT validation with provider key rotation; short-lived tokens | Implemented |
| GW-S-02 | S | Bypass auth on exempt routes to access sensitive data | Moderate | Unlikely | Exempt routes limited to health, static assets, auth config | Implemented |
| GW-T-01 | T | Tampered API requests modifying batch parameters | Catastrophic | Unlikely | OIDC auth + RBAC group checks; ControlRecipe immutability | Implemented |
| GW-T-02 | T | Tampered recipe approval to bypass quality review | Major | Unlikely | Electronic signatures (HMAC-SHA256) bind signer, timestamp, meaning | Implemented |
| GW-R-01 | R | Operator denies performing a critical action | Major | Possible | Immutable AuditRecords with user attribution; webhook enforcement | Implemented |
| GW-I-01 | I | Recipe IP or process parameters leaked via API | Major | Unlikely | OIDC auth required for API; NetworkPolicy restricts ingress sources | Implemented |
| GW-I-02 | I | Session token leaked in logs or error responses | Moderate | Rare | Structured logging without token values; no token echo in responses | Implemented |
| GW-T-03 | T | Replay of captured mutating request (e.g. Start batch) | Major | Unlikely | Anti-replay middleware requires unique X-DCS-Nonce on POST/PUT/DELETE; in-memory nonce store rejects duplicates within configurable window (default 10 min); IEC 62443 SR 3.8. Constraint: the nonce store is per-replica, so the guarantee holds only with a single gateway replica — the Helm chart refuses to render gateway.replicas > 1 while anti-replay is enabled (#468); a shared nonce store is future work gated on an HA-gateway requirement |
Implemented |
| GW-D-01 | D | API flooding causes gateway unavailability | Major | Possible | Per-IP rate limiting (configurable read/write limits in Helm values) | Implemented |
| GW-D-02 | D | WebSocket exhaustion from excessive connections | Moderate | Unlikely | Per-source-address ceiling on concurrent live streams, counting WebSocket and SSE together (gateway.streamLimits.maxConnectionsPerIP, default 10); a connect over it is refused with 429 + Retry-After and counted in dcs_gateway_stream_connections_refused_total. There is no setting that removes the ceiling. Resource limits on the gateway pod (CPU/memory) bound what the admitted connections can cost. Constraint: the count is per source address, so consumers behind one NAT share an allowance and the ceiling has to be sized for them (#1447) |
Implemented |
| GW-E-01 | E | Operator escalates to admin functions | Major | Unlikely | OIDC group-based authorization (dcs-admin group check) |
Implemented |
| GW-E-02 | E | Setup token reuse after initial configuration | — | — | Threat retired: the setup-token bootstrap and the /api/v1/setup* endpoints it authenticated were removed (ADR 0004, #487) — the asset no longer exists. ID kept for audit traceability of historical InvalidateSetupToken records |
Retired |
Operators (Physical, Procedural, Batch, Control)¶
| ID | Cat | Description | Impact | Likelihood | Mitigation | Status |
|---|---|---|---|---|---|---|
| OP-S-01 | S | Unauthorized pod impersonates an operator | Catastrophic | Rare | ServiceAccount per operator; RBAC restricts CRD access | Implemented |
| OP-S-02 | S | Spoofed operator identity on MQTT | Major | Unlikely | Per-role MQTT credentials (dcs-operator); topic ACLs |
Implemented |
| OP-T-01 | T | Tampered CRD specs (recipe parameters, FB networks) | Catastrophic | Unlikely | Kubernetes RBAC; AuditRecord on all changes; spec hash verification | Implemented |
| OP-T-02 | T | Phantom stale FB networks persist after pod restart | Major | Possible | Garbage collection of orphaned networks on reconcile | Implemented |
| OP-R-01 | R | Configuration change without audit attribution | Major | Unlikely | Audit recorder creates AuditRecords for CRD mutations | Implemented |
| OP-I-01 | I | Metrics endpoint exposes internal state | Minor | Unlikely | NetworkPolicy restricts metrics access to monitoring namespace | Implemented |
| OP-D-01 | D | Leader election disruption causes reconciler outage | Major | Rare | Leader election via controller-runtime; PDB available in Helm | Implemented |
| OP-D-02 | D | etcd storage exhaustion from excessive CRDs | Major | Unlikely | Audit archiver moves old records to PostgreSQL; resource limits | Implemented |
| OP-E-01 | E | RBAC escalation to modify resources beyond scope | Major | Rare | Least-privilege ClusterRole; separate ServiceAccount per operator | Implemented |
MQTT Broker¶
| ID | Cat | Description | Impact | Likelihood | Mitigation | Status |
|---|---|---|---|---|---|---|
| MQ-S-01 | S | Unauthorized client connects to broker | Major | Unlikely | TLS + per-role authentication; anonymous connections rejected | Implemented |
| MQ-S-02 | S | Credential theft enables impersonation of a role | Major | Unlikely | Credentials stored in Kubernetes Secrets; passwords auto-generated | Implemented |
| MQ-T-01 | T | Tampered telemetry data on MQTT topics | Major | Unlikely | TLS encryption in transit; per-role topic ACLs limit write access | Implemented |
| MQ-T-02 | T | Injected control commands on equipment command topics | Catastrophic | Unlikely | Runtime role restricted to read-only on command topics; ACL enforce | Implemented |
| MQ-R-01 | R | Unattributed message publishing | Moderate | Possible | MQTT username logged per connection; Mosquitto auth logging enabled | Implemented |
| MQ-I-01 | I | Eavesdropping on telemetry topics | Major | Unlikely | TLS encryption (port 8883); plaintext port 1883 disabled in prod | Implemented |
| MQ-D-01 | D | Broker resource exhaustion from message flood | Major | Possible | Resource limits on broker pod; persistence volume size limits | Partial |
| MQ-D-02 | D | Broker unavailability disrupts telemetry pipeline | Major | Unlikely | Runtime store-and-forward queue buffers messages during outage | Implemented |
| MQ-E-01 | E | Topic ACL bypass allows cross-role access | Major | Rare | ACL file generated from Helm values; tested via helm template |
Implemented |
Unit Runtime¶
| ID | Cat | Description | Impact | Likelihood | Mitigation | Status |
|---|---|---|---|---|---|---|
| UR-S-01 | S | Spoofed control commands to runtime HTTP API | Catastrophic | Unlikely | mTLS on HTTP API; fail-closed bearer token auth (unitRuntime.auth) |
Implemented |
| UR-T-01 | T | Tampered function block network deployed to runtime | Catastrophic | Unlikely | mTLS authenticates control-operator; spec hash verification | Implemented |
| UR-T-02 | T | Tampered I/O values via compromised runtime binary | Catastrophic | Rare | Distroless image; cosign-signed images; enforced read-only root filesystem | Implemented |
| UR-I-01 | I | Process data leaked from runtime HTTP API | Moderate | Unlikely | mTLS restricts API access; NetworkPolicy limits ingress to control | Implemented |
| UR-I-02 | I | Persisted FB network on host disk read by attacker | Moderate | Rare | Host path restricted to runtime pod; physical device security | Partial |
| UR-D-01 | D | Runtime crash or hang affects physical process | Catastrophic | Possible | Liveness/readiness probes; Kubernetes auto-restart; local autonomy | Implemented |
| UR-D-02 | D | Resource exhaustion from runaway control program | Major | Unlikely | Pod resource limits (CPU/memory) in Helm values | Implemented |
| UR-E-01 | E | Container escape from runtime pod to host | Catastrophic | Rare | Enforced securityContext (runAsNonRoot, no privilege escalation, all capabilities dropped, seccomp RuntimeDefault, read-only root FS); hostNetwork + hostPath still widen the host surface by design | Partial |
IO-Probe¶
| ID | Cat | Description | Impact | Likelihood | Mitigation | Status |
|---|---|---|---|---|---|---|
| IP-S-01 | S | Spoofed IO-Probe reports false connectivity status | Moderate | Rare | IO-Probe deployed by physical-operator; ServiceAccount scoped | Implemented |
| IP-T-01 | T | Tampered probe results hide I/O module failures | Major | Rare | Probe writes to Controller CRD status; RBAC restricts access | Implemented |
| IP-D-01 | D | Probe failure leaves stale connectivity information | Moderate | Unlikely | Kubernetes restarts failed probe pods; status age visible in UI | Implemented |
Historian and Audit Archiver¶
| ID | Cat | Description | Impact | Likelihood | Mitigation | Status |
|---|---|---|---|---|---|---|
| HI-S-01 | S | Spoofed MQTT telemetry data ingested as genuine | Major | Unlikely | MQTT per-role auth; historian uses dcs-historian read-only role |
Implemented |
| HI-T-01 | T | Tampered historical records in PostgreSQL | Major | Rare | Database credentials via Kubernetes Secret; CNPG manages access | Partial |
| HI-I-01 | I | Historical process data disclosed to unauthorized | Major | Unlikely | NetworkPolicy restricts historian ingress; DB credentials in Secret | Implemented |
| HI-D-01 | D | Database storage exhaustion from telemetry volume | Major | Possible | Configurable retention (default 365 days); buffer flush interval | Implemented |
| AA-T-01 | T | Audit records modified during archival | Major | Rare | Idempotent archival; webhook enforces immutability; archive flag | Implemented |
| AA-D-01 | D | Archiver job failure leaves stale records in etcd | Moderate | Unlikely | CronJob retry policy; idempotent operation; monitoring via metrics | Implemented |
MITRE ATT&CK for ICS Mapping¶
| Technique | Name | Component | Threat ID | Mitigation |
|---|---|---|---|---|
| T0819 | Exploit Public-Facing App | Gateway | GW-D-01, GW-E-01 | Rate limiting; OIDC auth; RBAC groups; input validation (gap) |
| T0886 | Remote Services | Gateway, Runtime | GW-S-01, UR-S-01 | OIDC JWT; mTLS on runtime API; fail-closed bearer token auth |
| T0859 | Valid Accounts | Gateway, MQTT | GW-S-01, MQ-S-02 | OIDC token expiry; auto-generated MQTT passwords; Secrets |
| T0862 | Supply Chain Compromise | All | UR-T-02 | Cosign image signing; SBOM (syft); Trivy scanning; govulncheck |
| T0821 | Modify Controller Tasking | Control Operator | OP-T-01, UR-T-01 | RBAC on ControlProgram CRDs; mTLS deploy; spec hash verify |
| T0836 | Modify Parameter | Batch Operator | GW-T-01, GW-T-03, OP-T-01 | ControlRecipe immutability; e-signatures; anti-replay nonce |
| T0855 | Unauthorized Command Message | MQTT Broker | MQ-T-02 | Topic ACLs restrict command topic write to operator role |
| T0831 | Manipulation of Control | Unit Runtime | UR-T-01, UR-T-02 | mTLS; distroless images; read-only FS; no shell in container |
| T0882 | Theft of Operational Information | Gateway, MQTT | GW-I-01, MQ-I-01 | OIDC auth on API; TLS on MQTT; NetworkPolicy segmentation |
| T0880 | Loss of Safety | Unit Runtime | UR-D-01 | Liveness probes; auto-restart; SIS assumed independent (IEC 61511) |
| T0826 | Loss of Availability | Gateway, MQTT | GW-D-01, MQ-D-01 | Rate limiting; pod resource limits; store-and-forward queue |
| T0832 | Manipulation of View | Historian | HI-S-01, HI-T-01 | MQTT auth prevents spoofed writes; DB credentials in Secrets |
| T0856 | Spoof Reporting Message | MQTT Broker | MQ-T-01, HI-S-01 | Per-role MQTT auth; TLS; topic ACLs; Mosquitto logging |
| T0830 | Man in the Middle | All | MQ-I-01, UR-I-01 | mTLS for HTTP; TLS for MQTT; cert-manager certificate mgmt |
Risk Assessment Matrix¶
Rating scale:
| Likelihood \ Impact | Negligible | Minor | Moderate | Major | Catastrophic |
|---|---|---|---|---|---|
| Almost Certain | Low | Medium | High | Critical | Critical |
| Likely | Low | Medium | High | Critical | Critical |
| Possible | Low | Low | Medium | High | High |
| Unlikely | Low | Low | Medium | Medium | High |
| Rare | Low | Low | Low | Medium | Medium |
Risk Ratings¶
| Threat ID | Description | Likelihood | Impact | Risk |
|---|---|---|---|---|
| GW-S-01 | Spoofed OIDC token | Unlikely | Major | Medium |
| GW-T-01 | Tampered batch parameters via API | Unlikely | Catastrophic | High |
| GW-T-02 | Tampered recipe approval | Unlikely | Major | Medium |
| GW-T-03 | Replay of captured mutating request | Unlikely | Major | Medium |
| GW-R-01 | Repudiated operator action | Possible | Major | High |
| GW-I-01 | Recipe IP leaked via API | Unlikely | Major | Medium |
| GW-D-01 | API flooding | Possible | Major | High |
| GW-D-02 | WebSocket exhaustion | Unlikely | Moderate | Medium |
| GW-E-01 | Operator privilege escalation | Unlikely | Major | Medium |
| OP-S-01 | Unauthorized operator pod | Rare | Catastrophic | Medium |
| OP-T-01 | Tampered CRD specs | Unlikely | Catastrophic | High |
| OP-T-02 | Phantom stale FB networks | Possible | Major | High |
| OP-R-01 | Unattributed config change | Unlikely | Major | Medium |
| OP-D-01 | Leader election disruption | Rare | Major | Medium |
| OP-D-02 | etcd storage exhaustion | Unlikely | Major | Medium |
| OP-E-01 | RBAC escalation | Rare | Major | Medium |
| MQ-S-01 | Unauthorized MQTT client | Unlikely | Major | Medium |
| MQ-T-01 | Tampered telemetry data | Unlikely | Major | Medium |
| MQ-T-02 | Injected control commands | Unlikely | Catastrophic | High |
| MQ-R-01 | Unattributed MQTT publish | Possible | Moderate | Medium |
| MQ-D-01 | Broker resource exhaustion | Possible | Major | High |
| MQ-D-02 | Broker unavailability | Unlikely | Major | Medium |
| MQ-E-01 | Topic ACL bypass | Rare | Major | Medium |
| UR-S-01 | Spoofed commands to runtime | Unlikely | Catastrophic | High |
| UR-T-01 | Tampered FB network deployment | Unlikely | Catastrophic | High |
| UR-T-02 | Tampered I/O via compromised binary | Rare | Catastrophic | Medium |
| UR-I-01 | Process data leak from runtime API | Unlikely | Moderate | Medium |
| UR-I-02 | Persisted FB network read from disk | Rare | Moderate | Low |
| UR-D-01 | Runtime crash affecting process | Possible | Catastrophic | High |
| UR-D-02 | Runaway control program resource exhaustion | Unlikely | Major | Medium |
| UR-E-01 | Container escape to host | Rare | Catastrophic | Medium |
| IP-S-01 | Spoofed IO-Probe connectivity report | Rare | Moderate | Low |
| IP-T-01 | Tampered probe hides I/O failures | Rare | Major | Medium |
| IP-D-01 | Stale probe data from pod failure | Unlikely | Moderate | Medium |
| HI-S-01 | Spoofed telemetry ingested | Unlikely | Major | Medium |
| HI-T-01 | Tampered historical records | Rare | Major | Medium |
| HI-D-01 | Database storage exhaustion | Possible | Major | High |
| AA-T-01 | Audit record modification during archival | Rare | Major | Medium |
Mitigations Summary¶
| Threat ID | Mitigation | Status | Evidence |
|---|---|---|---|
| GW-S-01 | OIDC JWT validation with key rotation | Implemented | gateway.auth.oidc in Helm values |
| GW-T-01 | OIDC auth + RBAC group checks | Implemented | OIDC auth middleware with group-based authorization |
| GW-T-02 | HMAC-SHA256 electronic signatures | Implemented | Electronic signatures bind signer, timestamp, and meaning |
| GW-T-03 | Anti-replay nonce on mutating requests | Implemented | gateway.antiReplay in Helm values; X-DCS-Nonce header required; single-replica constraint enforced at chart render |
| GW-R-01 | Immutable AuditRecords, webhook enforcement | Implemented | Validating webhook enforces create-only on AuditRecords |
| GW-I-01 | OIDC auth on all API routes; NetworkPolicy | Implemented | Helm NetworkPolicy for gateway; OIDC auth on all API routes |
| GW-D-01 | Per-IP rate limiting (read/write configurable) | Implemented | gateway.rateLimit in Helm values |
| GW-D-02 | Per-IP live-stream connection ceiling + pod limits | Implemented | gateway.streamLimits.maxConnectionsPerIP and gateway.resources in values.yaml |
| GW-E-01 | OIDC group-based authorization | Implemented | dcs-admin group check via OIDC claims |
| OP-S-01 | Per-operator ServiceAccount + RBAC | Implemented | Helm ServiceAccount templates; generated RBAC ClusterRoles |
| OP-T-01 | RBAC + audit trail + spec hash verification | Implemented | Spec hash verification on FB network deployment |
| OP-T-02 | Phantom network garbage collection | Implemented | Orphaned FB network cleanup during reconciliation |
| OP-R-01 | Audit recorder on all CRD mutations | Implemented | Audit recorder creates AuditRecords for all CRD mutations |
| OP-D-01 | controller-runtime leader election; PDB | Implemented | leaderElect: true in Helm values; PDB templates |
| OP-D-02 | Audit archiver CronJob | Implemented | historian.audit.archival in Helm values |
| OP-E-01 | Least-privilege ClusterRole per operator | Implemented | Generated RBAC ClusterRoles with least-privilege scoping |
| MQ-S-01 | TLS + per-role authentication | Implemented | Helm MQTT auth secret template; mqtt.securityProfile |
| MQ-T-01 | TLS encryption; topic ACLs | Implemented | mqtt.tls and mqtt.auth in Helm values |
| MQ-T-02 | Runtime ACL restricted to read on command topics | Implemented | Helm MQTT auth secret template with topic ACLs |
| MQ-R-01 | Mosquitto connection and auth logging | Implemented | mqtt.securityProfile: production enables logging |
| MQ-D-01 | Pod resource limits; persistence volume sizing | Partial | mqtt.resources and mqtt.persistence.size in Helm values |
| MQ-D-02 | Runtime store-and-forward queue | Implemented | --mqtt-queue-size flag on unit runtime |
| MQ-E-01 | ACL file generated from Helm templates | Implemented | Helm MQTT auth secret template |
| UR-S-01 | mTLS on HTTP API; fail-closed bearer token auth | Implemented | unitRuntime.auth in Helm values (runtime refuses to start without a token unless --insecure-no-auth); mTLS via cert-manager |
| UR-T-01 | mTLS + spec hash verification on deploy | Implemented | Spec hash verification on FB network deployment |
| UR-T-02 | Cosign-signed images; distroless; SBOM | Implemented | Release pipeline: Cosign signing, syft SBOM, Trivy scanning; read-only root filesystem enforced on the runtime pod (#722) |
| UR-I-01 | mTLS + NetworkPolicy | Implemented | Helm NetworkPolicy for runtime; mTLS via cert-manager |
| UR-I-02 | Host path restricted to runtime pod | Partial | Pod-level volume mount; no encryption at rest |
| UR-D-01 | Liveness probes; auto-restart; local autonomy | Implemented | Helm deployment templates; local persistence for autonomy |
| UR-D-02 | Pod CPU/memory limits | Implemented | unitRuntime.resources in Helm values |
| UR-E-01 | Enforced non-root securityContext | Partial | Distroless nonroot image (UID 65532) with enforced runAsNonRoot, allowPrivilegeEscalation=false, all capabilities dropped, seccomp RuntimeDefault, and read-only root FS (#722); still runs hostNetwork with a hostPath volume by design. A short-lived root init container (capabilities bounded to CHOWN/DAC_OVERRIDE/FOWNER, privileged: false) chowns the kubelet-created hostPath to the runtime UID (#731) |
| HI-S-01 | MQTT per-role auth; historian read-only | Implemented | mqtt.auth.users.historian in Helm values |
| HI-T-01 | Database credentials via Kubernetes Secret | Partial | CNPG manages credentials; no application-level record signing |
| HI-D-01 | Configurable retention; buffer flush interval | Implemented | historian.retention.days, historian.buffer in Helm values |
| AA-T-01 | Idempotent archival; webhook immutability | Implemented | Idempotent archival with webhook-enforced immutability |
| IP-S-01 | IO-Probe deployed by physical-operator | Implemented | Physical-operator manages IO-Probe pod lifecycle |
| IP-T-01 | RBAC restricts Controller status updates | Implemented | Generated RBAC ClusterRoles restrict status update access |
| IP-D-01 | Kubernetes auto-restarts failed probe pods | Implemented | Probe pod managed by physical-operator reconciler |
Residual Risks and Accepted Assumptions¶
-
Kubernetes control plane integrity is assumed. Cluster-admin responsibility includes securing kube-apiserver, etcd, and kubelet. This threat model does not cover control plane compromise.
-
etcd encryption at rest is assumed. Enabling
EncryptionConfigurationfor Secrets and CRDs is a cluster-level configuration outside the application scope. -
Physical security of edge devices is out of scope. An attacker with physical access to a Raspberry Pi or similar edge node can extract persisted FB networks, MQTT credentials, or TLS certificates from disk. Physical access controls are a site-level responsibility.
-
Safety-instrumented systems (SIS) are independent per IEC 61511. The DCS is a BPCS. It is not a safety-rated system. Where a process hazard analysis (PHA/LOPA) assigns a function to an SIS, that SIS is independent per IEC 61511, on separate hardware this software does not control, and is therefore outside this threat model's boundary.
-
OIDC provider security is delegated. Token issuance, revocation, password policies, and MFA are the responsibility of the configured identity provider (Keycloak, Azure AD, Okta, etc.).
-
Field bus protocol security depends on device vendor capabilities. Modbus TCP, EtherNet/IP, and OPC UA connections to field I/O modules use protocol-native security (or none). Network isolation via
networkPolicies.fieldBusCIDRsis the primary mitigation. -
No protection against insider threats with valid admin credentials. A user with
dcs-admingroup membership and cluster-admin Kubernetes access can bypass all application-level controls. Organizational policies (separation of duties, access reviews) are required. -
Container runtime (containerd/CRI-O) integrity is assumed. Container escape vulnerabilities in the runtime itself are outside application scope.
-
DNS resolution integrity within the cluster is assumed. CoreDNS poisoning could redirect inter-component traffic. DNSSEC or DNS-over-TLS within the cluster is not implemented.
-
Gateway input validation is incomplete. The REST API lacks comprehensive request validation and sanitization beyond CRD schema validation (OpenAPI). This is an identified gap (IEC 62443-3-3 SR 3.5).
-
No intrusion detection system (IDS). Anomalous network traffic or API usage patterns are not detected. Prometheus metrics provide operational monitoring but not security analytics. This is a planned SL 4 capability.
-
Historical record integrity relies on database access controls. No application-level cryptographic signing of historian records. Tamper detection depends on PostgreSQL access restrictions.
Related Documentation¶
- Security Hardening Guide -- step-by-step production hardening
- Security Operations -- authentication, authorization, electronic signatures
- IEC 62443 Traceability -- compliance requirements mapping
- 21 CFR Part 11 Traceability -- electronic records compliance
- Architecture -- system overview