Agent Tamper Protection
Tamper protection makes the Hermes agent hard to stop or uninstall on the machines you manage, and tells you when someone tries. While it is on, a local administrator cannot simply stop or delete the agent service, the agent's files are protected, and the only sanctioned local uninstall needs a single-use code that Athena issues for that one machine. Tamper attempts, uses of a code, and an agent that disappears right after either one show up on the Alerts board and in the audit log. Everything runs on your own server, so it works on an air-gapped network.
- Off by default. Nothing changes on any agent until an administrator turns it on.
- Fleet-wide. One switch covers every Windows, Linux and macOS agent connected to the server. You cannot turn it on for some collections only.
- It raises the bar; it does not make removal impossible. A determined local administrator or root user can still defeat it (see Limits). What tamper protection guarantees is that ordinary stop and uninstall attempts fail, and that the attempt becomes visible in Athena.
Turning it on#
- Open Settings → Agents → Configuration.
- In the Tamper Protection section, tick Enable tamper protection.
- Click Save at the bottom of the page.
The change is pushed straight away to every connected agent. Agents that are offline pick it up the next time they connect, because the setting is sent on every connect. Each agent applies its protections once per change, so saving the page again does not re-run them.
To turn it off, clear the box and save. Each agent then removes its protections and goes back to its normal state (see the table below).
The setting is stored as Agent:TamperProtectionEnabled in the server's
appsettings.json (see Configuration).
What it does on each OS#
| OS | While tamper protection is on | When it is turned off |
|---|---|---|
| Windows |
|
The default service permissions and audit settings are restored. The install folder's permissions are reset. Service recovery goes back to restart after 5 seconds, three times. |
| Linux |
|
The immutable flags are removed and the drop-in is deleted. The service's normal restart behaviour stays. |
| macOS |
|
The immutable flags are removed. The launchd restart behaviour stays. |
Limits#
Tamper protection is a deterrent and an alarm, not a lock. Someone with full control of a machine can still remove the agent. For example, a Windows administrator can take back ownership of the install folder, disable the service in the registry, or run commands as SYSTEM. A Linux or macOS root user can clear the immutable flags. When that happens, the self-check and the Agent removed unexpectedly alert are what make the attempt visible.
How tampering is detected#
Agents report these events to Athena:
- Protection removed. While tamper protection is on, the agent checks every 5 minutes and each time it connects that its protections are still in place. On Windows it checks the service permissions. On Linux it checks the systemd drop-in and the immutable flags. On macOS it checks the immutable flags. If something was removed or changed, the agent reports it and applies the protections again. The Windows install-folder permissions are not part of this check. This check is paused for 10 minutes after a valid uninstall code is used, so it does not fight a sanctioned uninstall.
- Uninstall refused. Someone ran the uninstall command from an elevated prompt or as root, but it refused to run because the code was missing, invalid, expired or already used.
- Uninstall authorised. A valid uninstall code was accepted and the uninstall is going ahead.
If the agent is offline when an event happens, the event is queued on the machine. It is sent the next time the agent connects.
Uninstall codes#
An uninstall code authorises one uninstall of the agent on one machine. Each code:
- is signed by your Athena server's certificate authority. The agent checks it offline against the CA certificate it already trusts, so the machine does not need to reach the server to accept it. No reusable secret is stored on the endpoint.
- is valid only for the agent it was issued for. A code issued for another machine is refused.
- can be used once. The agent keeps a record of the codes it has accepted and refuses a code it has already seen.
- expires, by default 60 minutes after it is issued. The machine checks the expiry against its own clock.
You only need a code while tamper protection is on. With protection off, the uninstall command runs without one.
Issuing a code in the console#
- Open the agent's Agent Details page.
- In the header, click the shield icon ("Issue a single-use uninstall code for this agent (needed only while tamper protection is on)"). It sits next to the revoke icon.
- Confirm with Issue code. The dialog warns that whoever holds the code can uninstall the agent on that machine until it expires.
- The code is shown once, with its expiry time. Copy it with the copy icon. When you close the dialog, it cannot be shown again. Issue a new one if you lose it.
Issuing a code requires the agents.revoke permission, which only the built-in
Admin role has. Without it, the shield icon is hidden. The icon is also hidden on a revoked
agent. Every issued code is audited. The code itself is never written to the audit log;
it is identified by its last four characters only.
Uninstalling a protected agent#
The sanctioned local uninstall is the uninstall command of the
HermesCLI, which every agent places next to itself. Run it from an elevated
(Administrator) prompt on Windows, or as root on Linux and macOS:
hermescli uninstall --code <CODE> [--purge]
hermescli is on the PATH on every OS (see HermesCLI on PATH).
You can also call the CLI by its full path:
| OS | Command |
|---|---|
| Windows (elevated prompt) | HermesCLI.exe uninstall --code <CODE>, run from the agent's install folder |
| Linux | sudo /opt/hermes/HermesCLI uninstall --code <CODE> |
| macOS | sudo "/Library/Application Support/Hermes/HermesCLI" uninstall --code <CODE> |
| Option | Meaning |
|---|---|
--code <CODE> (alias --uninstall-code) | The uninstall code issued by Athena. Required only while tamper protection is on. |
--purge | Also remove the configuration, data, certificates and logs. Without it, these are kept. |
The command checks whether the machine is protected by looking at the protections themselves. If they are in place, it removes them first, then removes the agent. Before it removes anything, it prints the list of what it will remove.
What is removed#
| OS | Without --purge | With --purge |
|---|---|---|
| Windows | The HermesAgent service and every file in the install folder except appsettings*.json. | Also the install folder itself and C:\ProgramData\Hermes (data, certificates and logs). |
| Linux | The hermes-agent systemd unit and its drop-in folder, and everything in /opt/hermes except appsettings*.json. | Also /opt/hermes and /var/lib/hermes (logs are inside it). |
| macOS | The com.athena.hermes-agent LaunchDaemon and the agent and CLI executables only. The install folder is also the data folder, so everything else in it stays. | Also /Library/Application Support/Hermes and /Library/Logs/Hermes. |
If the agent's Agent:DataPath is set in the install folder's appsettings.json, that
folder is used as the data folder. The hermescli PATH entry (Windows) or the
/usr/local/bin/hermescli link (Linux, macOS) is removed as well.
On Windows the removal runs as SYSTEM through a one-time scheduled task, because the protected service cannot be stopped by Administrators. The command returns straight away and prints "The service is being stopped and removed as SYSTEM; the files above are gone within ~15 seconds."
How Athena learns about it#
The command hands the event (authorised or refused) to the running agent service, and waits up to 5 seconds for
the agent to send it to Athena. It prints "Athena notified (…)" when the event was sent. If the agent
is not running or not connected, it prints "Athena not notified yet …" and continues. The event then
stays queued in the data folder unless --purge removes it. An uninstall on an unprotected machine
without a code is not a tamper event and is not reported.
Messages and exit codes#
Any non-zero exit code other than 1 means nothing was removed.
| Exit code | Message | Meaning |
|---|---|---|
0 | "Uninstall code accepted (server '…')." or "Tamper protection is off on this machine; no uninstall code is needed." | The uninstall went ahead. |
1 | "Failed: could not schedule the SYSTEM teardown …" (Windows) or "could not remove …" lines (Linux, macOS) | The removal could not be started (Windows, nothing was removed), or some items could not be removed (Linux, macOS). |
2 | "Run this from an elevated (Administrator) prompt." / "Run this as root (sudo)." | Not elevated. |
3 | "Tamper protection is on: an uninstall code is required. …" | No --code was given. |
4 | "The uninstall code has expired. Issue a new one." "The uninstall code was already used. Issue a new one." "The uninstall code was issued for a different agent." "The uninstall code is not signed by this agent's Athena CA." "The uninstall code is not valid." "Tamper protection is on but this agent holds no enrolment (agent id + CA certificate) to verify the code against." | The code was refused. |
An agent enrolled with more than one server accepts a code issued by any of them.
Alerts#
Tamper events raise alerts on the Alerts board, tied to the agent. They count in the agent's alert badge and can be filtered by agent like any other alert. One alert is kept per agent and condition: a repeat event updates the existing open alert (its count and last-seen time) instead of adding a new row.
| Alert | Severity | Raised when |
|---|---|---|
| Tamper detected | Critical | The agent reported protections removed, or a refused uninstall. |
| Uninstall code used | Warning | The agent accepted a valid uninstall code. |
| Agent removed unexpectedly | Critical, or Warning after a valid code | The agent went offline right after one of the events above and did not come back (see below). |
Agent removed unexpectedly is raised only for an agent that has just reported a tamper event or a code use, so a normal reboot or shutdown never raises it. It fires when the agent's last heartbeat falls within 15 minutes of the event and the agent has then been offline for at least 10 minutes. A machine that reboots and comes back within those 10 minutes does not raise it. After a valid code it is only a Warning, because a sanctioned uninstall looks the same. The watch for this alert is kept in memory, so if the Athena server restarts during that window, the alert is not raised. The tamper alert itself is not affected. Removing the agent from Athena also ends the watch.
Audit events#
These event types appear in the audit log:
| Event type | Severity | Written when |
|---|---|---|
AgentUninstallCodeIssued | Warning | A code was issued. Records who issued it, the agent, the expiry, and the code's last four characters. |
AgentUninstallCodeUsed | Warning | An agent reported that it accepted a valid code and is uninstalling. |
AgentTamperDetected | Critical | An agent reported a tamper attempt. The action column holds the kind of event (for example SelfProtectionRemoved or UninstallRefused), and the description holds the detail. |
AgentRemovedUnexpectedly | Critical, or Warning after a valid code | An agent went offline right after a tamper event or code use and did not come back. |
Configuration#
These keys go in the server's appsettings.json, or in environment variables using
__ for : (for example Tamper__UninstallCodeTtlMinutes=30). See
Configuration.
| Key | Default | Effect |
|---|---|---|
Agent:TamperProtectionEnabled | false | The fleet-wide switch. Normally set from Settings → Agents → Configuration. |
Tamper:UninstallCodeTtlMinutes | 60 | How long an issued code stays valid when no validity is given. Always kept between 5 and 1440 minutes. |
Tamper:RemovalWindowMinutes | 15 | For Agent removed unexpectedly: the agent's last heartbeat must fall within this many minutes after the event. |
Tamper:OfflineGraceMinutes | 10 | …and it must then stay offline this long before the alert is raised. |
Tamper:WatchIntervalSeconds | 60 | How often the server checks watched agents. Minimum 5. |
REST API#
Codes can also be issued over the REST API (see API Reference and
API Tokens). The response uses Athena's usual envelope, with the result in
data.
| Method & path | Permission | Description |
|---|---|---|
POST /api/agents/{id}/uninstall-code | agents.revoke | Issues a code for the agent. Optional query ttlMinutes (kept between 5 and 1440; default from Tamper:UninstallCodeTtlMinutes). 200 with data = { "agentId", "code", "expiresAt" }; 404 no such agent; 403 missing permission. |
curl -k -X POST -H "Authorization: Bearer <token>" \
"https://<server>:8443/api/agents/<agent-id>/uninstall-code?ttlMinutes=30"
PowerShell#
The Athena PowerShell module has New-AthenaAgentUninstallCode.
It asks for confirmation unless you pass -Force. The returned Code is sensitive.
| Parameter | Description |
|---|---|
-Id | The agent's id (mandatory; accepted from the pipeline by property name). |
-TtlMinutes | Validity in minutes, 5–1440. Omit it for the server default. |
-Force | Issue without confirmation. |
Get-AthenaAgent -Hostname "LAB-PC" | New-AthenaAgentUninstallCode -TtlMinutes 30
Troubleshooting#
| Symptom | What to check |
|---|---|
| No shield icon on Agent Details | Your role lacks agents.revoke (Admin only by default), or the agent is revoked. |
sc stop HermesAgent fails with "Access is denied" for an administrator | Expected while tamper protection is on. Use hermescli uninstall with a code, or turn protection off and wait for the agent to apply the change. |
| Uninstall says the code has expired, but it was just issued | The machine checks the expiry against its own clock. Check the machine's date and time, then issue a new code. |
| Uninstall says the code is for a different agent or not signed by this agent's CA | The code was issued for another machine, or by an Athena server this agent is not enrolled with. Issue the code from this machine's own Agent Details page. |
| "… this agent holds no enrolment …" | The agent's enrolment files are missing from its data folder. Reconnect the agent to Athena (it restores a missing agent id from the server on connect), or re-enrol it. |
| A refused or authorised uninstall did not appear in Athena | The agent was not running or not connected. The event is sent when the agent next connects, unless --purge removed it. |
| Tamper detected appeared, but nobody touched the machine | Open the alert or the AgentTamperDetected audit entry to see the event kind and detail. SelfProtectionRemoved means the agent found its protections changed during its self-check and put them back. |
Related#
- Agent CLI (HermesCLI): the local command-line tool that runs the uninstall.
- Remote Helper & HermesCLI Binaries: how the CLI reaches each agent.
- Retiring & Removing Agents: revoking and removing agents from Athena.
- Monitoring & Alerts: the Alerts board.
- Audit Event Types Reference: all audit events.
- Permission Model & Role Bundles: who holds
agents.revoke.