SELinux policy for confining the LOTA agent.
| Type | Description |
|---|---|
lota_agent_t |
LOTA agent process domain |
lota_agent_exec_t |
Agent binary executable |
lota_var_t |
Runtime data (AIK store, state) |
lota_etc_t |
Configuration files |
lota_bpf_t |
eBPF object files |
lota_log_t |
Log files |
lota_port_t |
Network port for attestation |
lota_tpm_device_t |
/dev/tpm[0-9]* and /dev/tpmrm[0-9]*
(relabelled by configs/udev/99-lota-tpm.rules) |
TPM 2.0 PC Client Platform TPM Profile p3.3 keeps PCR14 in the
OS-Loader-writable range (PCR8–15, Locality 0, auth-free TPM2_PCR_Extend).
The TPM exposes no on-chip mechanism that could gate the extend, so a
local-root attacker with /dev/tpmrm0 access can extend PCR14 between cold
boot and the agent's first tpm_extend_boot_commitment() call. The defence
has to live outside the TPM. LOTA stacks four layers:
- udev (
configs/udev/99-lota-tpm.rules) sets0600 root:rooton/dev/tpm*at device-add time and assigns thelota_tpm_device_tSELinux label. - SELinux (this module) is the only policy that allows
rw_chr_file_permsonlota_tpm_device_t, and only tolota_agent_t. Stock refpolicy interfaces (dev_rw_tpm(), ...) bind to the generictpm_device_tlabel and no longer apply once the udev rule has run — including forunconfined_t. - systemd ordering (
systemd/lota-agent.serviceBefore=multi-user.target getty.target) narrows the window in which any login-capable target could spawn a tool that races the agent's first extend. - Persistent clock-state attribution (
src/agent/tpm.c,/var/lib/lota/clock_state.dat) detects every PCR14 mismatch post-hoc and attributes it to one of cold-boot tamper, mid-session tamper, or live binary upgrade so the operator receives an actionable journal entry.
Operators MUST run SELinux in enforcing mode for the MAC layer to bind. A permissive system trusts every uid-0 process equally and LOTA cannot offer protection against that operator decision; the clock-state attribution layer still records the tamper but cannot prevent it.
The agent requires elevated privileges for hardware attestation:
- TPM Access: Read PCRs, generate quotes, manage AIK
- BPF Operations: Load LSM programs for execution monitoring
- System State: Read kernel, firmware, and security status
- Network: Connect to verifier servers for attestation
# Fedora/RHEL
sudo dnf install selinux-policy-devel policycoreutils-python-utils
# Verify SELinux is enforcing
getenforce # should return "Enforcing"cd selinux/
# Build policy modules
make
# Install (requires root)
sudo make install
# Verify installation
sudo make verifyAfter installing the agent binary and BPF object:
# Apply correct labels
sudo restorecon -Rv /usr/bin/lota-agent
sudo restorecon -Rv /usr/lib/lota
sudo restorecon -Rv /etc/lota
sudo restorecon -Rv /var/lib/lotaThe module loads on both the Fedora and the RHEL family. Types that exist
only on newer kernels are guarded so a base policy that lacks them still
links the module: pidfs_t (pidfs, kernel 6.9+) is referenced inside an
optional_policy block, so on the RHEL family (el9, kernel 5.14, whose
base policy has no pidfs_t) the block is dropped and the rest of the
module loads. Build the module against the target distribution's
selinux-policy-devel so the compiled lota.pp matches its policy.
Control policy behavior with SELinux booleans:
| Boolean | Default | Description |
|---|---|---|
lota_network_attest |
on | Allow network attestation to verifiers |
lota_bpf_load |
on | Allow loading eBPF LSM programs |
lota_module_enforce |
off | Allow kernel module enforcement |
lota_steam_attest |
on | Steam attestation integration |
lota_wine_attest |
on | Wine/Proton attestation integration |
lota_anticheat_extended |
on | Extended anti-cheat access |
# Disable network attestation
sudo setsebool -P lota_network_attest off
# Enable module enforcement
sudo setsebool -P lota_module_enforce on
# Show current values
getsebool -a | grep lotaSteam and games launched through Steam can query LOTA attestation status:
# Verify Steam can access LOTA
sudo sesearch -A -s steam_t -t lota_var_tWindows games running via Proton have access to LOTA for anti-cheat verification:
# Check Wine access
sudo sesearch -A -s wine_t -t lota_agent_tEAC, BattlEye, and other anti-cheat systems running in Wine can:
- Query current attestation status
- Request fresh attestation
- Verify system integrity
| Path | Context |
|---|---|
/usr/bin/lota-agent |
lota_agent_exec_t |
/usr/lib/lota/ |
lota_bpf_t |
/etc/lota/ |
lota_etc_t |
/var/lib/lota/ |
lota_var_t |
/var/log/lota/ |
lota_log_t |
/run/lota/ |
lota_var_t |
/dev/tpm[0-9]* |
lota_tpm_device_t |
/dev/tpmrm[0-9]* |
lota_tpm_device_t |
To add custom file contexts:
# example: Agent in non-standard location
sudo semanage fcontext -a -t lota_agent_exec_t "/opt/lota/bin/lota-agent"
sudo restorecon -v /opt/lota/bin/lota-agent# Recent denials
sudo ausearch -m avc -ts recent | grep lota
# Use make target
sudo make audit# From audit log
sudo make suggest
# Or manually
sudo ausearch -m avc | audit2allow -m lota_local# Check device labels
ls -Z /dev/tpm*
# Expected after install: lota_tpm_device_t. If the label is still
# tpm_device_t the LOTA udev rule never ran:
sudo install -m 644 ../configs/udev/99-lota-tpm.rules \
/usr/lib/udev/rules.d/99-lota-tpm.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --subsystem-match=tpm
ls -Z /dev/tpm*The agent reads security.selinux on /dev/tpmrm0 and /dev/tpm0 at
the hardening gate. If the udev relabel did not run (broken symlink in
/etc/udev/rules.d, missing /usr/lib/udev/rules.d/99-lota-tpm.rules, or
a host kernel that labelled the device before the rule was installed), any
other root SELinux domain (tpm2-abrmd_t, generic init_t, ...) still has
rw access to the TPM resource manager. That domain can flood the chip and
exhaust TPM sessions, the NV rate budget, or the DA counter; LOTA stays
fail-closed (LOTA_ERR_TPM_LOCKED -> LOTA_STATUS_TPM_LOCKOUT), but
attestation availability drops. Re-run the udev install steps above; the agent
will start once the label is lota_tpm_device_t.
When LOTA_STATUS_TPM_LOCKOUT shows up on lota-agent status or on the
D-Bus StatusChanged signal:
# Confirm the TPM is currently in dictionary-attack lockout.
sudo tpm2_getcap properties-variable | grep -E "lockoutCounter|lockoutInterval"
# Identify other processes holding the TPM resource manager fd.
sudo lsof /dev/tpmrm0 /dev/tpm0
# Inspect recent denials from non-LOTA domains.
sudo ausearch -ts recent -m AVC -c tpm2-abrmd
# Reset the lockout (requires lockoutAuth, typically empty on
# stock distributions; otherwise consult the platform admin):
sudo tpm2_dictionarylockout --setup-parameters \
--max-tries=32 --recovery-time=120 --lockout-recovery=120
sudo tpm2_dictionarylockout --clear-lockoutThe agent re-publishes the status flag on the next attestation cycle. If the
lockout returns on a host where ls -Z /dev/tpm* shows
lota_tpm_device_t, the offender is inside the lota_agent_t policy
boundary; check /var/log/lota for repeated LOTA_ERR_TPM_LOCKED retries
before escalating to hardware.
# Verify tunable is enabled
getsebool lota_bpf_load
# Check BPF capabilities
sudo sesearch -A -s lota_agent_t -c bpf# Verify socket label
ls -Z /run/lota/lota.sock
# Check Steam access
sudo sesearch -A -s steam_t -t lota_var_t -c sock_file# Check tunable
getsebool lota_network_attest
# Verify port definition
sudo semanage port -l | grep lotaThe policy allows lota_agent_t to reach lota_port_t, and a loadable
module cannot carry port contexts, so the type is assigned at bring-up:
lota-install labels the CA and verifier ports it was given. An endpoint
added later has to be labelled too, or the agent is refused at connect and the
operator sees a connection error against a server that is running:
sudo semanage port -a -t lota_port_t -p tcp 8473Port 8443 is an exception that hides the rule: the base policy already labels
it http_port_t, which lota_agent_t may also reach, so a deployment on
the default port works without any of this.
Other SELinux policies can use LOTA interfaces:
# Allow querying attestation status
lota_query_status(myapp_t)
# Full gaming client access
lota_gaming_client(myapp_t)# Extended anti-cheat access
lota_anticheat_client(myanticheat_t)# Administrative access
lota_admin(myadmin_t, myadmin_r)# Build without installing
make
# Check syntax
make check
# Reload after changes
sudo make reload# Set LOTA domain to permissive (logs but allows)
sudo semanage permissive -a lota_agent_t
# Test functionality
lota-agent --test-tpm
# Review denials
sudo ausearch -m avc -ts recent | grep lota
# Return to enforcing
sudo semanage permissive -d lota_agent_tWhen modifying the policy:
- Test in permissive mode first
- Use
audit2allowto identify required permissions - Add minimal permissions with comments explaining why
- Test with SELinux enforcing before submitting
GPL-2.0-only