main

1. Overview

The main.cpp file is the RootGuard daemon entry point. It parses startup options, securely loads policy, configures console/JSON/automatic-response sinks, starts the eBPF engine, publishes runtime status/policy views for the GUI, polls events and integrity checks, and implements fail-safe SIGHUP policy reload behavior.

The file integrates the following components, APIs, or runtime concepts:

  • PolicySecurity / PolicyLoader
  • RootGuardEngine
  • ConsoleEventSink
  • JsonEventSink
  • MultiEventSink
  • AutomaticResponseSink
  • /run/bastionguard-rootguard.status
  • /run/bastionguard-rootguard.policy-view

2. Command-Line Options

bastionguard-rootguard [--policy FILE] [--audit|--enforce] [--allow-unsafe-policy] [--check-policy]

The default policy path is /etc/bastionguard/rootguard.conf. Audit/enforce options override only the in-memory mode. --check-policy validates and prints policy summary without starting BPF. --allow-unsafe-policy disables the root-ownership/non-writable policy requirement at the secure-read layer.


3. Secure Policy Loading

The daemon first calls PolicySecurity::read(), requiring secure ownership unless explicitly overridden, then parses with PolicyLoader::loadFromText().


4. Runtime Status File

writeRuntimeStatus() atomically publishes mode, metadata/global actions, effective automatic service blocking, and user-home blocking to /run/bastionguard-rootguard.status. Automatic service blocking is reported true only when policy enables it and the engine is in enforce mode with metadata blocking.


5. Runtime Policy View

writeRuntimePolicyView() atomically publishes a non-secret GUI-oriented projection containing user-home/global metadata/burst settings plus trusted applications, trusted/denied executables, protected rules, and ignored rules to /run/bastionguard-rootguard.policy-view.


6. Event Sink Composition

A MultiEventSink always includes console logging. When eventLog is nonempty, the daemon creates the parent directory, opens JsonEventSink, and adds it to the fan-out. AutomaticResponseSink then wraps the combined sink so service containment can emit both original and follow-up events through the same outputs.


7. Engine Startup and Loop

The engine starts with the active policy, then the daemon publishes status/policy views and logs counts/warnings. The main loop polls the BPF ring buffer with a 250 ms timeout and invokes periodic userspace integrity checks each iteration.


8. Signal Handling

SIGINT and SIGTERM set a stop flag. SIGHUP sets a reload flag. Signal handlers only write sig_atomic_t values, leaving policy parsing and BPF changes to normal process context.


9. Fail-Safe SIGHUP Reload

Before opening or parsing the replacement policy, the daemon calls engine.enterAuditMode(), disables automatic service blocking, and writes an audit runtime status. Therefore syntax, ownership, security, or path-resolution failure cannot accidentally leave the prior enforcement policy active while administrators believe a reload occurred.

On success, reloadPolicy() installs the new policy, automatic response is recalculated, and runtime files are refreshed. A changed event-log path is explicitly reported as requiring restart because the current JSON sink remains open on the original file.


10. Shutdown and Failure Cleanup

Normal shutdown and the top-level exception handler remove /run/bastionguard-rootguard.status. Fatal errors are printed and return EXIT_FAILURE.


11. Runtime and Security Considerations

  • Secure policy by default: root ownership and restrictive write permissions are required unless explicitly overridden.
  • Reload fails into audit: enforcement is dropped before reading the new policy.
  • Automatic containment coupling: service blocking is active only with effective protected metadata enforcement.
  • Atomic runtime files: status and policy-view projections are written through temporary files and rename.
  • Signal-safe handlers: signal handlers only set atomic flags.