# Tamper resistance

URL: https://shellz.dev/docs/security/tamper-resistance

> How shellz keeps recording when someone with access to a server tries to stop it, and how it reports attempts.

Anyone with root access can interfere with any software on their own machine, shellz included. What shellz guarantees is that interference is undone where possible, and always reported: shellz can't be disabled quietly.

## On a server

### The recorder service

The `shellz-recorder` systemd unit is set up so that:

- `systemctl stop shellz-recorder` is refused (`RefuseManualStop=yes`);
- if the process is killed or crashes, systemd restarts it after one second, with no limit on restarts, and shellz raises a `tamper` alert on the next start;
- the kernel's out-of-memory killer avoids it, and it doesn't produce core dumps;
- the process can't be inspected with `ptrace` by unprivileged users.

### Continuous repair

Every 15 seconds, and immediately when `/etc/passwd` changes, the agent checks and repairs:

- the shellz binary and session shell;
- the recorder unit file, and whether it's enabled and running;
- the login shells of recorded accounts;
- the SSH drop-in that turns off `~/.ssh/rc`;
- the SELinux policy module and file labels, where SELinux is enabled;
- on Docker hosts, the Docker socket and drop-ins (checked every second).

Each repair raises a `tamper-repaired` alert describing what was changed. shellz files are also marked immutable (`chattr +i`), so changing them first requires removing the flag.

### Sessions that avoid the recorder

A session auditor looks for login sessions on the server that didn't go through the session shell, for example a login using a service account with a shell set by hand. Unless the recording policy is fail-open, it ends them and raises `unrecorded-session-terminated`. Under fail-open it raises `unrecorded-session` and lets them continue.

### Removal

`shellz uninstall` only works after an owner or admin has approved removal of that server in the dashboard, which sends the server a signed policy that allows it. Without that, the command is refused and an `uninstall-refused` alert names the account that tried. See [Updates and removal](https://shellz.dev/docs/guides/updates-and-removal.md#removing-shellz).

### Going silent

A server that stops sending heartbeats, for example because it was shut down, cut off from the network, or shellz was forcibly removed, is marked offline after 90 seconds and raises a `heartbeat-missing` alert. When it comes back, `heartbeat-recovered` is raised.

## Inside containers

In containers, shellz runs as PID 1:

- `kill -9 1` from inside the container has no effect, because the kernel doesn't deliver unhandled signals to PID 1 from inside its own namespace.
- The exec watcher finds sessions that bypass the shell shim within 500 ms. Under fail-closed, unrecorded sessions with a terminal are killed and raise `unrecorded-container-exec`.
- Stopping the container stops the workload too, and is reported as **stopped** with its reason and exit code. A container that disappears without a clean stop is shown as **lost** after 3 minutes.

## Recordings

Once uploaded, recordings are protected by signatures, the hash chain and write-once storage with a 365-day retention lock. See [Encryption](https://shellz.dev/docs/security/encryption.md#the-hash-chain).

A server also can't rewrite its own history: shellz refuses a segment that conflicts with one it already stored, and marks the session as tampered.

## Policies and keys

Servers only apply policies and org keys signed by your org, with a revision higher than the current one. Attempts to replay an older policy or substitute a key are refused and raise `policy-rejected` or `org-key-rejected`. With [Perfect Secrecy](https://shellz.dev/docs/security/perfect-secrecy.md), only your team's browsers can sign them.

## Limits

- Root can still interfere, for example by editing memory, loading a kernel module, or cutting the network. shellz is designed so that these leave a trace: a gap in a recording, a tamper alert, or a silent server.
- In an unprivileged LXC container, `chattr +i` isn't available. shellz still repairs changes every 15 seconds and raises a single `immutable-unsupported` warning.
- In containers, someone with access to the node can enter a container's namespaces without going through `kubectl exec` or `docker exec`. Record node logins with a [host install](https://shellz.dev/docs/install/linux.md) where you manage the nodes.

See [Alerts](https://shellz.dev/docs/guides/alerts.md) for every alert and its severity.
