# 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.