diff options
Diffstat (limited to 'gnu')
| -rw-r--r-- | gnu/local.mk | 1 | ||||
| -rw-r--r-- | gnu/services/opensnitch.scm | 230 | ||||
| -rw-r--r-- | gnu/tests/security.scm | 88 |
3 files changed, 318 insertions, 1 deletions
diff --git a/gnu/local.mk b/gnu/local.mk index f2d060c43b2..75accdbf20e 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -758,6 +758,7 @@ GNU_SYSTEM_MODULES = \ %D%/services/networking.scm \ %D%/services/nix.scm \ %D%/services/nfs.scm \ + %D%/services/opensnitch.scm \ %D%/services/pam-mount.scm \ %D%/services/power.scm \ %D%/services/science.scm \ diff --git a/gnu/services/opensnitch.scm b/gnu/services/opensnitch.scm new file mode 100644 index 00000000000..2f213a81b01 --- /dev/null +++ b/gnu/services/opensnitch.scm @@ -0,0 +1,230 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2025 Danny Milosavljevic <dannym@friendly-machines.com> +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. + +(define-module (gnu services opensnitch) + #:use-module (gnu packages networking) + #:use-module (gnu services) + #:use-module (gnu services base) + #:use-module (gnu services configuration) + #:use-module (gnu services shepherd) + #:use-module (guix gexp) + #:use-module (guix packages) + #:use-module (guix records) + #:use-module (json) + #:export (opensnitch-configuration + opensnitch-configuration? + opensnitch-service-type)) + +(define-configuration/no-serialization opensnitch-configuration + (opensnitch + (package opensnitch-daemon) + "The @code{opensnitch-daemon} package to use.") + + ;; Server settings + (server-address + (string "unix:///tmp/osui.sock") + "Address for the UI to connect to the daemon.") + (server-log-file + (string "/var/log/opensnitchd.log") + "Path to the daemon log file.") + + ;; Authentication settings + (authentication-type + (string "simple") + "Authentication type for UI-daemon communication.") + (tls-ca-cert + (string "") + "Path to TLS CA certificate.") + (tls-server-cert + (string "") + "Path to TLS server certificate.") + (tls-client-cert + (string "") + "Path to TLS client certificate.") + (tls-client-key + (string "") + "Path to TLS client key.") + (tls-skip-verify? + (boolean #f) + "Whether to skip TLS verification.") + (tls-client-auth-type + (string "no-client-cert") + "TLS client authentication type.") + + ;; Default behavior + (default-action + (string "allow") + "Default action for connections: @code{\"allow\"} or @code{\"deny\"}.") + (default-duration + (string "once") + "Default duration for rules: @code{\"once\"}, @code{\"until-restart\"}, +@code{\"always\"}, etc.") + (intercept-unknown? + (boolean #f) + "Whether to intercept connections from unknown processes.") + + ;; Process monitoring + (proc-monitor-method + (string "ebpf") + "Method for monitoring processes: @code{\"ebpf\"}, @code{\"proc\"}, or +@code{\"audit\"}.") + + ;; Logging + (log-level + (integer 2) + "Log level: 0=silent, 1=error, 2=warning, 3=important, 4=debug.") + (log-utc? + (boolean #t) + "Whether to log timestamps in UTC.") + (log-micro? + (boolean #f) + "Whether to include microseconds in log timestamps.") + + ;; Firewall settings + (firewall + (string "nftables") + "Firewall backend: @code{\"nftables\"} or @code{\"iptables\"}.") + (fw-config-path + (string "/etc/opensnitchd/system-fw.json") + "Path to the system firewall configuration file.") + (fw-monitor-interval + (string "15s") + "Interval for monitoring firewall rules.") + (fw-queue-bypass? + (boolean #t) + "Whether to bypass the queue when the daemon is not running.") + + ;; Rules settings + (rules-path + (string "/etc/opensnitchd/rules/") + "Directory where firewall rules are stored.") + (rules-enable-checksums? + (boolean #f) + "Whether to enable checksums for rules.") + + ;; eBPF settings + (ebpf-events-workers + (integer 8) + "Number of eBPF event worker threads.") + (ebpf-queue-events-size + (integer 0) + "Size of the eBPF events queue (0 = default).") + + ;; Statistics settings + (stats-max-events + (integer 250) + "Maximum number of events to keep in statistics.") + (stats-max-stats + (integer 25) + "Maximum number of statistics entries.") + (stats-workers + (integer 6) + "Number of statistics worker threads.") + + ;; Internal settings + (internal-gc-percent + (integer 100) + "Go garbage collector percentage.") + (internal-flush-conns-on-start? + (boolean #t) + "Whether to flush existing connections on daemon start.")) + +(define (opensnitch-configuration->json config) + "Convert CONFIG to a JSON string for the OpenSnitch daemon." + (match-record config <opensnitch-configuration> + (server-address server-log-file + authentication-type tls-ca-cert tls-server-cert tls-client-cert + tls-client-key tls-skip-verify? tls-client-auth-type + default-action default-duration intercept-unknown? + proc-monitor-method log-level log-utc? log-micro? + firewall fw-config-path fw-monitor-interval fw-queue-bypass? + rules-path rules-enable-checksums? + ebpf-events-workers ebpf-queue-events-size + stats-max-events stats-max-stats stats-workers + internal-gc-percent internal-flush-conns-on-start?) + (scm->json-string + `((Server . ((Address . ,server-address) + (Authentication . ((Type . ,authentication-type) + (TLSOptions . ((CACert . ,tls-ca-cert) + (ServerCert . ,tls-server-cert) + (ClientCert . ,tls-client-cert) + (ClientKey . ,tls-client-key) + (SkipVerify . ,tls-skip-verify?) + (ClientAuthType . ,tls-client-auth-type))))) + (LogFile . ,server-log-file))) + (DefaultAction . ,default-action) + (DefaultDuration . ,default-duration) + (InterceptUnknown . ,intercept-unknown?) + (ProcMonitorMethod . ,proc-monitor-method) + (LogLevel . ,log-level) + (LogUTC . ,log-utc?) + (LogMicro . ,log-micro?) + (Firewall . ,firewall) + (FwOptions . ((ConfigPath . ,fw-config-path) + (MonitorInterval . ,fw-monitor-interval) + (QueueBypass . ,fw-queue-bypass?))) + (Rules . ((Path . ,rules-path) + (EnableChecksums . ,rules-enable-checksums?))) + (Ebpf . ((EventsWorkers . ,ebpf-events-workers) + (QueueEventsSize . ,ebpf-queue-events-size))) + (Stats . ((MaxEvents . ,stats-max-events) + (MaxStats . ,stats-max-stats) + (Workers . ,stats-workers))) + (Internal . ((GCPercent . ,internal-gc-percent) + (FlushConnsOnStart . ,internal-flush-conns-on-start?)))) + #:pretty #t))) + +(define (opensnitch-config-file config) + "Return a file-like object for the OpenSnitch configuration." + (plain-file "opensnitch-config.json" + (opensnitch-configuration->json config))) + +(define (opensnitch-activation config) + "Return the activation gexp for CONFIG." + (match-record config <opensnitch-configuration> + (rules-path) + (with-imported-modules '((guix build utils)) + #~(begin + (use-modules (guix build utils)) + (mkdir-p #$rules-path))))) + +(define (opensnitch-shepherd-service config) + (match-record config <opensnitch-configuration> + (opensnitch server-log-file) + (list (shepherd-service + (documentation "Run the OpenSnitch application firewall daemon.") + (provision '(opensnitch)) + (requirement '(user-processes networking)) + (start #~(make-forkexec-constructor + (list #$(file-append opensnitch "/sbin/opensnitchd") + "-config-file" #$(opensnitch-config-file config)) + #:log-file #$server-log-file)) + (stop #~(make-kill-destructor)))))) + +(define opensnitch-service-type + (service-type + (name 'opensnitch) + (description "Run the OpenSnitch application firewall daemon.") + (extensions + (list (service-extension shepherd-root-service-type + opensnitch-shepherd-service) + (service-extension activation-service-type + opensnitch-activation) + (service-extension profile-service-type + (compose list opensnitch-configuration-opensnitch)))) + (default-value (opensnitch-configuration)))) diff --git a/gnu/tests/security.scm b/gnu/tests/security.scm index 8887396b89b..204f3262da8 100644 --- a/gnu/tests/security.scm +++ b/gnu/tests/security.scm @@ -1,5 +1,6 @@ ;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2022 muradm <mail@muradm.net> +;;; Copyright © 2025 Danny Milosavljevic <dannym@friendly-machines.com> ;;; ;;; This file is part of GNU Guix. ;;; @@ -19,8 +20,10 @@ (define-module (gnu tests security) #:use-module (guix gexp) #:use-module (gnu packages admin) + #:use-module (gnu packages linux) #:use-module (gnu services) #:use-module (gnu services base) + #:use-module (gnu services opensnitch) #:use-module (gnu services security) #:use-module (gnu services ssh) #:use-module (gnu system) @@ -28,7 +31,8 @@ #:use-module (gnu tests) #:export (%test-fail2ban-basic %test-fail2ban-extension - %test-fail2ban-simple)) + %test-fail2ban-simple + %test-opensnitch)) ;;; @@ -238,3 +242,85 @@ (name "fail2ban-extension") (description "Test extension fail2ban running capability.") (value (run-fail2ban-extension-test)))) + + +;;; +;;; OpenSnitch tests +;;; + +(define (run-opensnitch-test) + (define os + (marionette-operating-system + (simple-operating-system + (service opensnitch-service-type) + (service static-networking-service-type + (list %qemu-static-networking))) + #:imported-modules '((gnu services herd)))) + + (define vm + (virtual-machine + (operating-system os) + (port-forwardings '()))) + + (define test + (with-imported-modules '((gnu build marionette) + (guix build utils)) + #~(begin + (use-modules (srfi srfi-64) + (gnu build marionette)) + + (define marionette (make-marionette (list #$vm))) + + (test-runner-current (system-test-runner #$output)) + (test-begin "opensnitch") + + (test-assert "opensnitch running" + (marionette-eval + '(begin + (use-modules (gnu services herd)) + (start-service 'opensnitch)) + marionette)) + + (test-assert "opensnitch log file" + (marionette-eval + '(file-exists? "/var/log/opensnitchd.log") + marionette)) + + (test-assert "opensnitch rules directory" + (marionette-eval + '(file-exists? "/etc/opensnitchd/rules") + marionette)) + + (test-assert "opensnitch process running" + (marionette-eval + `(zero? (system* ,#$(file-append procps "/bin/pgrep") + "-x" "opensnitchd")) + marionette)) + + (test-assert "opensnitch running after restart" + (marionette-eval + '(begin + (use-modules (gnu services herd)) + (restart-service 'opensnitch)) + marionette)) + + (test-assert "opensnitch process running after restart" + (marionette-eval + `(let loop ((tries 0)) + (if (zero? (system* ,#$(file-append procps "/bin/pgrep") + "-x" "opensnitchd")) + #t + (if (< tries 30) + (begin (sleep 1) (loop (+ tries 1))) + #f))) + marionette)) + + (test-end)))) + + (gexp->derivation "opensnitch-test" test)) + +(define %test-opensnitch + (system-test + (name "opensnitch") + (description "Test OpenSnitch application firewall daemon.") + (value (run-opensnitch-test)))) |
