gnu: security: Add fail2ban-service-type.
* 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. Signed-off-by: Maxim Cournoyer <maxim.cournoyer@gmail.com>master
parent
ba0dbe3bf2
commit
3c2d2b4538
249
doc/guix.texi
249
doc/guix.texi
|
@ -36311,6 +36311,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 IP addresses that show
|
||||
malicious signs -- repeated password failures, attempts to make use of
|
||||
exploits, etc.
|
||||
|
||||
@code{fail2ban-service-type} service type is provided by the @code{(gnu
|
||||
services security)} module.
|
||||
|
||||
This service type runs the @code{fail2ban} daemon. It can be configured
|
||||
in various ways, which are:
|
||||
|
||||
@table @asis
|
||||
@item Basic configuration
|
||||
The basic parameters of the Fail2Ban service can be configured via its
|
||||
@code{fail2ban} configuration, which is documented below.
|
||||
|
||||
@item User-specified jail extensions
|
||||
The @code{fail2ban-jail-service} function can be used to add new
|
||||
Fail2Ban jails.
|
||||
|
||||
@item Shepherd extension mechanism
|
||||
Service developers can extend the @code{fail2ban-service-type} service
|
||||
type itself via the usual service extension mechanism.
|
||||
@end table
|
||||
|
||||
@defvr {Scheme Variable} fail2ban-service-type
|
||||
|
||||
This is the type of the service that runs @code{fail2ban} daemon. Below
|
||||
is an example of a basic, explicit configuration:
|
||||
|
||||
@lisp
|
||||
(append
|
||||
(list
|
||||
(service fail2ban-service-type
|
||||
(fail2ban-configuration
|
||||
(extra-jails
|
||||
(list
|
||||
(fail2ban-jail-configuration
|
||||
(name "sshd")
|
||||
(enabled #t))))))
|
||||
;; There is no implicit dependency on an actual SSH
|
||||
;; service, so you need to provide one.
|
||||
(service openssh-service-type))
|
||||
%base-services)
|
||||
@end lisp
|
||||
@end defvr
|
||||
|
||||
@deffn {Scheme Procedure} fail2ban-jail-service @var{svc-type} @var{jail}
|
||||
Extend @var{svc-type}, a @code{<service-type>} object with @var{jail}, a
|
||||
@code{fail2ban-jail-configuration} object.
|
||||
|
||||
For example:
|
||||
|
||||
@lisp
|
||||
(append
|
||||
(list
|
||||
(service
|
||||
;; The 'fail2ban-jail-service' procedure can extend any service type
|
||||
;; with a fail2ban jail. This removes the requirement 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
|
||||
|
||||
Below is the reference for the different @code{jail-service-type}
|
||||
configuration records.
|
||||
|
||||
@c The documentation is to be auto-generated via
|
||||
@c 'generate-documentation'. See at the bottom of (gnu services
|
||||
@c security).
|
||||
|
||||
@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 is used for both binaries and as
|
||||
base default configuration that is to be extended with
|
||||
@code{<fail2ban-jail-configuration>} objects.
|
||||
|
||||
@item @code{run-directory} (default: @code{"/var/run/fail2ban"}) (type: string)
|
||||
The state directory for the @code{fail2ban} daemon.
|
||||
|
||||
@item @code{jails} (default: @code{()}) (type: list-of-fail2ban-jail-configurations)
|
||||
Instances of @code{<fail2ban-jail-configuration>} collected from
|
||||
extensions.
|
||||
|
||||
@item @code{extra-jails} (default: @code{()}) (type: list-of-fail2ban-jail-configurations)
|
||||
Instances of @code{<fail2ban-jail-configuration>} explicitly provided.
|
||||
|
||||
@item @code{extra-content} (type: maybe-string)
|
||||
Extra raw content to add to the end of the @file{jail.local} file.
|
||||
|
||||
@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-configuration
|
||||
Available @code{fail2ban-jail-configuration} fields are:
|
||||
|
||||
@table @asis
|
||||
@item @code{name} (type: string)
|
||||
Required name of this jail configuration.
|
||||
|
||||
@item @code{enabled?} (default: @code{#t}) (type: boolean)
|
||||
Whether this jail is enabled.
|
||||
|
||||
@item @code{backend} (type: maybe-symbol)
|
||||
Backend to use to detect changes in the @code{ogpath}. The default is
|
||||
'auto. To consult the defaults of the jail configuration, refer to the
|
||||
@file{/etc/fail2ban/jail.conf} file of the @code{fail2ban} package.
|
||||
|
||||
@item @code{max-retry} (type: maybe-integer)
|
||||
The number of failures before a host get banned (e.g. @code{(max-retry
|
||||
5)}).
|
||||
|
||||
@item @code{max-matches} (type: maybe-integer)
|
||||
The number of matches stored in ticket (resolvable via tag
|
||||
@code{<matches>}) in action.
|
||||
|
||||
@item @code{find-time} (type: maybe-string)
|
||||
The time window during which the maximum retry count must be reached for
|
||||
an IP address to be banned. A host is banned if it has generated
|
||||
@code{max-retry} during the last @code{find-time} seconds (e.g.
|
||||
@code{(find-time "10m")}). It can be provided in seconds or using
|
||||
Fail2Ban's "time abbreviation format", as described in @command{man 5
|
||||
jail.conf}.
|
||||
|
||||
@item @code{ban-time} (type: maybe-string)
|
||||
The duration, in seconds or time abbreviated format, that a ban should
|
||||
last. (e.g. @code{(ban-time "10m")}).
|
||||
|
||||
@item @code{ban-time-increment?} (type: maybe-boolean)
|
||||
Whether to consider past bans to compute increases to the default ban
|
||||
time of a specific IP address.
|
||||
|
||||
@item @code{ban-time-factor} (type: maybe-string)
|
||||
The coefficient to use to compute an exponentially growing ban time.
|
||||
|
||||
@item @code{ban-time-formula} (type: maybe-string)
|
||||
This is the formula used to calculate the next value of a 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)
|
||||
The maximum number of seconds a ban should last.
|
||||
|
||||
@item @code{ban-time-rnd-time} (type: maybe-string)
|
||||
The maximum number of seconds a randomized ban time should last. This
|
||||
can be useful to stop ``clever'' botnets calculating the exact time an
|
||||
IP address can be unbanned again.
|
||||
|
||||
@item @code{ban-time-overall-jails?} (type: maybe-boolean)
|
||||
When true, it specifies the search of an IP address in the database
|
||||
should be made across all jails. Otherwise, only the current jail of
|
||||
the ban IP address is considered.
|
||||
|
||||
@item @code{ignore-self?} (type: maybe-boolean)
|
||||
Never ban the local machine's own IP address.
|
||||
|
||||
@item @code{ignore-ip} (default: @code{()}) (type: list-of-strings)
|
||||
A list of IP addresses, CIDR masks or DNS hosts to ignore.
|
||||
@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 the ignore failure check.
|
||||
|
||||
@item @code{filter} (type: maybe-fail2ban-jail-filter-configuration)
|
||||
The filter to use by the jail, specified via a
|
||||
@code{<fail2ban-jail-filter-configuration>} object. By default, jails
|
||||
have names matching their filter name.
|
||||
|
||||
@item @code{log-time-zone} (type: maybe-string)
|
||||
The default time zone for log lines that do not have one.
|
||||
|
||||
@item @code{log-encoding} (type: maybe-symbol)
|
||||
The encoding of the log files handled by the jail. Possible values are:
|
||||
@code{'ascii}, @code{'utf-8} and @code{'auto}.
|
||||
|
||||
@item @code{log-path} (default: @code{()}) (type: list-of-strings)
|
||||
The file names of the log files to be monitored.
|
||||
|
||||
@item @code{action} (default: @code{()}) (type: list-of-fail2ban-jail-actions)
|
||||
A list of @code{<fail2ban-jail-action-configuration>}.
|
||||
|
||||
@item @code{extra-content} (type: maybe-string)
|
||||
Extra content for the jail configuration.
|
||||
|
||||
@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
|
||||
|
||||
@c End of auto-generated fail2ban documentation.
|
||||
|
||||
@node Setuid Programs
|
||||
@section Setuid Programs
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
# Copyright © 2022 Remco van 't Veer <remco@remworks.net>
|
||||
# Copyright © 2022 Artyom V. Poptsov <poptsov.artyom@gmail.com>
|
||||
# Copyright © 2022 John Kehayias <john.kehayias@protonmail.com>
|
||||
# Copyright © 2022 muradm <mail@muradm.net>
|
||||
#
|
||||
# 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 \
|
||||
|
|
|
@ -0,0 +1,415 @@
|
|||
;;; GNU Guix --- Functional package management for GNU
|
||||
;;; Copyright © 2022 muradm <mail@muradm.net>
|
||||
;;;
|
||||
;;; This file is part of GNU Guix.
|
||||
;;;
|
||||
;;; GNU Guix is free software; you can redistribute it and/or modify it
|
||||
;;; under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation; either version 3 of the License, or (at
|
||||
;;; your option) any later version.
|
||||
;;;
|
||||
;;; GNU Guix is distributed in the hope that it will be useful, but
|
||||
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
(define-module (gnu services 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-ignore-cache-configuration
|
||||
fail2ban-jail-action-configuration
|
||||
fail2ban-jail-configuration
|
||||
fail2ban-jail-filter-configuration
|
||||
fail2ban-jail-service
|
||||
fail2ban-service-type))
|
||||
|
||||
(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
|
||||
(($ <fail2ban-ignore-cache-configuration> _ 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
|
||||
(($ <fail2ban-jail-filter-configuration> _ name mode)
|
||||
(format #f "~a~@[[mode=~a]~]" name (maybe-value 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
|
||||
(($ <fail2ban-jail-action-configuration> _ 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)))
|
||||
(else 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 (maybe-value-set? 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 (maybe-value-set? 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 (maybe-value-set? 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?
|
||||
(boolean #t)
|
||||
"Whether this jail is enabled.")
|
||||
(backend
|
||||
maybe-symbol
|
||||
"Backend to use to detect changes in the @code{ogpath}. The default is
|
||||
'auto. To consult the defaults of the jail configuration, refer to the
|
||||
@file{/etc/fail2ban/jail.conf} file of the @code{fail2ban} package."
|
||||
fail2ban-jail-configuration-serialize-backend)
|
||||
(max-retry
|
||||
maybe-integer
|
||||
"The number of failures before a host get banned
|
||||
(e.g. @code{(max-retry 5)}).")
|
||||
(max-matches
|
||||
maybe-integer
|
||||
"The number of matches stored in ticket (resolvable via
|
||||
tag @code{<matches>}) in action.")
|
||||
(find-time
|
||||
maybe-string
|
||||
"The time window during which the maximum retry count must be reached for
|
||||
an IP address to be banned. A host is banned if it has generated
|
||||
@code{max-retry} during the last @code{find-time}
|
||||
seconds (e.g. @code{(find-time \"10m\")}). It can be provided in seconds or
|
||||
using Fail2Ban's \"time abbreviation format\", as described in @command{man 5
|
||||
jail.conf}.")
|
||||
(ban-time
|
||||
maybe-string
|
||||
"The duration, in seconds or time abbreviated format, that a ban should last.
|
||||
(e.g. @code{(ban-time \"10m\")}).")
|
||||
(ban-time-increment?
|
||||
maybe-boolean
|
||||
"Whether to consider past bans to compute increases to the default ban time
|
||||
of a specific IP address.")
|
||||
(ban-time-factor
|
||||
maybe-string
|
||||
"The coefficient to use to compute an exponentially growing ban time.")
|
||||
(ban-time-formula
|
||||
maybe-string
|
||||
"This is the formula used to calculate the next value of a ban time.")
|
||||
(ban-time-multipliers
|
||||
maybe-string
|
||||
"Used to calculate next value of ban time instead of formula.")
|
||||
(ban-time-max-time
|
||||
maybe-string
|
||||
"The maximum number of seconds a ban should last.")
|
||||
(ban-time-rnd-time
|
||||
maybe-string
|
||||
"The maximum number of seconds a randomized ban time should last. This can
|
||||
be useful to stop ``clever'' botnets calculating the exact time an IP address
|
||||
can be unbanned again.")
|
||||
(ban-time-overall-jails?
|
||||
maybe-boolean
|
||||
"When true, it specifies the search of an IP address in the database should
|
||||
be made across all jails. Otherwise, only the current jail of the ban IP
|
||||
address is considered.")
|
||||
(ignore-self?
|
||||
maybe-boolean
|
||||
"Never ban the local machine's own IP address.")
|
||||
(ignore-ip
|
||||
(list-of-strings '())
|
||||
"A list of IP addresses, CIDR masks or DNS hosts to ignore.
|
||||
@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 the ignore failure check.")
|
||||
(filter
|
||||
maybe-fail2ban-jail-filter-configuration
|
||||
"The filter to use by the jail, specified via a
|
||||
@code{<fail2ban-jail-filter-configuration>} object. By default, jails have
|
||||
names matching their filter name.")
|
||||
(log-time-zone
|
||||
maybe-string
|
||||
"The default time zone for log lines that do not have one.")
|
||||
(log-encoding
|
||||
maybe-symbol
|
||||
"The encoding of the log files handled by the jail.
|
||||
Possible values are: @code{'ascii}, @code{'utf-8} and @code{'auto}."
|
||||
fail2ban-jail-configuration-serialize-log-encoding)
|
||||
(log-path
|
||||
(list-of-strings '())
|
||||
"The file names of the log files to be monitored.")
|
||||
(action
|
||||
(list-of-fail2ban-jail-actions '())
|
||||
"A list of @code{<fail2ban-jail-action-configuration>}.")
|
||||
(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 is used for both binaries and as
|
||||
base default configuration that is to be extended with
|
||||
@code{<fail2ban-jail-configuration>} objects.")
|
||||
(run-directory
|
||||
(string "/var/run/fail2ban")
|
||||
"The state directory for the @code{fail2ban} daemon.")
|
||||
(jails
|
||||
(list-of-fail2ban-jail-configurations '())
|
||||
"Instances of @code{<fail2ban-jail-configuration>} collected from
|
||||
extensions.")
|
||||
(extra-jails
|
||||
(list-of-fail2ban-jail-configurations '())
|
||||
"Instances of @code{<fail2ban-jail-configuration>} explicitly provided.")
|
||||
(extra-content
|
||||
maybe-string
|
||||
"Extra raw content to add to 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 (maybe-value-set? extra-content)
|
||||
extra-content
|
||||
""))))))
|
||||
|
||||
(define (config->fail2ban-etc-directory config)
|
||||
(let* ((fail2ban (fail2ban-configuration-fail2ban config))
|
||||
(jail-local (apply mixed-text-file "jail.local"
|
||||
(serialize-fail2ban-configuration config))))
|
||||
(directory-union
|
||||
"fail2ban-configuration"
|
||||
(list (computed-file
|
||||
"etc-fail2ban"
|
||||
(with-imported-modules '((guix build utils))
|
||||
#~(begin
|
||||
(use-modules (guix build utils))
|
||||
(let ((etc (string-append #$output "/etc")))
|
||||
(mkdir-p etc)
|
||||
(symlink #$(file-append fail2ban "/etc/fail2ban")
|
||||
(string-append etc "/fail2ban"))))))
|
||||
(computed-file
|
||||
"etc-fail2ban-jail.local"
|
||||
(with-imported-modules '((guix build utils))
|
||||
#~(begin
|
||||
(use-modules (guix build utils))
|
||||
(define etc/fail2ban (string-append #$output
|
||||
"/etc/fail2ban"))
|
||||
(mkdir-p etc/fail2ban)
|
||||
(symlink #$jail-local (string-append etc/fail2ban
|
||||
"/jail.local")))))))))
|
||||
|
||||
(define (fail2ban-shepherd-service config)
|
||||
(match-record config <fail2ban-configuration>
|
||||
(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 (file-append (config->fail2ban-etc-directory config)
|
||||
"/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)
|
||||
"Convenience procedure to add a fail2ban service extension to SVC-TYPE, a
|
||||
<service-type> object. The fail2ban extension is specified by JAIL, a
|
||||
<fail2ban-jail-configuration> object."
|
||||
(service-type
|
||||
(inherit svc-type)
|
||||
(extensions
|
||||
(append (service-type-extensions svc-type)
|
||||
(list (service-extension fail2ban-service-type
|
||||
(lambda _ (list jail))))))))
|
||||
|
||||
|
||||
;;;
|
||||
;;; Documentation generation.
|
||||
;;;
|
||||
(define (generate-doc)
|
||||
(configuration->documentation 'fail2ban-configuration)
|
||||
(configuration->documentation 'fail2ban-ignore-cache-configuration)
|
||||
(configuration->documentation 'fail2ban-jail-action-configuration)
|
||||
(configuration->documentation 'fail2ban-jail-configuration)
|
||||
(configuration->documentation 'fail2ban-jail-filter-configuration))
|
|
@ -0,0 +1,221 @@
|
|||
;;; GNU Guix --- Functional package management for GNU
|
||||
;;; Copyright © 2022 muradm <mail@muradm.net>
|
||||
;;;
|
||||
;;; This file is part of GNU Guix.
|
||||
;;;
|
||||
;;; GNU Guix is free software; you can redistribute it and/or modify it
|
||||
;;; under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation; either version 3 of the License, or (at
|
||||
;;; your option) any later version.
|
||||
;;;
|
||||
;;; GNU Guix is distributed in the hope that it will be useful, but
|
||||
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
(define-module (gnu 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")))))))
|
||||
|
||||
(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))))
|
Reference in New Issue