Add systemd hardening options
Hardening Options Applied:
- ProtectSystem=strict: Limits write access to the entire filesystem, allowing only specific directories.
- ProtectHome=tmpfs: Isolates user home directories with a temporary filesystem.
-
PrivateTmp=true: Provides an isolated
/tmp
directory for the service. -
PrivateDevices=true: Restricts access to device files in
/dev
. - ProtectKernelTunables=true: Prevents access to kernel tunable files, reducing risk of kernel settings modification.
- ProtectKernelModules=true: Disallows loading/unloading of kernel modules.
- ProtectKernelLogs=true: Blocks access to kernel logs, protecting sensitive system data.
- ProtectControlGroups=true: Restricts control group (cgroup) manipulation.
- ProtectProc=ptraceable: Limits access to process information, allowing only self-inspection (ptrace).
- MemoryDenyWriteExecute=true: Blocks writable memory from being executable, enhancing memory safety.
- LockPersonality=true: Locks process personality to prevent modification of binary execution domain.
- RestrictRealtime=true: Prevents real-time scheduling, mitigating potential CPU abuse.
-
SystemCallFilter=~[...]: Denies potentially risky system calls, enhancing syscall-level security:
-
@memlock, @module, @mount, @cpu-emulation, @debug, @keyring, @chown, @obsolete, @reboot, @sandbox, @setuid, @swap, @sync, @timer: Denied with
EPERM
, restricting access to privileged or deprecated operations.
-
@memlock, @module, @mount, @cpu-emulation, @debug, @keyring, @chown, @obsolete, @reboot, @sandbox, @setuid, @swap, @sync, @timer: Denied with
This change was tested by running curl http://localhost:9100/metrics
before and after the changes and ensuring all metrics were still reported.
- Run
curl http://localhost:9100/metrics > before_hardening.txt
- Apply hardening changes
- Run
curl http://localhost:9100/metrics > after_hardening.txt
- python3 verify.py
The source code to verify.py is
import re
def extract_metric_names(file_path):
metric_names = set()
with open(file_path, 'r') as f:
for line in f:
# Match lines that define a TYPE or have a metric name before a space
match = re.match(r"# TYPE (\w+)", line) or re.match(r"^(\w+)", line)
if match:
metric_names.add(match.group(1))
return metric_names
# Load metric names from both "before" and "after" files
before_metrics = extract_metric_names('before_hardening.txt')
after_metrics = extract_metric_names('after_hardening.txt')
# Check for missing or extra metrics
missing_in_after = before_metrics - after_metrics
extra_in_after = after_metrics - before_metrics
if missing_in_after:
print("Metrics missing in after_metrics.txt:")
for metric in missing_in_after:
print(f" - {metric}")
if extra_in_after:
print("Extra metrics in after_metrics.txt:")
for metric in extra_in_after:
print(f" - {metric}")
if not missing_in_after and not extra_in_after:
print("All metrics are present in both files.")