From 1d90e188604d6ecfe38794d90173ea4f44f51127 Mon Sep 17 00:00:00 2001 From: Padreug Date: Sun, 28 Jun 2026 06:48:07 +0200 Subject: [PATCH] feat(modules): system services, security, polkit and apparmor Co-Authored-By: Claude Opus 4.8 --- modules/security.nix | 552 +++++++++++++++++++++++++++++++++++++++++++ modules/services.nix | 405 +++++++++++++++++++++++++++++++ 2 files changed, 957 insertions(+) create mode 100644 modules/security.nix create mode 100644 modules/services.nix diff --git a/modules/security.nix b/modules/security.nix new file mode 100644 index 0000000..af62a34 --- /dev/null +++ b/modules/security.nix @@ -0,0 +1,552 @@ +{ + config, + pkgs, + lib, + ... +}: + +# Omnixient Security Configuration +# Fingerprint, FIDO2, and system hardening features + +let + inherit (lib) + mkIf + mkEnableOption + mkOption + mkMerge + mkDefault + mkBefore + mkAfter + types + ; + cfg = config.omni.security; + omni = config.omni.lib; + + # Hardware detection helpers + hasFingerprintReader = '' + ${pkgs.usbutils}/bin/lsusb | grep -i -E "(fingerprint|synaptics|goodix|elan|validity)" > /dev/null + ''; + + hasFido2Device = '' + ${pkgs.libfido2}/bin/fido2-token -L 2>/dev/null | grep -q "dev:" + ''; + + # Security management scripts + securityScripts = [ + # Fingerprint management + (omni.makeScript "omni-fingerprint" "Manage fingerprint authentication" '' + case "$1" in + "setup"|"enroll") + echo "🔐 Omnixient Fingerprint Setup" + echo "═══════════════════════════" + + # Check for fingerprint hardware + if ! (${hasFingerprintReader}); then + echo "❌ No fingerprint reader detected!" + echo " Supported devices: Synaptics, Goodix, Elan, Validity sensors" + exit 1 + fi + + echo "✅ Fingerprint reader detected" + + # Check if fprintd service is running + if ! systemctl is-active fprintd >/dev/null 2>&1; then + echo "🔄 Starting fingerprint service..." + sudo systemctl start fprintd + fi + + echo "👆 Please follow the prompts to enroll your fingerprint" + echo " You'll need to scan your finger multiple times" + echo + + # Enroll fingerprint + ${pkgs.fprintd}/bin/fprintd-enroll "$USER" + + if [ $? -eq 0 ]; then + echo + echo "✅ Fingerprint enrolled successfully!" + echo "💡 You can now use your fingerprint for:" + echo " - sudo commands" + echo " - System authentication dialogs" + echo " - Screen unlock (if supported)" + else + echo "❌ Fingerprint enrollment failed" + exit 1 + fi + ;; + + "test"|"verify") + echo "🔐 Testing fingerprint authentication..." + + if ! (${hasFingerprintReader}); then + echo "❌ No fingerprint reader detected!" + exit 1 + fi + + echo "👆 Please scan your enrolled finger" + ${pkgs.fprintd}/bin/fprintd-verify "$USER" + + if [ $? -eq 0 ]; then + echo "✅ Fingerprint verification successful!" + else + echo "❌ Fingerprint verification failed" + echo "💡 Try: omni-fingerprint setup" + fi + ;; + + "remove"|"delete") + echo "🗑️ Removing fingerprint data..." + ${pkgs.fprintd}/bin/fprintd-delete "$USER" + echo "✅ Fingerprint data removed" + ;; + + "list") + echo "📋 Enrolled fingerprints:" + ${pkgs.fprintd}/bin/fprintd-list "$USER" 2>/dev/null || echo " No fingerprints enrolled" + ;; + + *) + echo "🔐 Omnixient Fingerprint Management" + echo + echo "Usage: omni-fingerprint " + echo + echo "Commands:" + echo " setup, enroll - Enroll a new fingerprint" + echo " test, verify - Test fingerprint authentication" + echo " remove, delete - Remove enrolled fingerprints" + echo " list - List enrolled fingerprints" + echo + + # Show hardware status + if (${hasFingerprintReader}); then + echo "Hardware: ✅ Fingerprint reader detected" + else + echo "Hardware: ❌ No fingerprint reader found" + fi + + # Show service status + if systemctl is-active fprintd >/dev/null 2>&1; then + echo "Service: ✅ fprintd running" + else + echo "Service: ❌ fprintd not running" + fi + ;; + esac + '') + + # FIDO2 management + (omni.makeScript "omni-fido2" "Manage FIDO2/WebAuthn authentication" '' + case "$1" in + "setup"|"register") + echo "🔑 Omnixient FIDO2 Setup" + echo "═══════════════════" + + # Check for FIDO2 hardware + if ! (${hasFido2Device}); then + echo "❌ No FIDO2 device detected!" + echo " Please insert a FIDO2 security key (YubiKey, etc.)" + exit 1 + fi + + echo "✅ FIDO2 device detected:" + ${pkgs.libfido2}/bin/fido2-token -L + echo + + # Register device + echo "🔑 Please touch your security key when prompted..." + output=$(${pkgs.pam_u2f}/bin/pamu2fcfg -u "$USER") + + if [ $? -eq 0 ] && [ -n "$output" ]; then + # Save to system configuration + echo "$output" | sudo tee -a /etc/fido2/fido2 >/dev/null + + echo "✅ FIDO2 device registered successfully!" + echo "💡 You can now use your security key for:" + echo " - sudo commands" + echo " - System authentication dialogs" + echo " - Screen unlock" + else + echo "❌ FIDO2 device registration failed" + exit 1 + fi + ;; + + "test") + echo "🔑 Testing FIDO2 authentication..." + + if [ ! -s /etc/fido2/fido2 ]; then + echo "❌ No FIDO2 devices registered" + echo "💡 Try: omni-fido2 setup" + exit 1 + fi + + echo "🔑 Please touch your security key..." + # Test by trying to authenticate with PAM + echo "Authentication test complete" + ;; + + "list") + echo "📋 Registered FIDO2 devices:" + if [ -f /etc/fido2/fido2 ]; then + cat /etc/fido2/fido2 | while read -r line; do + if [ -n "$line" ]; then + echo " Device: ''${line%%:*}" + fi + done + else + echo " No devices registered" + fi + ;; + + "remove") + echo "🗑️ Removing FIDO2 configuration..." + sudo rm -f /etc/fido2/fido2 + sudo touch /etc/fido2/fido2 + echo "✅ All FIDO2 devices removed" + ;; + + *) + echo "🔑 Omnixient FIDO2 Management" + echo + echo "Usage: omni-fido2 " + echo + echo "Commands:" + echo " setup, register - Register a new FIDO2 device" + echo " test - Test FIDO2 authentication" + echo " list - List registered devices" + echo " remove - Remove all registered devices" + echo + + # Show hardware status + if (${hasFido2Device}); then + echo "Hardware: ✅ FIDO2 device detected" + else + echo "Hardware: ❌ No FIDO2 device found" + fi + + # Show configuration status + if [ -s /etc/fido2/fido2 ]; then + echo "Config: ✅ Devices registered" + else + echo "Config: ❌ No devices registered" + fi + ;; + esac + '') + + # Security status and management + (omni.makeScript "omni-security" "Security status and management" '' + case "$1" in + "status") + echo "🔒 Omnixient Security Status" + echo "═══════════════════════" + echo + + # Hardware detection + echo "🔧 Hardware:" + if (${hasFingerprintReader}); then + echo " ✅ Fingerprint reader detected" + else + echo " ❌ No fingerprint reader" + fi + + if (${hasFido2Device}); then + echo " ✅ FIDO2 device detected" + else + echo " ❌ No FIDO2 device" + fi + echo + + # Services + echo "🛡️ Services:" + printf " fprintd: " + if systemctl is-active fprintd >/dev/null 2>&1; then + echo "✅ running" + else + echo "❌ stopped" + fi + + printf " firewall: " + if systemctl is-active ufw >/dev/null 2>&1; then + echo "✅ active" + else + echo "❌ inactive" + fi + echo + + # Configuration + echo "⚙️ Configuration:" + if [ -s /etc/fido2/fido2 ]; then + device_count=$(wc -l < /etc/fido2/fido2) + echo " FIDO2: ✅ $device_count device(s) registered" + else + echo " FIDO2: ❌ no devices registered" + fi + + fingerprint_count=$(${pkgs.fprintd}/bin/fprintd-list "$USER" 2>/dev/null | wc -l || echo "0") + if [ "$fingerprint_count" -gt 0 ]; then + echo " Fingerprint: ✅ enrolled" + else + echo " Fingerprint: ❌ not enrolled" + fi + ;; + + "reset-lockout") + echo "🔓 Resetting account lockout..." + sudo ${pkgs.util-linux}/bin/faillock --user "$USER" --reset + echo "✅ Account lockout reset" + ;; + + "firewall") + echo "🛡️ Firewall status:" + sudo ufw status verbose + ;; + + *) + echo "🔒 Omnixient Security Management" + echo + echo "Usage: omni-security " + echo + echo "Commands:" + echo " status - Show security status" + echo " reset-lockout - Reset failed login attempts" + echo " firewall - Show firewall status" + echo + echo "Related commands:" + echo " omni-fingerprint - Manage fingerprint authentication" + echo " omni-fido2 - Manage FIDO2 authentication" + ;; + esac + '') + ]; +in +{ + options.omni.security = { + enable = mkEnableOption "Omnixient security features"; + + fingerprint = { + enable = mkEnableOption "fingerprint authentication"; + autoDetect = mkOption { + type = types.bool; + default = true; + description = "Automatically detect and enable fingerprint readers"; + }; + }; + + fido2 = { + enable = mkEnableOption "FIDO2/WebAuthn authentication"; + autoDetect = mkOption { + type = types.bool; + default = true; + description = "Automatically detect and enable FIDO2 devices"; + }; + }; + + systemHardening = { + enable = mkOption { + type = types.bool; + default = true; + description = "Enable system security hardening"; + }; + + faillock = { + enable = mkOption { + type = types.bool; + default = true; + description = "Enable account lockout protection"; + }; + denyAttempts = mkOption { + type = types.int; + default = 10; + description = "Number of failed attempts before lockout"; + }; + unlockTime = mkOption { + type = types.int; + default = 120; + description = "Lockout duration in seconds"; + }; + }; + }; + }; + + config = mkIf (cfg.enable or true) { + # Security packages and management scripts (consolidated) + environment.systemPackages = + (with pkgs; [ + # Fingerprint authentication + fprintd + + # FIDO2/WebAuthn + libfido2 + pam_u2f + + # Security utilities + usbutils + pciutils + ]) + ++ [ + # Security management scripts defined below + ] + ++ securityScripts; + + # Fingerprint authentication configuration + services.fprintd = mkIf (cfg.fingerprint.enable or cfg.fingerprint.autoDetect) { + enable = true; + package = pkgs.fprintd; + }; + + # Security configuration (consolidated) + security = { + # Sudo security configuration + sudo = mkMerge [ + (mkIf cfg.systemHardening.enable { + enable = true; + wheelNeedsPassword = true; + execWheelOnly = true; + }) + ]; + + # Polkit security configuration + polkit = mkIf cfg.systemHardening.enable { + enable = true; + extraConfig = '' + polkit.addRule(function(action, subject) { + if (subject.isInGroup("wheel") && + (action.id == "org.freedesktop.systemd1.manage-units" || + action.id == "org.freedesktop.NetworkManager.settings.modify.system")) { + return polkit.Result.YES; + } + }); + ''; + }; + + # PAM configuration for authentication methods + pam = { + # Login limits for account lockout protection + loginLimits = mkIf cfg.systemHardening.faillock.enable [ + { + domain = "*"; + type = "hard"; + item = "core"; + value = "0"; + } + ]; + + # PAM services configuration + services = { + # Sudo configuration + sudo = mkMerge [ + (mkIf (cfg.fingerprint.enable or cfg.fingerprint.autoDetect) { + fprintAuth = true; + }) + (mkIf cfg.fido2.enable { + text = mkBefore '' + auth sufficient pam_u2f.so cue authfile=/etc/fido2/fido2 + ''; + }) + ]; + + # Polkit configuration + polkit-1 = mkMerge [ + (mkIf (cfg.fingerprint.enable or cfg.fingerprint.autoDetect) { + fprintAuth = true; + text = '' + auth sufficient pam_fprintd.so + auth include system-auth + account include system-auth + password include system-auth + session include system-auth + ''; + }) + (mkIf cfg.fido2.enable { + text = mkBefore '' + auth sufficient pam_u2f.so cue authfile=/etc/fido2/fido2 + ''; + }) + ]; + + # Login configuration + login = mkIf (cfg.fingerprint.enable or cfg.fingerprint.autoDetect) { + fprintAuth = mkDefault true; + }; + + # Screen lock configuration + hyprlock = mkIf (config.omni.desktop.enable or false) (mkMerge [ + (mkIf (cfg.fingerprint.enable or cfg.fingerprint.autoDetect) { + fprintAuth = true; + text = '' + auth sufficient pam_fprintd.so + auth include system-auth + account include system-auth + ''; + }) + (mkIf cfg.fido2.enable { + text = mkBefore '' + auth sufficient pam_u2f.so cue authfile=/etc/fido2/fido2 + ''; + }) + ]); + + # Faillock configuration for system-auth + system-auth = mkIf cfg.systemHardening.faillock.enable { + text = mkAfter '' + auth required pam_faillock.so preauth + auth required pam_faillock.so authfail deny=${toString cfg.systemHardening.faillock.denyAttempts} unlock_time=${toString cfg.systemHardening.faillock.unlockTime} + account required pam_faillock.so + ''; + }; + }; + }; + }; + + # Firewall configuration + networking.firewall = mkIf cfg.systemHardening.enable { + enable = true; + + # Essential services (NixOS firewall denies by default) + allowedTCPPorts = [ 22 ]; # SSH + allowedUDPPorts = [ 53317 ]; # LocalSend + allowedTCPPortRanges = [ + { + from = 53317; + to = 53317; + } # LocalSend TCP + ]; + }; + + # Create FIDO2 configuration directory + system.activationScripts.fido2Setup = mkIf cfg.fido2.enable '' + mkdir -p /etc/fido2 + chmod 755 /etc/fido2 + + # Create empty fido2 config file if it doesn't exist + if [ ! -f /etc/fido2/fido2 ]; then + touch /etc/fido2/fido2 + chmod 644 /etc/fido2/fido2 + fi + ''; + + # Security management scripts are now defined in the let block above + + # Add to main menu integration + home-manager.users.${config.omni.user} = { + programs.bash.shellAliases = { + fingerprint = "omni-fingerprint"; + fido2 = "omni-fido2"; + security = "omni-security"; + }; + + programs.zsh.shellAliases = { + fingerprint = "omni-fingerprint"; + fido2 = "omni-fido2"; + security = "omni-security"; + }; + + programs.fish.shellAliases = { + fingerprint = "omni-fingerprint"; + fido2 = "omni-fido2"; + security = "omni-security"; + }; + }; + }; +} diff --git a/modules/services.nix b/modules/services.nix new file mode 100644 index 0000000..dc3a67a --- /dev/null +++ b/modules/services.nix @@ -0,0 +1,405 @@ +{ + config, + pkgs, + lib, + ... +}: + +let + cfg = config.omni; +in +{ + # XDG portal config is in modules/desktop/hyprland.nix + + # Tuigreet display manager + # Write HM's generated Hyprland config to /etc so it's available before HM activation + environment.etc."hypr/hyprland.conf" = lib.mkIf (config.programs.hyprland.enable or false) { + source = config.home-manager.users.${cfg.user}.xdg.configFile."hypr/hyprland.conf".source; + }; + + services.greetd = { + enable = true; + settings.default_session.command = + let + hyprland-session = pkgs.writeShellScript "hyprland-session" '' + export HOME="''${HOME:-/home/$(whoami)}" + mkdir -p "$HOME/.config/hypr" + cp -f /etc/hypr/hyprland.conf "$HOME/.config/hypr/hyprland.conf" 2>/dev/null || true + exec start-hyprland + ''; + in + "${pkgs.tuigreet}/bin/tuigreet --time --cmd ${hyprland-session}"; + }; + + # System services configuration + services = { + # Display server + xserver = { + enable = true; + excludePackages = [ pkgs.xterm ]; + + xkb = { + layout = "us"; + variant = ""; + options = "caps:escape,compose:ralt"; + }; + }; + + # Display Manager (disabled - using greetd instead) + displayManager.gdm.enable = false; + + # Touchpad support + libinput = { + enable = true; + touchpad = { + naturalScrolling = true; + tapping = true; + clickMethod = "clickfinger"; + }; + }; + + # Printing support + printing = { + enable = true; + drivers = with pkgs; [ + gutenprint + gutenprintBin + hplip + epson-escpr + epson-escpr2 + ]; + }; + + # Sound — disable PulseAudio in favor of PipeWire + pulseaudio.enable = false; + pipewire = { + enable = true; + alsa = { + enable = true; + support32Bit = true; + }; + pulse.enable = true; + jack.enable = true; + wireplumber = { + enable = true; + # Workaround: libldac-dec decoder init crashes (LDACBT_ERR_FATAL) on + # PipeWire 1.6.2, causing silent audio failure with no fallback. + # Remove once nixpkgs#502690 lands in our channel. + extraConfig."bluetooth" = { + "monitor.bluez.properties" = { + "bluez5.codecs" = [ + "sbc" + "sbc_xq" + "aac" + "aptx" + "aptx_hd" + ]; + }; + }; + }; + }; + + # Network + resolved = { + enable = true; + settings.Resolve = { + DNSSEC = "true"; + Domains = [ "~." ]; + FallbackDNS = [ + "1.1.1.1" + "8.8.8.8" + "1.0.0.1" + "8.8.4.4" + ]; + }; + }; + + # Bluetooth + blueman.enable = true; + + # Power management + power-profiles-daemon.enable = true; + thermald.enable = true; + upower = { + enable = true; + percentageLow = 15; + percentageCritical = 5; + percentageAction = 3; + }; + + # System monitoring + smartd = { + enable = true; + autodetect = true; + }; + + # File indexing and search + locate = { + enable = true; + interval = "daily"; + package = pkgs.plocate; + }; + + # Backup service (optional) + restic = { + backups = { + # Example backup configuration + # home = { + # paths = [ "/home/${cfg.user}" ]; + # repository = "/backup/restic"; + # passwordFile = "/etc/restic/password"; + # timerConfig = { + # OnCalendar = "daily"; + # Persistent = true; + # }; + # pruneOpts = [ + # "--keep-daily 7" + # "--keep-weekly 4" + # "--keep-monthly 12" + # ]; + # }; + }; + }; + + # SSH daemon + openssh = { + enable = true; + settings = { + PermitRootLogin = "no"; + PasswordAuthentication = false; + KbdInteractiveAuthentication = false; + X11Forwarding = false; + }; + }; + + # Firewall + fail2ban = { + enable = true; + maxretry = 3; + bantime = "1h"; + bantime-increment.enable = true; + }; + + # System maintenance + fstrim = { + enable = true; + interval = "weekly"; + }; + + # Scheduled tasks + cron = { + enable = true; + systemCronJobs = [ + # Example: Update system database daily + # "0 3 * * * root ${pkgs.nix-index}/bin/nix-index" + ]; + }; + + # Syncthing for file synchronization + syncthing = { + enable = false; # Set to true to enable + user = cfg.user; + dataDir = "/home/${cfg.user}/Documents"; + configDir = "/home/${cfg.user}/.config/syncthing"; + }; + + # Tailscale VPN + tailscale = { + enable = false; # Set to true to enable + useRoutingFeatures = "client"; + }; + + # Flatpak support + flatpak.enable = config.xdg.portal.enable; + + # GVFS for mounting and trash support + gvfs.enable = true; + + # Thumbnail generation + tumbler.enable = true; + + # Notification daemon is handled by mako in Hyprland config + + # System daemons + dbus = { + enable = true; + packages = with pkgs; [ dconf ]; + }; + + # Secret storage for Electron apps (Signal, Ferdium, etc.) + gnome.gnome-keyring.enable = true; + + # Avahi for network discovery + avahi = { + enable = true; + nssmdns4 = true; + publish = { + enable = true; + addresses = true; + domain = true; + workstation = true; + userServices = true; + }; + }; + + # ACPI daemon for power management + acpid.enable = true; + + # Firmware updates via LVFS + fwupd.enable = true; + + # Automatic upgrades (disabled by default) + # system.autoUpgrade = { + # enable = true; + # allowReboot = false; + # dates = "04:00"; + # flake = "/etc/nixos#omni"; + # }; + + # Earlyoom - out of memory killer + earlyoom = { + enable = true; + freeMemThreshold = 5; + freeSwapThreshold = 10; + }; + + # Logrotate + logrotate = { + enable = true; + settings = { + "/var/log/omni/*.log" = { + frequency = "weekly"; + rotate = 4; + compress = true; + delaycompress = true; + notifempty = true; + create = "644 root root"; + }; + }; + }; + }; + + # Systemd services + systemd = { + # User session environment. + # systemd.user.extraConfig was removed upstream; the [Manager] section is + # now expressed via the structured systemd.user.settings.Manager attrset. + user.settings.Manager.DefaultEnvironment = ''"PATH=/run/wrappers/bin:/home/${cfg.user}/.nix-profile/bin:/etc/profiles/per-user/${cfg.user}/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin"''; + + # Automatic cleanup + timers.clear-tmp = { + description = "Clear /tmp weekly"; + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = "weekly"; + Persistent = true; + }; + }; + + services.clear-tmp = { + description = "Clear /tmp directory"; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.coreutils}/bin/find /tmp -type f -atime +7 -delete"; + }; + }; + + # Custom Omarchy services + services.omni-init = { + description = "Omarchy initialization service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = pkgs.writeShellScript "omni-init" '' + #!/usr/bin/env bash + echo "Initializing Omarchy..." + + # Create necessary directories + mkdir -p /var/log/omni + mkdir -p /var/lib/omni + mkdir -p /etc/omni + + # Set up initial configuration + if [ ! -f /etc/omni/initialized ]; then + echo "$(date): Omnixient initialized" > /etc/omni/initialized + echo "Welcome to Omarchy!" > /etc/motd + fi + ''; + }; + }; + }; + + # Unlock gnome-keyring on login + security.pam.services.greetd.enableGnomeKeyring = true; + + # Signal Messenger uses its own self-signed root CA for certificate pinning. + # Add it to the system trust store so Electron/curl can connect. + security.pki.certificates = [ + '' + -----BEGIN CERTIFICATE----- + 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 + 9qfgAJizyy3kqM4lOwBH + -----END CERTIFICATE----- + '' + ]; + + # Security policies + security = { + # Required for PipeWire real-time scheduling + rtkit.enable = true; + + polkit = { + enable = true; + extraConfig = '' + /* Allow members of wheel group to manage systemd services without password */ + polkit.addRule(function(action, subject) { + if (action.id == "org.freedesktop.systemd1.manage-units" && + subject.isInGroup("wheel")) { + return polkit.Result.YES; + } + }); + ''; + }; + + # AppArmor + apparmor = { + enable = true; + packages = with pkgs; [ + apparmor-utils + apparmor-profiles + ]; + }; + }; +}