SIENTIAPDE-1973

SIENTIAPDE-1973: Implement per-connector OPC client certificates and remove legacy read engine.
This update overhauls certificate management, transitioning from a single platform certificate to individual client certificates defined in the slot configuration. The CertProvider now downloads certificates from MinIO based on certificate_id, applying dynamic security policies (mode, policy) from the slot. The legacy READ_ENGINE=legacy option and OpcManager have been entirely removed, standardizing on the ServerReader with strict slot v2 enforcement. Write command resolution (ServerConfigResolver) now consistently uses connectors joined with opc_certificates. The CERT_BUCKET environment variable is no longer used.
This commit is contained in:
2026-10-29 12:39:21 -03:00
parent d68e842e6e
commit ecb055eefb
32 changed files with 1639 additions and 2284 deletions

View File

@@ -9,7 +9,7 @@ from sientia_do.observability.sientia_monitoring import SientiaMonitoring
import ingestor.metrics as metrics
from ingestor.connection.certs import CertProvider
from ingestor.connection.clock import MonotonicClock
from ingestor.connection.security import apply_security
from ingestor.connection.security import apply_security, is_secured
class OpcConnectionPool(SientiaMonitoring):
@@ -65,11 +65,10 @@ class OpcConnectionPool(SientiaMonitoring):
"""Warn when a caller asks for a server with other connection settings than
the pooled client was opened with. The client is reused as is: reconnecting
on every mismatch would let one malformed write command drop the reads.
Config changes from the slot evict the client first (ServerReader.shutdown)."""
Config changes from the slot (security, certificate) evict the client first:
the IngestorManager re-initializes the reader (ServerReader.shutdown)."""
opened = self._configs.get(server_id, {})
diff = [
k for k in ('url', 'server_uri', 'use_security') if opened.get(k) != server_cfg.get(k)
]
diff = [k for k in ('url', 'security', 'certificate') if opened.get(k) != server_cfg.get(k)]
if diff:
self.logger.warning(
f'OPC server {server_id}: reusing the pooled connection, but {diff} differ '
@@ -98,10 +97,10 @@ class OpcConnectionPool(SientiaMonitoring):
try:
client = await self._connect(server_cfg)
except Exception:
if server_cfg.get('use_security'):
if is_secured(server_cfg):
# The cached certificate may be the cause (replaced in MinIO,
# rejected by the server): download it again on the next attempt.
self.cert_provider.invalidate()
self.cert_provider.invalidate(server_cfg['certificate']['certificate_id'])
await self.emit_metric(
metric_object=metrics.OPC_CONNECTIONS_FAILED,
tags={'pod_id': self.pod_id, 'server_name': server_id},
@@ -123,13 +122,12 @@ class OpcConnectionPool(SientiaMonitoring):
pod_uri = self.pod_id.replace('-', ':')
client.product_uri = pod_uri
client.application_uri = pod_uri
server_uri = server_cfg.get('server_uri')
if server_uri:
# Must match the certificate's SAN URI (KEPServer: BadCertificateUriInvalid).
client.application_uri = server_uri
if server_cfg.get('use_security'):
cert, key = await self.cert_provider.fetch()
await apply_security(client, server_cfg.get('server_uri'), cert, key, None)
# Security only with a client certificate (SIENTIAPDE-1974 D5); apply_security
# sets the certificate's application URI, which must match its SAN URI.
if is_secured(server_cfg):
certificate = server_cfg['certificate']
cert, key = await self.cert_provider.fetch(certificate)
await apply_security(client, server_cfg['security'], certificate, cert, key)
await client.connect()
return client