@@ -25,6 +25,8 @@ dotnet run --project src/ZB.MOM.WW.OtOpcUa.Driver.S7.Cli -- --help
|
||||
| `--tsap-mode` | `Auto` | ISO-on-TCP connection class: `Auto` / `Pg` / `Op` / `S7Basic` / `Other`. Hardened S7-1500 / ET 200SP CPUs may require `Op` or `S7Basic`. See [s7.md TSAP / Connection Type](v2/s7.md#tsap--connection-type). |
|
||||
| `--local-tsap` | (unset) | Optional 16-bit local TSAP override (e.g. `0x0200`). Required when `--tsap-mode Other`; wins over class default under Pg/Op/S7Basic. |
|
||||
| `--remote-tsap` | (unset) | Optional 16-bit remote TSAP override. Required when `--tsap-mode Other`; wins over class default under Pg/Op/S7Basic. |
|
||||
| `--password` | (unset) | Connection-level password sent right after `OpenAsync`. Used by hardened S7-300/400 (protection levels 1-3) and S7-1200/1500 (TIA Portal *Connection Mechanism* gate). Never logged. NB: S7netplus 0.20 doesn't expose `SendPassword`; the CLI prints a one-line warning and continues. See [s7.md "PLC password / protection levels"](v2/s7.md#plc-password--protection-levels). |
|
||||
| `--protection-level` | `Auto` | Declarative hint: `Auto` / `None` / `Level1` / `Level2` / `Level3` (S7-300/400) / `ConnectionMechanism` (S7-1200/1500). Diagnostic only — the wire-side unlock is driven by `--password`. |
|
||||
| `--verbose` | off | Serilog debug output |
|
||||
|
||||
## PUT/GET must be enabled
|
||||
@@ -139,6 +141,43 @@ wrong `--slot` produces also shows up when the CPU rejects PG class — try
|
||||
endpoint config. See [s7.md TSAP / Connection Type](v2/s7.md#tsap--connection-type)
|
||||
for the byte table and motivation.
|
||||
|
||||
### Hardened CPU — supplying a connection-level password
|
||||
|
||||
```powershell
|
||||
# S7-300 protection-level 2 — read+write protected without unlock.
|
||||
otopcua-s7-cli read -h 192.168.1.31 -c S7300 --slot 2 `
|
||||
--password "tia-portal-set-password" `
|
||||
--protection-level Level2 `
|
||||
-a DB1.DBW0 -t Int16
|
||||
|
||||
# S7-1500 ConnectionMechanism — TIA Portal Protection & Security pane gate.
|
||||
otopcua-s7-cli probe -h 10.50.12.30 `
|
||||
--tsap-mode Op `
|
||||
--password "tia-portal-set-password" `
|
||||
--protection-level ConnectionMechanism
|
||||
```
|
||||
|
||||
The password is emitted to the PLC immediately after `OpenAsync` succeeds and
|
||||
before the pre-flight PUT/GET probe runs (the same probe that would otherwise
|
||||
be the first operation a hardened CPU refuses). Never logged in any form;
|
||||
identifier-only success line is `S7 password sent for {Host}`.
|
||||
|
||||
**S7netplus 0.20 does not yet expose a public `SendPassword`** — the driver
|
||||
discovers the method reflectively, so a future minor release will be picked
|
||||
up automatically. Until then, configuring `--password` on a hardened CPU
|
||||
emits this warning at Init:
|
||||
|
||||
```
|
||||
[Warning] S7 password is set on driver '<id>' against host '<host>', but
|
||||
the linked S7netplus library does not expose SendPassword; password is
|
||||
being ignored at the wire.
|
||||
```
|
||||
|
||||
Init still completes (the COTP handshake itself doesn't require the
|
||||
password) but the first read against a hardened CPU will surface
|
||||
`BadDeviceFailure`. See [s7.md "PLC password / protection levels"](v2/s7.md#plc-password--protection-levels)
|
||||
for the full motivation, the no-log invariant, and the workaround matrix.
|
||||
|
||||
### `subscribe`
|
||||
|
||||
```powershell
|
||||
|
||||
@@ -109,6 +109,27 @@ or we ship a raw S7comm PDU helper. See
|
||||
[`docs/v2/s7.md` "CPU diagnostics (SZL)"](../v2/s7.md#cpu-diagnostics-szl)
|
||||
for the wire-status detail.
|
||||
|
||||
### 7. Password / protection levels — not modelled by snap7
|
||||
|
||||
PR-S7-E2 / [#303](https://github.com/dohertj2/lmxopcua/issues/303) adds
|
||||
`Password` + `ProtectionLevel` options that emit a connection-level password
|
||||
right after `OpenAsync`. **snap7 does not model S7 protection levels** — the
|
||||
simulator accepts every connection regardless of the password set on the
|
||||
client, so the integration profile cannot distinguish "password sent
|
||||
correctly" from "password ignored". Coverage stays at the unit-test seam:
|
||||
`S7PasswordOptionsTests` injects a fake `IS7PlcAuthGate` to assert the
|
||||
dispatch contract (Password=null skips the call; Password+SupportsSendPassword
|
||||
calls the gate; auth-failed wraps to a clean `InvalidOperationException`),
|
||||
plus the no-log invariant on `S7DriverOptions.ToString()`.
|
||||
|
||||
The wire path is also fundamentally limited until S7netplus 0.20 exposes a
|
||||
public `SendPassword` — the driver currently logs a warning and continues
|
||||
when the API is missing. See
|
||||
[`docs/v2/s7.md` "PLC password / protection levels"](../v2/s7.md#plc-password--protection-levels)
|
||||
for the library-limitation note. Live-firmware coverage of the unlock path
|
||||
requires a hardened S7-1500 lab rig with TIA Portal "Protection & Security"
|
||||
configured, which is parked as a follow-up.
|
||||
|
||||
## When to trust the S7 tests, when to reach for a rig
|
||||
|
||||
| Question | Unit tests | Real PLC |
|
||||
|
||||
117
docs/v2/s7.md
117
docs/v2/s7.md
@@ -1156,6 +1156,123 @@ diagnostics tests where you want every read to hit the wire.
|
||||
}
|
||||
```
|
||||
|
||||
## PLC password / protection levels
|
||||
|
||||
PR-S7-E2 (issue #303) adds a connection-level password option for hardened
|
||||
deployments. The driver emits the password to the PLC immediately after
|
||||
`OpenAsync` succeeds and before the pre-flight PUT/GET probe runs (the same
|
||||
pre-flight read that would otherwise be the first operation a hardened CPU
|
||||
refuses).
|
||||
|
||||
### Options
|
||||
|
||||
| Option | Default | Purpose |
|
||||
| ----------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `Password` | `null` | Connection-level password. Secret — never logged. `null` or empty = no password is sent. |
|
||||
| `ProtectionLevel` | `Auto` | Declarative hint about the PLC's protection scheme. One of `Auto`, `None`, `Level1`, `Level2`, `Level3` (S7-300/400 SFC 109/110 levels), or `ConnectionMechanism` (S7-1200/1500 TIA Portal "Protection & Security" pane). |
|
||||
|
||||
### S7-300 / S7-400 protection levels (1, 2, 3)
|
||||
|
||||
S7-300/400 firmware exposes three CPU-side protection levels:
|
||||
|
||||
* **Level 1** — write protection. Reads work without a password; writes
|
||||
(parameter, DB, M/Q changes) require an unlock.
|
||||
* **Level 2** — read and write protection. Both kinds of operation require
|
||||
the password.
|
||||
* **Level 3** — full protection. Even online presence detection / status
|
||||
list reads require the password.
|
||||
|
||||
Set `ProtectionLevel = Level1` / `Level2` / `Level3` and supply
|
||||
`Password` to match the level configured in the CPU's HW Config dialog.
|
||||
The level value is descriptive — the driver doesn't switch behaviour
|
||||
between Level1/2/3, since the wire-side `SendPassword` is the same call
|
||||
in all three cases. The hint surfaces in the driver-diagnostics RPC so a
|
||||
"PLC said Level 3 but config says Level 1" mismatch is spottable from the
|
||||
Admin UI.
|
||||
|
||||
### S7-1200 / S7-1500 connection mechanism
|
||||
|
||||
S7-1200/1500 firmware uses a different gate: TIA Portal's "Protection &
|
||||
Security" pane has a single **Connection Mechanism** dropdown that, when
|
||||
set to anything stricter than "No access", requires every PG/HMI/SCADA
|
||||
connection to authenticate after the COTP handshake. The wire-level
|
||||
exchange is the same `SendPassword` call but the diagnostic flag is
|
||||
distinct, so set `ProtectionLevel = ConnectionMechanism` for these
|
||||
families.
|
||||
|
||||
### No-log invariant
|
||||
|
||||
`Password` is a secret. The driver MUST NOT include the password value in
|
||||
log lines, exception messages, or diagnostic surfaces. Specifically:
|
||||
|
||||
* `S7DriverOptions.ToString()` redacts the field as `***`.
|
||||
* `S7Driver`'s success log line is `S7 password sent for {Host}` —
|
||||
identifier-only, no value.
|
||||
* The "S7netplus does not expose SendPassword" warning logs the host name
|
||||
and driver instance ID only, never the password.
|
||||
* Authentication-failure exceptions wrap the inner `S7.Net.PlcException`
|
||||
but their own message says only "S7 password authentication failed for
|
||||
host '{Host}'" — no password value.
|
||||
|
||||
Any new logging surface that flows an `S7DriverOptions` value MUST
|
||||
continue to redact. See the FOCAS-F4-d
|
||||
`docs/v2/focas-deployment.md` § "FOCAS password handling" entry for the
|
||||
sister no-log discipline on the FOCAS driver.
|
||||
|
||||
### Library limitation — S7netplus 0.20
|
||||
|
||||
**S7netplus 0.20.0 (the pinned dependency) does not expose a public
|
||||
`SendPassword` method.** The driver discovers the method reflectively
|
||||
(checking for `SendPasswordAsync(string, CancellationToken)` first, then
|
||||
`SendPassword(string)`) so a future minor release that ships the API
|
||||
will be picked up automatically without a code change here.
|
||||
|
||||
Until the upstream lands, configuring `Password` on a hardened CPU
|
||||
produces this Init-time warning:
|
||||
|
||||
```
|
||||
[Warning] S7 password is set on driver '<DriverInstanceId>' against
|
||||
host '<Host>', but the linked S7netplus library does not expose
|
||||
SendPassword; password is being ignored at the wire. Hardened-CPU
|
||||
connect may fail at first read.
|
||||
```
|
||||
|
||||
Init still completes — the COTP/S7comm handshake itself doesn't require
|
||||
the password — but the first read against a hardened CPU will surface
|
||||
`BadDeviceFailure` because PUT/GET-disabled and "level-3 protection"
|
||||
return identical "function not allowed" PDUs at the wire layer.
|
||||
|
||||
If your S7-1200/1500 deployment requires `ConnectionMechanism`, the
|
||||
near-term workarounds are:
|
||||
|
||||
1. **Lower the protection setting** in TIA Portal's Protection & Security
|
||||
pane to "Full access (no protection)" for the duration of the
|
||||
evaluation.
|
||||
2. **Configure a separate non-hardened connection** on a CP module that
|
||||
the driver can target while keeping the production endpoint hardened.
|
||||
3. **Track upstream S7netplus** for a `SendPassword` PR (the package owner
|
||||
has discussed adding it; see issue
|
||||
<https://github.com/S7NetPlus/s7netplus/issues>).
|
||||
|
||||
For S7-300/400 CPUs, levels 1 and 2 leave at least *read* access open
|
||||
without a password, so most monitoring use cases work without
|
||||
`SendPassword` until the library catches up — only Level 3 and the
|
||||
S7-1200/1500 ConnectionMechanism require the wire-level unlock.
|
||||
|
||||
### JSON config example
|
||||
|
||||
```json
|
||||
{
|
||||
"DriverConfig": {
|
||||
"Host": "192.168.10.50",
|
||||
"Port": 102,
|
||||
"CpuType": "S71500",
|
||||
"Password": "tia-portal-set-password",
|
||||
"ProtectionLevel": "ConnectionMechanism"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
1. Siemens Industry Online Support, *Modbus/TCP Communication between SIMATIC S7-1500 / S7-1200 and Modbus/TCP Controllers with Instructions `MB_CLIENT` and `MB_SERVER`*, Entry ID 102020340, V6 (Feb 2021). https://cache.industry.siemens.com/dl/files/340/102020340/att_118119/v6/net_modbus_tcp_s7-1500_s7-1200_en.pdf
|
||||
|
||||
Reference in New Issue
Block a user