- WP-1-3: Central/site failover + dual-node recovery tests (17 tests) - WP-4: Performance testing framework for target scale (7 tests) - WP-5: Security hardening (LDAPS, JWT key length, no secrets in logs) (11 tests) - WP-6: Script sandboxing adversarial tests (28 tests, all forbidden APIs) - WP-7: Recovery drill test scaffolds (5 tests) - WP-8: Observability validation (structured logs, correlation IDs, metrics) (6 tests) - WP-9: Message contract compatibility (forward/backward compat) (18 tests) - WP-10: Deployment packaging (installation guide, production checklist, topology) - WP-11: Operational runbooks (failover, troubleshooting, maintenance) 92 new tests, all passing. Zero warnings.
196 lines
5.7 KiB
Markdown
196 lines
5.7 KiB
Markdown
# ScadaLink Installation Guide
|
|
|
|
## Prerequisites
|
|
|
|
- Windows Server 2019 or later
|
|
- .NET 10.0 Runtime
|
|
- SQL Server 2019+ (Central nodes only)
|
|
- Network connectivity between all cluster nodes (TCP ports 8081-8082)
|
|
- LDAP/Active Directory server accessible from Central nodes
|
|
- SMTP server accessible from all nodes (for Notification Service)
|
|
|
|
## Single Binary Deployment
|
|
|
|
ScadaLink ships as a single executable (`ScadaLink.Host.exe`) that runs in either Central or Site role based on configuration.
|
|
|
|
### Windows Service Installation
|
|
|
|
```powershell
|
|
# Central Node
|
|
sc.exe create "ScadaLink-Central" binPath="C:\ScadaLink\ScadaLink.Host.exe" start=auto
|
|
sc.exe description "ScadaLink-Central" "ScadaLink SCADA Central Hub"
|
|
|
|
# Site Node
|
|
sc.exe create "ScadaLink-Site" binPath="C:\ScadaLink\ScadaLink.Host.exe" start=auto
|
|
sc.exe description "ScadaLink-Site" "ScadaLink SCADA Site Agent"
|
|
```
|
|
|
|
### Directory Structure
|
|
|
|
```
|
|
C:\ScadaLink\
|
|
ScadaLink.Host.exe
|
|
appsettings.json
|
|
appsettings.Production.json
|
|
data\ # Site: SQLite databases
|
|
site.db # Deployed configs, static overrides
|
|
store-and-forward.db # S&F message buffer
|
|
logs\ # Rolling log files
|
|
scadalink-20260316.log
|
|
```
|
|
|
|
## Configuration Templates
|
|
|
|
### Central Node — `appsettings.json`
|
|
|
|
```json
|
|
{
|
|
"ScadaLink": {
|
|
"Node": {
|
|
"Role": "Central",
|
|
"NodeHostname": "central-01.example.com",
|
|
"RemotingPort": 8081
|
|
},
|
|
"Cluster": {
|
|
"SeedNodes": [
|
|
"akka.tcp://scadalink@central-01.example.com:8081",
|
|
"akka.tcp://scadalink@central-02.example.com:8081"
|
|
]
|
|
},
|
|
"Database": {
|
|
"ConfigurationDb": "Server=sqlserver.example.com;Database=ScadaLink;User Id=scadalink_svc;Password=<CHANGE_ME>;Encrypt=true;TrustServerCertificate=false",
|
|
"MachineDataDb": "Server=sqlserver.example.com;Database=ScadaLink_MachineData;User Id=scadalink_svc;Password=<CHANGE_ME>;Encrypt=true;TrustServerCertificate=false"
|
|
},
|
|
"Security": {
|
|
"LdapServer": "ldap.example.com",
|
|
"LdapPort": 636,
|
|
"LdapUseTls": true,
|
|
"AllowInsecureLdap": false,
|
|
"LdapSearchBase": "dc=example,dc=com",
|
|
"JwtSigningKey": "<GENERATE_A_32_PLUS_CHAR_RANDOM_STRING>",
|
|
"JwtExpiryMinutes": 15,
|
|
"IdleTimeoutMinutes": 30
|
|
},
|
|
"HealthMonitoring": {
|
|
"ReportInterval": "00:00:30",
|
|
"OfflineTimeout": "00:01:00"
|
|
},
|
|
"Logging": {
|
|
"MinimumLevel": "Information"
|
|
}
|
|
},
|
|
"Serilog": {
|
|
"MinimumLevel": {
|
|
"Default": "Information",
|
|
"Override": {
|
|
"Microsoft": "Warning",
|
|
"Akka": "Warning"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Site Node — `appsettings.json`
|
|
|
|
```json
|
|
{
|
|
"ScadaLink": {
|
|
"Node": {
|
|
"Role": "Site",
|
|
"NodeHostname": "site-01-node-a.example.com",
|
|
"SiteId": "plant-north",
|
|
"RemotingPort": 8081
|
|
},
|
|
"Cluster": {
|
|
"SeedNodes": [
|
|
"akka.tcp://scadalink@site-01-node-a.example.com:8081",
|
|
"akka.tcp://scadalink@site-01-node-b.example.com:8081"
|
|
]
|
|
},
|
|
"Database": {
|
|
"SiteDbPath": "C:\\ScadaLink\\data\\site.db"
|
|
},
|
|
"DataConnection": {
|
|
"ReconnectInterval": "00:00:05",
|
|
"TagResolutionRetryInterval": "00:00:30"
|
|
},
|
|
"StoreAndForward": {
|
|
"SqliteDbPath": "C:\\ScadaLink\\data\\store-and-forward.db",
|
|
"DefaultRetryInterval": "00:00:30",
|
|
"DefaultMaxRetries": 50,
|
|
"ReplicationEnabled": true
|
|
},
|
|
"SiteRuntime": {
|
|
"ScriptTimeoutSeconds": 30,
|
|
"StaggeredStartupDelayMs": 50
|
|
},
|
|
"SiteEventLog": {
|
|
"RetentionDays": 30,
|
|
"MaxStorageMB": 1024,
|
|
"PurgeIntervalHours": 24
|
|
},
|
|
"Communication": {
|
|
"CentralSeedNode": "akka.tcp://scadalink@central-01.example.com:8081"
|
|
},
|
|
"HealthMonitoring": {
|
|
"ReportInterval": "00:00:30"
|
|
},
|
|
"Logging": {
|
|
"MinimumLevel": "Information"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Database Setup (Central Only)
|
|
|
|
### SQL Server
|
|
|
|
1. Create the configuration database:
|
|
```sql
|
|
CREATE DATABASE ScadaLink;
|
|
CREATE LOGIN scadalink_svc WITH PASSWORD = '<STRONG_PASSWORD>';
|
|
USE ScadaLink;
|
|
CREATE USER scadalink_svc FOR LOGIN scadalink_svc;
|
|
ALTER ROLE db_owner ADD MEMBER scadalink_svc;
|
|
```
|
|
|
|
2. Create the machine data database:
|
|
```sql
|
|
CREATE DATABASE ScadaLink_MachineData;
|
|
USE ScadaLink_MachineData;
|
|
CREATE USER scadalink_svc FOR LOGIN scadalink_svc;
|
|
ALTER ROLE db_owner ADD MEMBER scadalink_svc;
|
|
```
|
|
|
|
3. Apply EF Core migrations (development):
|
|
- Migrations auto-apply on startup in Development environment.
|
|
|
|
4. Apply EF Core migrations (production):
|
|
- Generate SQL script: `dotnet ef migrations script --project src/ScadaLink.ConfigurationDatabase`
|
|
- Review and execute the SQL script against the production database.
|
|
|
|
## Network Requirements
|
|
|
|
| Source | Destination | Port | Protocol | Purpose |
|
|
|--------|------------|------|----------|---------|
|
|
| Central A | Central B | 8081 | TCP | Akka.NET remoting |
|
|
| Site A | Site B | 8081 | TCP | Akka.NET remoting |
|
|
| Site nodes | Central nodes | 8081 | TCP | Central-site communication |
|
|
| Central nodes | LDAP server | 636 | TCP/TLS | Authentication |
|
|
| All nodes | SMTP server | 587 | TCP/TLS | Notification delivery |
|
|
| Central nodes | SQL Server | 1433 | TCP | Configuration database |
|
|
| Users | Central nodes | 443 | HTTPS | Blazor Server UI |
|
|
|
|
## Firewall Rules
|
|
|
|
Ensure bidirectional TCP connectivity between all Akka.NET cluster peers. The remoting port (default 8081) must be open in both directions.
|
|
|
|
## Post-Installation Verification
|
|
|
|
1. Start the service: `sc.exe start ScadaLink-Central`
|
|
2. Check the log file: `type C:\ScadaLink\logs\scadalink-*.log`
|
|
3. Verify the readiness endpoint: `curl http://localhost:5000/health/ready`
|
|
4. For Central: verify the UI is accessible at `https://central-01.example.com/`
|