This repository contains the safesetid script — a policy manager for the SafeSetID Linux Security Module (LSM).
The script writes base policies to the SafeSetID sysfs interface, detects newly created users/UIDs, and can restrict them. It features a robust self-healing model (wrapper, hardened backups, tmpfs runtime store) to ensure the integrity of the system.
Important: This is a Bash script and requires kernel support for SafeSetID exposed at /sys/kernel/security/safesetid. If this directory is missing, no kernel policies will be modified.
Before using this tool, it's helpful to understand what SafeSetID provides compared to other security mechanisms like systemd's NoNewPrivileges=yes.
In short:
NoNewPrivileges=yesis a hammer. It's a one-way switch that prevents a process and all of its children from gaining new privileges (e.g., from setuid binaries). It's powerful but coarse.- SafeSetID is a scalpel. It's a rule-based LSM that lets you define exactly which UID/GID identity transitions are allowed. It doesn’t block all privilege transitions, only those not explicitly on the allow-list.
| Feature | NoNewPrivileges=yes (systemd) |
SafeSetID (Kernel LSM) |
|---|---|---|
| Goal | Prevent any future privilege gains in a process tree. | Control identity transitions by explicitly allow-listing UID/GID changes. |
| Granularity | Coarse (global on/off per process tree). | Fine-grained (per-transition rules). |
| Flexibility | Low—can break legitimate workflows. | High—allows specific transitions while blocking unexpected ones. |
| Use Case | Long-running daemons that never need privilege changes. | Tools or workflows that require specific, controlled transitions. |
| Operational Complexity | Easy to enable, but blunt for complex apps. | Requires policy management (which this script automates). |
| Recovery / Self-Healing | None. | Supported by this project (wrapper, backups, tmpfs, monitors). |
This safesetid script acts as a policy manager, making the powerful SafeSetID kernel feature practical and automated.
- Oneshoot and path-triggered units:
safesetid.serviceisType=oneshot. It may appearinactiveafter successful execution (Result=success).safesetid-monitor.serviceandsafesetid-file-monitor.serviceare triggered by their respective.pathunits and are typicallystatic/inactiveuntil a file change occurs.
- Self-healing:
- Hardened backups are kept in
/var/lib/safesetid(optionally immutable). - A runtime trust anchor (
tmpfsat/var/tmp/safesetid) holds authoritative copies; monitors and verify repair deviations.
- Hardened backups are kept in
- Internal commands (protected):
- Internal maintenance commands (prefixed with
_andcheck_function) cannot be run directly from a terminal and are executed only via the wrapper or systemd.
- Internal maintenance commands (prefixed with
- DoS mitigation:
- High-frequency events are tracked; a mitigator may be scheduled to reduce load and perform conservative repairs.
- Locks:
- Global locking avoids concurrent modifications; if
flockis unavailable, the script falls back to best-effort temporary locks.
- Global locking avoids concurrent modifications; if
The installer is idempotent and sets up all components, backups, hashes, and systemd units.
sudo ./safesetid install_scriptDuring installation a 32-bit uninstall token (hex, 8 chars) is generated and stored at /var/lib/safesetid/.uninstall_token. The installer logs the token once — store it securely.
Edit and apply UID/GID policies. Must be invoked via sudo/doas (not from a direct root shell).
sudo safesetid configure_uid
sudo safesetid configure_gidRun integrity checks and self-healing. Typically triggered automatically by .path units.
sudo safesetid verifyUninstall requires:
- Invocation via
sudo/doasfrom a regular user. - The uninstall token as the second argument (generated at install, stored in
/var/lib/safesetid/.uninstall_token). - Interactive confirmation.
sudo safesetid uninstall_script <UNINSTALL_TOKEN>var-tmp-safesetid.mount: mounts the runtimetmpfs.safesetid.service: oneshot boot-time application of policies.safesetid-wrapper-restore.path+.service: restores the wrapper if changed/deleted.safesetid-monitor.path+.service: watches/etc/passwdand runs a UID check.safesetid-file-monitor.path+.service: watches critical script/config files and triggers verification.
Note: Path-triggered services are expected to be inactive/static and only run on file events. Oneshoot safesetid.service is inactive after successful completion.
A non-destructive smoke test is provided under Test/smoke_test. It:
- Runs syntax and ShellCheck.
- Invokes
verifyand validates datastore integrity and wrapper backups. - Tests lock behavior by running verify and check_function concurrently (check_function is blocked from terminal, as designed).
- Checks all systemd units, treating oneshot/static/path-triggered units correctly.
- Optionally triggers
.pathunits viatouch /etc/passwdandtouch /etc/groupand prints recent journal entries.
Run:
sudo ./Test/smoke_test- View unit status and logs:
sudo systemctl status safesetid-*.path safesetid-*.service var-tmp-safesetid.mount
sudo journalctl -u safesetid.service -n 50 --no-pager
sudo journalctl -u safesetid-file-monitor.service -n 50 --no-pager- If
/sys/kernel/security/safesetidis missing, kernel support is not available; the script will skip kernel policy writes.
- This is not a high-security product; it’s a practical policy manager with bonus self-protection.
- Self-healing and immutable backups raise the bar against accidental changes and simple tampering. A local root attacker can still disable protections.
- For stronger guarantees, consider Secure Boot, IMA/EVM, and remote/WORM logging.
MIT License