From unknown Sat Aug 16 15:54:09 2025 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-Mailer: MIME-tools 5.509 (Entity 5.509) Content-Type: text/plain; charset=utf-8 From: bug#56608 <56608@debbugs.gnu.org> To: bug#56608 <56608@debbugs.gnu.org> Subject: Status: [PATCH] gnu: security: Add fail2ban-service-type. Reply-To: bug#56608 <56608@debbugs.gnu.org> Date: Sat, 16 Aug 2025 22:54:09 +0000 retitle 56608 [PATCH] gnu: security: Add fail2ban-service-type. reassign 56608 guix-patches submitter 56608 muradm severity 56608 normal tag 56608 patch thanks From debbugs-submit-bounces@debbugs.gnu.org Sat Jul 16 22:32:34 2022 Received: (at submit) by debbugs.gnu.org; 17 Jul 2022 02:32:34 +0000 Received: from localhost ([127.0.0.1]:46442 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oCu5A-000732-LC for submit@debbugs.gnu.org; Sat, 16 Jul 2022 22:32:33 -0400 Received: from lists.gnu.org ([209.51.188.17]:35652) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oCu58-00072s-1Q for submit@debbugs.gnu.org; Sat, 16 Jul 2022 22:32:31 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:34108) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1oCu57-0003lM-Sx for guix-patches@gnu.org; Sat, 16 Jul 2022 22:32:29 -0400 Received: from nomad-cl1.staging.muradm.net ([139.162.159.157]:37326 helo=nomad-cl1.muradm.net) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1oCu54-0002Ni-To for guix-patches@gnu.org; Sat, 16 Jul 2022 22:32:29 -0400 Received: from localhost ([127.0.0.1]:58532) by nomad-cl1.muradm.net with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1oCu4t-0002ua-1K for guix-patches@gnu.org; Sun, 17 Jul 2022 02:32:15 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=muradm.net; s=mail; h=Content-Transfer-Encoding:Content-Type:MIME-Version:Message-Id:Date :Subject:To:From:Sender:Reply-To:Cc:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=02aFMVSLxaLxNs7IGdolRH06pvmwmTu7K4fbOd3Wdzw=; b=eFU43Y3xTDTP1UconVgWn25mWG PCVc0aRB+tmYAnOpONx7IRPFEUE9T8q9JN7N5agU6bfOFk6Z9X8q88zcmUybB9+lISK4Z+fA8lJB1 7yxgnQXUUTKR6ci9w875beb4Onk67+e0nxwjCrzO5t/Xv13BIweyFXut+m0Miy9YiJyJFCBmtqAtz VXL7OB/iP32fdvYFLNdp44To7x29AzqquI4bE2GKDaPS5UZ4eWu3zf/x4UpxNSI3d7ZR0i6trXGPN DCYYaQHGhcPZDur/WkmsDr+co/euY+el1WnjUsI0ixmIC00M5JF5ZpRIcaVZn6LI78HCiQF3b4L18 1g+5nzQjL/zdvasPO2u9Vnt87yKHWcrwT+RTO8ik23W/71n82RwvXOs2Bo+gmYBY/+hH/2burL4+Z dQQx9wP64elf8gsJVHnxQfWH/+Zvj1p0MkNdFQdlsx1BAxb4gsdmXdT7+cPNC/Ew80FUC7xUAShvl b0sIHNnT8O1LZHP7cTnIQYQP; Received: from muradm by localhost with local (Exim 4.96) (envelope-from ) id 1oCu51-00007E-1K for guix-patches@gnu.org; Sun, 17 Jul 2022 05:32:23 +0300 From: muradm To: guix-patches@gnu.org Subject: [PATCH] gnu: security: Add fail2ban-service-type. Date: Sun, 17 Jul 2022 05:32:23 +0300 Message-Id: <20220717023223.440-1-mail@muradm.net> X-Mailer: git-send-email 2.36.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Received-SPF: pass client-ip=139.162.159.157; envelope-from=mail@muradm.net; helo=nomad-cl1.muradm.net X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-Spam-Score: -1.4 (-) X-Debbugs-Envelope-To: submit X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -2.4 (--) * gnu/services/security.scm: New module. * gnu/local.mk: Add new security module. * doc/guix.text: Add fail2ban-service-type documentation. --- doc/guix.texi | 215 +++++++++++++++++++++++++ gnu/local.mk | 2 + gnu/services/security.scm | 328 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 545 insertions(+) create mode 100644 gnu/services/security.scm diff --git a/doc/guix.texi b/doc/guix.texi index 8b09bcd4eb..1fc327c4bc 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -36285,6 +36285,221 @@ Extra command line options for @code{nix-service-type}. @end table @end deftp +@cindex Fail2ban +@subsubheading Fail2ban service + +@uref{http://www.fail2ban.org/, @code{fail2ban}} scans log files +(e.g. @code{/var/log/apache/error_log}) and bans IPs that show the malicious +signs -- too many password failures, seeking for exploits, etc. + +@code{fail2ban} service is provided in @code{(gnu services security)} module. + +This is the type of the service that runs @code{fail2ban} daemon. It can be +used in various ways, which are: + +@itemize + +@item Explicit configuration +users are free to enable @code{fail2ban} configuration without strong +dependency. + +@item On-demand extending configuration +convenience @code{fail2ban-jail-service} function is provided, in order +to extend existing services on-demand. + +@item Permanent extending configuration +service developers may not @code{fail2ban-service-type} in service-type's +extensions. + +@end itemize + +@defvr {Scheme Variable} fail2ban-service-type + +This is the type of the service that runs @code{fail2ban} daemon. It can be +configured explicitly as following: + +@lisp +(append + (list + ;; excplicit configuration, this way fail2ban daemon + ;; will start and do its thing for sshd jail + (service fail2ban-service-type + (fail2ban-configuration + (extra-jails + (list + (fail2ban-jail-configuration + (name "sshd") + (enabled #t)))))) + ;; there is no direct dependency with actual openssh + ;; server configuration, it could even be omited here + (service openssh-service-type)) + %base-services) +@end lisp +@end defvr + +@deffn {Scheme Procedure} fail2ban-jail-service @var{svc-type} @var{jail} +Return extended @var{svc-type} of @code{} with added +@var{jail} of type @code{fail2ban-jail-configuration} extension +for @code{fail2ban-service-type}. + +For example: + +@lisp +(append + (list + (service + ;; using convenience function we can extend virutally + ;; any service type with any fail2ban jail + ;; this way we don't have to explicitly add + ;; (service fail2ban-service-type) to our configuration + (fail2ban-jail-service + openssh-service-type + (fail2ban-jail-configuration + (name "sshd") + (enabled #t))) + (openssh-configuration ...)))) +@end lisp +@end deffn + +@deftp {Data Type} fail2ban-configuration +Configuration record for the @code{fail2ban-service-type}. +@table @asis + +@item @code{fail2ban} (default: @code{fail2ban}) +The @code{fail2ban} package to use. It used for both binaries and +as base default configuration that will be extended with +@code{}s. + +@item @code{run-directory} (default: @file{"/var/run/fail2ban"}) +State directory for @code{fail2ban} daemon. + +@item @code{jails} (default: @code{'()}) +Instances of @code{} collected from +extensions. + +@item @code{extra-jails} (default: @code{'()}) +Instances of @code{} provided by user +explicitly. + +@item @code{extra-content} (default: @code{""}) +Extra raw content to add at the end of @file{jail.local}. + +@end table +@end deftp + +@deftp {Data Type} fail2ban-jail-configuration +@code{fail2ban} jail configuration to be added to @file{jail.local}. +@table @asis + +Fields with default value of @code{*unspecified*} will not be serialized +to configuration, thus default values of @code{fail2ban} will apply. + +For details of field meanings, please refer to @code{fail2ban} documentation. + +@item @code{name} +Required name of this jail configuration. + +@item @code{enabled} (default: @code{*unspecified*}) +Either @code{#t} or @code{#f} for @samp{true} and @samp{false} respectively. + +@item @code{max-retry} (default: @code{*unspecified*}) +Is the number of failures before a host get banned (e.g. @code{(max-retry 5)}). + +@item @code{max-matches} (default: @code{*unspecified*}) +Is the number of matches stored in ticket (resolvable via +tag @code{}) in action. + +@item @code{find-time} (default: @code{*unspecified*}) +A host is banned if it has geneerated @code{max-retry} during the last +@code{find-time} seconds (e.g. @code{(find-time "10m")}). + +@item @code{ban-time} (default: @code{*unspecified*}) +Is the number of seconds that a host is banned (e.g. @code{(ban-time "10m")}). + +@item @code{ban-time-increment} (default: @code{*unspecified*}) +Allows to use database for searching of previously banned ip's to increase a +default ban time using special formula. + +@item @code{ban-time-factor} (default: @code{*unspecified*}) +Is a coefficient to calculate exponent growing of the formula or common multiplier. + +@item @code{ban-time-formula} (default: @code{*unspecified*}) +Used by default to calculate next value of ban time. + +@item @code{ban-time-multipliers} (default: @code{*unspecified*}) +Used to calculate next value of ban time instead of formula. + +@item @code{ban-time-maxtime} (default: @code{*unspecified*}) +Is the max number of seconds using the ban time can reach (doesn't grow further). + +@item @code{ban-time-rndtime} (default: @code{*unspecified*}) +Is the max number of seconds using for mixing with random time +to prevent ``clever'' botnets calculate exact time IP can be unbanned again. + +@item @code{ban-time-overalljails} (default: @code{*unspecified*}) +Either @code{#t} or @code{#f} for @samp{true} and @samp{false} respectively. +@itemize +@item @code{true} - specifies the search of IP in the database will be executed cross over all jails +@item @code{false} - only current jail of the ban IP will be searched +@end itemize + +@item @code{ignore-command} (default: @code{*unspecified*}) +External command that will take an tagged arguments to ignore. +Note: while provided, currently unimplemented in the context of @code{guix}. + +@item @code{ignore-self} (default: @code{*unspecified*}) +Specifies whether the local resp. own IP addresses should be ignored. + +@item @code{ignore-ip} (default: @code{'()}) +Can be a list of IP addresses, CIDR masks or DNS hosts. @code{fail2ban} +will not ban a host which matches an address in this list + +@item @code{ignore-cache} (default: @code{*unspecified*}) + +@item @code{filter} (default: @code{*unspecified*}) +Defines the filter to use by the jail, using +@code{}. +By default jails have names matching their filter name. + +@item @code{log-time-zone} (default: @code{*unspecified*}) + +@item @code{log-encoding} (default: @code{*unspecified*}) +Specifies the encoding of the log files handled by the jail. +Possible values: @code{'ascii}, @code{'utf-8}, @code{'auto}. + +@item @code{log-path} (default: @code{*unspecified*}) + +@item @code{action} (default: @code{'()}) +List of @code{}. + +@end table +@end deftp + +@deftp {Data Type} fail2ban-jail-filter-configuration +@code{fail2ban} jail filter configuration. +@table @asis + +@item @code{name} +Name part required. + +@item @code{mode} (default: @code{*unspecified*}) + +@end table +@end deftp + +@deftp {Data Type} fail2ban-jail-action-configuration +@code{fail2ban} jail action configuration. +@table @asis + +@item @code{name} +Name part required. + +@item @code{arguments} (default: @code{'()}) +Association list of key value pairs. + +@end table +@end deftp + @node Setuid Programs @section Setuid Programs diff --git a/gnu/local.mk b/gnu/local.mk index 07e3497d10..eef7d09fd4 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -50,6 +50,7 @@ # Copyright © 2022 Daniel Meißner # Copyright © 2022 Remco van 't Veer # Copyright © 2022 Artyom V. Poptsov +# Copyright © 2022 muradm # # This file is part of GNU Guix. # @@ -670,6 +671,7 @@ GNU_SYSTEM_MODULES = \ %D%/services/nfs.scm \ %D%/services/pam-mount.scm \ %D%/services/science.scm \ + %D%/services/security.scm \ %D%/services/security-token.scm \ %D%/services/shepherd.scm \ %D%/services/sound.scm \ diff --git a/gnu/services/security.scm b/gnu/services/security.scm new file mode 100644 index 0000000000..db95d68035 --- /dev/null +++ b/gnu/services/security.scm @@ -0,0 +1,328 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2022 muradm +;;; +;;; 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 . + +(define-module (gnu services security) + #:use-module (guix gexp) + #:use-module (guix records) + #:use-module (gnu packages admin) + #:use-module (ice-9 format) + #:use-module (ice-9 match) + #:use-module (srfi srfi-1) + #:use-module (gnu services) + #:use-module (gnu services shepherd) + #:export (fail2ban-ignore-cache-configuration + fail2ban-jail-filter-configuration + fail2ban-jail-action-configuration + fail2ban-jail-configuration + fail2ban-configuration + fail2ban-service-type + fail2ban-jail-service)) + +(define (fail2ban-section->string name) + (format #f "[~a]" name)) + +(define fail2ban-backend->string + (match-lambda + ('auto "auto") + ('pyinotify "pyinotify") + ('gamin "gamin") + ('polling "polling") + ('systemd "systemd") + (unknown (error (format #f "Unknown fail2ban backend: ~a" unknown))))) + +(define fail2ban-log-encoding->string + (match-lambda + ('auto "auto") + ('utf-8 "utf-8") + ('ascii "ascii") + (unknown (error (format #f "Unknown fail2ban log-encoding: ~a" unknown))))) + +(define-record-type* + fail2ban-ignore-cache-configuration make-fail2ban-ignore-cache-configuration + fail2ban-ignore-cache-configuration? + (key fail2ban-ignore-cache-configuration-key) + (max-count fail2ban-ignore-cache-configuration-max-count) + (max-time fail2ban-ignore-cache-configuration-max-time)) + +(define fail2ban-ignore-cache-configuration->string + (match-lambda + (($ key max-count max-time) + (format #f "key=\"~a\", max-count=~d, max-time=~d" key max-count max-time)))) + +(define-record-type* + fail2ban-jail-filter-configuration make-fail2ban-jail-filter-configuration + fail2ban-jail-filter-configuration? + (name fail2ban-jail-filter-configuration-name) + (mode fail2ban-jail-filter-configuration-node + (default *unspecified*))) + +(define fail2ban-jail-filter-configuration->string + (match-lambda + (($ name mode) + (format #f "~a~a" + name (if (unspecified? mode) "" (format #f "[mode=~a]" mode)))))) + +(define-record-type* + fail2ban-jail-action-configuration make-fail2ban-jail-action-configuration + fail2ban-jail-action-configuration? + (name fail2ban-jail-action-configuration-name) + (arguments fail2ban-jail-action-configuration-arguments + (default '()))) + +(define (fail2ban-arguments->string args) + (let* ((multi-value + (lambda (v) + (format #f "\"~a\"" (string-join (map object->string v) ",")))) + (any-value + (lambda (v) + (if (list? v) (multi-value v) v))) + (key-value + (lambda (e) + (format #f "~a=~a" (car e) (any-value (cdr e)))))) + (format #f "~a" (string-join (map key-value args) ",")))) + +(define fail2ban-jail-action-configuration->string + (match-lambda + (($ name arguments) + (format #f "~a~a" + name (if (null? arguments) "" + (format #f "[~a]" + (fail2ban-arguments->string arguments))))))) + +(define-record-type* + fail2ban-jail-configuration make-fail2ban-jail-configuration + fail2ban-jail-configuration? + (name fail2ban-jail-configuration-name) + (enabled fail2ban-jail-configuration-enabled + (default *unspecified*)) + (backend fail2ban-jail-configuration-backend + (default *unspecified*)) + (max-retry fail2ban-jail-configuration-max-retry + (default *unspecified*)) + (max-matches fail2ban-jail-configuration-max-matches + (default *unspecified*)) + (find-time fail2ban-jail-configuration-find-time + (default *unspecified*)) + (ban-time fail2ban-jail-configuration-ban-time + (default *unspecified*)) + (ban-time-increment fail2ban-jail-configuration-ban-time-increment + (default *unspecified*)) + (ban-time-factor fail2ban-jail-configuration-ban-time-factor + (default *unspecified*)) + (ban-time-formula fail2ban-jail-configuration-ban-time-formula + (default *unspecified*)) + (ban-time-multipliers fail2ban-jail-configuration-ban-time-multipliers + (default *unspecified*)) + (ban-time-maxtime fail2ban-jail-configuration-ban-time-maxtime + (default *unspecified*)) + (ban-time-rndtime fail2ban-jail-configuration-ban-time-rndtime + (default *unspecified*)) + (ban-time-overalljails fail2ban-jail-configuration-ban-time-overalljails + (default *unspecified*)) + (ignore-command fail2ban-jail-configuration-ignore-command + (default *unspecified*)) + (ignore-self fail2ban-jail-configuration-ignore-self + (default *unspecified*)) + (ignore-ip fail2ban-jail-configuration-ignore-ip + (default '())) + (ignore-cache fail2ban-jail-configuration-ignore-cache + (default *unspecified*)) + (filter fail2ban-jail-configuration-filter + (default *unspecified*)) + (log-time-zone fail2ban-jail-configuration-log-time-zone + (default *unspecified*)) + (log-encoding fail2ban-jail-configuration-log-encoding + (default *unspecified*)) + (log-path fail2ban-jail-configuration-log-path + (default *unspecified*)) + (action fail2ban-jail-configuration-action + (default '()))) + +(define fail2ban-jail-configuration->string + (match-lambda + (($ name enabled backend + max-retry max-matches + find-time ban-time + ban-time-increment ban-time-factor + ban-time-formula ban-time-multipliers + ban-time-maxtime ban-time-rndtime + ban-time-overalljails + ignore-command ignore-self + ignore-ip ignore-cache + fltr + log-time-zone log-encoding log-path + action) + (string-join + (filter + (lambda (s) (not (unspecified? s))) + (list + (fail2ban-section->string name) + (unless (unspecified? enabled) + (format #f "enabled = ~a" + (if enabled "true" "false"))) + (unless (unspecified? backend) + (format #f "backend = ~a" + (fail2ban-backend->string backend))) + (unless (unspecified? max-retry) + (format #f "maxretry = ~d" max-retry)) + (unless (unspecified? max-matches) + (format #f "maxmatches = ~d" max-matches)) + (unless (unspecified? find-time) + (format #f "findtime = ~a" find-time)) + (unless (unspecified? ban-time) + (format #f "bantime = ~a" ban-time)) + (unless (unspecified? ban-time-increment) + (format #f "bantime.increment = ~a" + (if ban-time-increment "true" "false"))) + (unless (unspecified? ban-time-factor) + (format #f "bantime.factor = ~a" ban-time-factor)) + (unless (unspecified? ban-time-formula) + (format #f "bantime.formula = ~a" ban-time-formula)) + (unless (unspecified? ban-time-multipliers) + (format #f "bantime.multipliers = ~a" ban-time-multipliers)) + (unless (unspecified? ban-time-maxtime) + (format #f "bantime.maxtime = ~a" ban-time-maxtime)) + (unless (unspecified? ban-time-rndtime) + (format #f "bantime.rndtime = ~a" ban-time-rndtime)) + (unless (unspecified? ban-time-overalljails) + (format #f "bantime.overalljails = ~a" + (if ban-time-overalljails "true" "false"))) + (unless (unspecified? ignore-command) + (format #f "ignorecommand = ~a" ignore-command)) + (unless (unspecified? ignore-self) + (format #f "ignoreself = ~a" + (if ignore-self "true" "false"))) + (unless (null? ignore-ip) + (format #f "ignoreip = ~a" (string-join ignore-ip " "))) + (unless (unspecified? ignore-cache) + (format #f "ignorecache = ~a" + (fail2ban-ignore-cache-configuration->string + ignore-cache))) + (unless (unspecified? fltr) + (format #f "filter = ~a" + (fail2ban-jail-filter-configuration->string fltr))) + (unless (unspecified? log-time-zone) + (format #f "logtimezone = ~a" log-time-zone)) + (unless (unspecified? log-encoding) + (format #f "logencoding = ~a" + (fail2ban-log-encoding->string log-encoding))) + (unless (unspecified? log-path) + (format #f "logpath = ~a" log-path)) + (unless (null? action) + (format #f "action = ~a" + (string-join + (map fail2ban-jail-action-configuration->string action) + "\n"))))) + "\n")))) + +(define-record-type* + fail2ban-configuration make-fail2ban-configuration + fail2ban-configuration? + (fail2ban fail2ban-configuration-fail2ban + (default fail2ban)) + (run-directory fail2ban-configuration-run-directory + (default "/var/run/fail2ban")) + (jails fail2ban-configuration-jails + (default '())) + (extra-jails fail2ban-configuration-extra-jails + (default '())) + (extra-content fail2ban-configuration-extra-content + (default ""))) + +(define (fail2ban-configuration->string config) + (let* ((jails (fail2ban-configuration-jails config)) + (extra-jails (fail2ban-configuration-extra-jails config)) + (extra-content (fail2ban-configuration-extra-content config))) + (string-append + (string-join + (map fail2ban-jail-configuration->string + (append jails extra-jails)) + "\n") + "\n" extra-content "\n"))) + +(define (make-fail2ban-configuration-package config) + (let* ((fail2ban (fail2ban-configuration-fail2ban config)) + (jail-local + (plain-file "jail.local" + (fail2ban-configuration->string config)))) + (computed-file + "fail2ban-configuration" + (with-imported-modules '((guix build utils)) + #~(begin + (use-modules (guix build utils)) + (let* ((out (ungexp output))) + (mkdir-p (string-append out "/etc/fail2ban")) + (copy-recursively + (string-append #$fail2ban "/etc/fail2ban") + (string-append out "/etc/fail2ban")) + (symlink + #$jail-local + (string-append out "/etc/fail2ban/jail.local")))))))) + +(define (fail2ban-shepherd-service config) + (match-record config + (fail2ban run-directory) + (let* ((fail2ban-server (file-append fail2ban "/bin/fail2ban-server")) + (pid-file (in-vicinity run-directory "fail2ban.pid")) + (socket-file (in-vicinity run-directory "fail2ban.sock")) + (config-dir (make-fail2ban-configuration-package config)) + (config-dir (file-append config-dir "/etc/fail2ban")) + (fail2ban-action + (lambda args + #~(lambda _ + (invoke #$fail2ban-server + "-c" #$config-dir + "-p" #$pid-file + "-s" #$socket-file + "-b" + #$@args))))) + + ;; TODO: Add 'reload' action. + (list (shepherd-service + (provision '(fail2ban)) + (documentation "Run the fail2ban daemon.") + (requirement '(user-processes)) + (modules `((ice-9 match) + ,@%default-modules)) + (start (fail2ban-action "start")) + (stop (fail2ban-action "stop"))))))) + +(define fail2ban-service-type + (service-type (name 'fail2ban) + (extensions + (list (service-extension shepherd-root-service-type + fail2ban-shepherd-service))) + (compose concatenate) + (extend (lambda (config jails) + (fail2ban-configuration + (inherit config) + (jails + (append + (fail2ban-configuration-jails config) + jails))))) + (default-value (fail2ban-configuration)) + (description "Run the fail2ban server."))) + +(define (fail2ban-jail-service svc-type jail) + (service-type + (inherit svc-type) + (extensions + (append + (service-type-extensions svc-type) + (list (service-extension fail2ban-service-type + (lambda _ (list jail)))))))) -- 2.36.1 From debbugs-submit-bounces@debbugs.gnu.org Wed Aug 03 12:09:32 2022 Received: (at 56608) by debbugs.gnu.org; 3 Aug 2022 16:09:32 +0000 Received: from localhost ([127.0.0.1]:49824 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oJGw5-0006kZ-8b for submit@debbugs.gnu.org; Wed, 03 Aug 2022 12:09:32 -0400 Received: from mail-qt1-f170.google.com ([209.85.160.170]:43944) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oJGw1-0006kJ-E6 for 56608@debbugs.gnu.org; Wed, 03 Aug 2022 12:09:27 -0400 Received: by mail-qt1-f170.google.com with SMTP id a15so3158741qto.10 for <56608@debbugs.gnu.org>; Wed, 03 Aug 2022 09:09:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:references:date:in-reply-to:message-id :user-agent:mime-version:content-transfer-encoding; bh=adhENkVpdYhJ0qWaOOZopCo6D+tw/Fb5vLfNC9pHSoQ=; b=gqDu+3FZCxmJDEtPfP92b6B62BOpBo17MBXRMDr66YtlCAAJaGMHrAJDKtOPlE4pJ2 gxhK1/Y2TbnaW03mTrsV5l43fXDqNPlpuZHhD/3RSaqDWosF9CTnH8ynAx/9XVLmd4nI fjZJPFCfRHxpTEeBenYHtjPT1nXBFmh00T7n6cyjLsiV121q6hHfswzw4DMcvqdL1L47 iIAgb0/BpOhx0zC8WyebJ6NCRcGzWu2lh5FJ/J9q/UN8jeWTXQdTUY74xb35sB9VP6Ug DJ4oVQCQizPOF0ZL9bIo+oBjQ3h2oXMY0pbS7EQcbm7+6W2wI+adAbB6GnyfJggkCSgI i9qg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:references:date:in-reply-to :message-id:user-agent:mime-version:content-transfer-encoding; bh=adhENkVpdYhJ0qWaOOZopCo6D+tw/Fb5vLfNC9pHSoQ=; b=AXY2yEmgGkPJzycxrYEmsxeDk/0TanUmyyC9aYds/T+UdFzvN5rC+xCUrPFTF8F+UX JZl8j78rvT4961aWjH9QhWw8Hr8wwYy2h7dfun3nd0EvvgWZau87OstjDAJobO9Hea4H LK2DyTCXpTdvtKyFXCpFOl9HW+EQUmFOi+ed6+bJinDa4uWbhwd3bDgKiommS2UdRr9S Bnamb/cp+7euLe0vhxLN7UyYs06e8oTyPzt0lIqetVK94qG/nfXc25apOGC1uyzgldGu CJWem8tbYbPmWuBYxXF5dp7VxklToAY6M49xifoZv+Kg79lh3K6t5ebnPXqT7Jn1er1h D/IA== X-Gm-Message-State: AJIora/dwD3BuxH9/F9SSIrWsdQuSJwv1sNSunbUYH2DvoVPSUHDcbQv jLbUH/cVc5mLtFJHzgpqJrNuvOqE3Zo= X-Google-Smtp-Source: AGRyM1srUSrgG2KN2NrfqqUM7NyfDWbmzLas7nuzPA5d+EZUjwhmGtHY6vxQfMYhWjy1EspdnlsTEw== X-Received: by 2002:a05:622a:609:b0:31e:f083:7dff with SMTP id z9-20020a05622a060900b0031ef0837dffmr22480538qta.38.1659542957689; Wed, 03 Aug 2022 09:09:17 -0700 (PDT) Received: from hurd (dsl-151-182.b2b2c.ca. [66.158.151.182]) by smtp.gmail.com with ESMTPSA id cd4-20020a05622a418400b0031f27b82268sm11357340qtb.71.2022.08.03.09.09.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 03 Aug 2022 09:09:17 -0700 (PDT) From: Maxim Cournoyer To: muradm Subject: Re: bug#56608: [PATCH] gnu: security: Add fail2ban-service-type. References: <20220717023223.440-1-mail@muradm.net> Date: Wed, 03 Aug 2022 12:09:16 -0400 In-Reply-To: <20220717023223.440-1-mail@muradm.net> (muradm's message of "Sun, 17 Jul 2022 05:32:23 +0300") Message-ID: <87edxxqpg3.fsf@gmail.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/28.1 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 56608 Cc: 56608@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) Hi muradm, muradm writes: > * gnu/services/security.scm: New module. > * gnu/local.mk: Add new security module. > * doc/guix.text: Add fail2ban-service-type documentation. > --- > doc/guix.texi | 215 +++++++++++++++++++++++++ > gnu/local.mk | 2 + > gnu/services/security.scm | 328 ++++++++++++++++++++++++++++++++++++++ > 3 files changed, 545 insertions(+) > create mode 100644 gnu/services/security.scm > > diff --git a/doc/guix.texi b/doc/guix.texi > index 8b09bcd4eb..1fc327c4bc 100644 > --- a/doc/guix.texi > +++ b/doc/guix.texi > @@ -36285,6 +36285,221 @@ Extra command line options for @code{nix-servic= e-type}. > @end table > @end deftp > > +@cindex Fail2ban > +@subsubheading Fail2ban service > + > +@uref{http://www.fail2ban.org/, @code{fail2ban}} scans log files > +(e.g. @code{/var/log/apache/error_log}) and bans IPs that show the malic= ious > +signs -- too many password failures, seeking for exploits, etc. > + > +@code{fail2ban} service is provided in @code{(gnu services security)} mo= dule. > + > +This is the type of the service that runs @code{fail2ban} daemon. It can= be > +used in various ways, which are: > + > +@itemize > + > +@item Explicit configuration > +users are free to enable @code{fail2ban} configuration without strong > +dependency. > + > +@item On-demand extending configuration > +convenience @code{fail2ban-jail-service} function is provided, in order > +to extend existing services on-demand. > + > +@item Permanent extending configuration > +service developers may not @code{fail2ban-service-type} in service-type's > +extensions. > + > +@end itemize > + > +@defvr {Scheme Variable} fail2ban-service-type > + > +This is the type of the service that runs @code{fail2ban} daemon. It can= be > +configured explicitly as following: > + > +@lisp > +(append > + (list > + ;; excplicit configuration, this way fail2ban daemon > + ;; will start and do its thing for sshd jail > + (service fail2ban-service-type > + (fail2ban-configuration > + (extra-jails > + (list > + (fail2ban-jail-configuration > + (name "sshd") > + (enabled #t)))))) > + ;; there is no direct dependency with actual openssh > + ;; server configuration, it could even be omited here ^omitted > + (service openssh-service-type)) > + %base-services) > +@end lisp > +@end defvr > + > +@deffn {Scheme Procedure} fail2ban-jail-service @var{svc-type} @var{jail} > +Return extended @var{svc-type} of @code{} with added > +@var{jail} of type @code{fail2ban-jail-configuration} extension > +for @code{fail2ban-service-type}. > + > +For example: > + > +@lisp > +(append > + (list > + (service > + ;; using convenience function we can extend virutally Please use full sentences for standalone (not inline) comments. Also, typo: virutally -> virtually. > + ;; any service type with any fail2ban jail > + ;; this way we don't have to explicitly add > + ;; (service fail2ban-service-type) to our configuration > + (fail2ban-jail-service > + openssh-service-type > + (fail2ban-jail-configuration > + (name "sshd") > + (enabled #t))) > + (openssh-configuration ...)))) > +@end lisp > +@end deffn > + > +@deftp {Data Type} fail2ban-configuration > +Configuration record for the @code{fail2ban-service-type}. > +@table @asis > + > +@item @code{fail2ban} (default: @code{fail2ban}) > +The @code{fail2ban} package to use. It used for both binaries and > +as base default configuration that will be extended with > +@code{}s. > + > +@item @code{run-directory} (default: @file{"/var/run/fail2ban"}) > +State directory for @code{fail2ban} daemon. > + > +@item @code{jails} (default: @code{'()}) > +Instances of @code{} collected from > +extensions. > + > +@item @code{extra-jails} (default: @code{'()}) > +Instances of @code{} provided by user > +explicitly. > + > +@item @code{extra-content} (default: @code{""}) > +Extra raw content to add at the end of @file{jail.local}. > + > +@end table > +@end deftp > + > +@deftp {Data Type} fail2ban-jail-configuration > +@code{fail2ban} jail configuration to be added to @file{jail.local}. > +@table @asis > + > +Fields with default value of @code{*unspecified*} will not be serialized > +to configuration, thus default values of @code{fail2ban} will apply. Perhaps this configuration should use the 'define-configuration' mechanism, which allows to declare which fields can be left unspecified, without *unspecified* being a visible part of the API (which is not great, in my opinion). > +For details of field meanings, please refer to @code{fail2ban} documenta= tion. > + > +@item @code{name} > +Required name of this jail configuration. > + > +@item @code{enabled} (default: @code{*unspecified*}) > +Either @code{#t} or @code{#f} for @samp{true} and @samp{false} respectiv= ely. > + > +@item @code{max-retry} (default: @code{*unspecified*}) > +Is the number of failures before a host get banned (e.g. @code{(max-retr= y 5)}). > + > +@item @code{max-matches} (default: @code{*unspecified*}) > +Is the number of matches stored in ticket (resolvable via > +tag @code{}) in action. > + > +@item @code{find-time} (default: @code{*unspecified*}) > +A host is banned if it has geneerated @code{max-retry} during the last > +@code{find-time} seconds (e.g. @code{(find-time "10m")}). > + > +@item @code{ban-time} (default: @code{*unspecified*}) > +Is the number of seconds that a host is banned (e.g. @code{(ban-time "10= m")}). > + > +@item @code{ban-time-increment} (default: @code{*unspecified*}) > +Allows to use database for searching of previously banned ip's to increa= se a > +default ban time using special formula. > + > +@item @code{ban-time-factor} (default: @code{*unspecified*}) > +Is a coefficient to calculate exponent growing of the formula or common = multiplier. > + > +@item @code{ban-time-formula} (default: @code{*unspecified*}) > +Used by default to calculate next value of ban time. > + > +@item @code{ban-time-multipliers} (default: @code{*unspecified*}) > +Used to calculate next value of ban time instead of formula. > + > +@item @code{ban-time-maxtime} (default: @code{*unspecified*}) > +Is the max number of seconds using the ban time can reach (doesn't grow = further). > + > +@item @code{ban-time-rndtime} (default: @code{*unspecified*}) > +Is the max number of seconds using for mixing with random time > +to prevent ``clever'' botnets calculate exact time IP can be unbanned ag= ain. > + > +@item @code{ban-time-overalljails} (default: @code{*unspecified*}) > +Either @code{#t} or @code{#f} for @samp{true} and @samp{false} respectiv= ely. > +@itemize > +@item @code{true} - specifies the search of IP in the database will be e= xecuted cross over all jails > +@item @code{false} - only current jail of the ban IP will be searched > +@end itemize > + > +@item @code{ignore-command} (default: @code{*unspecified*}) > +External command that will take an tagged arguments to ignore. > +Note: while provided, currently unimplemented in the context of @code{gu= ix}. > + > +@item @code{ignore-self} (default: @code{*unspecified*}) > +Specifies whether the local resp. own IP addresses should be ignored. > + > +@item @code{ignore-ip} (default: @code{'()}) > +Can be a list of IP addresses, CIDR masks or DNS hosts. @code{fail2ban} > +will not ban a host which matches an address in this list > + > +@item @code{ignore-cache} (default: @code{*unspecified*}) > + > +@item @code{filter} (default: @code{*unspecified*}) > +Defines the filter to use by the jail, using > +@code{}. > +By default jails have names matching their filter name. > + > +@item @code{log-time-zone} (default: @code{*unspecified*}) > + > +@item @code{log-encoding} (default: @code{*unspecified*}) > +Specifies the encoding of the log files handled by the jail. > +Possible values: @code{'ascii}, @code{'utf-8}, @code{'auto}. > + > +@item @code{log-path} (default: @code{*unspecified*}) > + > +@item @code{action} (default: @code{'()}) > +List of @code{}. > + > +@end table > +@end deftp See above comment about *unspecified*. > +@deftp {Data Type} fail2ban-jail-filter-configuration > +@code{fail2ban} jail filter configuration. > +@table @asis > + > +@item @code{name} > +Name part required. > + > +@item @code{mode} (default: @code{*unspecified*}) > + > +@end table > +@end deftp > + > +@deftp {Data Type} fail2ban-jail-action-configuration > +@code{fail2ban} jail action configuration. > +@table @asis > + > +@item @code{name} > +Name part required. > + > +@item @code{arguments} (default: @code{'()}) > +Association list of key value pairs. > + > +@end table > +@end deftp > + > @node Setuid Programs > @section Setuid Programs > > diff --git a/gnu/local.mk b/gnu/local.mk > index 07e3497d10..eef7d09fd4 100644 > --- a/gnu/local.mk > +++ b/gnu/local.mk > @@ -50,6 +50,7 @@ > # Copyright =C2=A9 2022 Daniel Mei=C3=9Fner > # Copyright =C2=A9 2022 Remco van 't Veer > # Copyright =C2=A9 2022 Artyom V. Poptsov > +# Copyright =C2=A9 2022 muradm > # > # This file is part of GNU Guix. > # > @@ -670,6 +671,7 @@ GNU_SYSTEM_MODULES =3D \ > %D%/services/nfs.scm \ > %D%/services/pam-mount.scm \ > %D%/services/science.scm \ > + %D%/services/security.scm \ > %D%/services/security-token.scm \ > %D%/services/shepherd.scm \ > %D%/services/sound.scm \ > diff --git a/gnu/services/security.scm b/gnu/services/security.scm > new file mode 100644 > index 0000000000..db95d68035 > --- /dev/null > +++ b/gnu/services/security.scm > @@ -0,0 +1,328 @@ > +;;; GNU Guix --- Functional package management for GNU > +;;; Copyright =C2=A9 2022 muradm > +;;; > +;;; 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 . > + > +(define-module (gnu services security) > + #:use-module (guix gexp) > + #:use-module (guix records) > + #:use-module (gnu packages admin) > + #:use-module (ice-9 format) > + #:use-module (ice-9 match) > + #:use-module (srfi srfi-1) > + #:use-module (gnu services) > + #:use-module (gnu services shepherd) Please re-order lexicographically. > + #:export (fail2ban-ignore-cache-configuration > + fail2ban-jail-filter-configuration > + fail2ban-jail-action-configuration > + fail2ban-jail-configuration > + fail2ban-configuration > + fail2ban-service-type > + fail2ban-jail-service)) Configuration and services are often separated by a blank line to ease the reading. I'd order them here as well. > +(define (fail2ban-section->string name) > + (format #f "[~a]" name)) > + > +(define fail2ban-backend->string > + (match-lambda > + ('auto "auto") > + ('pyinotify "pyinotify") > + ('gamin "gamin") > + ('polling "polling") > + ('systemd "systemd") > + (unknown (error (format #f "Unknown fail2ban backend: ~a" unknown)))= )) This should use the (guix diagnostics), e.g. report-error module to produce standardized errors, and possibly hints. Errors and warnings should not be full sentences, while hints should be, by convention. > +(define fail2ban-log-encoding->string > + (match-lambda > + ('auto "auto") > + ('utf-8 "utf-8") > + ('ascii "ascii") > + (unknown (error (format #f "Unknown fail2ban log-encoding: ~a" unkno= wn))))) > + > +(define-record-type* > + fail2ban-ignore-cache-configuration make-fail2ban-ignore-cache-configu= ration > + fail2ban-ignore-cache-configuration? > + (key fail2ban-ignore-cache-configuration-key) > + (max-count fail2ban-ignore-cache-configuration-max-count) > + (max-time fail2ban-ignore-cache-configuration-max-time)) > + > +(define fail2ban-ignore-cache-configuration->string > + (match-lambda > + (($ key max-count max-time) > + (format #f "key=3D\"~a\", max-count=3D~d, max-time=3D~d" key max-co= unt max-time)))) > + > +(define-record-type* > + fail2ban-jail-filter-configuration make-fail2ban-jail-filter-configura= tion > + fail2ban-jail-filter-configuration? > + (name fail2ban-jail-filter-configuration-name) > + (mode fail2ban-jail-filter-configuration-node > + (default *unspecified*))) > + > +(define fail2ban-jail-filter-configuration->string > + (match-lambda > + (($ name mode) > + (format #f "~a~a" > + name (if (unspecified? mode) "" (format #f "[mode=3D~a]" mo= de)))))) > + > +(define-record-type* > + fail2ban-jail-action-configuration make-fail2ban-jail-action-configura= tion > + fail2ban-jail-action-configuration? > + (name fail2ban-jail-action-configuration-name) > + (arguments fail2ban-jail-action-configuration-arguments > + (default '()))) > + > +(define (fail2ban-arguments->string args) > + (let* ((multi-value > + (lambda (v) > + (format #f "\"~a\"" (string-join (map object->string v) ",")= ))) > + (any-value > + (lambda (v) > + (if (list? v) (multi-value v) v))) > + (key-value > + (lambda (e) > + (format #f "~a=3D~a" (car e) (any-value (cdr e)))))) > + (format #f "~a" (string-join (map key-value args) ",")))) > + > +(define fail2ban-jail-action-configuration->string > + (match-lambda > + (($ name arguments) > + (format #f "~a~a" > + name (if (null? arguments) "" > + (format #f "[~a]" > + (fail2ban-arguments->string arguments)))))= )) > + > +(define-record-type* > + fail2ban-jail-configuration make-fail2ban-jail-configuration > + fail2ban-jail-configuration? > + (name fail2ban-jail-configuration-name) > + (enabled fail2ban-jail-configuration-enabled > + (default *unspecified*)) > + (backend fail2ban-jail-configuration-backend > + (default *unspecified*)) > + (max-retry fail2ban-jail-configuration-max-retry > + (default *unspecified*)) > + (max-matches fail2ban-jail-configuration-max-matches > + (default *unspecified*)) > + (find-time fail2ban-jail-configuration-find-time > + (default *unspecified*)) > + (ban-time fail2ban-jail-configuration-ban-time > + (default *unspecified*)) > + (ban-time-increment fail2ban-jail-configuration-ban-time-increment > + (default *unspecified*)) > + (ban-time-factor fail2ban-jail-configuration-ban-time-factor > + (default *unspecified*)) > + (ban-time-formula fail2ban-jail-configuration-ban-time-formula > + (default *unspecified*)) > + (ban-time-multipliers fail2ban-jail-configuration-ban-time-multipliers > + (default *unspecified*)) > + (ban-time-maxtime fail2ban-jail-configuration-ban-time-maxtime > + (default *unspecified*)) > + (ban-time-rndtime fail2ban-jail-configuration-ban-time-rndtime > + (default *unspecified*)) > + (ban-time-overalljails fail2ban-jail-configuration-ban-time-overalljai= ls > + (default *unspecified*)) > + (ignore-command fail2ban-jail-configuration-ignore-command > + (default *unspecified*)) > + (ignore-self fail2ban-jail-configuration-ignore-self > + (default *unspecified*)) > + (ignore-ip fail2ban-jail-configuration-ignore-ip > + (default '())) > + (ignore-cache fail2ban-jail-configuration-ignore-cache > + (default *unspecified*)) > + (filter fail2ban-jail-configuration-filter > + (default *unspecified*)) > + (log-time-zone fail2ban-jail-configuration-log-time-zone > + (default *unspecified*)) > + (log-encoding fail2ban-jail-configuration-log-encoding > + (default *unspecified*)) > + (log-path fail2ban-jail-configuration-log-path > + (default *unspecified*)) > + (action fail2ban-jail-configuration-action > + (default '()))) > + > +(define fail2ban-jail-configuration->string > + (match-lambda > + (($ name enabled backend > + max-retry max-matches > + find-time ban-time > + ban-time-increment ban-time-factor > + ban-time-formula ban-time-multipli= ers > + ban-time-maxtime ban-time-rndtime > + ban-time-overalljails > + ignore-command ignore-self > + ignore-ip ignore-cache > + fltr > + log-time-zone log-encoding log-path > + action) > + (string-join > + (filter > + (lambda (s) (not (unspecified? s))) > + (list > + (fail2ban-section->string name) > + (unless (unspecified? enabled) > + (format #f "enabled =3D ~a" > + (if enabled "true" "false"))) > + (unless (unspecified? backend) > + (format #f "backend =3D ~a" > + (fail2ban-backend->string backend))) > + (unless (unspecified? max-retry) > + (format #f "maxretry =3D ~d" max-retry)) > + (unless (unspecified? max-matches) > + (format #f "maxmatches =3D ~d" max-matches)) > + (unless (unspecified? find-time) > + (format #f "findtime =3D ~a" find-time)) > + (unless (unspecified? ban-time) > + (format #f "bantime =3D ~a" ban-time)) > + (unless (unspecified? ban-time-increment) > + (format #f "bantime.increment =3D ~a" > + (if ban-time-increment "true" "false"))) > + (unless (unspecified? ban-time-factor) > + (format #f "bantime.factor =3D ~a" ban-time-factor)) > + (unless (unspecified? ban-time-formula) > + (format #f "bantime.formula =3D ~a" ban-time-formula)) > + (unless (unspecified? ban-time-multipliers) > + (format #f "bantime.multipliers =3D ~a" ban-time-multipliers)) > + (unless (unspecified? ban-time-maxtime) > + (format #f "bantime.maxtime =3D ~a" ban-time-maxtime)) > + (unless (unspecified? ban-time-rndtime) > + (format #f "bantime.rndtime =3D ~a" ban-time-rndtime)) > + (unless (unspecified? ban-time-overalljails) > + (format #f "bantime.overalljails =3D ~a" > + (if ban-time-overalljails "true" "false"))) > + (unless (unspecified? ignore-command) > + (format #f "ignorecommand =3D ~a" ignore-command)) > + (unless (unspecified? ignore-self) > + (format #f "ignoreself =3D ~a" > + (if ignore-self "true" "false"))) > + (unless (null? ignore-ip) > + (format #f "ignoreip =3D ~a" (string-join ignore-ip " "))) > + (unless (unspecified? ignore-cache) > + (format #f "ignorecache =3D ~a" > + (fail2ban-ignore-cache-configuration->string > + ignore-cache))) > + (unless (unspecified? fltr) > + (format #f "filter =3D ~a" > + (fail2ban-jail-filter-configuration->string fltr))) > + (unless (unspecified? log-time-zone) > + (format #f "logtimezone =3D ~a" log-time-zone)) > + (unless (unspecified? log-encoding) > + (format #f "logencoding =3D ~a" > + (fail2ban-log-encoding->string log-encoding))) > + (unless (unspecified? log-path) > + (format #f "logpath =3D ~a" log-path)) > + (unless (null? action) > + (format #f "action =3D ~a" > + (string-join > + (map fail2ban-jail-action-configuration->string actio= n) > + "\n"))))) > + "\n")))) > + > +(define-record-type* > + fail2ban-configuration make-fail2ban-configuration > + fail2ban-configuration? > + (fail2ban fail2ban-configuration-fail2ban > + (default fail2ban)) > + (run-directory fail2ban-configuration-run-directory > + (default "/var/run/fail2ban")) > + (jails fail2ban-configuration-jails > + (default '())) > + (extra-jails fail2ban-configuration-extra-jails > + (default '())) > + (extra-content fail2ban-configuration-extra-content > + (default ""))) > + > +(define (fail2ban-configuration->string config) > + (let* ((jails (fail2ban-configuration-jails config)) > + (extra-jails (fail2ban-configuration-extra-jails config)) > + (extra-content (fail2ban-configuration-extra-content config))) > + (string-append > + (string-join > + (map fail2ban-jail-configuration->string > + (append jails extra-jails)) > + "\n") > + "\n" extra-content "\n"))) > + > +(define (make-fail2ban-configuration-package config) > + (let* ((fail2ban (fail2ban-configuration-fail2ban config)) > + (jail-local > + (plain-file "jail.local" > + (fail2ban-configuration->string config)))) > + (computed-file > + "fail2ban-configuration" > + (with-imported-modules '((guix build utils)) > + #~(begin > + (use-modules (guix build utils)) > + (let* ((out (ungexp output))) > + (mkdir-p (string-append out "/etc/fail2ban")) > + (copy-recursively > + (string-append #$fail2ban "/etc/fail2ban") > + (string-append out "/etc/fail2ban")) > + (symlink > + #$jail-local > + (string-append out "/etc/fail2ban/jail.local")))))))) > + > +(define (fail2ban-shepherd-service config) > + (match-record config > + (fail2ban run-directory) > + (let* ((fail2ban-server (file-append fail2ban "/bin/fail2ban-server"= )) > + (pid-file (in-vicinity run-directory "fail2ban.pid")) > + (socket-file (in-vicinity run-directory "fail2ban.sock")) > + (config-dir (make-fail2ban-configuration-package config)) > + (config-dir (file-append config-dir "/etc/fail2ban")) > + (fail2ban-action > + (lambda args > + #~(lambda _ > + (invoke #$fail2ban-server > + "-c" #$config-dir > + "-p" #$pid-file > + "-s" #$socket-file > + "-b" > + #$@args))))) > + > + ;; TODO: Add 'reload' action. > + (list (shepherd-service > + (provision '(fail2ban)) > + (documentation "Run the fail2ban daemon.") > + (requirement '(user-processes)) > + (modules `((ice-9 match) > + ,@%default-modules)) > + (start (fail2ban-action "start")) > + (stop (fail2ban-action "stop"))))))) > + > +(define fail2ban-service-type > + (service-type (name 'fail2ban) > + (extensions > + (list (service-extension shepherd-root-service-type > + fail2ban-shepherd-service))) > + (compose concatenate) > + (extend (lambda (config jails) > + (fail2ban-configuration > + (inherit config) > + (jails > + (append > + (fail2ban-configuration-jails config) > + jails))))) > + (default-value (fail2ban-configuration)) > + (description "Run the fail2ban server."))) > + > +(define (fail2ban-jail-service svc-type jail) > + (service-type > + (inherit svc-type) > + (extensions > + (append > + (service-type-extensions svc-type) > + (list (service-extension fail2ban-service-type > + (lambda _ (list jail)))))))) Overall this looks very good to me. A system test attempting to brute force an SSH port and being blocked would be neat, to provide some insurance that it actually works, and that it continues working in the future. An OS template with an SSH service, the fail2ban service, and then localhost attempting a few erroneous login attempts to self, and a check to see if localhost was blocked in iptables, or something similar. Do you think it'd be feasible? Thanks, Maxim From debbugs-submit-bounces@debbugs.gnu.org Mon Aug 22 13:26:16 2022 Received: (at 56608) by debbugs.gnu.org; 22 Aug 2022 17:26:17 +0000 Received: from localhost ([127.0.0.1]:41640 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oQBBo-00076L-Bc for submit@debbugs.gnu.org; Mon, 22 Aug 2022 13:26:16 -0400 Received: from nomad-cl1.staging.muradm.net ([139.162.159.157]:48154 helo=nomad-cl1.muradm.net) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oQBBm-000766-3O for 56608@debbugs.gnu.org; Mon, 22 Aug 2022 13:26:14 -0400 Received: from localhost ([127.0.0.1]:58066) by nomad-cl1.muradm.net with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1oQBAr-0006U0-2N; Mon, 22 Aug 2022 17:25:17 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=muradm.net; s=mail; h=Content-Transfer-Encoding:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:To:From:Sender:Reply-To:Cc:Content-Type:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=K2NdbGjJeyoYQd9F46j5JMu99CWck9HrR4Bjc6HpY7U=; b=YDmYnnq6wC+0okDjH9ozNHbEJq R7erGp+/dIkks/4EU8igyMW3m8w3L0qzr+y5qMcZOHr9AUIV3BPoqAnA8uOs7kZh+rPrqP0PxoOD0 PwEiL6W/w2lvF+H6nysAqH9w6iFls46Cam4Rw4kTfCKBW0lZh9P8ODoTosrAUElzGCR/rwOfhcNmZ oe5N4Z0zz8jbnH5eJzagX0yi5VA6t7fYr8NpJVw66lzL9m7SKP1+FkwMcIWVbuB4UMDikaAe6zDx1 2KB6zOst7JDmpVUBmMARL1hvtTZQnBCTa8K7Y12pmcz1C1/pjQvynwaemEk4n5lNVsElbbbzQey2C 4FhSao+6bQjyBaZ0ynOTChjeduJWFlJitOeQMi5dwNnnJlbPq9lf89Z6Knj2wEruQoiVJcidPHvwe 1vR6B0wQPzqiumDQAAmgrJBoabtW/Rotf8cmw1v6e2lI41VWe5mFs/C6IcdLsMj5fu95GblseA4D8 AABahpt7/ogSeyLqBcnm3rXi; Received: from muradm by localhost with local (Exim 4.96) (envelope-from ) id 1oQBBf-0008CV-1m; Mon, 22 Aug 2022 20:26:07 +0300 From: muradm To: 56608@debbugs.gnu.org, Maxim Cournoyer Subject: Re: bug#56608: [PATCH v2 0/2] Add fail2ban-service-type. Date: Mon, 22 Aug 2022 20:26:05 +0300 Message-Id: <20220822172607.31515-1-mail@muradm.net> X-Mailer: git-send-email 2.37.1 In-Reply-To: <87edxxqpg3.fsf@gmail.com> References: <87edxxqpg3.fsf@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 56608 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) Hi, This is v2 version which is: - configuration uses (gnu services configuration) - adds system tests Maxim, this should include all your comments. Regarding test cases I don't find it feasible to test fail2ban functionality. If you will have a look at my tests for instance, I make fail2ban running and ask it if ssh jail is working. Actual ip blocking should be already tested with fail2ban tests. I hope I could explain my point. Thanks in advance, muradm muradm (2): gnu: security: Add fail2ban-service-type. gnu: tests: Add fail2ban tests. doc/guix.texi | 249 ++++++++++++++++++++++++ gnu/local.mk | 3 + gnu/services/security.scm | 385 ++++++++++++++++++++++++++++++++++++++ gnu/tests/security.scm | 314 +++++++++++++++++++++++++++++++ 4 files changed, 951 insertions(+) create mode 100644 gnu/services/security.scm create mode 100644 gnu/tests/security.scm -- 2.37.1 From debbugs-submit-bounces@debbugs.gnu.org Mon Aug 22 13:26:26 2022 Received: (at 56608) by debbugs.gnu.org; 22 Aug 2022 17:26:26 +0000 Received: from localhost ([127.0.0.1]:41642 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oQBBx-00076n-TY for submit@debbugs.gnu.org; Mon, 22 Aug 2022 13:26:26 -0400 Received: from nomad-cl1.staging.muradm.net ([139.162.159.157]:48166 helo=nomad-cl1.muradm.net) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oQBBn-000769-Ha for 56608@debbugs.gnu.org; Mon, 22 Aug 2022 13:26:16 -0400 Received: from localhost ([127.0.0.1]:58074) by nomad-cl1.muradm.net with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1oQBAt-0006U8-1Q; Mon, 22 Aug 2022 17:25:19 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=muradm.net; s=mail; h=Content-Transfer-Encoding:Content-Type:MIME-Version:References: In-Reply-To:Message-Id:Date:Subject:To:From:Sender:Reply-To:Cc:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=8GvMZx8eJA6O0wm5nsULpGla+hWqq1CY4pDpZ+kzMZg=; b=DLGB+1x5WlYldeTsG7+ARSA8kU +jQ1b5OvARbr7lWlgYz0pFC6dxIiHNop32zecEVSGNvWdDJsYHscncDrlGsBZL6MrfE+6WstKRGW3 eFOAij4frCkkc+FfMwZg0jN4Ht0lKOQWXi+qxOfxQtVWMf1c59FmPExhLbFVSKKCE4uF2hdyCYpQp 3vW2e4LomuZCEco2gjrQ540bfpcDjDIBWisPogj8C7ftaPZxV9Bega1ENfTY39eSkcVOTfno8jBzq oyPeODAKaxe7ytwcgnrA4Ocim5mycJL1dix+PnsHnsvS1kX7QmYoPugtLXvFkP/iOLMr0JGDFMHWd lAiS/ip1F32FXKD24q0//fanlujdEI8gmM6P3Pv0NmwLq0WtY0BanK6unpRkfKKJQTEeSSAeAIqHJ 9S/XBVONczRBGfgC9jkyMt0Kj98DHiQm9911CeFwTKPA7VhMsnUfTodysYvhU41okxYKziQNbfgcd gipnSHzqT3S5oMAwWo5JqoSL; Received: from muradm by localhost with local (Exim 4.96) (envelope-from ) id 1oQBBh-0008Cb-1L; Mon, 22 Aug 2022 20:26:09 +0300 From: muradm To: 56608@debbugs.gnu.org, Maxim Cournoyer Subject: Re: bug#56608: [PATCH v2 2/2] gnu: tests: Add fail2ban tests. Date: Mon, 22 Aug 2022 20:26:07 +0300 Message-Id: <20220822172607.31515-3-mail@muradm.net> X-Mailer: git-send-email 2.37.1 In-Reply-To: <20220822172607.31515-1-mail@muradm.net> References: <87edxxqpg3.fsf@gmail.com> <20220822172607.31515-1-mail@muradm.net> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 56608 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) * gnu/tests/security.scm: New module. * gnu/local.mk: Add new security module. --- gnu/local.mk | 1 + gnu/tests/security.scm | 314 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 315 insertions(+) create mode 100644 gnu/tests/security.scm diff --git a/gnu/local.mk b/gnu/local.mk index acd41797b9..31569033bc 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -758,6 +758,7 @@ GNU_SYSTEM_MODULES = \ %D%/tests/package-management.scm \ %D%/tests/reconfigure.scm \ %D%/tests/rsync.scm \ + %D%/tests/security.scm \ %D%/tests/security-token.scm \ %D%/tests/singularity.scm \ %D%/tests/ssh.scm \ diff --git a/gnu/tests/security.scm b/gnu/tests/security.scm new file mode 100644 index 0000000000..4003eff1e5 --- /dev/null +++ b/gnu/tests/security.scm @@ -0,0 +1,314 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2022 muradm +;;; +;;; 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 . + +(define-module (gnu tests security) + #:use-module (guix gexp) + #:use-module (gnu packages admin) + #:use-module (gnu services) + #:use-module (gnu services security) + #:use-module (gnu services ssh) + #:use-module (gnu system) + #:use-module (gnu system vm) + #:use-module (gnu tests) + #:export (%test-fail2ban-basic + %test-fail2ban-simple + %test-fail2ban-extending)) + + +;;; +;;; fail2ban tests +;;; + +(define (run-fail2ban-basic-test) + + (define os + (marionette-operating-system + (simple-operating-system + (service fail2ban-service-type)) + #:imported-modules '((gnu services herd) + (guix combinators)))) + + (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))) + + (define (wait-for-unix-socket-m socket) + (wait-for-unix-socket socket marionette)) + + (test-runner-current (system-test-runner #$output)) + (test-begin "fail2ban-basic-test") + + (test-assert "fail2ban running" + (marionette-eval + '(begin + (use-modules (gnu services herd)) + (start-service 'fail2ban)) + marionette)) + + (test-assert "fail2ban socket ready" + (wait-for-unix-socket-m + "/var/run/fail2ban/fail2ban.sock")) + + (test-assert "fail2ban pid ready" + (marionette-eval + '(file-exists? "/var/run/fail2ban/fail2ban.pid") + marionette)) + + (test-assert "fail2ban log file" + (marionette-eval + '(file-exists? "/var/log/fail2ban.log") + marionette)) + + (test-end)))) + + (gexp->derivation "fail2ban-basic-test" test)) + +(define %test-fail2ban-basic + (system-test + (name "fail2ban-basic") + (description "Test basic fail2ban running capability.") + (value (run-fail2ban-basic-test)))) + +(define %fail2ban-server-cmd + (program-file + "fail2ban-server-cmd" + #~(begin + (let ((cmd #$(file-append fail2ban "/bin/fail2ban-server"))) + (apply execl cmd cmd `("-p" "/var/run/fail2ban/fail2ban.pid" + "-s" "/var/run/fail2ban/fail2ban.sock" + ,@(cdr (program-arguments)))))))) + +(define (run-fail2ban-simple-test) + + (define os + (marionette-operating-system + (simple-operating-system + (service + fail2ban-service-type + (fail2ban-configuration + (jails + (list + (fail2ban-jail-configuration (name "sshd") (enabled #t))))))) + #:imported-modules '((gnu services herd) + (guix combinators)))) + + (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) + (ice-9 popen) + (ice-9 rdelim) + (rnrs io ports) + (gnu build marionette) + (guix build utils)) + + (define marionette (make-marionette (list #$vm))) + + (define (wait-for-unix-socket-m socket) + (wait-for-unix-socket socket marionette)) + + (test-runner-current (system-test-runner #$output)) + (test-begin "fail2ban-simple-test") + + (test-assert "fail2ban running" + (marionette-eval + '(begin + (use-modules (gnu services herd)) + (start-service 'fail2ban)) + marionette)) + + (test-assert "fail2ban socket ready" + (wait-for-unix-socket-m + "/var/run/fail2ban/fail2ban.sock")) + + (test-assert "fail2ban pid ready" + (marionette-eval + '(file-exists? "/var/run/fail2ban/fail2ban.pid") + marionette)) + + (test-assert "fail2ban log file" + (marionette-eval + '(file-exists? "/var/log/fail2ban.log") + marionette)) + + (test-equal "fail2ban sshd jail running" + '("Status for the jail: sshd" + "|- Filter" + "| |- Currently failed:\t0" + "| |- Total failed:\t0" + "| `- File list:\t/var/log/secure" + "`- Actions" + " |- Currently banned:\t0" + " |- Total banned:\t0" + " `- Banned IP list:\t" + "") + (marionette-eval + '(begin + (use-modules (ice-9 rdelim) (ice-9 popen) (rnrs io ports)) + (let ((call-command + (lambda (cmd) + (let* ((err-cons (pipe)) + (port (with-error-to-port (cdr err-cons) + (lambda () (open-input-pipe cmd)))) + (_ (setvbuf (car err-cons) 'block + (* 1024 1024 16))) + (result (read-delimited "" port))) + (close-port (cdr err-cons)) + (values result (read-delimited "" (car err-cons))))))) + (string-split + (call-command + (string-join (list #$%fail2ban-server-cmd "status" "sshd") " ")) + #\newline))) + marionette)) + + (test-equal "fail2ban sshd jail running" + 0 + (marionette-eval + '(status:exit-val (system* #$%fail2ban-server-cmd "status" "sshd")) + marionette)) + + (test-end)))) + + (gexp->derivation "fail2ban-simple-test" test)) + +(define %test-fail2ban-simple + (system-test + (name "fail2ban-simple") + (description "Test simple fail2ban running capability.") + (value (run-fail2ban-simple-test)))) + +(define (run-fail2ban-extending-test) + + (define os + (marionette-operating-system + (simple-operating-system + (service + (fail2ban-jail-service + openssh-service-type + (fail2ban-jail-configuration + (name "sshd") (enabled #t))) + (openssh-configuration))) + #:imported-modules '((gnu services herd) + (guix combinators)))) + + (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) + (ice-9 popen) + (ice-9 rdelim) + (rnrs io ports) + (gnu build marionette) + (guix build utils)) + + (define marionette (make-marionette (list #$vm))) + + (define (wait-for-unix-socket-m socket) + (wait-for-unix-socket socket marionette)) + + (test-runner-current (system-test-runner #$output)) + (test-begin "fail2ban-extending-test") + + (test-assert "sshd running" + (marionette-eval + '(begin + (use-modules (gnu services herd)) + (start-service 'ssh-daemon)) + marionette)) + + (test-assert "fail2ban socket ready" + (wait-for-unix-socket-m + "/var/run/fail2ban/fail2ban.sock")) + + (test-assert "fail2ban pid ready" + (marionette-eval + '(file-exists? "/var/run/fail2ban/fail2ban.pid") + marionette)) + + (test-assert "fail2ban log file" + (marionette-eval + '(file-exists? "/var/log/fail2ban.log") + marionette)) + + (test-equal "fail2ban sshd jail running" + '("Status for the jail: sshd" + "|- Filter" + "| |- Currently failed:\t0" + "| |- Total failed:\t0" + "| `- File list:\t/var/log/secure" + "`- Actions" + " |- Currently banned:\t0" + " |- Total banned:\t0" + " `- Banned IP list:\t" + "") + (marionette-eval + '(begin + (use-modules (ice-9 rdelim) (ice-9 popen) (rnrs io ports)) + (let ((call-command + (lambda (cmd) + (let* ((err-cons (pipe)) + (port (with-error-to-port (cdr err-cons) + (lambda () (open-input-pipe cmd)))) + (_ (setvbuf (car err-cons) 'block + (* 1024 1024 16))) + (result (read-delimited "" port))) + (close-port (cdr err-cons)) + (values result (read-delimited "" (car err-cons))))))) + (string-split + (call-command + (string-join (list #$%fail2ban-server-cmd "status" "sshd") " ")) + #\newline))) + marionette)) + + (test-equal "fail2ban sshd jail running" + 0 + (marionette-eval + '(status:exit-val (system* #$%fail2ban-server-cmd "status" "sshd")) + marionette)) + + (test-end)))) + + (gexp->derivation "fail2ban-extending-test" test)) + +(define %test-fail2ban-extending + (system-test + (name "fail2ban-extending") + (description "Test extending fail2ban running capability.") + (value (run-fail2ban-extending-test)))) -- 2.37.1 From debbugs-submit-bounces@debbugs.gnu.org Mon Aug 22 13:26:28 2022 Received: (at 56608) by debbugs.gnu.org; 22 Aug 2022 17:26:28 +0000 Received: from localhost ([127.0.0.1]:41644 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oQBBy-00076q-Kb for submit@debbugs.gnu.org; Mon, 22 Aug 2022 13:26:28 -0400 Received: from nomad-cl1.staging.muradm.net ([139.162.159.157]:48164 helo=nomad-cl1.muradm.net) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oQBBm-000767-Lx for 56608@debbugs.gnu.org; Mon, 22 Aug 2022 13:26:16 -0400 Received: from localhost ([127.0.0.1]:58070) by nomad-cl1.muradm.net with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1oQBAs-0006U4-1a; Mon, 22 Aug 2022 17:25:18 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=muradm.net; s=mail; h=Content-Transfer-Encoding:Content-Type:MIME-Version:References: In-Reply-To:Message-Id:Date:Subject:To:From:Sender:Reply-To:Cc:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=i6gPBEhqwx1G+NINPKAVv68azDKRi4exkM0mszk6yNk=; b=Jv4NtRkKe6TigjqZcX/IWnk2QH NNvqljQcFZBrgPdJh9VKVcYLv8poiYkG5MKUxLm0sMBkoeUbDSOVWBeubFAhACCCceU+uulMiXyt7 zzMcdgExE2/S2Y74dY3aG9ftAWJ2DSntuccWifeifBbuops/R0jQ+6eAOZNNUm9bxkHZyFlfwbjxk lPlaG58K3AQ33P2sArl+3Jq+7+zZfQZWMzOzWfpTUsXMXNDPs0cGViGNy2nXRAlP6lnSBH5nmwW1X 6Qdh72Wpn7xKCU6Y/EG5hmv7U4OEo9Z130U/wVC9yuzklANIMSE5V64JATcVr4BJpS4f1E89ooeab PQIocTkTIq2PIwYm7xK6KQqcPR2q1J7PHBFpx91w+kZWQVxRrSKB9vxxjQlU7j8XmGJprjmvMu4wG qpPlPLrK1LihxaCNggaRaJzwTn11wY+Prc6t2/tHPKqu7kd5G5xiRXkoJo+sa6Z/Vy9M0ZXcHRe+i Uv+PGSNsvT2d8d4AZirzRcrn; Received: from muradm by localhost with local (Exim 4.96) (envelope-from ) id 1oQBBg-0008CY-1e; Mon, 22 Aug 2022 20:26:08 +0300 From: muradm To: 56608@debbugs.gnu.org, Maxim Cournoyer Subject: Re: bug#56608: [PATCH v2 1/2] gnu: security: Add fail2ban-service-type. Date: Mon, 22 Aug 2022 20:26:06 +0300 Message-Id: <20220822172607.31515-2-mail@muradm.net> X-Mailer: git-send-email 2.37.1 In-Reply-To: <20220822172607.31515-1-mail@muradm.net> References: <87edxxqpg3.fsf@gmail.com> <20220822172607.31515-1-mail@muradm.net> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 56608 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) * gnu/services/security.scm: New module. * gnu/local.mk: Add new security module. * doc/guix.text: Add fail2ban-service-type documentation. --- doc/guix.texi | 249 ++++++++++++++++++++++++ gnu/local.mk | 2 + gnu/services/security.scm | 385 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 636 insertions(+) create mode 100644 gnu/services/security.scm diff --git a/doc/guix.texi b/doc/guix.texi index 023b48ae35..5467f47412 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -36283,6 +36283,255 @@ Extra command line options for @code{nix-service-type}. @end table @end deftp +@cindex Fail2ban +@subsubheading Fail2ban service + +@uref{http://www.fail2ban.org/, @code{fail2ban}} scans log files +(e.g. @code{/var/log/apache/error_log}) and bans IPs that show the malicious +signs -- too many password failures, seeking for exploits, etc. + +@code{fail2ban} service is provided in @code{(gnu services security)} module. + +This is the type of the service that runs @code{fail2ban} daemon. It can be +used in various ways, which are: + +@itemize + +@item Explicit configuration +users are free to enable @code{fail2ban} configuration without strong +dependency. + +@item On-demand extending configuration +convenience @code{fail2ban-jail-service} function is provided, in order +to extend existing services on-demand. + +@item Permanent extending configuration +service developers may not @code{fail2ban-service-type} in service-type's +extensions. + +@end itemize + +@defvr {Scheme Variable} fail2ban-service-type + +This is the type of the service that runs @code{fail2ban} daemon. It can be +configured explicitly as following: + +@lisp +(append + (list + ;; excplicit configuration, this way fail2ban daemon + ;; will start and do its thing for sshd jail + (service fail2ban-service-type + (fail2ban-configuration + (extra-jails + (list + (fail2ban-jail-configuration + (name "sshd") + (enabled #t)))))) + ;; there is no direct dependency with actual openssh + ;; server configuration, it could even be omitted here + (service openssh-service-type)) + %base-services) +@end lisp +@end defvr + +@deffn {Scheme Procedure} fail2ban-jail-service @var{svc-type} @var{jail} +Return extended @var{svc-type} of @code{} with added +@var{jail} of type @code{fail2ban-jail-configuration} extension +for @code{fail2ban-service-type}. + +For example: + +@lisp +(append + (list + (service + ;; Using convenience function we can extend virtually + ;; any service type with any fail2ban jail. + ;; This way we don't have to explicitly extend services + ;; with fail2ban-service-type. + (fail2ban-jail-service + openssh-service-type + (fail2ban-jail-configuration + (name "sshd") + (enabled #t))) + (openssh-configuration ...)))) +@end lisp +@end deffn + +@deftp {Data Type} fail2ban-configuration +Available @code{fail2ban-configuration} fields are: + +@table @asis +@item @code{fail2ban} (default: @code{fail2ban}) (type: package) +The @code{fail2ban} package to use. It used for both binaries and as +base default configuration that will be extended with +@code{}s. + +@item @code{run-directory} (default: @code{"/var/run/fail2ban"}) (type: string) +State directory for @code{fail2ban} daemon. + +@item @code{jails} (default: @code{()}) (type: list-of-fail2ban-jail-configurations) +Instances of @code{} collected from +extensions. + +@item @code{extra-jails} (default: @code{()}) (type: list-of-fail2ban-jail-configurations) +Instances of @code{} provided by user +explicitly. + +@item @code{extra-content} (type: maybe-string) +Extra raw content to add at the end of @file{jail.local}. + +@end table + +@end deftp + + +@deftp {Data Type} fail2ban-jail-configuration +Available @code{fail2ban-jail-configuration} fields are: + +@table @asis +@item @code{name} (type: string) +Required name of this jail configuration. + +@item @code{enabled} (type: maybe-boolean) +Either @code{#t} or @code{#f} for @samp{true} and @samp{false} +respectively. + +@item @code{backend} (type: maybe-symbol) +Backend to be used to detect changes in the @code{ogpath}. + +@item @code{maxretry} (type: maybe-integer) +Is the number of failures before a host get banned (e.g. @code{(maxretry +5)}). + +@item @code{maxmatches} (type: maybe-integer) +Is the number of matches stored in ticket (resolvable via tag +@code{}) in action. + +@item @code{findtime} (type: maybe-string) +A host is banned if it has generated @code{maxretry} during the last +@code{findtime} seconds (e.g. @code{(findtime "10m")}). + +@item @code{bantime} (type: maybe-string) +Is the number of seconds that a host is banned (e.g. @code{(bantime +"10m")}). + +@item @code{bantime.increment} (type: maybe-boolean) +Allows to use database for searching of previously banned ip's to +increase a default ban time using special formula. + +@item @code{bantime.factor} (type: maybe-string) +Is a coefficient to calculate exponent growing of the formula or common +multiplier. + +@item @code{bantime.formula} (type: maybe-string) +Used by default to calculate next value of ban time. + +@item @code{bantime.multipliers} (type: maybe-string) +Used to calculate next value of ban time instead of formula. + +@item @code{bantime.maxtime} (type: maybe-string) +Is the max number of seconds using the ban time can reach (doesn't grow +further). + +@item @code{bantime.rndtime} (type: maybe-string) +Is the max number of seconds using for mixing with random time to +prevent ``clever'' botnets calculate exact time IP can be unbanned +again. + +@item @code{bantime.overalljails} (type: maybe-boolean) +Either @code{#t} or @code{#f} for @samp{true} and @samp{false} +respectively. +@itemize @bullet +@item @code{true} - specifies the search of IP in the database will be executed cross over all jails +@item @code{false} - only current jail of the ban IP will be searched +@end itemize + +@item @code{ignorecommand} (type: maybe-string) +External command that will take an tagged arguments to ignore. Note: +while provided, currently unimplemented in the context of @code{guix}. + +@item @code{ignoreself} (type: maybe-boolean) +Specifies whether the local resp. own IP addresses should be ignored. + +@item @code{ignoreip} (default: @code{()}) (type: list-of-strings) +Can be a list of IP addresses, CIDR masks or DNS hosts. @code{fail2ban} +will not ban a host which matches an address in this list. + +@item @code{ignorecache} (type: maybe-fail2ban-ignorecache-configuration) +Provide cache parameters for ignore failure check. + +@item @code{filter} (type: maybe-fail2ban-jail-filter-configuration) +Defines the filter to use by the jail, using +@code{}. By default jails have +names matching their filter name. + +@item @code{logtimezone} (type: maybe-string) +Force the time zone for log lines that don't have one. + +@item @code{logencoding} (type: maybe-symbol) +Specifies the encoding of the log files handled by the jail. Possible +values: @code{'ascii}, @code{'utf-8}, @code{'auto}. + +@item @code{logpath} (default: @code{()}) (type: list-of-strings) +Filename(s) of the log files to be monitored. + +@item @code{action} (default: @code{()}) (type: list-of-fail2ban-jail-actions) +List of @code{}. + +@item @code{extra-content} (type: maybe-string) +Extra content for the jail configuration. + +@end table + +@end deftp + +@deftp {Data Type} fail2ban-ignorecache-configuration +Available @code{fail2ban-ignorecache-configuration} fields are: + +@table @asis +@item @code{key} (type: string) +Cache key. + +@item @code{max-count} (type: integer) +Cache size. + +@item @code{max-time} (type: integer) +Cache time. + +@end table + +@end deftp + +@deftp {Data Type} fail2ban-jail-action-configuration +Available @code{fail2ban-jail-action-configuration} fields are: + +@table @asis +@item @code{name} (type: string) +Action name. + +@item @code{arguments} (default: @code{()}) (type: list-of-arguments) +Action arguments. + +@end table + +@end deftp + +@deftp {Data Type} fail2ban-jail-filter-configuration +Available @code{fail2ban-jail-filter-configuration} fields are: + +@table @asis +@item @code{name} (type: string) +Filter to use. + +@item @code{mode} (type: maybe-string) +Mode for filter. + +@end table + +@end deftp + @node Setuid Programs @section Setuid Programs diff --git a/gnu/local.mk b/gnu/local.mk index 26dfb6afe2..acd41797b9 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -51,6 +51,7 @@ # Copyright © 2022 Remco van 't Veer # Copyright © 2022 Artyom V. Poptsov # Copyright © 2022 John Kehayias +# Copyright © 2022 muradm # # This file is part of GNU Guix. # @@ -672,6 +673,7 @@ GNU_SYSTEM_MODULES = \ %D%/services/nfs.scm \ %D%/services/pam-mount.scm \ %D%/services/science.scm \ + %D%/services/security.scm \ %D%/services/security-token.scm \ %D%/services/shepherd.scm \ %D%/services/sound.scm \ diff --git a/gnu/services/security.scm b/gnu/services/security.scm new file mode 100644 index 0000000000..79e202565c --- /dev/null +++ b/gnu/services/security.scm @@ -0,0 +1,385 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2022 muradm +;;; +;;; 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 . + +(define-module (gnu services security) + #:use-module (gnu packages admin) + #:use-module (gnu services) + #:use-module (gnu services configuration) + #:use-module (gnu services shepherd) + #:use-module (guix gexp) + #:use-module (guix packages) + #:use-module (guix records) + #:use-module (guix ui) + #:use-module (ice-9 format) + #:use-module (ice-9 match) + #:use-module (srfi srfi-1) + #:export (fail2ban-configuration + fail2ban-configuration-fields + fail2ban-jail-configuration + fail2ban-jail-configuration-fields + + fail2ban-ignorecache-configuration + fail2ban-ignorecache-configuration-fields + fail2ban-jail-action-configuration + fail2ban-jail-action-configuration-fields + fail2ban-jail-filter-configuration + fail2ban-jail-filter-configuration-fields + + fail2ban-service-type + fail2ban-jail-service)) + +(define-configuration/no-serialization fail2ban-ignorecache-configuration + (key (string) "Cache key.") + (max-count (integer) "Cache size.") + (max-time (integer) "Cache time.")) + +(define serialize-fail2ban-ignorecache-configuration + (match-lambda + (($ _ key max-count max-time) + (format #f "key=\"~a\", max-count=~d, max-time=~d" + key max-count max-time)))) + +(define-maybe/no-serialization string) + +(define-configuration/no-serialization fail2ban-jail-filter-configuration + (name (string) "Filter to use.") + (mode maybe-string "Mode for filter.")) + +(define serialize-fail2ban-jail-filter-configuration + (match-lambda + (($ _ name mode) + (format #f "~a~a" + name (if (eq? 'unset mode) "" (format #f "[mode=~a]" mode)))))) + +(define (list-of-arguments? lst) + (every + (lambda (e) (and (pair? e) + (string? (car e)) + (or (string? (cdr e)) + (list-of-strings? (cdr e))))) + lst)) + +(define-configuration/no-serialization fail2ban-jail-action-configuration + (name (string) "Action name.") + (arguments (list-of-arguments '()) "Action arguments.")) + +(define list-of-fail2ban-jail-actions? + (list-of fail2ban-jail-action-configuration?)) + +(define (serialize-fail2ban-jail-action-configuration-arguments args) + (let* ((multi-value + (lambda (v) + (format #f "~a" (string-join v ",")))) + (any-value + (lambda (v) + (if (list? v) (string-append "\"" (multi-value v) "\"") v))) + (key-value + (lambda (e) + (format #f "~a=~a" (car e) (any-value (cdr e)))))) + (format #f "~a" (string-join (map key-value args) ",")))) + +(define serialize-fail2ban-jail-action-configuration + (match-lambda + (($ _ name arguments) + (format + #f "~a~a" + name + (if (null? arguments) "" + (format + #f "[~a]" + (serialize-fail2ban-jail-action-configuration-arguments + arguments))))))) + +(define fail2ban-backend->string + (match-lambda + ('auto "auto") + ('pyinotify "pyinotify") + ('gamin "gamin") + ('polling "polling") + ('systemd "systemd") + (unknown + (leave + (G_ "fail2ban: '~a' is not a supported backend~%") unknown)))) + +(define fail2ban-logencoding->string + (match-lambda + ('auto "auto") + ('utf-8 "utf-8") + ('ascii "ascii") + (unknown + (leave + (G_ "fail2ban: '~a' is not a supported log encoding~%") unknown)))) + +(define (fail2ban-jail-configuration-serialize-string field-name value) + #~(string-append #$(symbol->string field-name) " = " #$value "\n")) + +(define (fail2ban-jail-configuration-serialize-integer field-name value) + (fail2ban-jail-configuration-serialize-string + field-name (number->string value))) + +(define (fail2ban-jail-configuration-serialize-boolean field-name value) + (fail2ban-jail-configuration-serialize-string + field-name (if value "true" "false"))) + +(define (fail2ban-jail-configuration-serialize-backend field-name value) + (if (eq? 'unset value) "" + (fail2ban-jail-configuration-serialize-string + field-name (fail2ban-backend->string value)))) + +(define (fail2ban-jail-configuration-serialize-fail2ban-ignorecache-configuration field-name value) + (fail2ban-jail-configuration-serialize-string + field-name (serialize-fail2ban-ignorecache-configuration value))) + +(define (fail2ban-jail-configuration-serialize-fail2ban-jail-filter-configuration field-name value) + (fail2ban-jail-configuration-serialize-string + field-name (serialize-fail2ban-jail-filter-configuration value))) + +(define (fail2ban-jail-configuration-serialize-logencoding field-name value) + (if (eq? 'unset value) "" + (fail2ban-jail-configuration-serialize-string + field-name (fail2ban-logencoding->string value)))) + +(define (fail2ban-jail-configuration-serialize-list-of-strings field-name value) + (if (null? value) "" + (fail2ban-jail-configuration-serialize-string + field-name (string-join value " ")))) + +(define (fail2ban-jail-configuration-serialize-list-of-fail2ban-jail-actions field-name value) + (if (null? value) "" + (fail2ban-jail-configuration-serialize-string + field-name (string-join + (map serialize-fail2ban-jail-action-configuration value) "\n")))) + +(define (fail2ban-jail-configuration-serialize-symbol field-name value) + (fail2ban-jail-configuration-serialize-string field-name (symbol->string value))) + +(define (fail2ban-jail-configuration-serialize-extra-content field-name value) + (if (eq? 'unset value) "" (string-append "\n" value "\n"))) + +(define-maybe integer (prefix fail2ban-jail-configuration-)) +(define-maybe string (prefix fail2ban-jail-configuration-)) +(define-maybe boolean (prefix fail2ban-jail-configuration-)) +(define-maybe symbol (prefix fail2ban-jail-configuration-)) +(define-maybe fail2ban-ignorecache-configuration (prefix fail2ban-jail-configuration-)) +(define-maybe fail2ban-jail-filter-configuration (prefix fail2ban-jail-configuration-)) + +(define-configuration fail2ban-jail-configuration + (name + (string) + "Required name of this jail configuration.") + (enabled + maybe-boolean + "Either @code{#t} or @code{#f} for @samp{true} and +@samp{false} respectively.") + (backend + maybe-symbol + "Backend to be used to detect changes in the @code{ogpath}." + fail2ban-jail-configuration-serialize-backend) + (maxretry + maybe-integer + "Is the number of failures before a host get banned +(e.g. @code{(maxretry 5)}).") + (maxmatches + maybe-integer + "Is the number of matches stored in ticket (resolvable via +tag @code{}) in action.") + (findtime + maybe-string + "A host is banned if it has generated @code{maxretry} during the last +@code{findtime} seconds (e.g. @code{(findtime \"10m\")}).") + (bantime + maybe-string + "Is the number of seconds that a host is banned +(e.g. @code{(bantime \"10m\")}).") + (bantime.increment + maybe-boolean + "Allows to use database for searching of previously banned +ip's to increase a default ban time using special formula.") + (bantime.factor + maybe-string + "Is a coefficient to calculate exponent growing of the +formula or common multiplier.") + (bantime.formula + maybe-string + "Used by default to calculate next value of ban time.") + (bantime.multipliers + maybe-string + "Used to calculate next value of ban time instead of formula.") + (bantime.maxtime + maybe-string + "Is the max number of seconds using the ban time can reach +(doesn't grow further).") + (bantime.rndtime + maybe-string + "Is the max number of seconds using for mixing with random time +to prevent ``clever'' botnets calculate exact time IP can be unbanned again.") + (bantime.overalljails + maybe-boolean + "Either @code{#t} or @code{#f} for @samp{true} and @samp{false} respectively. +@itemize +@item @code{true} - specifies the search of IP in the database will be executed cross over all jails +@item @code{false} - only current jail of the ban IP will be searched +@end itemize") + (ignorecommand + maybe-string + "External command that will take an tagged arguments to ignore. +Note: while provided, currently unimplemented in the context of @code{guix}.") + (ignoreself + maybe-boolean + "Specifies whether the local resp. own IP addresses should be ignored.") + (ignoreip + (list-of-strings '()) + "Can be a list of IP addresses, CIDR masks or DNS hosts. @code{fail2ban} +will not ban a host which matches an address in this list.") + (ignorecache + maybe-fail2ban-ignorecache-configuration + "Provide cache parameters for ignore failure check.") + (filter + maybe-fail2ban-jail-filter-configuration + "Defines the filter to use by the jail, using +@code{}. +By default jails have names matching their filter name.") + (logtimezone + maybe-string + "Force the time zone for log lines that don't have one.") + (logencoding + maybe-symbol + "Specifies the encoding of the log files handled by the jail. +Possible values: @code{'ascii}, @code{'utf-8}, @code{'auto}." + fail2ban-jail-configuration-serialize-logencoding) + (logpath + (list-of-strings '()) + "Filename(s) of the log files to be monitored.") + (action + (list-of-fail2ban-jail-actions '()) + "List of @code{}.") + (extra-content + maybe-string + "Extra content for the jail configuration." + fail2ban-jail-configuration-serialize-extra-content) + (prefix fail2ban-jail-configuration-)) + +(define list-of-fail2ban-jail-configurations? + (list-of fail2ban-jail-configuration?)) + +(define (serialize-fail2ban-jail-configuration config) + #~(string-append + #$(format #f "[~a]\n" (fail2ban-jail-configuration-name config)) + #$(serialize-configuration + config fail2ban-jail-configuration-fields))) + +(define-configuration/no-serialization fail2ban-configuration + (fail2ban + (package fail2ban) + "The @code{fail2ban} package to use. It used for both binaries and +as base default configuration that will be extended with +@code{}s.") + (run-directory + (string "/var/run/fail2ban") + "State directory for @code{fail2ban} daemon.") + (jails + (list-of-fail2ban-jail-configurations '()) + "Instances of @code{} collected from +extensions.") + (extra-jails + (list-of-fail2ban-jail-configurations '()) + "Instances of @code{} provided by user +explicitly.") + (extra-content + maybe-string + "Extra raw content to add at the end of @file{jail.local}.")) + +(define (serialize-fail2ban-configuration config) + (let* ((jails (fail2ban-configuration-jails config)) + (extra-jails (fail2ban-configuration-extra-jails config)) + (extra-content (fail2ban-configuration-extra-content config))) + (interpose + (append (map serialize-fail2ban-jail-configuration + (append jails extra-jails)) + (list (if (eq? 'unset extra-content) "" extra-content)))))) + +(define (make-fail2ban-configuration-package config) + (let* ((fail2ban (fail2ban-configuration-fail2ban config)) + (jail-local (apply mixed-text-file "jail.local" + (serialize-fail2ban-configuration config)))) + (computed-file + "fail2ban-configuration" + (with-imported-modules '((guix build utils)) + #~(begin + (use-modules (guix build utils)) + (let* ((out (ungexp output))) + (mkdir-p (string-append out "/etc/fail2ban")) + (copy-recursively + (string-append #$fail2ban "/etc/fail2ban") + (string-append out "/etc/fail2ban")) + (symlink + #$jail-local + (string-append out "/etc/fail2ban/jail.local")))))))) + +(define (fail2ban-shepherd-service config) + (match-record config + (fail2ban run-directory) + (let* ((fail2ban-server (file-append fail2ban "/bin/fail2ban-server")) + (pid-file (in-vicinity run-directory "fail2ban.pid")) + (socket-file (in-vicinity run-directory "fail2ban.sock")) + (config-dir (make-fail2ban-configuration-package config)) + (config-dir (file-append config-dir "/etc/fail2ban")) + (fail2ban-action + (lambda args + #~(lambda _ + (invoke #$fail2ban-server + "-c" #$config-dir + "-p" #$pid-file + "-s" #$socket-file + "-b" + #$@args))))) + + ;; TODO: Add 'reload' action. + (list (shepherd-service + (provision '(fail2ban)) + (documentation "Run the fail2ban daemon.") + (requirement '(user-processes)) + (modules `((ice-9 match) + ,@%default-modules)) + (start (fail2ban-action "start")) + (stop (fail2ban-action "stop"))))))) + +(define fail2ban-service-type + (service-type (name 'fail2ban) + (extensions + (list (service-extension shepherd-root-service-type + fail2ban-shepherd-service))) + (compose concatenate) + (extend (lambda (config jails) + (fail2ban-configuration + (inherit config) + (jails + (append + (fail2ban-configuration-jails config) + jails))))) + (default-value (fail2ban-configuration)) + (description "Run the fail2ban server."))) + +(define (fail2ban-jail-service svc-type jail) + (service-type + (inherit svc-type) + (extensions + (append + (service-type-extensions svc-type) + (list (service-extension fail2ban-service-type + (lambda _ (list jail)))))))) -- 2.37.1 From debbugs-submit-bounces@debbugs.gnu.org Mon Aug 22 14:54:09 2022 Received: (at 56608) by debbugs.gnu.org; 22 Aug 2022 18:54:09 +0000 Received: from localhost ([127.0.0.1]:41748 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oQCYq-000368-Gl for submit@debbugs.gnu.org; Mon, 22 Aug 2022 14:54:09 -0400 Received: from mail-qk1-f181.google.com ([209.85.222.181]:42882) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oQCYm-00035S-RU for 56608@debbugs.gnu.org; Mon, 22 Aug 2022 14:54:06 -0400 Received: by mail-qk1-f181.google.com with SMTP id h27so8596470qkk.9 for <56608@debbugs.gnu.org>; Mon, 22 Aug 2022 11:54:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:user-agent:message-id :in-reply-to:date:references:subject:cc:to:from:from:to:cc; bh=tcSoAvgDjCzIIl+dHyrvCo+mwK63u727eiP2Nr00zbY=; b=D1uMx8+a4iTsK6PLr38IdvaONVArW5K3owW3XgF/1xIoTfd/IFFP7pJHotSjpX/Kbo 9weg/IY0m3QmWayAQWB91R2bKU7ESeiXcjPXWs33XmoK2NsbUSZXuTZLHNU3mV+D+4R+ +5DRNagEIGe7w9G8TWZcpBw+vft9MWeqrBdXdV0YUaIjJJJsgUPLf76kLIq4MsGTWdtV PL2dZL1lvyxzf2g8h7M7W5stUA6W1LHO5I2BVDhKfQuhrnIAGXTLFbXpkLWSEytb2gRL qsLVT8iql16rLl1kMcw2nni5VzvcgsTnkmA3Ja+ivlqTyerLo27K8pU2PA57cXUPS4rY 76Hw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:user-agent:message-id :in-reply-to:date:references:subject:cc:to:from:x-gm-message-state :from:to:cc; bh=tcSoAvgDjCzIIl+dHyrvCo+mwK63u727eiP2Nr00zbY=; b=bvZiEk3Tpgdl2TZpA1GqZtjSdA2JHu1ebyxYUVTEhk6GgWRqMAP01pykCtlJSs1r4o tcpT7Cg1jaR+z1nZ0vPGZT78WTA6yoRS2y2uD/qQ4ztxsMSXKfv7n4TjHeROr1b4iKHB OJxp8sJs/R90Ze+7Y3CCOWYAKoiWoZuQ21CNJDZpFGMfGTgnEdZZ/VndUv8DeY48D6iO xEcLtPH6nv04IqudqAkPoKoAyN8/lug826xeO3P1kuaj1T3wlt5MJBCmoomCKweZ95Q9 HeqQzDCzbba+zz60nhChcpK/jju6EaHYjB3DtI0dMxRiALv7OQNYMLt9QEkcTVzVCcD2 dG4g== X-Gm-Message-State: ACgBeo1qqUqP4w8pHcyUh3cGJMSFg4FTtMlGx8DoYeWu/l506cp/Su+A 5aFu4IyJNgCaquK0G6gbAtLja7N7skY= X-Google-Smtp-Source: AA6agR5xtAhNu6D0WKJ4oUzN7eDTnohAKziV7wCHj9g398ToDH/frxQI440wo83xd1ie5ZazsXzvlA== X-Received: by 2002:ae9:f40b:0:b0:6ba:c027:8d2d with SMTP id y11-20020ae9f40b000000b006bac0278d2dmr13372377qkl.642.1661194438754; Mon, 22 Aug 2022 11:53:58 -0700 (PDT) Received: from hurd (dsl-150-55.b2b2c.ca. [66.158.150.55]) by smtp.gmail.com with ESMTPSA id l16-20020a37f910000000b006bbe6e89bdcsm7908551qkj.31.2022.08.22.11.53.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 22 Aug 2022 11:53:58 -0700 (PDT) From: Maxim Cournoyer To: muradm Subject: Re: bug#56608: [PATCH v2 1/2] gnu: security: Add fail2ban-service-type. References: <87edxxqpg3.fsf@gmail.com> <20220822172607.31515-1-mail@muradm.net> <20220822172607.31515-2-mail@muradm.net> Date: Mon, 22 Aug 2022 14:53:57 -0400 In-Reply-To: <20220822172607.31515-2-mail@muradm.net> (muradm's message of "Mon, 22 Aug 2022 20:26:06 +0300") Message-ID: <87o7wcgldm.fsf@gmail.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/28.1 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 56608 Cc: 56608@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) Hi, Well done implementing the configuration records using define-configuration! I believe it'll help its users avoiding mistakes. Here are some comments for the guix.texi bits which are not automatically generated: muradm writes: > * gnu/services/security.scm: New module. > * gnu/local.mk: Add new security module. > * doc/guix.text: Add fail2ban-service-type documentation. > --- > doc/guix.texi | 249 ++++++++++++++++++++++++ > gnu/local.mk | 2 + > gnu/services/security.scm | 385 ++++++++++++++++++++++++++++++++++++++ > 3 files changed, 636 insertions(+) > create mode 100644 gnu/services/security.scm > > diff --git a/doc/guix.texi b/doc/guix.texi > index 023b48ae35..5467f47412 100644 > --- a/doc/guix.texi > +++ b/doc/guix.texi > @@ -36283,6 +36283,255 @@ Extra command line options for @code{nix-servic= e-type}. > @end table > @end deftp > > +@cindex Fail2ban > +@subsubheading Fail2ban service > + > +@uref{http://www.fail2ban.org/, @code{fail2ban}} scans log files > +(e.g. @code{/var/log/apache/error_log}) and bans IPs that show the malic= ious > +signs -- too many password failures, seeking for exploits, etc. > + > +@code{fail2ban} service is provided in @code{(gnu services security)} mo= dule. > + > +This is the type of the service that runs @code{fail2ban} daemon. It can= be ^ two spa= ces > +used in various ways, which are: > + > +@itemize > + > +@item Explicit configuration > +users are free to enable @code{fail2ban} configuration without strong > +dependency. > + > +@item On-demand extending configuration > +convenience @code{fail2ban-jail-service} function is provided, in order > +to extend existing services on-demand. > + > +@item Permanent extending configuration > +service developers may not @code{fail2ban-service-type} in service-type's ^ missing verb > +extensions. > + > +@end itemize > + > +@defvr {Scheme Variable} fail2ban-service-type > + > +This is the type of the service that runs @code{fail2ban} daemon. It can= be ^ two spa= ces > +configured explicitly as following: > + > +@lisp > +(append > + (list > + ;; excplicit configuration, this way fail2ban daemon > + ;; will start and do its thing for sshd jail Typo (excplicit). Also, please use full sentences for stand-alone comments (with proper punctuation). > + (service fail2ban-service-type > + (fail2ban-configuration > + (extra-jails > + (list > + (fail2ban-jail-configuration > + (name "sshd") > + (enabled #t)))))) > + ;; there is no direct dependency with actual openssh > + ;; server configuration, it could even be omitted here Likewise. [...] > diff --git a/gnu/services/security.scm b/gnu/services/security.scm > new file mode 100644 > index 0000000000..79e202565c > --- /dev/null > +++ b/gnu/services/security.scm > @@ -0,0 +1,385 @@ [...] > +(define-configuration/no-serialization fail2ban-ignorecache-configuration > + (key (string) "Cache key.") > + (max-count (integer) "Cache size.") > + (max-time (integer) "Cache time.")) Note that when you do not use a default value, you can leave out the parenthesizes. > + > +(define serialize-fail2ban-ignorecache-configuration > + (match-lambda > + (($ _ key max-count max-time) > + (format #f "key=3D\"~a\", max-count=3D~d, max-time=3D~d" > + key max-count max-time)))) > + > +(define-maybe/no-serialization string) > + > +(define-configuration/no-serialization fail2ban-jail-filter-configuration > + (name (string) "Filter to use.") > + (mode maybe-string "Mode for filter.")) > + > +(define serialize-fail2ban-jail-filter-configuration > + (match-lambda > + (($ _ name mode) > + (format #f "~a~a" > + name (if (eq? 'unset mode) "" (format #f "[mode=3D~a]" mode= )))))) You could use (ice-9 format) with a conditional formatting here. The example from info '(guile) Formatted Output' is --8<---------------cut here---------------start------------->8--- (format #f "~:[false~;not false~]" 'abc) =E2=87=92 "not false" --8<---------------cut here---------------end--------------->8--- > +(define (list-of-arguments? lst) > + (every > + (lambda (e) (and (pair? e) > + (string? (car e)) > + (or (string? (cdr e)) > + (list-of-strings? (cdr e))))) > + lst)) It could be better to define a argument? predicate, use the 'list-of' procedure from (gnu services configuration) on it. [...] > +(define (fail2ban-jail-configuration-serialize-fail2ban-ignorecache-conf= iguration field-name value) > + (fail2ban-jail-configuration-serialize-string > + field-name (serialize-fail2ban-ignorecache-configuration value))) > + > +(define (fail2ban-jail-configuration-serialize-fail2ban-jail-filter-conf= iguration field-name value) > + (fail2ban-jail-configuration-serialize-string > + field-name (serialize-fail2ban-jail-filter-configuration value))) > + > +(define (fail2ban-jail-configuration-serialize-logencoding field-name va= lue) > + (if (eq? 'unset value) "" > + (fail2ban-jail-configuration-serialize-string > + field-name (fail2ban-logencoding->string value)))) > + > +(define (fail2ban-jail-configuration-serialize-list-of-strings field-nam= e value) > + (if (null? value) "" > + (fail2ban-jail-configuration-serialize-string > + field-name (string-join value " ")))) > + > +(define (fail2ban-jail-configuration-serialize-list-of-fail2ban-jail-act= ions field-name value) > + (if (null? value) "" > + (fail2ban-jail-configuration-serialize-string > + field-name (string-join > + (map serialize-fail2ban-jail-action-configuration val= ue) "\n")))) > + > +(define (fail2ban-jail-configuration-serialize-symbol field-name value) > + (fail2ban-jail-configuration-serialize-string field-name (symbol->stri= ng value))) > + > +(define (fail2ban-jail-configuration-serialize-extra-content field-name = value) > + (if (eq? 'unset value) "" (string-append "\n" value "\n"))) > + > +(define-maybe integer (prefix fail2ban-jail-configuration-)) > +(define-maybe string (prefix fail2ban-jail-configuration-)) > +(define-maybe boolean (prefix fail2ban-jail-configuration-)) > +(define-maybe symbol (prefix fail2ban-jail-configuration-)) > +(define-maybe fail2ban-ignorecache-configuration (prefix fail2ban-jail-c= onfiguration-)) > +(define-maybe fail2ban-jail-filter-configuration (prefix fail2ban-jail-c= onfiguration-)) Is using the prefix absolutely necessary? It makes things awkwardly long. Since fail2ban-service-type it's the first citizen of (gnu services security), it could enjoy the luxury of not using a prefix, in my opinion. Later services may need to define their own prefix. > +(define-configuration fail2ban-jail-configuration > + (name > + (string) > + "Required name of this jail configuration.") > + (enabled I'd use enabled? and employ a name normalizer (e.g., stripping any trailing '?') in the boolean serializer. > + maybe-boolean > + "Either @code{#t} or @code{#f} for @samp{true} and > +@samp{false} respectively.") > + (backend > + maybe-symbol > + "Backend to be used to detect changes in the @code{ogpath}." > + fail2ban-jail-configuration-serialize-backend) > + (maxretry I think we could use hyphen in the field names, and use a normalizer to strip them at serialization time (assuming hyphens are never allowed in a key name). > + maybe-integer > + "Is the number of failures before a host get banned > +(e.g. @code{(maxretry 5)}).") > + (maxmatches > + maybe-integer > + "Is the number of matches stored in ticket (resolvable via > +tag @code{}) in action.") > + (findtime > + maybe-string > + "A host is banned if it has generated @code{maxretry} during the last > +@code{findtime} seconds (e.g. @code{(findtime \"10m\")}).") > + (bantime > + maybe-string > + "Is the number of seconds that a host is banned > +(e.g. @code{(bantime \"10m\")}).") > + (bantime.increment > + maybe-boolean > + "Allows to use database for searching of previously banned > +ip's to increase a default ban time using special formula.") > + (bantime.factor > + maybe-string > + "Is a coefficient to calculate exponent growing of the > +formula or common multiplier.") > + (bantime.formula > + maybe-string > + "Used by default to calculate next value of ban time.") > + (bantime.multipliers > + maybe-string > + "Used to calculate next value of ban time instead of formula.") > + (bantime.maxtime > + maybe-string > + "Is the max number of seconds using the ban time can reach > +(doesn't grow further).") > + (bantime.rndtime > + maybe-string > + "Is the max number of seconds using for mixing with random time > +to prevent ``clever'' botnets calculate exact time IP can be unbanned ag= ain.") > + (bantime.overalljails We could have the normalization "bantime-" -> "bantime." done in the serializers, to use more Schemey names. > + maybe-boolean > + "Either @code{#t} or @code{#f} for @samp{true} and @samp{false} respe= ctively. > +@itemize > +@item @code{true} - specifies the search of IP in the database will be e= xecuted cross over all jails > +@item @code{false} - only current jail of the ban IP will be searched > +@end itemize") > + (ignorecommand > + maybe-string > + "External command that will take an tagged arguments to ignore. > +Note: while provided, currently unimplemented in the context of @code{gu= ix}.") Then I'd remove it, as I don't see in which other context it would be useful. > + (ignoreself > + maybe-boolean > + "Specifies whether the local resp. own IP addresses should be ignored= .") > + (ignoreip > + (list-of-strings '()) > + "Can be a list of IP addresses, CIDR masks or DNS hosts. @code{fail2b= an} ^ Please use two spaces after period. > +will not ban a host which matches an address in this list.") > + (ignorecache > + maybe-fail2ban-ignorecache-configuration > + "Provide cache parameters for ignore failure check.") > + (filter > + maybe-fail2ban-jail-filter-configuration > + "Defines the filter to use by the jail, using > +@code{}. > +By default jails have names matching their filter name.") > + (logtimezone > + maybe-string > + "Force the time zone for log lines that don't have one.") > + (logencoding > + maybe-symbol > + "Specifies the encoding of the log files handled by the jail. > +Possible values: @code{'ascii}, @code{'utf-8}, @code{'auto}." > + fail2ban-jail-configuration-serialize-logencoding) > + (logpath > + (list-of-strings '()) > + "Filename(s) of the log files to be monitored.") The convention in GNU is to use "file name" rather than "filename". > + (action > + (list-of-fail2ban-jail-actions '()) > + "List of @code{}.") > + (extra-content > + maybe-string > + "Extra content for the jail configuration." > + fail2ban-jail-configuration-serialize-extra-content) > + (prefix fail2ban-jail-configuration-)) > + > +(define list-of-fail2ban-jail-configurations? > + (list-of fail2ban-jail-configuration?)) > + > +(define (serialize-fail2ban-jail-configuration config) > + #~(string-append > + #$(format #f "[~a]\n" (fail2ban-jail-configuration-name config)) > + #$(serialize-configuration > + config fail2ban-jail-configuration-fields))) > + > +(define-configuration/no-serialization fail2ban-configuration > + (fail2ban > + (package fail2ban) > + "The @code{fail2ban} package to use. It used for both binaries and > +as base default configuration that will be extended with > +@code{}s.") > + (run-directory > + (string "/var/run/fail2ban") > + "State directory for @code{fail2ban} daemon.") > + (jails > + (list-of-fail2ban-jail-configurations '()) > + "Instances of @code{} collected from > +extensions.") > + (extra-jails > + (list-of-fail2ban-jail-configurations '()) > + "Instances of @code{} provided by user > +explicitly.") > + (extra-content > + maybe-string > + "Extra raw content to add at the end of @file{jail.local}.")) ^the @file{jail.local} file. > + > +(define (serialize-fail2ban-configuration config) > + (let* ((jails (fail2ban-configuration-jails config)) > + (extra-jails (fail2ban-configuration-extra-jails config)) > + (extra-content (fail2ban-configuration-extra-content config))) > + (interpose > + (append (map serialize-fail2ban-jail-configuration > + (append jails extra-jails)) > + (list (if (eq? 'unset extra-content) "" extra-content)))))) > + > +(define (make-fail2ban-configuration-package config) > + (let* ((fail2ban (fail2ban-configuration-fail2ban config)) > + (jail-local (apply mixed-text-file "jail.local" > + (serialize-fail2ban-configuration config)))) > + (computed-file > + "fail2ban-configuration" > + (with-imported-modules '((guix build utils)) > + #~(begin > + (use-modules (guix build utils)) > + (let* ((out (ungexp output))) ^ use just let here > + (mkdir-p (string-append out "/etc/fail2ban")) > + (copy-recursively > + (string-append #$fail2ban "/etc/fail2ban") > + (string-append out "/etc/fail2ban")) > + (symlink > + #$jail-local > + (string-append out "/etc/fail2ban/jail.local")))))))) > + > +(define (fail2ban-shepherd-service config) > + (match-record config > + (fail2ban run-directory) > + (let* ((fail2ban-server (file-append fail2ban "/bin/fail2ban-server"= )) > + (pid-file (in-vicinity run-directory "fail2ban.pid")) > + (socket-file (in-vicinity run-directory "fail2ban.sock")) > + (config-dir (make-fail2ban-configuration-package config)) > + (config-dir (file-append config-dir "/etc/fail2ban")) > + (fail2ban-action > + (lambda args > + #~(lambda _ > + (invoke #$fail2ban-server > + "-c" #$config-dir > + "-p" #$pid-file > + "-s" #$socket-file > + "-b" > + #$@args))))) > + > + ;; TODO: Add 'reload' action. > + (list (shepherd-service > + (provision '(fail2ban)) > + (documentation "Run the fail2ban daemon.") > + (requirement '(user-processes)) > + (modules `((ice-9 match) > + ,@%default-modules)) > + (start (fail2ban-action "start")) > + (stop (fail2ban-action "stop"))))))) > + > +(define fail2ban-service-type > + (service-type (name 'fail2ban) > + (extensions > + (list (service-extension shepherd-root-service-type > + fail2ban-shepherd-service))) > + (compose concatenate) > + (extend (lambda (config jails) > + (fail2ban-configuration > + (inherit config) > + (jails > + (append > + (fail2ban-configuration-jails config) > + jails))))) > + (default-value (fail2ban-configuration)) > + (description "Run the fail2ban server."))) > + > +(define (fail2ban-jail-service svc-type jail) > + (service-type > + (inherit svc-type) > + (extensions > + (append > + (service-type-extensions svc-type) > + (list (service-extension fail2ban-service-type > + (lambda _ (list jail)))))))) ^ nitpick, but (compose list jail) is more common That looks very good. I haven't checked the tests yet, but I think with the extra polishing suggested above, it should be in a good place to be merged soon! Thanks, Maxim From debbugs-submit-bounces@debbugs.gnu.org Mon Aug 22 15:13:20 2022 Received: (at 56608) by debbugs.gnu.org; 22 Aug 2022 19:13:20 +0000 Received: from localhost ([127.0.0.1]:41803 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oQCrP-0005lz-FF for submit@debbugs.gnu.org; Mon, 22 Aug 2022 15:13:20 -0400 Received: from mail-qk1-f182.google.com ([209.85.222.182]:39529) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oQCrN-0005li-4f for 56608@debbugs.gnu.org; Mon, 22 Aug 2022 15:13:18 -0400 Received: by mail-qk1-f182.google.com with SMTP id c9so7507271qkk.6 for <56608@debbugs.gnu.org>; Mon, 22 Aug 2022 12:13:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:user-agent:message-id :in-reply-to:date:references:subject:cc:to:from:from:to:cc; bh=p4u18mBkXOucIlLnxTvcRAnzoavrt3QphiO/2Ew5Ssw=; b=Ar48FwVLU/bhnpK1NwNW6YW9CgB64LzqEO4VBimolL7IyFqt8eZKI4LGk2P2Uza1z0 T1ogbP7k+ekKsY2aakhEBJ94ih/zmmbjLRoSbQz6IbfI189Vwf5f7J9Xd+r8s+t4c7Jq uBx+J0j94kA2/6D0RuC6ciZ5+ju490fu5pyN8RfHIvN1qhaySAG6q4UStok7s1dpFhQN 52Kc5ZATCKNMByQX07xQkBhN5qo8zjoOTz21Oj/cANibj3Sx4gto6aUd65G6KQRove9r ZdikKMyNaB7NtYtfWosZ3O2yawBM0EcsLlUHoP4wmpIFKeFzuLUFkdVYYYQKViGxT4Wp fkAw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:user-agent:message-id :in-reply-to:date:references:subject:cc:to:from:x-gm-message-state :from:to:cc; bh=p4u18mBkXOucIlLnxTvcRAnzoavrt3QphiO/2Ew5Ssw=; b=ue29SghpIFZFD981d42u+QNjJRQXmP9QZadt5uflw+Udx9jze9CnL6xR4SLylSHbGr Yj9JZSAn1J7qyDpxMI9CG0JSATpwYLb5gxyr2ik9cKMHZ/cLNW7udOapJGOZEu1LK2RJ kPv/r8CKafyeWaNufQyeJD5ABAhu7auhwJ69M5BJXRVmz/Hjr6zIFSrMMl++axAOvdZv pl5tB5jSiCGHSNFHV00WYGiSR4d2RC72uM+Z95s4d9we0RFpPVkJoZH4VoEFTKlbk4Tl qOW0C/V2wYZoxI4+GJZq4EIO8mlMiOsXMLdeu6Ft8X1YJpUnyK52XYhjXh+jUsW9zeWp y9Iw== X-Gm-Message-State: ACgBeo1bKXBfbqOVUV+mZhngybm+SMGQsNoNuQgc+kODdwYNzEaPAjjs LkbjbqqTVEGh06w/U5dhwLpePsxYZqE= X-Google-Smtp-Source: AA6agR5pznNm3Mxrm/KbhWt8c1qsch3EDDSWS+3yPs9psybxTqiB2E6SWkV5YT/rSaQXrH7bFFTe7A== X-Received: by 2002:a37:902:0:b0:6bb:7640:3113 with SMTP id 2-20020a370902000000b006bb76403113mr13544450qkj.613.1661195591228; Mon, 22 Aug 2022 12:13:11 -0700 (PDT) Received: from hurd (dsl-150-55.b2b2c.ca. [66.158.150.55]) by smtp.gmail.com with ESMTPSA id h11-20020ac8514b000000b00342f4fc290esm9451636qtn.71.2022.08.22.12.13.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 22 Aug 2022 12:13:10 -0700 (PDT) From: Maxim Cournoyer To: muradm Subject: Re: bug#56608: [PATCH v2 2/2] gnu: tests: Add fail2ban tests. References: <87edxxqpg3.fsf@gmail.com> <20220822172607.31515-1-mail@muradm.net> <20220822172607.31515-3-mail@muradm.net> Date: Mon, 22 Aug 2022 15:13:09 -0400 In-Reply-To: <20220822172607.31515-3-mail@muradm.net> (muradm's message of "Mon, 22 Aug 2022 20:26:07 +0300") Message-ID: <87edx8gkhm.fsf@gmail.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/28.1 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 56608 Cc: 56608@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) Hi, muradm writes: [...] > --- /dev/null > +++ b/gnu/tests/security.scm I'd keep the tests with the introductory commit (squashed in preceding one). > @@ -0,0 +1,314 @@ > +;;; GNU Guix --- Functional package management for GNU > +;;; Copyright =C2=A9 2022 muradm > +;;; > +;;; 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 . > + > +(define-module (gnu tests security) > + #:use-module (guix gexp) > + #:use-module (gnu packages admin) > + #:use-module (gnu services) > + #:use-module (gnu services security) > + #:use-module (gnu services ssh) > + #:use-module (gnu system) > + #:use-module (gnu system vm) > + #:use-module (gnu tests) > + #:export (%test-fail2ban-basic > + %test-fail2ban-simple > + %test-fail2ban-extending)) > + > + > +;;; > +;;; fail2ban tests > +;;; > + > +(define (run-fail2ban-basic-test) > + > + (define os > + (marionette-operating-system > + (simple-operating-system > + (service fail2ban-service-type)) > + #:imported-modules '((gnu services herd) > + (guix combinators)))) ^ (guix combinators) seems unused > + (define vm > + (virtual-machine > + (operating-system os) > + (port-forwardings '()))) (define vm (virtual-machine (operating-system os))) should be sufficient. > + > + (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))) > + > + (define (wait-for-unix-socket-m socket) > + (wait-for-unix-socket socket marionette)) Overkill as used once in scope. > + > + (test-runner-current (system-test-runner #$output)) > + (test-begin "fail2ban-basic-test") > + > + (test-assert "fail2ban running" > + (marionette-eval > + '(begin > + (use-modules (gnu services herd)) > + (start-service 'fail2ban)) > + marionette)) I like to test that services can be restarted too, as in my experience there can be races and other situations that may cause them to fail restarting. > + > + (test-assert "fail2ban socket ready" > + (wait-for-unix-socket-m > + "/var/run/fail2ban/fail2ban.sock")) Same comment as above. > + (test-assert "fail2ban pid ready" > + (marionette-eval > + '(file-exists? "/var/run/fail2ban/fail2ban.pid") > + marionette)) > + > + (test-assert "fail2ban log file" > + (marionette-eval > + '(file-exists? "/var/log/fail2ban.log") > + marionette)) > + > + (test-end)))) > + > + (gexp->derivation "fail2ban-basic-test" test)) > + > +(define %test-fail2ban-basic > + (system-test > + (name "fail2ban-basic") > + (description "Test basic fail2ban running capability.") > + (value (run-fail2ban-basic-test)))) > + > +(define %fail2ban-server-cmd > + (program-file > + "fail2ban-server-cmd" > + #~(begin > + (let ((cmd #$(file-append fail2ban "/bin/fail2ban-server"))) > + (apply execl cmd cmd `("-p" "/var/run/fail2ban/fail2ban.pid" > + "-s" "/var/run/fail2ban/fail2ban.sock" > + ,@(cdr (program-arguments)))))))) > + > +(define (run-fail2ban-simple-test) > + > + (define os > + (marionette-operating-system > + (simple-operating-system > + (service > + fail2ban-service-type > + (fail2ban-configuration > + (jails > + (list > + (fail2ban-jail-configuration (name "sshd") (enabled #t))))))) > + #:imported-modules '((gnu services herd) > + (guix combinators)))) ^ (guix combinators) not needed =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20 > + > + (define vm > + (virtual-machine > + (operating-system os) > + (port-forwardings '()))) Same comment as above. > + (define test > + (with-imported-modules '((gnu build marionette) > + (guix build utils)) > + #~(begin > + (use-modules (srfi srfi-64) > + (ice-9 popen) > + (ice-9 rdelim) > + (rnrs io ports) > + (gnu build marionette) > + (guix build utils)) > + > + (define marionette (make-marionette (list #$vm))) > + > + (define (wait-for-unix-socket-m socket) > + (wait-for-unix-socket socket marionette)) Likewise. > + (test-runner-current (system-test-runner #$output)) > + (test-begin "fail2ban-simple-test") > + > + (test-assert "fail2ban running" > + (marionette-eval > + '(begin > + (use-modules (gnu services herd)) > + (start-service 'fail2ban)) > + marionette)) > + > + (test-assert "fail2ban socket ready" > + (wait-for-unix-socket-m > + "/var/run/fail2ban/fail2ban.sock")) > + > + (test-assert "fail2ban pid ready" > + (marionette-eval > + '(file-exists? "/var/run/fail2ban/fail2ban.pid") > + marionette)) > + > + (test-assert "fail2ban log file" > + (marionette-eval > + '(file-exists? "/var/log/fail2ban.log") > + marionette)) > + > + (test-equal "fail2ban sshd jail running" > + '("Status for the jail: sshd" > + "|- Filter" > + "| |- Currently failed:\t0" > + "| |- Total failed:\t0" > + "| `- File list:\t/var/log/secure" > + "`- Actions" > + " |- Currently banned:\t0" > + " |- Total banned:\t0" > + " `- Banned IP list:\t" > + "") > + (marionette-eval > + '(begin > + (use-modules (ice-9 rdelim) (ice-9 popen) (rnrs io ports= )) > + (let ((call-command > + (lambda (cmd) > + (let* ((err-cons (pipe)) > + (port (with-error-to-port (cdr err-cons) > + (lambda () (open-input-pipe cmd)= ))) > + (_ (setvbuf (car err-cons) 'block > + (* 1024 1024 16))) > + (result (read-delimited "" port))) > + (close-port (cdr err-cons)) > + (values result (read-delimited "" (car err-co= ns))))))) > + (string-split > + (call-command > + (string-join (list #$%fail2ban-server-cmd "status" "= sshd") " ")) > + #\newline))) > + marionette)) Perhaps this could be turned into an Shepherd action, and the Guile procedure could do the above to return the text output; to simplify the test and reduce boilerplate, while providing value to the user. > + > + (test-equal "fail2ban sshd jail running" > + 0 > + (marionette-eval > + '(status:exit-val (system* #$%fail2ban-server-cmd "status" = "sshd")) > + marionette)) > + > + (test-end)))) > + > + (gexp->derivation "fail2ban-simple-test" test)) > + > +(define %test-fail2ban-simple > + (system-test > + (name "fail2ban-simple") > + (description "Test simple fail2ban running capability.") > + (value (run-fail2ban-simple-test)))) > + > +(define (run-fail2ban-extending-test) > + > + (define os > + (marionette-operating-system > + (simple-operating-system > + (service > + (fail2ban-jail-service > + openssh-service-type > + (fail2ban-jail-configuration > + (name "sshd") (enabled #t))) > + (openssh-configuration))) > + #:imported-modules '((gnu services herd) > + (guix combinators)))) Same comment as above w.r.t. (guix combinators) > + > + (define vm > + (virtual-machine > + (operating-system os) > + (port-forwardings '()))) Same comment as above. > + (define test > + (with-imported-modules '((gnu build marionette) > + (guix build utils)) > + #~(begin > + (use-modules (srfi srfi-64) > + (ice-9 popen) > + (ice-9 rdelim) > + (rnrs io ports) > + (gnu build marionette) > + (guix build utils)) > + > + (define marionette (make-marionette (list #$vm))) > + > + (define (wait-for-unix-socket-m socket) > + (wait-for-unix-socket socket marionette)) Same comment as above. > + (test-runner-current (system-test-runner #$output)) > + (test-begin "fail2ban-extending-test") > + > + (test-assert "sshd running" > + (marionette-eval > + '(begin > + (use-modules (gnu services herd)) > + (start-service 'ssh-daemon)) > + marionette)) > + > + (test-assert "fail2ban socket ready" > + (wait-for-unix-socket-m > + "/var/run/fail2ban/fail2ban.sock")) > + > + (test-assert "fail2ban pid ready" > + (marionette-eval > + '(file-exists? "/var/run/fail2ban/fail2ban.pid") > + marionette)) > + > + (test-assert "fail2ban log file" > + (marionette-eval > + '(file-exists? "/var/log/fail2ban.log") > + marionette)) > + > + (test-equal "fail2ban sshd jail running" > + '("Status for the jail: sshd" > + "|- Filter" > + "| |- Currently failed:\t0" > + "| |- Total failed:\t0" > + "| `- File list:\t/var/log/secure" > + "`- Actions" > + " |- Currently banned:\t0" > + " |- Total banned:\t0" > + " `- Banned IP list:\t" > + "") > + (marionette-eval > + '(begin > + (use-modules (ice-9 rdelim) (ice-9 popen) (rnrs io ports= )) > + (let ((call-command > + (lambda (cmd) > + (let* ((err-cons (pipe)) > + (port (with-error-to-port (cdr err-cons) > + (lambda () (open-input-pipe cmd)= ))) > + (_ (setvbuf (car err-cons) 'block > + (* 1024 1024 16))) > + (result (read-delimited "" port))) > + (close-port (cdr err-cons)) > + (values result (read-delimited "" (car err-co= ns))))))) > + (string-split > + (call-command > + (string-join (list #$%fail2ban-server-cmd "status" "= sshd") " ")) > + #\newline))) > + marionette)) > + > + (test-equal "fail2ban sshd jail running" > + 0 > + (marionette-eval > + '(status:exit-val (system* #$%fail2ban-server-cmd "status" = "sshd")) > + marionette)) > + > + (test-end)))) > + > + (gexp->derivation "fail2ban-extending-test" test)) > + > +(define %test-fail2ban-extending Perhaps %test-fail2ban-extension ? Otherwise, that last test seems to test exactly the same things as the preceding one, so there should be a procedure to generate the test, taking the OS as an argument to avoid code duplication. Thanks for working on this! Maxim From debbugs-submit-bounces@debbugs.gnu.org Tue Aug 23 14:51:40 2022 Received: (at 56608) by debbugs.gnu.org; 23 Aug 2022 18:51:40 +0000 Received: from localhost ([127.0.0.1]:45062 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oQYzz-0005JU-Df for submit@debbugs.gnu.org; Tue, 23 Aug 2022 14:51:39 -0400 Received: from nomad-cl1.staging.muradm.net ([139.162.159.157]:48008 helo=nomad-cl1.muradm.net) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oQYzx-0005JH-MR for 56608@debbugs.gnu.org; Tue, 23 Aug 2022 14:51:38 -0400 Received: from localhost ([127.0.0.1]:49098) by nomad-cl1.muradm.net with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1oQYz1-0006nN-30; Tue, 23 Aug 2022 18:50:40 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=muradm.net; s=mail; h=Content-Type:MIME-Version:Message-ID:In-reply-to:Date:Subject:Cc:To :From:References:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=eM+bCvqGOqWe0SZ6QfUOdAEK30xx0ga0vJM7llnqb0A=; b=x654jYVHyy3zp9DDy1S77TwtjE DMbnJstTzo8QlRToH51dcxrUYNxp4iWA51djhRrzbsw87cRhLwRXwWNx9H/ldv2566r9Zg9KvdGSE v9Jc6bfHdKOXizrGq767MBzzCa7rowhx24owcP3ZwdGylZzXseZ2EiFADVHPzc+3glTbECrqBKos4 ibZAfdqVr7L8t5yXIHnZ+rsNaPUcr4OTMMmgKEESbZ16vbxJfoSui/SCLsH9WjKmlEctzL2La9U6r tWm6BVeDOeTlsTsAnw72u90zUdxXrluaCzZAtLQff/3lg36hM5VS0sD399evuswjc3LcWPT/jnLE5 YyU+0zsu+XVOVdAG2qs67c0yNHEPhIhhZ3uBwGv8Z9/NMu4yfz+MDtgqokb7KPvflrj/Ye0/Je5Ed SvUU2ChLWcPQst8Qtp8IjOopwb7m4sLZBPSAec5NZuD8zIBBnedZGmpOJ1aYWJRcYiN9qq2vodyc+ CtA3YMOWylt8RzQ7SzHLmhFn; Received: from muradm by localhost with local (Exim 4.96) (envelope-from ) id 1oQYzo-0004Ma-0l; Tue, 23 Aug 2022 21:51:28 +0300 References: <87edxxqpg3.fsf@gmail.com> <20220822172607.31515-1-mail@muradm.net> <20220822172607.31515-2-mail@muradm.net> <87o7wcgldm.fsf@gmail.com> User-agent: mu4e 1.8.7; emacs 29.0.50 From: muradm To: Maxim Cournoyer Subject: Re: bug#56608: [PATCH v2 1/2] gnu: security: Add fail2ban-service-type. Date: Tue, 23 Aug 2022 21:22:28 +0300 In-reply-to: <87o7wcgldm.fsf@gmail.com> Message-ID: <87v8qiyes0.fsf@muradm.net> MIME-Version: 1.0 Content-Type: multipart/signed; boundary="=-=-="; micalg=pgp-sha256; protocol="application/pgp-signature" X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 56608 Cc: 56608@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: quoted-printable Hi, Here are comments only, changes will be with squashed patch later=20 on. Maxim Cournoyer writes: > Hi, > > Well done implementing the configuration records using > define-configuration! I believe it'll help its users avoiding=20 > mistakes. Honestly, it was not very pleasant experience... IMHO :) I see why you want it, but in my experience/opinion it is not=20 right way.. > Here are some comments for the guix.texi bits which are not > automatically generated: > > muradm writes: > [...] >> +This is the type of the service that runs @code{fail2ban}=20 >> daemon. It can be > ^ > two=20 > spaces Done. [...] >> +@item Permanent extending configuration >> +service developers may not @code{fail2ban-service-type} in=20 >> service-type's > ^ missing verb Actually type s/not/use/, done. [...] >> +This is the type of the service that runs @code{fail2ban}=20 >> daemon. It can be > ^ > two=20 > spaces Done. [...] >> + ;; excplicit configuration, this way fail2ban daemon >> + ;; will start and do its thing for sshd jail > > Typo (excplicit). Also, please use full sentences for=20 > stand-alone > comments (with proper punctuation). Done. [...] >> + ;; there is no direct dependency with actual openssh >> + ;; server configuration, it could even be omitted here > > Likewise. Done. [...] >> +(define-configuration/no-serialization=20 >> fail2ban-ignorecache-configuration >> + (key (string) "Cache key.") >> + (max-count (integer) "Cache size.") >> + (max-time (integer) "Cache time.")) > > Note that when you do not use a default value, you can leave out=20 > the > parenthesizes. > Done in all relevant places. [...] >> +(define serialize-fail2ban-jail-filter-configuration >> + (match-lambda >> + (($ _ name mode) >> + (format #f "~a~a" >> + name (if (eq? 'unset mode) "" (format #f=20 >> "[mode=3D~a]" mode)))))) > > You could use (ice-9 format) with a conditional formatting here.=20 > The > example from info '(guile) Formatted Output' is > > (format #f "~:[false~;not false~]" 'abc) =E2=87=92 "not false" > Done with ~@[] instead. >> +(define (list-of-arguments? lst) >> + (every >> + (lambda (e) (and (pair? e) >> + (string? (car e)) >> + (or (string? (cdr e)) >> + (list-of-strings? (cdr e))))) >> + lst)) > > It could be better to define a argument? predicate, use the=20 > 'list-of' > procedure from (gnu services configuration) on it. > Done. [...] >> +(define-maybe boolean (prefix fail2ban-jail-configuration-)) >> +(define-maybe symbol (prefix fail2ban-jail-configuration-)) >> +(define-maybe fail2ban-ignorecache-configuration (prefix=20 >> fail2ban-jail-configuration-)) >> +(define-maybe fail2ban-jail-filter-configuration (prefix=20 >> fail2ban-jail-configuration-)) > > Is using the prefix absolutely necessary? It makes things=20 > awkwardly > long. Since fail2ban-service-type it's the first citizen of (gnu > services security), it could enjoy the luxury of not using a=20 > prefix, in > my opinion. Later services may need to define their own prefix. > There are already four configuration records which are somewhat overlapping. Pay attention to *serialize-string functions. Also security may/will have more stuff probably, why do future contrubutors life harder. I feel bad/uncomfortable in removing prefix and poluting name space, so I left it long. [...] >> + (enabled > > I'd use enabled? and employ a name normalizer (e.g., stripping=20 > any > trailing '?') in the boolean serializer. > Done including few other places. [...] >> + (maxretry > > I think we could use hyphen in the field names, and use a=20 > normalizer to > strip them at serialization time (assuming hyphens are never=20 > allowed in > a key name). > Done. [...] >> + (bantime.overalljails > > We could have the normalization "bantime-" -> "bantime." done in=20 > the > serializers, to use more Schemey names. > Done, although I find it a bit fragile. [...] >> + (ignorecommand >> + maybe-string >> + "External command that will take an tagged arguments to=20 >> ignore. >> +Note: while provided, currently unimplemented in the context=20 >> of @code{guix}.") > > Then I'd remove it, as I don't see in which other context it=20 > would be > useful. > What I wanted to say here, is that field does not support gexp=20 like thing at the moment. Still if user really needs this, command can be used from global package or via publishing to easy location like /etc/scripts/some-tool. [...] >> + "Can be a list of IP addresses, CIDR masks or DNS hosts.=20 >> @code{fail2ban} > ^ > Please use two spaces after period. > Done. [...] >> + "Filename(s) of the log files to be monitored.") > > The convention in GNU is to use "file name" rather than=20 > "filename". > Done, however I just copied from fail2ban own documentation/code,=20 so I'm not very sure if such things should be fixed like this. [...] >> + "Extra raw content to add at the end of=20 >> @file{jail.local}.")) > ^the=20 > @file{jail.local}=20 > file. > Done. [...] >> + (let* ((out (ungexp output))) > ^ use just let here > Done. [...] >> +(define (fail2ban-jail-service svc-type jail) >> + (service-type >> + (inherit svc-type) >> + (extensions >> + (append >> + (service-type-extensions svc-type) >> + (list (service-extension fail2ban-service-type >> + (lambda _ (list jail)))))))) > ^ nitpick, but (compose list=20 > jail) is > more common > Done. > That looks very good. I haven't checked the tests yet, but I=20 > think with > the extra polishing suggested above, it should be in a good=20 > place to be > merged soon! > > Thanks, > > Maxim --=-=-= Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQIzBAEBCAAdFiEESPY5lma9A9l5HGLP6M7O0mLOBeIFAmMFIa8ACgkQ6M7O0mLO BeLRERAAoyYSDmCJNjuUWs8JqRa0h78PQR+MjxYCTaoVPIghnHO16tRYi6GrtiFB vlrWNsEC0rHrD50oilzO/sTSUSpeSJMLf+QN6irUa+/DqDel8rzgCEhuFtYdNrR5 R80j7OzrtRLSec41oMTa/q7F17vNj3qg7RBY6Up6p6Gl9sQ9sSix8U5ZJwbjyspy wOLj7kTvDZKvB3MhMgXu7k/r2yJ+enf+h1qnjVIwGHHX0/Io0s1DScW1XBLfPAMa URrc40fglqfNfAbhAaTKTpLUqZNRZS6OEsisfIZPGdU9zJQ/q+Q3jCwU17Cc2Oxh AGeN4i2gnlImrSVzaYR74LUGUo3OvkVBwWLgOBUguOvU2emGYL0vb+4xS9ni6XIM KZF2ytMHvwIKs4REL70gYxsSVjb4OWCUOaVStzQjmuVuZAET3Cu2qhVRgeo2o/6a 5ewxIu7F4sfVOJA7EmeYiOXN++PCzFbQbyvZi2M8HwOTeFP6FzJdM00hbxMuz9sy tMxbxecOOmPF5qXMfpl2SM8IT+uEUxetbKta5Y5M2pPy/YrmUwoNqKdWLE0nMBjz HrSeCkWhgPjq1ss0Iph4Q4ZmC6g0YlvlFUjZ9Nv8QYFOt2BbJzAtstVZnsOOxxjJ YhopV+P4f4V8JbggUZ9fazjqy88Xi6SHdbxwtsCE1ndmtcC61wE= =qcAJ -----END PGP SIGNATURE----- --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Tue Aug 23 16:01:45 2022 Received: (at 56608) by debbugs.gnu.org; 23 Aug 2022 20:01:45 +0000 Received: from localhost ([127.0.0.1]:45091 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oQa5p-00070p-4O for submit@debbugs.gnu.org; Tue, 23 Aug 2022 16:01:45 -0400 Received: from nomad-cl1.staging.muradm.net ([139.162.159.157]:42384 helo=nomad-cl1.muradm.net) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oQa5n-00070c-4a for 56608@debbugs.gnu.org; Tue, 23 Aug 2022 16:01:43 -0400 Received: from localhost ([127.0.0.1]:50900) by nomad-cl1.muradm.net with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1oQa4r-0006ny-15; Tue, 23 Aug 2022 20:00:45 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=muradm.net; s=mail; h=Content-Type:MIME-Version:Message-ID:In-reply-to:Date:Subject:Cc:To :From:References:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=u5M7b+R+H8vC6lBbb7032r2Ax7Ar3JTgA52udfZvGqI=; b=vE/LnQGiuj/POKRWZkNaT1fPdV SxEu2LB5n0alpVDKxV02NsfRqQf6xov2vo1EKTPbhvCfoEwS3KRiK7fCqLhKWi4KG2n7E4idciUK5 UoquVV4FGZ4M2bknhDhpci0kUD5BzdtsaWveaYklWHL1Uy9LXEFIbfzst54vjpfS7ufS3B8cIAkzz qkoyEviNS5Rz0JpcERvPrtxEVZCmqgZqG0m8o8fbzVonTiiCrS1YuQEmgzue+GFonOmvQjPe6Ch/0 4O+JniTyCxqUYMG27kjWLFyybzFR2dZGxmhNUa+oDoxUjaxpFQkl+J79udyp+2u9D5D+l6bGGrwHT zgT7tSJ5FW+mejI98s3KuufKUWw4tR+LNsGsMNld0E4XthQXpvzqcSb3xcYHrGTPHdjpNotuORfz4 L7Fp7H0tuQRBTQ7MNX+DWA50XUXUpDwU/3EBMujRF6lblnXJJRR0/5xuIw1lwRWoLw6wvbIY5puu1 923SXAjQe1iNZs+CrIvNNGhj; Received: from muradm by localhost with local (Exim 4.96) (envelope-from ) id 1oQa5g-0005xZ-0x; Tue, 23 Aug 2022 23:01:36 +0300 References: <87edxxqpg3.fsf@gmail.com> <20220822172607.31515-1-mail@muradm.net> <20220822172607.31515-3-mail@muradm.net> <87edx8gkhm.fsf@gmail.com> User-agent: mu4e 1.8.7; emacs 29.0.50 From: muradm To: Maxim Cournoyer Subject: Re: bug#56608: [PATCH v2 2/2] gnu: tests: Add fail2ban tests. Date: Tue, 23 Aug 2022 21:51:57 +0300 In-reply-to: <87edx8gkhm.fsf@gmail.com> Message-ID: <87r116ybj4.fsf@muradm.net> MIME-Version: 1.0 Content-Type: multipart/signed; boundary="=-=-="; micalg=pgp-sha256; protocol="application/pgp-signature" X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 56608 Cc: 56608@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain; format=flowed Hi, Squashed patch will come later on. Maxim Cournoyer writes: > Hi, > > muradm writes: > > [...] > >> --- /dev/null >> +++ b/gnu/tests/security.scm > > I'd keep the tests with the introductory commit (squashed in > preceding > one). > Done. [...] >> +(define (run-fail2ban-basic-test) >> + >> + (define os >> + (marionette-operating-system >> + (simple-operating-system >> + (service fail2ban-service-type)) >> + #:imported-modules '((gnu services herd) >> + (guix combinators)))) > ^ (guix combinators) seems unused > Done including other places. >> + (define vm >> + (virtual-machine >> + (operating-system os) >> + (port-forwardings '()))) > > (define vm (virtual-machine (operating-system os))) should be > sufficient. > For me it does not work without specfying port-forwardings. I get wierd error like following: gnu/tests/security.scm:47:5: error: os: invalid field specifier I suppose it is something todo with virtual-machine. So I'm leaving port-forwardings as is. [...] >> + (define (wait-for-unix-socket-m socket) >> + (wait-for-unix-socket socket marionette)) > > Overkill as used once in scope. > Done including other places. >> + >> + (test-runner-current (system-test-runner #$output)) >> + (test-begin "fail2ban-basic-test") >> + >> + (test-assert "fail2ban running" >> + (marionette-eval >> + '(begin >> + (use-modules (gnu services herd)) >> + (start-service 'fail2ban)) >> + marionette)) > > I like to test that services can be restarted too, as in my > experience > there can be races and other situations that may cause them to > fail > restarting. > Done. [...] >> + (test-equal "fail2ban sshd jail running" >> + '("Status for the jail: sshd" >> + "|- Filter" >> + "| |- Currently failed:\t0" >> + "| |- Total failed:\t0" >> + "| `- File list:\t/var/log/secure" >> + "`- Actions" >> + " |- Currently banned:\t0" >> + " |- Total banned:\t0" >> + " `- Banned IP list:\t" >> + "") >> + (marionette-eval >> + '(begin >> + (use-modules (ice-9 rdelim) (ice-9 popen) >> (rnrs io ports)) >> + (let ((call-command >> + (lambda (cmd) >> + (let* ((err-cons (pipe)) >> + (port (with-error-to-port (cdr >> err-cons) >> + (lambda () >> (open-input-pipe cmd)))) >> + (_ (setvbuf (car err-cons) >> 'block >> + (* 1024 1024 16))) >> + (result (read-delimited "" >> port))) >> + (close-port (cdr err-cons)) >> + (values result (read-delimited "" >> (car err-cons))))))) >> + (string-split >> + (call-command >> + (string-join (list #$%fail2ban-server-cmd >> "status" "sshd") " ")) >> + #\newline))) >> + marionette)) > > Perhaps this could be turned into an Shepherd action, and the > Guile > procedure could do the above to return the text output; to > simplify the > test and reduce boilerplate, while providing value to the user. > [...] >> + (gexp->derivation "fail2ban-extending-test" test)) >> + >> +(define %test-fail2ban-extending > > Perhaps %test-fail2ban-extension ? Done, s/extending/extension/. > Otherwise, that last test seems to > test exactly the same things as the preceding one, so there > should be a > procedure to generate the test, taking the OS as an argument to > avoid > code duplication. > Done, refactored with define-syntax-rule. > Thanks for working on this! > > Maxim --=-=-= Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQIzBAEBCAAdFiEESPY5lma9A9l5HGLP6M7O0mLOBeIFAmMFMh8ACgkQ6M7O0mLO BeLlJxAAqpV6nFNan2XXBZ97RzYc7L+pVu1Pq7qM1kHzImmGSYgUY+fbNYDlhjIf 2gURCX+Gdfw35mU2N4g3uUjJgRES5TRKsGYG/86+QKpbD/LMOHLGoBhCtHatCSJM 65TRD4kr83HTOGt8EN4RglwElsgGlO24JZcHqdOflL1uCF/c2uJVXtswfrYcK85z 58t8Antj8E0ky8HjlRuT7BCambL8pVN8d/Dw/Ac47EqqQQrek0F5eVuhJFxEGVDQ 6i5wJdsq60NMflRr9+SZNgZF0OT2d5VQutwv4P666RQ3qtSt7r87v+0AZhlQWF1g bqDTzbgukc2F7qxWZNC6bEnqSKZPaCTc5epCOE0Sa6EW+LJ96d6MidTadxo4fxSV NU8th8jH/GyNNZ4QkWVwVGgxUC4nhnZ4F2vof9DEFFHQyeo7mDt9+Pog9rXqnO/N 5rmJpXX+yelVWhM30BMHGaPjdBYiQw9qHLS/Cgcx8g1TqwRlTiZZdPYzH9I8B2e4 I0nxSchvCM6SuTolwcoEVNaOlUdzEZMZGW0yyCLJUO5dDrUi98OoGYXPiVfMAILU EDl71UgIP06hedVcmRUyDunuU8t87syq2CLkIzfDzAm4vhSAHDEGxDukeV5RbyAi iNFB814Vf5rCNpOQiOjjgi3p3UfCIvXxyq6ROQZ9HFk9aw5iWrM= =oalh -----END PGP SIGNATURE----- --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Tue Aug 23 16:14:07 2022 Received: (at 56608) by debbugs.gnu.org; 23 Aug 2022 20:14:08 +0000 Received: from localhost ([127.0.0.1]:45108 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oQaHm-0007Iu-Br for submit@debbugs.gnu.org; Tue, 23 Aug 2022 16:14:07 -0400 Received: from nomad-cl1.staging.muradm.net ([139.162.159.157]:41012 helo=nomad-cl1.muradm.net) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oQaHi-0007IG-PP for 56608@debbugs.gnu.org; Tue, 23 Aug 2022 16:14:04 -0400 Received: from localhost ([127.0.0.1]:37512) by nomad-cl1.muradm.net with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1oQaGm-0006o3-2r; Tue, 23 Aug 2022 20:13:05 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=muradm.net; s=mail; h=Content-Transfer-Encoding:Content-Type:MIME-Version:References: In-Reply-To:Message-Id:Date:Subject:To:From:Sender:Reply-To:Cc:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=aQgQwmD2oKwwdgjkfRaF7Dsx6I7hYHoNHbZ+ZWGJiA4=; b=mU48DXqk/N7BJgepYikPUG9cxo 94IsRODz1ZcpYSXC+aMK6cl8kM8lSYWAmFzP2u/IiLtZGTpDhb6wv929I9AmeL2xl5AlL3z3mWA2y fRZb9qHBxMWIuRGSrcpyXJux8XHOfsC2FMyMLiamnt1FSoSIL2G0lQCgpwtu1sc9Cf2AI98IQ4wVw SRknmR0cGcM4bBlPEnn4oWco8+SL0W5kBc95cpwH512QRcJ9jb6ZKBYtyqDOItksSOR1q+nKE91Bm uqfzeKbHAsBcxL0qse/SbJHOrBTKoeETHId5BH/G5I3pVJmWMHfLHKn3EnvNNfae3Wr2UOzOsr7i+ I/AJGsRGeeGj6JkEG3IR65wHJKCCORn29COlbcaRl63DbnR+r4/y6ala6gfodKm3PWa2qDq6NyTr8 L/BxNwDDL0zQwuulaLz52nOkJRy8wA6QO2wWcpeb/0AyBH6WDHCerOw9+K7fnN1X/6ysQHAy+WmTb 3fR+zkGn1k207v0rojsm8yxc; Received: from muradm by localhost with local (Exim 4.96) (envelope-from ) id 1oQaHb-0006AM-2m; Tue, 23 Aug 2022 23:13:55 +0300 From: muradm To: 56608@debbugs.gnu.org, Maxim Cournoyer Subject: [PATCH v3] gnu: security: Add fail2ban-service-type. Date: Tue, 23 Aug 2022 23:13:55 +0300 Message-Id: <20220823201355.23691-1-mail@muradm.net> X-Mailer: git-send-email 2.37.1 In-Reply-To: <87r116ybj4.fsf@muradm.net> References: <87r116ybj4.fsf@muradm.net> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 56608 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) * gnu/services/security.scm: New module. * gnu/tests/security.scm: New module. * gnu/local.mk: Add new security module and tests. * doc/guix.text: Add fail2ban-service-type documentation. --- doc/guix.texi | 249 +++++++++++++++++++++++ gnu/local.mk | 3 + gnu/services/security.scm | 401 ++++++++++++++++++++++++++++++++++++++ gnu/tests/security.scm | 227 +++++++++++++++++++++ 4 files changed, 880 insertions(+) create mode 100644 gnu/services/security.scm create mode 100644 gnu/tests/security.scm diff --git a/doc/guix.texi b/doc/guix.texi index 023b48ae35..62a9e76a2c 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -36283,6 +36283,255 @@ Extra command line options for @code{nix-service-type}. @end table @end deftp +@cindex Fail2ban +@subsubheading Fail2ban service + +@uref{http://www.fail2ban.org/, @code{fail2ban}} scans log files +(e.g. @code{/var/log/apache/error_log}) and bans IPs that show the malicious +signs -- too many password failures, seeking for exploits, etc. + +@code{fail2ban} service is provided in @code{(gnu services security)} module. + +This is the type of the service that runs @code{fail2ban} daemon. It can be +used in various ways, which are: + +@itemize + +@item Explicit configuration +users are free to enable @code{fail2ban} configuration without strong +dependency. + +@item On-demand extending configuration +convenience @code{fail2ban-jail-service} function is provided, in order +to extend existing services on-demand. + +@item Permanent extending configuration +service developers may use @code{fail2ban-service-type} in service-type's +extensions. + +@end itemize + +@defvr {Scheme Variable} fail2ban-service-type + +This is the type of the service that runs @code{fail2ban} daemon. It can be +configured explicitly as following: + +@lisp +(append + (list + ;; Here is explicit configuration, this way fail2ban daemon + ;; will start and do its thing for sshd jail. + (service fail2ban-service-type + (fail2ban-configuration + (extra-jails + (list + (fail2ban-jail-configuration + (name "sshd") + (enabled #t)))))) + ;; There is no direct dependency on actual openssh + ;; server configuration, it could be customizaed as needed + ;; or started by different means. + (service openssh-service-type)) + %base-services) +@end lisp +@end defvr + +@deffn {Scheme Procedure} fail2ban-jail-service @var{svc-type} @var{jail} +Return extended @var{svc-type} of @code{} with added +@var{jail} of type @code{fail2ban-jail-configuration} extension +for @code{fail2ban-service-type}. + +For example: + +@lisp +(append + (list + (service + ;; Using convenience function we can extend virtually + ;; any service type with any fail2ban jail. + ;; This way we don't have to explicitly extend services + ;; with fail2ban-service-type. + (fail2ban-jail-service + openssh-service-type + (fail2ban-jail-configuration + (name "sshd") + (enabled #t))) + (openssh-configuration ...)))) +@end lisp +@end deffn + +@deftp {Data Type} fail2ban-configuration +Available @code{fail2ban-configuration} fields are: + +@table @asis +@item @code{fail2ban} (default: @code{fail2ban}) (type: package) +The @code{fail2ban} package to use. It used for both binaries and as +base default configuration that will be extended with +@code{}s. + +@item @code{run-directory} (default: @code{"/var/run/fail2ban"}) (type: string) +State directory for @code{fail2ban} daemon. + +@item @code{jails} (default: @code{()}) (type: list-of-fail2ban-jail-configurations) +Instances of @code{} collected from +extensions. + +@item @code{extra-jails} (default: @code{()}) (type: list-of-fail2ban-jail-configurations) +Instances of @code{} provided by user +explicitly. + +@item @code{extra-content} (type: maybe-string) +Extra raw content to add at the end of the @file{jail.local} file. + +@end table + +@end deftp + + +@deftp {Data Type} fail2ban-jail-configuration +Available @code{fail2ban-jail-configuration} fields are: + +@table @asis +@item @code{name} (type: string) +Required name of this jail configuration. + +@item @code{enabled?} (type: maybe-boolean) +Either @code{#t} or @code{#f} for @samp{true} and @samp{false} +respectively. + +@item @code{backend} (type: maybe-symbol) +Backend to be used to detect changes in the @code{ogpath}. + +@item @code{max-retry} (type: maybe-integer) +Is the number of failures before a host get banned (e.g. +@code{(max-retry 5)}). + +@item @code{max-matches} (type: maybe-integer) +Is the number of matches stored in ticket (resolvable via tag +@code{}) in action. + +@item @code{find-time} (type: maybe-string) +A host is banned if it has generated @code{max-retry} during the last +@code{find-time} seconds (e.g. @code{(find-time "10m")}). + +@item @code{ban-time} (type: maybe-string) +Is the number of seconds that a host is banned (e.g. @code{(ban-time +"10m")}). + +@item @code{ban-time-increment?} (type: maybe-boolean) +Allows to use database for searching of previously banned ip's to +increase a default ban time using special formula. + +@item @code{ban-time-factor} (type: maybe-string) +Is a coefficient to calculate exponent growing of the formula or common +multiplier. + +@item @code{ban-time-formula} (type: maybe-string) +Used by default to calculate next value of ban time. + +@item @code{ban-time-multipliers} (type: maybe-string) +Used to calculate next value of ban time instead of formula. + +@item @code{ban-time-max-time} (type: maybe-string) +Is the max number of seconds using the ban time can reach (doesn't grow +further). + +@item @code{ban-time-rnd-time} (type: maybe-string) +Is the max number of seconds using for mixing with random time to +prevent ``clever'' botnets calculate exact time IP can be unbanned +again. + +@item @code{ban-time-overall-jails?} (type: maybe-boolean) +Either @code{#t} or @code{#f} for @samp{true} and @samp{false} +respectively. @itemize @bullet @item @code{true} - specifies the search +of IP in the database will be executed cross over all jails @item +@code{false} - only current jail of the ban IP will be searched @end +itemize + +@item @code{ignore-command} (type: maybe-string) +External command that will take an tagged arguments to ignore. Note: +while provided, currently unimplemented in the context of @code{guix}. + +@item @code{ignore-self?} (type: maybe-boolean) +Specifies whether the local resp. own IP addresses should be ignored. + +@item @code{ignore-ip} (default: @code{()}) (type: list-of-strings) +Can be a list of IP addresses, CIDR masks or DNS hosts. @code{fail2ban} +will not ban a host which matches an address in this list. + +@item @code{ignore-cache} (type: maybe-fail2ban-ignore-cache-configuration) +Provide cache parameters for ignore failure check. + +@item @code{filter} (type: maybe-fail2ban-jail-filter-configuration) +Defines the filter to use by the jail, using +@code{}. By default jails have +names matching their filter name. + +@item @code{log-time-zone} (type: maybe-string) +Force the time zone for log lines that don't have one. + +@item @code{log-encoding} (type: maybe-symbol) +Specifies the encoding of the log files handled by the jail. Possible +values: @code{'ascii}, @code{'utf-8}, @code{'auto}. + +@item @code{log-path} (default: @code{()}) (type: list-of-strings) +File name(s) of the log files to be monitored. + +@item @code{action} (default: @code{()}) (type: list-of-fail2ban-jail-actions) +List of @code{}. + +@item @code{extra-content} (type: maybe-string) +Extra content for the jail configuration. + +@end table + +@end deftp + +@deftp {Data Type} fail2ban-ignore-cache-configuration +Available @code{fail2ban-ignore-cache-configuration} fields are: + +@table @asis +@item @code{key} (type: string) +Cache key. + +@item @code{max-count} (type: integer) +Cache size. + +@item @code{max-time} (type: integer) +Cache time. + +@end table + +@end deftp + +@deftp {Data Type} fail2ban-jail-action-configuration +Available @code{fail2ban-jail-action-configuration} fields are: + +@table @asis +@item @code{name} (type: string) +Action name. + +@item @code{arguments} (default: @code{()}) (type: list-of-arguments) +Action arguments. + +@end table + +@end deftp + +@deftp {Data Type} fail2ban-jail-filter-configuration +Available @code{fail2ban-jail-filter-configuration} fields are: + +@table @asis +@item @code{name} (type: string) +Filter to use. + +@item @code{mode} (type: maybe-string) +Mode for filter. + +@end table + +@end deftp + @node Setuid Programs @section Setuid Programs diff --git a/gnu/local.mk b/gnu/local.mk index 26dfb6afe2..31569033bc 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -51,6 +51,7 @@ # Copyright © 2022 Remco van 't Veer # Copyright © 2022 Artyom V. Poptsov # Copyright © 2022 John Kehayias +# Copyright © 2022 muradm # # This file is part of GNU Guix. # @@ -672,6 +673,7 @@ GNU_SYSTEM_MODULES = \ %D%/services/nfs.scm \ %D%/services/pam-mount.scm \ %D%/services/science.scm \ + %D%/services/security.scm \ %D%/services/security-token.scm \ %D%/services/shepherd.scm \ %D%/services/sound.scm \ @@ -756,6 +758,7 @@ GNU_SYSTEM_MODULES = \ %D%/tests/package-management.scm \ %D%/tests/reconfigure.scm \ %D%/tests/rsync.scm \ + %D%/tests/security.scm \ %D%/tests/security-token.scm \ %D%/tests/singularity.scm \ %D%/tests/ssh.scm \ diff --git a/gnu/services/security.scm b/gnu/services/security.scm new file mode 100644 index 0000000000..58c785fb6c --- /dev/null +++ b/gnu/services/security.scm @@ -0,0 +1,401 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2022 muradm +;;; +;;; 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 . + +(define-module (gnu services security) + #:use-module (gnu packages admin) + #:use-module (gnu services) + #:use-module (gnu services configuration) + #:use-module (gnu services shepherd) + #:use-module (guix gexp) + #:use-module (guix packages) + #:use-module (guix records) + #:use-module (guix ui) + #:use-module (ice-9 format) + #:use-module (ice-9 match) + #:use-module (srfi srfi-1) + #:export (fail2ban-configuration + fail2ban-configuration-fields + fail2ban-jail-configuration + fail2ban-jail-configuration-fields + + fail2ban-ignore-cache-configuration + fail2ban-ignore-cache-configuration-fields + fail2ban-jail-action-configuration + fail2ban-jail-action-configuration-fields + fail2ban-jail-filter-configuration + fail2ban-jail-filter-configuration-fields + + fail2ban-service-type + fail2ban-jail-service)) + +(define-configuration/no-serialization fail2ban-ignore-cache-configuration + (key string "Cache key.") + (max-count integer "Cache size.") + (max-time integer "Cache time.")) + +(define serialize-fail2ban-ignore-cache-configuration + (match-lambda + (($ _ key max-count max-time) + (format #f "key=\"~a\", max-count=~d, max-time=~d" + key max-count max-time)))) + +(define-maybe/no-serialization string) + +(define-configuration/no-serialization fail2ban-jail-filter-configuration + (name string "Filter to use.") + (mode maybe-string "Mode for filter.")) + +(define serialize-fail2ban-jail-filter-configuration + (match-lambda + (($ _ name mode) + (format #f "~a~@[[mode=~a]~]" name (and (not (eq? 'unset mode)) mode))))) + +(define (argument? a) + (and (pair? a) + (string? (car a)) + (or (string? (cdr a)) + (list-of-strings? (cdr a))))) + +(define list-of-arguments? (list-of argument?)) + +(define-configuration/no-serialization fail2ban-jail-action-configuration + (name string "Action name.") + (arguments (list-of-arguments '()) "Action arguments.")) + +(define list-of-fail2ban-jail-actions? + (list-of fail2ban-jail-action-configuration?)) + +(define (serialize-fail2ban-jail-action-configuration-arguments args) + (let* ((multi-value + (lambda (v) + (format #f "~a" (string-join v ",")))) + (any-value + (lambda (v) + (if (list? v) (string-append "\"" (multi-value v) "\"") v))) + (key-value + (lambda (e) + (format #f "~a=~a" (car e) (any-value (cdr e)))))) + (format #f "~a" (string-join (map key-value args) ",")))) + +(define serialize-fail2ban-jail-action-configuration + (match-lambda + (($ _ name arguments) + (format + #f "~a~a" + name + (if (null? arguments) "" + (format + #f "[~a]" + (serialize-fail2ban-jail-action-configuration-arguments + arguments))))))) + +(define fail2ban-backend->string + (match-lambda + ('auto "auto") + ('pyinotify "pyinotify") + ('gamin "gamin") + ('polling "polling") + ('systemd "systemd") + (unknown + (leave + (G_ "fail2ban: '~a' is not a supported backend~%") unknown)))) + +(define fail2ban-log-encoding->string + (match-lambda + ('auto "auto") + ('utf-8 "utf-8") + ('ascii "ascii") + (unknown + (leave + (G_ "fail2ban: '~a' is not a supported log encoding~%") unknown)))) + +(define (fail2ban-jail-configuration-serialize-field-name name) + (cond ((symbol? name) + (fail2ban-jail-configuration-serialize-field-name + (symbol->string name))) + ((string-suffix? "?" name) + (fail2ban-jail-configuration-serialize-field-name + (string-drop-right name 1))) + ((string-prefix? "ban-time-" name) + (fail2ban-jail-configuration-serialize-field-name + (string-append "bantime." (substring name 9)))) + ((string-contains name "-") + (fail2ban-jail-configuration-serialize-field-name + (string-filter (lambda (c) (equal? c #\-)) name))) + (#t name))) + +(define (fail2ban-jail-configuration-serialize-string field-name value) + #~(string-append + #$(fail2ban-jail-configuration-serialize-field-name field-name) + " = " #$value "\n")) + +(define (fail2ban-jail-configuration-serialize-integer field-name value) + (fail2ban-jail-configuration-serialize-string + field-name (number->string value))) + +(define (fail2ban-jail-configuration-serialize-boolean field-name value) + (fail2ban-jail-configuration-serialize-string + field-name (if value "true" "false"))) + +(define (fail2ban-jail-configuration-serialize-backend field-name value) + (if (eq? 'unset value) "" + (fail2ban-jail-configuration-serialize-string + field-name (fail2ban-backend->string value)))) + +(define (fail2ban-jail-configuration-serialize-fail2ban-ignore-cache-configuration field-name value) + (fail2ban-jail-configuration-serialize-string + field-name (serialize-fail2ban-ignore-cache-configuration value))) + +(define (fail2ban-jail-configuration-serialize-fail2ban-jail-filter-configuration field-name value) + (fail2ban-jail-configuration-serialize-string + field-name (serialize-fail2ban-jail-filter-configuration value))) + +(define (fail2ban-jail-configuration-serialize-log-encoding field-name value) + (if (eq? 'unset value) "" + (fail2ban-jail-configuration-serialize-string + field-name (fail2ban-log-encoding->string value)))) + +(define (fail2ban-jail-configuration-serialize-list-of-strings field-name value) + (if (null? value) "" + (fail2ban-jail-configuration-serialize-string + field-name (string-join value " ")))) + +(define (fail2ban-jail-configuration-serialize-list-of-fail2ban-jail-actions field-name value) + (if (null? value) "" + (fail2ban-jail-configuration-serialize-string + field-name (string-join + (map serialize-fail2ban-jail-action-configuration value) "\n")))) + +(define (fail2ban-jail-configuration-serialize-symbol field-name value) + (fail2ban-jail-configuration-serialize-string field-name (symbol->string value))) + +(define (fail2ban-jail-configuration-serialize-extra-content field-name value) + (if (eq? 'unset value) "" (string-append "\n" value "\n"))) + +(define-maybe integer (prefix fail2ban-jail-configuration-)) +(define-maybe string (prefix fail2ban-jail-configuration-)) +(define-maybe boolean (prefix fail2ban-jail-configuration-)) +(define-maybe symbol (prefix fail2ban-jail-configuration-)) +(define-maybe fail2ban-ignore-cache-configuration (prefix fail2ban-jail-configuration-)) +(define-maybe fail2ban-jail-filter-configuration (prefix fail2ban-jail-configuration-)) + +(define-configuration fail2ban-jail-configuration + (name + string + "Required name of this jail configuration.") + (enabled? + maybe-boolean + "Either @code{#t} or @code{#f} for @samp{true} and +@samp{false} respectively.") + (backend + maybe-symbol + "Backend to be used to detect changes in the @code{ogpath}." + fail2ban-jail-configuration-serialize-backend) + (max-retry + maybe-integer + "Is the number of failures before a host get banned +(e.g. @code{(max-retry 5)}).") + (max-matches + maybe-integer + "Is the number of matches stored in ticket (resolvable via +tag @code{}) in action.") + (find-time + maybe-string + "A host is banned if it has generated @code{max-retry} during the last +@code{find-time} seconds (e.g. @code{(find-time \"10m\")}).") + (ban-time + maybe-string + "Is the number of seconds that a host is banned +(e.g. @code{(ban-time \"10m\")}).") + (ban-time-increment? + maybe-boolean + "Allows to use database for searching of previously banned +ip's to increase a default ban time using special formula.") + (ban-time-factor + maybe-string + "Is a coefficient to calculate exponent growing of the +formula or common multiplier.") + (ban-time-formula + maybe-string + "Used by default to calculate next value of ban time.") + (ban-time-multipliers + maybe-string + "Used to calculate next value of ban time instead of formula.") + (ban-time-max-time + maybe-string + "Is the max number of seconds using the ban time can reach +(doesn't grow further).") + (ban-time-rnd-time + maybe-string + "Is the max number of seconds using for mixing with random time +to prevent ``clever'' botnets calculate exact time IP can be unbanned again.") + (ban-time-overall-jails? + maybe-boolean + "Either @code{#t} or @code{#f} for @samp{true} and @samp{false} respectively. +@itemize +@item @code{true} - specifies the search of IP in the database will be executed cross over all jails +@item @code{false} - only current jail of the ban IP will be searched +@end itemize") + (ignore-command + maybe-string + "External command that will take an tagged arguments to ignore. +Note: while provided, currently unimplemented in the context of @code{guix}.") + (ignore-self? + maybe-boolean + "Specifies whether the local resp. own IP addresses should be ignored.") + (ignore-ip + (list-of-strings '()) + "Can be a list of IP addresses, CIDR masks or DNS hosts. @code{fail2ban} +will not ban a host which matches an address in this list.") + (ignore-cache + maybe-fail2ban-ignore-cache-configuration + "Provide cache parameters for ignore failure check.") + (filter + maybe-fail2ban-jail-filter-configuration + "Defines the filter to use by the jail, using +@code{}. +By default jails have names matching their filter name.") + (log-time-zone + maybe-string + "Force the time zone for log lines that don't have one.") + (log-encoding + maybe-symbol + "Specifies the encoding of the log files handled by the jail. +Possible values: @code{'ascii}, @code{'utf-8}, @code{'auto}." + fail2ban-jail-configuration-serialize-log-encoding) + (log-path + (list-of-strings '()) + "File name(s) of the log files to be monitored.") + (action + (list-of-fail2ban-jail-actions '()) + "List of @code{}.") + (extra-content + maybe-string + "Extra content for the jail configuration." + fail2ban-jail-configuration-serialize-extra-content) + (prefix fail2ban-jail-configuration-)) + +(define list-of-fail2ban-jail-configurations? + (list-of fail2ban-jail-configuration?)) + +(define (serialize-fail2ban-jail-configuration config) + #~(string-append + #$(format #f "[~a]\n" (fail2ban-jail-configuration-name config)) + #$(serialize-configuration + config fail2ban-jail-configuration-fields))) + +(define-configuration/no-serialization fail2ban-configuration + (fail2ban + (package fail2ban) + "The @code{fail2ban} package to use. It used for both binaries and +as base default configuration that will be extended with +@code{}s.") + (run-directory + (string "/var/run/fail2ban") + "State directory for @code{fail2ban} daemon.") + (jails + (list-of-fail2ban-jail-configurations '()) + "Instances of @code{} collected from +extensions.") + (extra-jails + (list-of-fail2ban-jail-configurations '()) + "Instances of @code{} provided by user +explicitly.") + (extra-content + maybe-string + "Extra raw content to add at the end of the @file{jail.local} file.")) + +(define (serialize-fail2ban-configuration config) + (let* ((jails (fail2ban-configuration-jails config)) + (extra-jails (fail2ban-configuration-extra-jails config)) + (extra-content (fail2ban-configuration-extra-content config))) + (interpose + (append (map serialize-fail2ban-jail-configuration + (append jails extra-jails)) + (list (if (eq? 'unset extra-content) "" extra-content)))))) + +(define (make-fail2ban-configuration-package config) + (let* ((fail2ban (fail2ban-configuration-fail2ban config)) + (jail-local (apply mixed-text-file "jail.local" + (serialize-fail2ban-configuration config)))) + (computed-file + "fail2ban-configuration" + (with-imported-modules '((guix build utils)) + #~(begin + (use-modules (guix build utils)) + (let ((out (ungexp output))) + (mkdir-p (string-append out "/etc/fail2ban")) + (copy-recursively + (string-append #$fail2ban "/etc/fail2ban") + (string-append out "/etc/fail2ban")) + (symlink + #$jail-local + (string-append out "/etc/fail2ban/jail.local")))))))) + +(define (fail2ban-shepherd-service config) + (match-record config + (fail2ban run-directory) + (let* ((fail2ban-server (file-append fail2ban "/bin/fail2ban-server")) + (pid-file (in-vicinity run-directory "fail2ban.pid")) + (socket-file (in-vicinity run-directory "fail2ban.sock")) + (config-dir (make-fail2ban-configuration-package config)) + (config-dir (file-append config-dir "/etc/fail2ban")) + (fail2ban-action + (lambda args + #~(lambda _ + (invoke #$fail2ban-server + "-c" #$config-dir + "-p" #$pid-file + "-s" #$socket-file + "-b" + #$@args))))) + + ;; TODO: Add 'reload' action. + (list (shepherd-service + (provision '(fail2ban)) + (documentation "Run the fail2ban daemon.") + (requirement '(user-processes)) + (modules `((ice-9 match) + ,@%default-modules)) + (start (fail2ban-action "start")) + (stop (fail2ban-action "stop"))))))) + +(define fail2ban-service-type + (service-type (name 'fail2ban) + (extensions + (list (service-extension shepherd-root-service-type + fail2ban-shepherd-service))) + (compose concatenate) + (extend (lambda (config jails) + (fail2ban-configuration + (inherit config) + (jails + (append + (fail2ban-configuration-jails config) + jails))))) + (default-value (fail2ban-configuration)) + (description "Run the fail2ban server."))) + +(define (fail2ban-jail-service svc-type jail) + (service-type + (inherit svc-type) + (extensions + (append + (service-type-extensions svc-type) + (list (service-extension fail2ban-service-type + (lambda _ (list jail)))))))) diff --git a/gnu/tests/security.scm b/gnu/tests/security.scm new file mode 100644 index 0000000000..1e6d939b12 --- /dev/null +++ b/gnu/tests/security.scm @@ -0,0 +1,227 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2022 muradm +;;; +;;; 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 . + +(define-module (gnu tests security) + #:use-module (guix gexp) + #:use-module (gnu packages admin) + #:use-module (gnu services) + #:use-module (gnu services security) + #:use-module (gnu services ssh) + #:use-module (gnu system) + #:use-module (gnu system vm) + #:use-module (gnu tests) + #:export (%test-fail2ban-basic + %test-fail2ban-extension + %test-fail2ban-simple)) + + +;;; +;;; fail2ban tests +;;; + +(define-syntax-rule (fail2ban-test test-name test-os tests-more ...) + (lambda () + (define os + (marionette-operating-system + test-os + #: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 test-name) + + (test-assert "fail2ban running" + (marionette-eval + '(begin + (use-modules (gnu services herd)) + (start-service 'fail2ban)) + marionette)) + + (test-assert "fail2ban socket ready" + (wait-for-unix-socket + "/var/run/fail2ban/fail2ban.sock" marionette)) + + (test-assert "fail2ban running after restart" + (marionette-eval + '(begin + (use-modules (gnu services herd)) + (restart-service 'fail2ban)) + marionette)) + + (test-assert "fail2ban socket ready after restart" + (wait-for-unix-socket + "/var/run/fail2ban/fail2ban.sock" marionette)) + + (test-assert "fail2ban pid ready" + (marionette-eval + '(file-exists? "/var/run/fail2ban/fail2ban.pid") + marionette)) + + (test-assert "fail2ban log file" + (marionette-eval + '(file-exists? "/var/log/fail2ban.log") + marionette)) + + tests-more ... + + (test-end)))) + + (gexp->derivation test-name test))) + +(define run-fail2ban-basic-test + (fail2ban-test + "fail2ban-basic-test" + + (simple-operating-system + (service fail2ban-service-type)))) + +(define %test-fail2ban-basic + (system-test + (name "fail2ban-basic") + (description "Test basic fail2ban running capability.") + (value (run-fail2ban-basic-test)))) + +(define %fail2ban-server-cmd + (program-file + "fail2ban-server-cmd" + #~(begin + (let ((cmd #$(file-append fail2ban "/bin/fail2ban-server"))) + (apply execl cmd cmd `("-p" "/var/run/fail2ban/fail2ban.pid" + "-s" "/var/run/fail2ban/fail2ban.sock" + ,@(cdr (program-arguments)))))))) + +(define run-fail2ban-simple-test + (fail2ban-test + "fail2ban-basic-test" + + (simple-operating-system + (service + fail2ban-service-type + (fail2ban-configuration + (jails + (list + (fail2ban-jail-configuration (name "sshd") (enabled? #t))))))) + + (test-equal "fail2ban sshd jail running status output" + '("Status for the jail: sshd" + "|- Filter" + "| |- Currently failed:\t0" + "| |- Total failed:\t0" + "| `- File list:\t/var/log/secure" + "`- Actions" + " |- Currently banned:\t0" + " |- Total banned:\t0" + " `- Banned IP list:\t" + "") + (marionette-eval + '(begin + (use-modules (ice-9 rdelim) (ice-9 popen) (rnrs io ports)) + (let ((call-command + (lambda (cmd) + (let* ((err-cons (pipe)) + (port (with-error-to-port (cdr err-cons) + (lambda () (open-input-pipe cmd)))) + (_ (setvbuf (car err-cons) 'block + (* 1024 1024 16))) + (result (read-delimited "" port))) + (close-port (cdr err-cons)) + (values result (read-delimited "" (car err-cons))))))) + (string-split + (call-command + (string-join (list #$%fail2ban-server-cmd "status" "sshd") " ")) + #\newline))) + marionette)) + + (test-equal "fail2ban sshd jail running exit code" + 0 + (marionette-eval + '(status:exit-val (system* #$%fail2ban-server-cmd "status" "sshd")) + marionette)))) + +(define %test-fail2ban-simple + (system-test + (name "fail2ban-simple") + (description "Test simple fail2ban running capability.") + (value (run-fail2ban-simple-test)))) + +(define run-fail2ban-extension-test + (fail2ban-test + "fail2ban-extension-test" + + (simple-operating-system + (service + (fail2ban-jail-service + openssh-service-type + (fail2ban-jail-configuration + (name "sshd") (enabled? #t))) + (openssh-configuration))) + + (test-equal "fail2ban sshd jail running status output" + '("Status for the jail: sshd" + "|- Filter" + "| |- Currently failed:\t0" + "| |- Total failed:\t0" + "| `- File list:\t/var/log/secure" + "`- Actions" + " |- Currently banned:\t0" + " |- Total banned:\t0" + " `- Banned IP list:\t" + "") + (marionette-eval + '(begin + (use-modules (ice-9 rdelim) (ice-9 popen) (rnrs io ports)) + (let ((call-command + (lambda (cmd) + (let* ((err-cons (pipe)) + (port (with-error-to-port (cdr err-cons) + (lambda () (open-input-pipe cmd)))) + (_ (setvbuf (car err-cons) 'block + (* 1024 1024 16))) + (result (read-delimited "" port))) + (close-port (cdr err-cons)) + (values result (read-delimited "" (car err-cons))))))) + (string-split + (call-command + (string-join (list #$%fail2ban-server-cmd "status" "sshd") " ")) + #\newline))) + marionette)) + + (test-equal "fail2ban sshd jail running exit code" + 0 + (marionette-eval + '(status:exit-val (system* #$%fail2ban-server-cmd "status" "sshd")) + marionette)))) + +(define %test-fail2ban-extension + (system-test + (name "fail2ban-extension") + (description "Test extension fail2ban running capability.") + (value (run-fail2ban-extension-test)))) -- 2.37.1 From debbugs-submit-bounces@debbugs.gnu.org Tue Aug 23 16:24:07 2022 Received: (at 56608) by debbugs.gnu.org; 23 Aug 2022 20:24:07 +0000 Received: from localhost ([127.0.0.1]:45113 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oQaRS-0007Xt-RL for submit@debbugs.gnu.org; Tue, 23 Aug 2022 16:24:07 -0400 Received: from nomad-cl1.staging.muradm.net ([139.162.159.157]:57524 helo=nomad-cl1.muradm.net) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oQaRR-0007XO-JX for 56608@debbugs.gnu.org; Tue, 23 Aug 2022 16:24:06 -0400 Received: from localhost ([127.0.0.1]:54074) by nomad-cl1.muradm.net with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1oQaQW-0006oI-0O; Tue, 23 Aug 2022 20:23:08 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=muradm.net; s=mail; h=Content-Type:MIME-Version:Message-ID:In-reply-to:Date:Subject:Cc:To :From:References:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=VRSCUGYEbZ7ypTN4UM1TJLm7X0kD271KCN2CB4JkL4o=; b=Ytkp7f62fEM0aezGpHIXehq4aW zRCn6Th7krWcQHwBfKOCaddtS94YVyoZ1HRi2ERq3uqSWQ5AMXQ6RaR7mBAsfM27YARvwLuPpCmkp wstMTJTDq8Id6iqofAVpd76hQhwlSxjDAMKOly1WXvI/tX5QXAqwH7gTlq1SIFjraqVw/e/JMZ8+X Ih4K2gRcjdThxGQoyDd1bdnx1fcn6JchyJY8A6i+xIDzpxGfobW3NfjaWsCMiHLMUBZ6OqWthqMf/ B19CDwcNTMvlHb4CSweOIH/49OwTcJq3Bjsh/ds7hW2su0/Am9wSDb66EpvNvJMNM0DDrHGHi+aSp Lqlg9RTpbnfxYpnUkJpQiqSs1c4DyltzKYKMOGph4zw02JEiQzXRf3Nnwsb0A+gMYfY1kiyilRuMx jJZGjRQLO9T3SfmMKrOnZhy9CLXsWfDGjHnyO8jP9AASL52mSY9ti5AATvoinDGKO8ilwXQurPzUw on9M4wzJDX5FXzpwkLGoRDd1; Received: from muradm by localhost with local (Exim 4.96) (envelope-from ) id 1oQaRK-0006Dp-2Q; Tue, 23 Aug 2022 23:23:58 +0300 References: <87edxxqpg3.fsf@gmail.com> <20220822172607.31515-1-mail@muradm.net> <20220822172607.31515-3-mail@muradm.net> <87edx8gkhm.fsf@gmail.com> <87r116ybj4.fsf@muradm.net> User-agent: mu4e 1.8.7; emacs 29.0.50 From: muradm To: Maxim Cournoyer Subject: Re: bug#56608: [PATCH v2 2/2] gnu: tests: Add fail2ban tests. Date: Tue, 23 Aug 2022 23:19:34 +0300 In-reply-to: <87r116ybj4.fsf@muradm.net> Message-ID: <87mtbuyaht.fsf@muradm.net> MIME-Version: 1.0 Content-Type: multipart/signed; boundary="=-=-="; micalg=pgp-sha256; protocol="application/pgp-signature" X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 56608 Cc: 56608@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain; format=flowed muradm writes: [...] >>> + (test-equal "fail2ban sshd jail running" >>> + '("Status for the jail: sshd" >>> + "|- Filter" >>> + "| |- Currently failed:\t0" >>> + "| |- Total failed:\t0" >>> + "| `- File list:\t/var/log/secure" >>> + "`- Actions" >>> + " |- Currently banned:\t0" >>> + " |- Total banned:\t0" >>> + " `- Banned IP list:\t" >>> + "") >>> + (marionette-eval >>> + '(begin >>> + (use-modules (ice-9 rdelim) (ice-9 popen) >>> (rnrs io >>> ports)) >>> + (let ((call-command >>> + (lambda (cmd) >>> + (let* ((err-cons (pipe)) >>> + (port (with-error-to-port >>> (cdr >>> err-cons) >>> + (lambda () >>> (open-input-pipe cmd)))) >>> + (_ (setvbuf (car err-cons) >>> 'block >>> + (* 1024 1024 >>> 16))) >>> + (result (read-delimited "" >>> port))) >>> + (close-port (cdr err-cons)) >>> + (values result (read-delimited "" >>> (car >>> err-cons))))))) >>> + (string-split >>> + (call-command >>> + (string-join (list #$%fail2ban-server-cmd >>> "status" "sshd") " ")) >>> + #\newline))) >>> + marionette)) >> >> Perhaps this could be turned into an Shepherd action, and the >> Guile >> procedure could do the above to return the text output; to >> simplify >> the >> test and reduce boilerplate, while providing value to the user. >> Here I use a cli of fail2ban. It's arguments are very extended. I'm not sure how it should be implemented in terms of shepherd-action. I will continue thinking about it, but I would prefer to skip this for now, if you don't mind. thanks in advance, muradm --=-=-= Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQIzBAEBCAAdFiEESPY5lma9A9l5HGLP6M7O0mLOBeIFAmMFN14ACgkQ6M7O0mLO BeI8gxAAgM9Grih6A+uIX/JdLwXXmW6CLi5Vj1zZs63Kw0KoMoknvOIOcabFvf7f WhUwFfaHkoBvuUAdCy6+rb6Is4B11Xn7ihFOZ1/C5xHL9uo0HtGys8lRht2t+Q7V RcM90CEN75NITU6fsT5fRBOU0CUNrx7PD8HqKAGwjYu7npGC9jQ9nt72nq4gwigA S+ANrPn4QmS3xCspbY5qXtxSmkElDwCELzQvA9PBbtsmEyKwVw5ZJifpBtVK6ZVu lsq7hi2i75HQ2hQsnTAQBvkGu6Y4ptGJumgOP4Jzhct96bQ76h7I+8iZe4/RvNCN uF0vYzjYsJ6VyZrz/E2Qovtq9gZB680zk6y6VpV7v5P/z3+uZuIwnpgtPJBr93Nm sOBYiop17y3I9HiMIr9ZNb5FonE/A3voEWDAQmiGGyYRJfRYkKPGXSmQYXAlvKCM F9nexpRyUUeUYY4mxh3kxUVx84JpXaDkge9EpFzUaVxikzTePnQ0Rc7ikD5kAzFS RK/JMxteTrWmWujOyvk3PKUNBkIwGcPiIwOoPhRumvqFNfe9pCqmDj0nzuDUPITA piw+rsCOEPFiBNzr4q+Iy8ioTCNdPYx3/duCuf6v7FVIJUzHcYjUjftbRZAWWA8W /wsofO8FMrs+PZp/WjbSFyyjFDfOBP5H5vUmdFSsYH5KpxzbXiE= =j3/U -----END PGP SIGNATURE----- --=-=-=-- From debbugs-submit-bounces@debbugs.gnu.org Sun Aug 28 22:01:17 2022 Received: (at 56608-done) by debbugs.gnu.org; 29 Aug 2022 02:01:17 +0000 Received: from localhost ([127.0.0.1]:59771 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oSU5V-0005EQ-0y for submit@debbugs.gnu.org; Sun, 28 Aug 2022 22:01:17 -0400 Received: from mail-qt1-f173.google.com ([209.85.160.173]:38846) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1oSU5T-0005ED-Dr for 56608-done@debbugs.gnu.org; Sun, 28 Aug 2022 22:01:16 -0400 Received: by mail-qt1-f173.google.com with SMTP id y18so5219683qtv.5 for <56608-done@debbugs.gnu.org>; Sun, 28 Aug 2022 19:01:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=mime-version:user-agent:message-id:in-reply-to:date:references :subject:cc:to:from:from:to:cc; bh=mbmS/Xnep9DRTsn5jTEV/QzyN0mWeC4HRDftFjw4bwU=; b=mBtzvVnPg3TcllSWWomvmSTUHpH61TFZyk93K43h48CbyJ2DwGGr95aEoZRTPxQDtP +lfSkMT8tNO2tcTW86iw7C3nOQgdNEBXxpo7SsNWM9eWVZIr80+/YwctowytT8Umzwf9 YklJ7Kg7DYoxt4npIBonEDetCuoL5hH0JJcGGqX79y/ELdg3BzDi/YsfJoZkoiYTvry6 Fpdw3u5JE9rGOT37SPeYmtS5ftBlMTaNIkaZiVjxTvrlhL6kVJX/VCCUjeKwmx2IRVy1 X+JrS7xfYS2ugfCAkBfgB4BZLDyae75UYIzWHFeAogw10f5MKcn2kwfl56kwuuiEGKUw OaDg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=mime-version:user-agent:message-id:in-reply-to:date:references :subject:cc:to:from:x-gm-message-state:from:to:cc; bh=mbmS/Xnep9DRTsn5jTEV/QzyN0mWeC4HRDftFjw4bwU=; b=R5pK8CUg9+y03+MbdRK0jBOo3HsFgWToGu5OZEp/3LBNEeeYAXZsGTT4sXG0D7zMBR USMVwPN/4UpGdlELJeFFqKYKD3nZzzZ143Z5cjOlqBOoxXlZzXIYA0eglE1Gk01g8+TL YsuqtTjg037ueOd7AtRhsB7uYkBCtuUM8eEWxQQIm9cjv0l5vcqctw0XEylUMFIDVjZh sVU0nMN3KB/Ar0d/UK9gsKvw1sX3SuZQZTFyi9sBnElZ0bbqIm/kgS+AMtKObVg4ln8Z JTJJmBewKwJTJ5ROT2SRNIGI1F9i0fldIVdQPGICDP/h0ezgg0kPrHM1y6W6+fjDDKko Es3Q== X-Gm-Message-State: ACgBeo3H+vjpOWPCSTBsfmV/a8R+FuWRMc4YhDAOAHbxXtLZ0kX3XmTo MTVAWnwtsOt7LjdrdsEl1JxQ5UQXC6Y= X-Google-Smtp-Source: AA6agR42uVtNggAwPa4Nt9F/UZXRZoelTjRlEc7bTBKI3RlaoO4SF+HuDzXFyRRGDKjpDKsYwogx/A== X-Received: by 2002:ac8:7f03:0:b0:344:6f5c:6aee with SMTP id f3-20020ac87f03000000b003446f5c6aeemr8710086qtk.558.1661738469673; Sun, 28 Aug 2022 19:01:09 -0700 (PDT) Received: from hurd (dsl-205-233-125-60.b2b2c.ca. [205.233.125.60]) by smtp.gmail.com with ESMTPSA id n19-20020ac85b53000000b003447c4f5aa5sm4566242qtw.24.2022.08.28.19.01.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 28 Aug 2022 19:01:09 -0700 (PDT) From: Maxim Cournoyer To: muradm Subject: Re: [PATCH v3] gnu: security: Add fail2ban-service-type. References: <87r116ybj4.fsf@muradm.net> <20220823201355.23691-1-mail@muradm.net> Date: Sun, 28 Aug 2022 22:01:08 -0400 In-Reply-To: <20220823201355.23691-1-mail@muradm.net> (muradm's message of "Tue, 23 Aug 2022 23:13:55 +0300") Message-ID: <87ler7byfv.fsf@gmail.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/28.1 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain X-Spam-Score: 0.0 (/) X-Debbugs-Envelope-To: 56608-done Cc: 56608-done@debbugs.gnu.org X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) Hello! muradm writes: > * gnu/services/security.scm: New module. > * gnu/tests/security.scm: New module. > * gnu/local.mk: Add new security module and tests. > * doc/guix.text: Add fail2ban-service-type documentation. I've made improvements to the docs, and other mostly cosmetic changes, and pushed with 3c2d2b453832167df02f4aa25de4857a003fbecf. Thank you! Closing. Maxim From unknown Sat Aug 16 15:54:09 2025 Received: (at fakecontrol) by fakecontrolmessage; To: internal_control@debbugs.gnu.org From: Debbugs Internal Request Subject: Internal Control Message-Id: bug archived. Date: Mon, 26 Sep 2022 11:24:04 +0000 User-Agent: Fakemail v42.6.9 # This is a fake control message. # # The action: # bug archived. thanks # This fakemail brought to you by your local debbugs # administrator