Appearance
Policy Signing
The Telovix Console signs policy files before delivering them to sensors. Each sensor verifies the signature before loading or activating any policy. This prevents a policy from being tampered with after the Console dispatches it and ensures that a policy active on a sensor can be traced back to a specific reviewed and authorized artifact.
This matters most for enforcement-mode deployments. When a policy controls whether processes are killed or network connections are dropped, operators need to know that what is running on the sensor is exactly what was reviewed in the Console.
Prerequisites
- Console already installed and running
- Ed25519 signing key generated (or use existing key)
adminrole to configure the signing key path
How signing works
Signing side (Console)
The Console holds an Ed25519 private key at the path configured during setup (default: policy-signing.ed25519). When a policy pack is prepared for delivery, the Console signs each policy YAML file using this key.
The signed message for each policy file is:
telovix-policy-v1:{policy_name}\n{yaml_content}The policy name is the filename without path. The YAML content is the full text of the policy file. Binding the name to the content means a valid signature for policy-1.yaml cannot be applied to a policy with a different name. The resulting signature is base64-encoded and written to a sidecar file with the same name and a .sig extension.
Verification side (sensor)
During enrollment, the Console returns the corresponding Ed25519 public key (policy_signing_public_key_pem). The sensor stores it as policy-signing.pub in the state directory.
When the sensor loads a policy pack, it applies this logic to every YAML file:
- If a
.sigsidecar exists alongside the policy YAML:- Load the public key from
policy-signing.pub - Base64-decode the signature from the sidecar
- Reconstruct the message:
telovix-policy-v1:{policy_name}\n{yaml_content} - Verify the signature using Ed25519
- If verification fails: reject the policy file (not loaded, not applied)
- Load the public key from
- If no
.sigsidecar exists but a signing key is present in the state directory:- Log a warning
- Proceed with loading (backward compatibility with pre-signing deployments)
- If no
.sigsidecar and no signing key present:- Skip verification and load the policy
Case 1 is the production behavior. Cases 2 and 3 exist to avoid hard failures in configurations that were deployed before policy signing was introduced.
Policy pack file structure
Policy files are stored locally at the sensor in compiled-policies/{pack_id}/{pack_version}/. Each policy YAML has a corresponding .sig sidecar when signing is active:
compiled-policies/
└── pack-security-v2/
└── 2.0.0/
├── policy-1.yaml
├── policy-1.yaml.sig
├── policy-2.yaml
├── policy-2.yaml.sig
└── ...The sensor reads all .yaml files in the directory. For each one it checks for a .sig sidecar before proceeding. The public key is loaded from policy-signing.pub in the root state directory, not from within the pack directory.
TLS uprobe auto-generated policies
The TLS monitoring module generates policy files at sensor startup from /proc inspection and writes them to compiled-policies/tls-uprobe-*.yaml. These auto-generated files are not signed because they are created locally by the sensor process itself, not delivered from the Console. The sensor does not attempt signature verification on files it generated locally.
Configure Console signing
Configure the policy signing key path during Console setup (default: policy-signing.ed25519). The Console loads the signing key at startup. If the file is missing or unreadable at startup, policy signing is disabled and the Console delivers all policies unsigned (case 3 behavior on sensors).
Generate the signing key
The Console requires an Ed25519 private key in PEM format. Generate one with:
bash
openssl genpkey -algorithm Ed25519 -out policy-signing.ed25519Store this file at the path configured during setup. The Console derives the public key from the private key internally; you do not need to provide the public key separately. The public key is distributed to sensors during enrollment.
Sensor state files
| File | Contents |
|---|---|
policy-signing.pub | Ed25519 public key received from Console at enrollment |
compiled-policies/ | Directory containing policy packs by {pack_id}/{pack_version}/ |
assigned-pack.json | Current pack ID, version, enforcement state, and namespace scope |
The public key in policy-signing.pub is set once at enrollment. It is updated if the sensor re-enrolls. It does not change during normal certificate renewal.
What happens when verification fails
If a policy file's signature does not verify against the known public key, the sensor rejects that policy file. It is not applied to the runtime engine. The sensor logs a rejection at error level including the policy name and the nature of the failure.
The remaining policy files in the same pack are still evaluated independently. A single tampered file does not invalidate the entire pack. However, a tampered file that covers a critical detection will simply be absent from the sensor's active rule set until the pack is redelivered with a valid signature.
This behavior is intentional. Failing silently (loading the policy anyway) would make signing useless for security. Failing hard for the entire pack would create a denial-of-service vector where any corrupted file removes all policy protection.
Rotate the signing key
When the policy signing key must be rotated (Console host compromise, key material exposure, or scheduled rotation):
- Generate a new Ed25519 private key and replace the file at the configured signing key path.
- Restart the Console to load the new key.
- Re-sign and re-deploy all policy packs. The Console signs policies at dispatch time; packs already deployed to sensors carry signatures from the old key.
- Sensors that already have the old public key in
policy-signing.pubwill reject policies signed with the new key until they re-enroll and receive the updated public key. - Trigger re-enrollment for all sensors, or manually push the new public key to
policy-signing.pubon each sensor if re-enrollment is impractical.
After key rotation, sensors operating in case 2 mode (no .sig sidecar) will continue loading policies with a warning until new signed packs are delivered. This is the window of reduced policy integrity assurance; minimize it by completing full re-deployment promptly.
Relationship to enforcement mode
The enforcement readiness check (enforce_ready and enforced states) requires the pack to have enforcement_capable = true. It does not independently require that policies be signed. However, deploying unsigned policies in enforcement mode leaves the enforcement behavior unverifiable.
Best practice: require that all packs used in enforcement mode carry .sig sidecars. Verify this by checking the compiled-policies directory on the sensor before enabling enforcement.
Review the audit trail
Every policy pack assignment is recorded in the Console audit log with the actor, pack ID, version, and timestamp. Every enforcement state change is also recorded. When investigating unexpected sensor behavior, the audit log provides the chain of pack deployments that led to the current state.
The audit log is accessible from the Audit Log page in the Console and can be exported from there.
Operational guidance
Keep the signing key off the Console host: The policy signing key should not reside on the same host as the Console process if the threat model includes Console host compromise. Use a separate key management system or at minimum store the key on a separate volume that can be unmounted after the Console starts.
Verify before enforcing: Before enabling enforcement mode on a sensor, confirm that the sensor's compiled-policies directory contains both .yaml and .yaml.sig files for all active policies. A policy loaded in case 2 (unsigned, warning logged) provides behavioral detection but no tamper protection.
Do not edit policy files on the sensor host: Any modification to a .yaml file on the sensor invalidates its .sig sidecar and will cause the policy to be rejected on next load. Policy changes must go through the Console pack management workflow and be re-signed before delivery.