Security#
The IoT security helpers define transport security and authentication configuration for telemetry clients. They do not implement cryptography directly. TLS, certificates, and authentication headers are passed to the concrete transport layer, while the configuration objects keep logs, manifests, and notebooks from leaking secrets.
Use this page for security configuration, redaction, environment loading, and validation. The examples use placeholder certificate paths and tokens; replace them with deployment-specific secrets outside version control.
Configure TLS And Bearer Authentication#
Use pycsamt.iot.TLSConfig for TLS material and
pycsamt.iot.Credential for authentication. A
pycsamt.iot.SecurityConfig combines both and can restrict allowed
telemetry protocols.
1from pycsamt.iot import AuthScheme, Credential, SecurityConfig, TLSConfig
2
3security = SecurityConfig(
4 tls=TLSConfig(
5 enabled=True,
6 ca_cert="certs/field-ca.pem",
7 certfile="certs/node-client.pem",
8 keyfile="certs/node-client.key",
9 verify=True,
10 min_version="TLSv1.2",
11 ),
12 credential=Credential(
13 scheme=AuthScheme.BEARER,
14 token="field-secret-token",
15 ),
16 require_tls=True,
17 allowed_protocols=["https", "mqtt", "websocket"],
18)
19
20print(security.as_dict())
Output:
{
"tls": {
"enabled": true,
"ca_cert": "certs/field-ca.pem",
"certfile": "certs/node-client.pem",
"keyfile": "certs/node-client.key",
"verify": true,
"min_version": "TLSv1.2"
},
"credential": {
"scheme": "bearer",
"token": "***REDACTED***",
"username": null,
"password": null,
"api_key": null,
"api_key_header": "X-API-Key"
},
"require_tls": true,
"allowed_protocols": [
"https",
"mqtt",
"websocket"
]
}
Build Client Options#
pycsamt.iot.SecurityConfig.client_options() returns the live options
that can be passed to telemetry clients. These options include raw secrets
because the transport needs them. Redact them before printing.
1from pycsamt.iot import redact_secret
2
3options = security.client_options()
4print(
5 {
6 "tls": options.get("tls"),
7 "ca_cert": options.get("ca_cert"),
8 "certfile": options.get("certfile"),
9 "keyfile": options.get("keyfile"),
10 "headers": {
11 key: redact_secret(value)
12 for key, value in options.get("headers", {}).items()
13 },
14 "token": redact_secret(options.get("token")),
15 }
16)
Output:
{
"tls": true,
"ca_cert": "certs/field-ca.pem",
"certfile": "certs/node-client.pem",
"keyfile": "certs/node-client.key",
"headers": {
"Authorization": "***REDACTED***"
},
"token": "***REDACTED***"
}
Other Credential Schemes#
Bearer tokens, API keys, and basic authentication are supported. Use
headers() only when feeding a live transport. Use as_dict() for
logs, manifests, and notebooks.
1api_key = Credential(
2 scheme="api_key",
3 api_key="api-key-123",
4 api_key_header="X-Field-Key",
5)
6basic = Credential(
7 scheme="basic",
8 username="operator",
9 password="station-password",
10)
11
12print(
13 {
14 "api_key_redacted": {
15 key: redact_secret(value)
16 for key, value in api_key.headers().items()
17 },
18 "basic_header_prefix": basic.headers()["Authorization"][:16],
19 "basic_summary": basic.as_dict(),
20 }
21)
Output:
{
"api_key_redacted": {
"X-Field-Key": "***REDACTED***"
},
"basic_header_prefix": "Basic b3BlcmF0b3",
"basic_summary": {
"scheme": "basic",
"token": null,
"username": "operator",
"password": "***REDACTED***",
"api_key": null,
"api_key_header": "X-API-Key"
}
}
Load From Environment#
pycsamt.iot.SecurityConfig.from_env() reads environment variables
with the PYCSAMT_IOT_ prefix. Recognised variables include TOKEN,
API_KEY, USERNAME, PASSWORD, TLS, CA_CERT,
CERTFILE, and KEYFILE.
1import os
2
3os.environ["PYCSAMT_IOT_TOKEN"] = "env-token-456"
4os.environ["PYCSAMT_IOT_TLS"] = "true"
5os.environ["PYCSAMT_IOT_CA_CERT"] = "certs/env-ca.pem"
6
7from_env = SecurityConfig.from_env()
8
9for key in ["PYCSAMT_IOT_TOKEN", "PYCSAMT_IOT_TLS", "PYCSAMT_IOT_CA_CERT"]:
10 os.environ.pop(key, None)
11
12print(from_env.as_dict())
Output:
{
"tls": {
"enabled": true,
"ca_cert": "certs/env-ca.pem",
"certfile": null,
"keyfile": null,
"verify": true,
"min_version": null
},
"credential": {
"scheme": "bearer",
"token": "***REDACTED***",
"username": null,
"password": null,
"api_key": null,
"api_key_header": "X-API-Key"
},
"require_tls": false,
"allowed_protocols": null
}
Protocol Policy#
Use allowed_protocols as a small policy gate before building telemetry
clients. This is especially useful when a deployment should only use
encrypted or centrally approved transports.
1print(f"allows mqtt: {security.allows('mqtt')}")
2print(f"allows serial: {security.allows('serial')}")
Output:
allows mqtt: True
allows serial: False
Validation Checks#
Invalid security configurations fail early. This prevents field scripts from silently running without the required credential or TLS material.
1for label, factory in [
2 ("missing bearer token", lambda: Credential(scheme="bearer")),
3 (
4 "require_tls without TLS",
5 lambda: SecurityConfig(tls=TLSConfig(enabled=False),
6 require_tls=True),
7 ),
8 (
9 "certfile without keyfile",
10 lambda: TLSConfig(enabled=True, certfile="client.pem"),
11 ),
12]:
13 try:
14 factory()
15 except Exception as exc:
16 print(f"{label}: {type(exc).__name__}: {exc}")
Output:
missing bearer token: ValueError: bearer scheme requires a token.
require_tls without TLS: ValueError: require_tls is set but TLS is not enabled.
certfile without keyfile: ValueError: certfile and keyfile must be provided together.
Field Guidance#
Use as_dict() for anything that may be logged, stored in a manifest, or
shown in a notebook. Use client_options() only at the boundary where a
live transport client is created. Keep certificate files and environment
variables outside the documentation tree and out of version control.
For production deployments, prefer TLS-enabled transports and short-lived tokens. Record the redacted security summary in the acquisition manifest so reviewers can see which security policy was active without exposing the actual secrets.