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 .SYNOPSIS
Registers the v2 Windows service on a node: OtOpcUaHost (fused binary, .NET 10) 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 .DESCRIPTION
v2 consolidates the legacy OtOpcUa + OtOpcUaAdmin services into a single role-gated v2 consolidates the legacy OtOpcUa + OtOpcUaAdmin services into a single role-gated
OtOpcUaHost binary. The -Roles parameter sets the OTOPCUA_ROLES service env so OtOpcUaHost binary. The -Roles parameter sets the OTOPCUA_ROLES service env so
Program.cs decides what to mount (admin / driver / both). The Wonderware historian 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 Galaxy access flows through the mxaccessgw sibling repo (separate service); see
docs/v2/Galaxy.ParityRig.md for the gateway setup. 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-process secret passed to the historian sidecar via env var. Generated freshly
per install when not supplied. 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 .EXAMPLE
.\Install-Services.ps1 -InstallRoot 'C:\Program Files\OtOpcUa' ` .\Install-Services.ps1 -InstallRoot 'C:\Program Files\OtOpcUa' `
-ServiceAccount 'OTOPCUA\svc-otopcua' -Roles 'admin,driver' -ServiceAccount 'OTOPCUA\svc-otopcua' -Roles 'admin,driver'
@@ -57,12 +84,16 @@ param(
[int]$HttpPort = 9000, [int]$HttpPort = 9000,
# Wonderware historian sidecar. Optional; gates the OtOpcUaWonderwareHistorian # 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, [switch]$InstallWonderwareHistorian,
[string]$HistorianSharedSecret, [string]$HistorianSharedSecret,
[string]$HistorianPipeName = 'OtOpcUaWonderwareHistorian',
[string]$HistorianServer = 'localhost', [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') [string[]]$AvevaServiceDependencies = @('NmxSvc', 'aaBootstrap', 'aaGR')
) )
@@ -87,29 +118,43 @@ if ($InstallWonderwareHistorian -and -not (Test-Path "$InstallRoot\WonderwareHis
exit 1 exit 1
} }
# Resolve the SID — the IPC ACL needs the SID, not the down-level name. # --- OtOpcUaWonderwareHistorian sidecar (optional, TCP transport) -----------
$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) -------
$historianDepend = $null $historianDepend = $null
if ($InstallWonderwareHistorian) { 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 = @( $historianEnv = @(
"OTOPCUA_HISTORIAN_PIPE=$HistorianPipeName"
"OTOPCUA_ALLOWED_SID=$sid"
"OTOPCUA_HISTORIAN_SECRET=$HistorianSharedSecret" "OTOPCUA_HISTORIAN_SECRET=$HistorianSharedSecret"
"OTOPCUA_HISTORIAN_ENABLED=true" "OTOPCUA_HISTORIAN_ENABLED=true"
"OTOPCUA_HISTORIAN_ALARM_WRITE_ENABLED=true" "OTOPCUA_HISTORIAN_ALARM_WRITE_ENABLED=true"
"OTOPCUA_HISTORIAN_SERVER=$HistorianServer" "OTOPCUA_HISTORIAN_SERVER=$HistorianServer"
"OTOPCUA_HISTORIAN_PORT=$HistorianPort" "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..." Write-Host "Installing OtOpcUaWonderwareHistorian..."
& sc.exe create OtOpcUaWonderwareHistorian binPath= "`"$InstallRoot\WonderwareHistorian\OtOpcUa.Driver.Historian.Wonderware.exe`"" ` & 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 ` start= auto `
depend= ($AvevaServiceDependencies -join '/') ` depend= ($AvevaServiceDependencies -join '/') `
obj= $ServiceAccount | Out-Null 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 & 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' $historianDepend = 'OtOpcUaWonderwareHistorian'
} }
+6 -1
View File
@@ -125,7 +125,7 @@ Run {
# `dotnet publish` above) silently drops the net48 binding-redirect # `dotnet publish` above) silently drops the net48 binding-redirect
# .exe.config and the transitive runtime DLLs MessagePack needs — most # .exe.config and the transitive runtime DLLs MessagePack needs — most
# notably System.Memory.dll. The sidecar then JIT-load-faults inside # 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 # 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 # 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. # 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 # Step 5: Service env block — ensure OTOPCUA_HISTORIAN_ALARM_WRITE_ENABLED
# is set on the Wonderware historian service (PR C.2 toggle). # 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') { if (Test-NssmService 'OtOpcUaWonderwareHistorian') {