Files
scadalink-design/deprecated/lmxproxy/docs/requirements/Component-Security.md
Joseph Doherty 9dccf8e72f deprecate(lmxproxy): move all LmxProxy code, tests, and docs to deprecated/
LmxProxy is no longer needed. Moved the entire lmxproxy/ workspace, DCL
adapter files, and related docs to deprecated/. Removed LmxProxy registration
from DataConnectionFactory, project reference from DCL, protocol option from
UI, and cleaned up all requirement docs.
2026-04-08 15:56:23 -04:00

120 lines
5.1 KiB
Markdown

# Component: Security
## Purpose
Provides API key-based authentication and role-based authorization for the gRPC service, along with TLS certificate management for transport security.
## Location
- `src/ZB.MOM.WW.LmxProxy.Host/Security/ApiKeyService.cs` — API key storage and validation.
- `src/ZB.MOM.WW.LmxProxy.Host/Security/ApiKeyInterceptor.cs` — gRPC server interceptor for authentication/authorization.
- `src/ZB.MOM.WW.LmxProxy.Client/Security/GrpcChannelFactory.cs` — Client-side TLS channel factory.
## Responsibilities
- Load and hot-reload API keys from a JSON configuration file.
- Validate API keys on every gRPC request via a server interceptor.
- Enforce role-based access control (ReadOnly vs ReadWrite).
- Manage TLS certificates for server and optional mutual TLS.
## 1. API Key Service
### 1.1 Key Storage
- Keys are stored in a JSON file (default `apikeys.json`).
- File format: `{ "ApiKeys": [{ "Key": "...", "Description": "...", "Role": "ReadOnly|ReadWrite", "Enabled": true|false }] }`.
- If the file does not exist at startup, the service auto-generates a default file with two random keys: one ReadOnly and one ReadWrite.
### 1.2 Hot Reload
- A `FileSystemWatcher` monitors the API key file for changes.
- Rapid changes are debounced (1-second minimum between reloads).
- `ReloadConfigurationAsync` uses a `SemaphoreSlim` to serialize reload operations.
- New and modified keys take effect on the next request. Removed or disabled keys reject future requests immediately.
- Active sessions are not affected by key changes — sessions are tracked independently by SessionManager.
### 1.3 Validation
- `ValidateApiKey(apiKey)` — Returns the `ApiKey` object if the key exists and `Enabled` is true, otherwise null.
- `HasRole(apiKey, requiredRole)` — Returns true if the key has the required role. Role hierarchy: ReadWrite implies ReadOnly.
## 2. API Key Interceptor
### 2.1 Authentication Flow
The `ApiKeyInterceptor` intercepts every unary and server-streaming RPC:
1. Extracts the `x-api-key` header from gRPC request metadata.
2. Calls `ApiKeyService.ValidateApiKey()`.
3. If the key is invalid or missing, returns `StatusCode.Unauthenticated`.
4. For write-protected methods (`Write`, `WriteBatch`, `WriteBatchAndWait`), checks that the key has the `ReadWrite` role. Returns `StatusCode.PermissionDenied` if the key is `ReadOnly`.
5. Adds the validated `ApiKey` to `context.UserState["ApiKey"]` for downstream use.
6. Continues to the service method.
### 2.2 Write-Protected Methods
These RPCs require the `ReadWrite` role:
- `Write`
- `WriteBatch`
- `WriteBatchAndWait`
All other RPCs (`Connect`, `Disconnect`, `GetConnectionState`, `Read`, `ReadBatch`, `Subscribe`, `CheckApiKey`) are allowed for `ReadOnly` keys.
## 3. API Key Model
| Field | Type | Description |
|-------|------|-------------|
| Key | string | The secret API key value |
| Description | string | Human-readable name for the key |
| Role | ApiKeyRole | `ReadOnly` or `ReadWrite` |
| Enabled | bool | Whether the key is active |
`ApiKeyRole` enum: `ReadOnly` (read and subscribe only), `ReadWrite` (full access including writes).
## 4. TLS Configuration
### 4.1 Server-Side (Host)
Configured via `TlsConfiguration` in `appsettings.json`:
| Setting | Default | Description |
|---------|---------|-------------|
| Enabled | false | Enable TLS on the gRPC server |
| ServerCertificatePath | `certs/server.crt` | PEM server certificate |
| ServerKeyPath | `certs/server.key` | PEM server private key |
| ClientCaCertificatePath | `certs/ca.crt` | CA certificate for mTLS client validation |
| RequireClientCertificate | false | Require client certificates (mutual TLS) |
| CheckCertificateRevocation | false | Check certificate revocation lists |
If TLS is enabled but certificates are missing, the service generates self-signed certificates at startup.
### 4.2 Client-Side
`ClientTlsConfiguration` in the client library:
| Setting | Default | Description |
|---------|---------|-------------|
| UseTls | false | Enable TLS on the client connection |
| ClientCertificatePath | null | Client certificate for mTLS |
| ClientKeyPath | null | Client private key for mTLS |
| ServerCaCertificatePath | null | Custom CA for server validation |
| ServerNameOverride | null | SNI/hostname override |
| ValidateServerCertificate | true | Validate the server certificate chain |
| AllowSelfSignedCertificates | false | Accept self-signed server certificates |
| IgnoreAllCertificateErrors | false | Skip all certificate validation (dangerous) |
- SSL protocols: TLS 1.2 and TLS 1.3.
- Client certificates loaded from PEM files and converted to PKCS12.
- Custom CA trust store support via chain building.
## Dependencies
- **Configuration** — TLS settings and API key file path from `appsettings.json`.
- **System.IO.FileSystemWatcher** — API key file change detection.
## Interactions
- **GrpcServer** — the ApiKeyInterceptor runs before every RPC in ScadaGrpcService.
- **ServiceHost** — creates ApiKeyService and ApiKeyInterceptor at startup, configures gRPC server credentials.
- **Client** — GrpcChannelFactory creates TLS-configured gRPC channels in LmxProxyClient.