-
Notifications
You must be signed in to change notification settings - Fork 1
drift
(v2.2.0)
Per-device file integrity monitoring. The agent computes SHA-256
hashes of a list of watched config files on every few heartbeats
and reports them. The server compares against a stored baseline
and fires a drift_detected webhook when a hash diverges.
Hash-only by design. The contents of /etc/sudoers,
/etc/ssh/sshd_config, etc. never cross the wire on routine
polling. To see what actually changed, the operator triggers a
separate "fetch contents" action that queues a cat command
through the existing exec mechanism (subject to the same audit and
permission checks as any other command).
The default watched list (configurable):
| Path | Why |
|---|---|
/etc/ssh/sshd_config |
SSH daemon config — port, auth methods, root login policy |
/etc/sudoers |
Sudo policy — privilege escalation rules |
/etc/fstab |
Mount points — drive layout, NFS / CIFS mounts |
/etc/crontab |
System cron — scheduled root-owned jobs |
/etc/hosts |
Local DNS overrides |
/etc/resolv.conf |
DNS resolver config |
/etc/nsswitch.conf |
Name service order (files vs DNS vs LDAP) |
/etc/pam.d/sshd |
PAM stack for SSH logins |
Each one is operationally significant — a change here is either a
deliberate operator action or something that should make you look
twice. Files that legitimately change often (/etc/passwd on
distros that update it on login, /etc/mtab, runtime-generated
configs) are not in the default list.
Global default — edit cfg['drift']['default_watched_files'] in
config.json to change the default list for new devices.
Per-device override — set devices[<id>]['watched_files'] to a
list to replace the global default for that device. The agent picks
up the new list on its next heartbeat.
Drift profiles (v3.13.0) — reusable, named sets of watched files, managed from the Drift page (the Drift profiles panel: create, edit, delete). Assign a profile to a device, tag, or group and every matching host monitors that set — no need to edit each device individually.
Resolution precedence for a device's watched list:
- an explicit per-device
watched_fileslist (set in the device drawer) — always wins; - an assigned profile — device assignment, then tag, then group;
- the global default above.
Endpoints: GET/POST /api/drift/profiles,
PUT/DELETE /api/drift/profiles/<id>, and POST /api/drift/assign
({scope_type: device|tag|group, scope_value, profile_id}; a null
profile_id clears the assignment). Profiles and assignments live
under cfg['drift']['profiles'] / ['assignments']. Admin-only to
mutate; changes take effect on each device's next heartbeat.
A device's drift detail view shows how its watched list was
resolved — device-override, profile:<device|tag|group> (with the
profile name), default, or disabled — so it's never a mystery
which rule won.
- On every poll, the server hands the agent the current watched-files list in the heartbeat response.
- Every few polls (
DRIFT_EVERYin the agent), the agent walks the list and computes:- SHA-256 of the file content
- File size
- mtime
- existence flag (some watched files are conditional)
- Submits this report as the
driftfield in the next heartbeat. - Server's
_ingest_drift_report:- On first sighting → records as baseline, drift_count=0.
- On unchanged hash → updates
last_check, no event. - On hash change → adds to history, increments
drift_count, firesdrift_detectedwebhook once (not on every subsequent poll that reports the same new hash — debounced viaprior_hash).
- Operator sees the drift on the Drift page, can drill into the device-detail modal to see when each file changed.
A file is drifted if current_hash != baseline_hash.
drift_count is the number of distinct changes that have
crossed the baseline boundary. It only increments when a change
crosses from baseline to non-baseline — repeated reports of the
same new hash don't bump it. This means a one-time legitimate
config change shows drift_count=1 even after weeks of polls;
true noise (an attacker who keeps editing a file) shows a high
count.
When you've reviewed a drifted file and decided the change is
legitimate, click Accept as baseline on that row. The current
hash becomes the new baseline, drift_count resets to 0, and
future changes are measured from the new baseline.
Accept all current as new baseline on the device modal does this for every drifted file on that device in one click — useful after a planned config change rollout.
drift_detected events carry:
{
"device_id": "WKFB...",
"device_name": "web01.example.com",
"path": "/etc/ssh/sshd_config",
"exists": true,
"baseline_hash": "sha256:original...",
"current_hash": "sha256:new..."
}Route these to a Slack / Discord / ntfy channel you actually check. Configuration changes during business hours are usually legitimate; the same alert at 3am is the one you want to see.
-
Not a remediation tool. Drift detection tells you that a
file changed; rolling back is your call, done via whatever
configuration management you already use (Ansible, manual edit,
etckeeper). -
Not full file integrity monitoring in the AIDE / Tripwire
sense — those tools watch every binary in
/usr, signed manifests, kernel modules, etc. RemotePower watches a small list of high-signal config files. The two complement each other; this is the lightweight always-on baseline, not the forensic deep-dive. -
Not change attribution. We see that the hash changed, not
who changed it. For attribution, look at
auth.logon the device, or pair this withauditdrules on the watched paths.
Configuration drift detection is an expected control for SOC 2
(CC6.1, CC6.6), ISO 27001 (A.12.4.3, A.14.2.4), HIPAA (164.312(c)),
PCI DSS (11.5), and FedRAMP. The audit-log entries the server
writes when baselines are reset (drift_baseline events with
actor and timestamp) are designed to be readable as evidence.
GET /api/drift — fleet-wide overview
GET /api/devices/<id>/drift — full drift state for one device
POST /api/devices/<id>/drift/baseline — accept current as new baseline
body: {paths: [...]} or {all: true}
DELETE /api/devices/<id>/drift — wipe drift state (re-bootstrap)
All require authentication. Baseline-acceptance is audit-logged.
data/drift_state.json, one entry per device:
{
"WKFB...": {
"files": {
"/etc/ssh/sshd_config": {
"current_hash": "sha256:...",
"current_size": 3024,
"current_mtime": 1700000000,
"baseline_hash": "sha256:...",
"baseline_size": 3024,
"baseline_set_at": 1700000000,
"baseline_set_by": "admin",
"first_seen": 1700000000,
"last_check": 1700000000,
"drift_count": 0,
"exists": true,
"history": [
{"ts": 1700000000, "hash": "sha256:...", "size": 3024, "exists": true}
]
}
}
}
}Per-file history capped at the last 20 changes.
Drift reporting needs agent v2.2.0+. Older agents simply don't
send the drift field; the device shows up as "no data" on the
Drift page until the agent is upgraded.
To check your agent versions: Devices page, the OS column shows "agent vX.Y.Z" for each agent-managed device. The standard agent update flow (Settings → Agent updates → Push update) works for the drift upgrade just like any other agent release.
RemotePower · README · CHANGELOG · remotepower.tvipper.com — generated from docs/, do not edit pages here directly.
Getting started
- Install
- Admin guide
- Deployment map
- Docker / Compose
- HTTPS / TLS
- Self-signed TLS
- Upgrading
- Troubleshooting
Agents & devices
Monitoring & health
Security
Integrations & automation
- Homelab integrations
- OPNsense
- Scripts
- Custom scripts
- MCP server
- Webhooks
- Terraform / IaC
- AI assistant
- RAG
Reference
- Architecture
- CMDB
- Feature inventory
- REST API
- Swagger / OpenAPI
- Fleet management
- Scaling
- Satellites
- Keyboard shortcuts
Release notes