feat(install): historian TCP env + firewall rule

Replace OTOPCUA_HISTORIAN_PIPE/OTOPCUA_ALLOWED_SID with TCP transport
env (OTOPCUA_HISTORIAN_TCP_PORT, OTOPCUA_HISTORIAN_BIND,
OTOPCUA_HISTORIAN_TLS_ENABLED, OTOPCUA_HISTORIAN_TLS_CERT/PASSWORD)
in Install-Services.ps1; add idempotent Windows Firewall inbound rule
for the TCP port. Add new params for all TCP/TLS options with cert
provisioning guidance. Update Refresh-Services.ps1 Step 4b comment
(PipeServer → TcpFrameServer) and add a Step 5 note clarifying that
TCP/TLS env is set at install time, not on refresh.
This commit is contained in:
Joseph Doherty
2026-06-12 12:02:26 -04:00
parent e2960515cf
commit ce25581596
2 changed files with 72 additions and 17 deletions
+66 -16
View File
@@ -1,13 +1,16 @@
<#
.SYNOPSIS
Registers the v2 Windows service on a node: OtOpcUaHost (fused binary, .NET 10)
and optionally OtOpcUaWonderwareHistorian (Wonderware historian sidecar, net48 x86).
and optionally OtOpcUaWonderwareHistorian (Wonderware historian sidecar, net48 x86
— communicates over TCP, optionally TLS, instead of a Windows named pipe).
.DESCRIPTION
v2 consolidates the legacy OtOpcUa + OtOpcUaAdmin services into a single role-gated
OtOpcUaHost binary. The -Roles parameter sets the OTOPCUA_ROLES service env so
Program.cs decides what to mount (admin / driver / both). The Wonderware historian
sidecar logic is unchanged from v1; install it with -InstallWonderwareHistorian.
sidecar is installed with -InstallWonderwareHistorian; it listens on a TCP port
(default 32569, configurable via -HistorianTcpPort) protected by the shared secret.
Optional TLS (-HistorianUseTls) is recommended for production cross-host deployments.
Galaxy access flows through the mxaccessgw sibling repo (separate service); see
docs/v2/Galaxy.ParityRig.md for the gateway setup.
@@ -39,6 +42,30 @@
Per-process secret passed to the historian sidecar via env var. Generated freshly
per install when not supplied.
.PARAMETER HistorianTcpPort
TCP port the sidecar listens on (OTOPCUA_HISTORIAN_TCP_PORT). Default 32569. A
matching Windows Firewall inbound rule is created automatically.
.PARAMETER HistorianBind
IP address the sidecar binds to (OTOPCUA_HISTORIAN_BIND). Default 0.0.0.0
(all interfaces). Set to 127.0.0.1 for loopback-only on same-host deployments.
.PARAMETER HistorianUseTls
When set, enables TLS on the sidecar TCP listener. Supply -HistorianTlsCert with
a certificate path or store thumbprint; recommended for cross-host production use.
.PARAMETER HistorianTlsCert
Certificate source for TLS. Either:
- Absolute path to a .pfx file (e.g. C:\ProgramData\OtOpcUa\pki\historian.pfx),
exported with MachineKeySet so the service account can read the private key; or
- A LocalMachine\My store thumbprint (40-hex-char string, no spaces).
Do NOT embed a cert path or thumbprint in automation scripts for unattended installs;
pass it at call time or from a secrets vault.
.PARAMETER HistorianTlsCertPassword
Password for the .pfx file (if using a pfx path). Leave empty when using a store
thumbprint. Never store passwords in scripts or source control.
.EXAMPLE
.\Install-Services.ps1 -InstallRoot 'C:\Program Files\OtOpcUa' `
-ServiceAccount 'OTOPCUA\svc-otopcua' -Roles 'admin,driver'
@@ -57,12 +84,16 @@ param(
[int]$HttpPort = 9000,
# Wonderware historian sidecar. Optional; gates the OtOpcUaWonderwareHistorian
# service. Secret + pipe defaults match the server's Historian:Wonderware appsettings.
# service. Secret defaults match the server's Historian:Wonderware appsettings.
[switch]$InstallWonderwareHistorian,
[string]$HistorianSharedSecret,
[string]$HistorianPipeName = 'OtOpcUaWonderwareHistorian',
[string]$HistorianServer = 'localhost',
[int]$HistorianPort = 32568,
[int]$HistorianPort = 32568, # SDK port (OTOPCUA_HISTORIAN_PORT) — Wonderware InSQL connection
[int]$HistorianTcpPort = 32569, # TCP listen port for the IPC channel (OTOPCUA_HISTORIAN_TCP_PORT)
[string]$HistorianBind = '0.0.0.0',
[switch]$HistorianUseTls,
[string]$HistorianTlsCert,
[string]$HistorianTlsCertPassword,
[string[]]$AvevaServiceDependencies = @('NmxSvc', 'aaBootstrap', 'aaGR')
)
@@ -87,29 +118,43 @@ if ($InstallWonderwareHistorian -and -not (Test-Path "$InstallRoot\WonderwareHis
exit 1
}
# Resolve the SID — the IPC ACL needs the SID, not the down-level name.
$sid = if ($ServiceAccount.StartsWith('S-1-')) {
$ServiceAccount
} else {
(New-Object System.Security.Principal.NTAccount $ServiceAccount).Translate([System.Security.Principal.SecurityIdentifier]).Value
}
# --- OtOpcUaWonderwareHistorian sidecar (optional, unchanged from v1) -------
# --- OtOpcUaWonderwareHistorian sidecar (optional, TCP transport) -----------
$historianDepend = $null
if ($InstallWonderwareHistorian) {
# Build the TCP/TLS env block.
# The Windows named-pipe transport and OTOPCUA_ALLOWED_SID have been retired;
# the sidecar now listens on a TCP port protected by the shared secret.
# TLS is optional but strongly recommended for cross-host production deployments.
#
# Cert provisioning for prod (when -HistorianUseTls):
# Option A — pfx file: export with -KeyExportPolicy Exportable -KeyStorageFlags MachineKeySet;
# place at e.g. C:\ProgramData\OtOpcUa\pki\historian.pfx;
# ACL the file so the service account has Read access.
# Option B — store thumbprint: import into LocalMachine\My; grant the service account
# Read on the private key via certlm.msc → Manage Private Keys.
# Either value is passed via -HistorianTlsCert at install time; NEVER hard-code it here.
$historianEnv = @(
"OTOPCUA_HISTORIAN_PIPE=$HistorianPipeName"
"OTOPCUA_ALLOWED_SID=$sid"
"OTOPCUA_HISTORIAN_SECRET=$HistorianSharedSecret"
"OTOPCUA_HISTORIAN_ENABLED=true"
"OTOPCUA_HISTORIAN_ALARM_WRITE_ENABLED=true"
"OTOPCUA_HISTORIAN_SERVER=$HistorianServer"
"OTOPCUA_HISTORIAN_PORT=$HistorianPort"
"OTOPCUA_HISTORIAN_TCP_PORT=$HistorianTcpPort"
"OTOPCUA_HISTORIAN_BIND=$HistorianBind"
)
if ($HistorianUseTls) {
$historianEnv += "OTOPCUA_HISTORIAN_TLS_ENABLED=true"
$historianEnv += "OTOPCUA_HISTORIAN_TLS_CERT=$HistorianTlsCert"
if ($HistorianTlsCertPassword) {
$historianEnv += "OTOPCUA_HISTORIAN_TLS_CERT_PASSWORD=$HistorianTlsCertPassword"
}
} else {
$historianEnv += "OTOPCUA_HISTORIAN_TLS_ENABLED=false"
}
Write-Host "Installing OtOpcUaWonderwareHistorian..."
& sc.exe create OtOpcUaWonderwareHistorian binPath= "`"$InstallRoot\WonderwareHistorian\OtOpcUa.Driver.Historian.Wonderware.exe`"" `
DisplayName= 'OtOpcUa Wonderware Historian Sidecar (out-of-process aahClient)' `
DisplayName= 'OtOpcUa Wonderware Historian Sidecar (out-of-process aahClient, TCP)' `
start= auto `
depend= ($AvevaServiceDependencies -join '/') `
obj= $ServiceAccount | Out-Null
@@ -120,6 +165,11 @@ if ($InstallWonderwareHistorian) {
& sc.exe failure OtOpcUaWonderwareHistorian reset= 86400 actions= restart/5000/restart/30000/restart/60000 | Out-Null
# Idempotent Windows Firewall rule for the sidecar TCP port.
$fwName = "OtOpcUa Wonderware Historian (TCP $HistorianTcpPort)"
Get-NetFirewallRule -DisplayName $fwName -ErrorAction SilentlyContinue | Remove-NetFirewallRule -ErrorAction SilentlyContinue
New-NetFirewallRule -DisplayName $fwName -Direction Inbound -Action Allow -Protocol TCP -LocalPort $HistorianTcpPort | Out-Null
$historianDepend = 'OtOpcUaWonderwareHistorian'
}
+6 -1
View File
@@ -125,7 +125,7 @@ Run {
# `dotnet publish` above) silently drops the net48 binding-redirect
# .exe.config and the transitive runtime DLLs MessagePack needs — most
# notably System.Memory.dll. The sidecar then JIT-load-faults inside
# PipeServer.RunOneConnectionAsync ("FileNotFoundException: System.Memory,
# TcpFrameServer.RunOneConnectionAsync ("FileNotFoundException: System.Memory,
# Version=4.0.1.2") and NSSM crash-loops it (exit 2 every ~2 min, ~120 s of
# retry backoff before it gives up). Fail loudly here so an incomplete deploy
# is caught at publish time instead of by a production crash-loop.
@@ -158,6 +158,11 @@ if (-not $WhatIf) {
# ------------------------------------------------------------------------
# Step 5: Service env block — ensure OTOPCUA_HISTORIAN_ALARM_WRITE_ENABLED
# is set on the Wonderware historian service (PR C.2 toggle).
# Note: the TCP transport env (OTOPCUA_HISTORIAN_TCP_PORT, OTOPCUA_HISTORIAN_BIND,
# OTOPCUA_HISTORIAN_TLS_ENABLED, OTOPCUA_HISTORIAN_TLS_CERT) and the Windows
# Firewall rule are set once at install time by Install-Services.ps1 and are
# not touched here on refresh; re-run Install-Services.ps1 to change them.
# OTOPCUA_ALLOWED_SID is no longer used (TCP transport retired the named pipe).
# ------------------------------------------------------------------------
if (Test-NssmService 'OtOpcUaWonderwareHistorian') {