Wizard Setup Core Component – Privileged System Configuration Orchestrator

1. Component purpose

This file is a core component of the BastionGuard Wizard: it implements the post-install setup logic that requires administrative privileges (root) and must run reliably across multiple Linux distributions.

In practice, this module is responsible for:

  • executing privileged steps (DNS override, firewall rules, certificates, etc.) with a single authentication prompt (pkexec/sudo);
  • handling Polkit compatibility (legacy and new syntax);
  • capturing output and exit codes for diagnostics;
  • providing multiple fallbacks for DNS configuration (resolvectl, nmcli, direct file write);
  • opening the required firewall ports (firewalld/ufw/nftables/iptables);
  • generating and installing a local CA plus certificates for the “local warning server”;
  • installing the phishing blacklist and the Native Messaging Host integration.

2. High-level architecture

The design revolves around two guiding principles:

  • Privileged batching: instead of triggering many Polkit prompts, the wizard builds a temporary script and executes it as root only once.
  • Best-effort multi-backend support: it detects what tools are available on the system and applies the most appropriate configuration, with safe fallbacks.

The code is organized into:

  • namespace { ... } for internal helpers (not exported)
  • namespace BastionGuard::wizard for the public API used by the Wizard UI

3. Privileged execution strategy

3.1 run_cmd_capture

run_cmd_capture(cmd, out, exit_code):

  • executes a command via popen() and captures stdout;
  • normalizes the exit status using WIFEXITED/WEXITSTATUS and WIFSIGNALED;
  • returns true only when the exit code is 0.

3.2 elev_cmd

elev_cmd(inner, action_id) builds an elevated command line using:

  • pkexec --disable-internal-agent --action-id ... when supported (legacy Polkit);
  • pkexec ... when --action-id is not supported (Polkit 124+ behavior);
  • sudo ... as a fallback when pkexec is unavailable.

This preserves compatibility across distributions and Polkit versions, while keeping behavior consistent.

3.3 write_temp_script_and_elevate

This helper:

  • creates a secure temporary file in /tmp using mkstemp();
  • writes the script contents, marks it executable, and runs it elevated (pkexec/sudo);
  • captures output, prints a detailed debug block (command, exit code, output);
  • removes the temporary file on completion.

This is primarily used for one-off privileged operations where batching is not yet aggregated.


4. Combined privileged execution (single authentication)

4.1 run_all_elevated_steps

run_all_elevated_steps(commands, out_log) is the preferred mechanism for wizard execution. It:

  • writes a single bash script with set -euo pipefail;
  • adds each command line as provided (commands must already be safe/sanitized);
  • executes the script using pkexec/sudo, prompting the user only once;
  • returns success only when the script completes with exit code 0.

This approach ensures the wizard remains user-friendly (one authentication prompt) and reduces partial-failure states.


5. DNS override (resolver setup)

The module supports multiple strategies, applied in order:

  1. systemd-resolved / resolvectl:
    • detects resolvectl or an active systemd-resolved service;
    • tries to identify the active link (via ip route get 1.1.1.1);
    • sets DNS to 127.0.0.1 on the selected link.
  2. NetworkManager / nmcli:
    • detects nmcli;
    • finds the active connection;
    • sets ipv4.dns to 127.0.0.1 and enables ipv4.ignore-auto-dns.
  3. Direct file override:
    • writes /etc/resolv.conf with a BastionGuard override (nameserver 127.0.0.1);
    • keeps a backup at /etc/resolv.conf.BastionGuard.bak.

This guarantees that DNS can be forced locally even on minimal systems without systemd-resolved or NetworkManager.


6. Firewall port setup

The wizard must ensure the required ports are reachable. It attempts multiple backends:

  • firewalld: firewall-cmd --permanent --add-port=... + reload
  • ufw: ufw allow ...
  • nftables: creates a dedicated table/chain and adds accept rules
  • iptables: inserts INPUT accept rules (best-effort)

It also attempts to:

  • load nf_nat (best-effort) and persist it via /etc/modules-load.d/BastionGuard.conf;
  • add the effective user to groups such as adm and systemd-journal so the UI can read logs;
  • restart BastionGuard-phishing-scanner.service if the unit exists.

7. Certificates and local CA

The wizard can generate:

  • a BastionGuard local CA (BastionGuard-ca.key.pem + BastionGuard-ca.crt.pem);
  • a server key/certificate for the warning server (local-warning.key.pem + local-warning.crt.pem), including SAN: DNS:localhost and IP:127.0.0.2.

Then it attempts to install the CA into the system trust store using distribution-specific tools:

  • update-ca-certificates (Debian/Ubuntu/Gentoo)
  • trust anchor / p11-kit (Fedora/openSUSE and others)
  • update-ca-trust (RHEL/CentOS variants)
  • Arch-style anchors path + trust extract compatibility

If none succeed, it still copies the CA into common directories and reports a warning.


8. Native Messaging Host installation

The wizard generates JSON manifests for Chromium-based browsers and Firefox-based browsers, writing them into:

  • user-level XDG config locations (Chrome/Chromium/Brave/Edge)
  • user-level Firefox locations (Firefox, LibreWolf)
  • system-wide locations when possible (best-effort)

This enables the BastionGuard browser extension to communicate with the native host binary.


9. Orchestration entry point

run_wizard_setup(options) is the main orchestrator:

  • if options is empty, it runs the full setup;
  • otherwise it runs only the requested steps (e.g., resolv, firewall, certs, blacklist, native-host);
  • builds a single privileged script (cmds) and executes it once via run_all_elevated_steps.

This structure gives the Wizard UI the ability to re-run single steps, or perform a full guided setup.