security.scm 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2022 muradm <mail@muradm.net>
  3. ;;;
  4. ;;; This file is part of GNU Guix.
  5. ;;;
  6. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  7. ;;; under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation; either version 3 of the License, or (at
  9. ;;; your option) any later version.
  10. ;;;
  11. ;;; GNU Guix is distributed in the hope that it will be useful, but
  12. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;;; GNU General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU General Public License
  17. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  18. (define-module (gnu services security)
  19. #:use-module (gnu packages admin)
  20. #:use-module (gnu services)
  21. #:use-module (gnu services configuration)
  22. #:use-module (gnu services shepherd)
  23. #:use-module (guix gexp)
  24. #:use-module (guix packages)
  25. #:use-module (guix records)
  26. #:use-module (guix ui)
  27. #:use-module (ice-9 format)
  28. #:use-module (ice-9 match)
  29. #:use-module (srfi srfi-1)
  30. #:export (fail2ban-configuration
  31. fail2ban-ignore-cache-configuration
  32. fail2ban-jail-action-configuration
  33. fail2ban-jail-configuration
  34. fail2ban-jail-filter-configuration
  35. fail2ban-jail-service
  36. fail2ban-service-type))
  37. (define-configuration/no-serialization fail2ban-ignore-cache-configuration
  38. (key string "Cache key.")
  39. (max-count integer "Cache size.")
  40. (max-time integer "Cache time."))
  41. (define serialize-fail2ban-ignore-cache-configuration
  42. (match-lambda
  43. (($ <fail2ban-ignore-cache-configuration> _ key max-count max-time)
  44. (format #f "key=\"~a\", max-count=~d, max-time=~d"
  45. key max-count max-time))))
  46. (define-maybe/no-serialization string)
  47. (define-configuration/no-serialization fail2ban-jail-filter-configuration
  48. (name string "Filter to use.")
  49. (mode maybe-string "Mode for filter."))
  50. (define serialize-fail2ban-jail-filter-configuration
  51. (match-lambda
  52. (($ <fail2ban-jail-filter-configuration> _ name mode)
  53. (format #f "~a~@[[mode=~a]~]" name (maybe-value mode)))))
  54. (define (argument? a)
  55. (and (pair? a)
  56. (string? (car a))
  57. (or (string? (cdr a))
  58. (list-of-strings? (cdr a)))))
  59. (define list-of-arguments? (list-of argument?))
  60. (define-configuration/no-serialization fail2ban-jail-action-configuration
  61. (name string "Action name.")
  62. (arguments (list-of-arguments '()) "Action arguments."))
  63. (define list-of-fail2ban-jail-actions?
  64. (list-of fail2ban-jail-action-configuration?))
  65. (define (serialize-fail2ban-jail-action-configuration-arguments args)
  66. (let* ((multi-value
  67. (lambda (v)
  68. (format #f "~a" (string-join v ","))))
  69. (any-value
  70. (lambda (v)
  71. (if (list? v) (string-append "\"" (multi-value v) "\"") v)))
  72. (key-value
  73. (lambda (e)
  74. (format #f "~a=~a" (car e) (any-value (cdr e))))))
  75. (format #f "~a" (string-join (map key-value args) ","))))
  76. (define serialize-fail2ban-jail-action-configuration
  77. (match-lambda
  78. (($ <fail2ban-jail-action-configuration> _ name arguments)
  79. (format
  80. #f "~a~a"
  81. name
  82. (if (null? arguments) ""
  83. (format
  84. #f "[~a]"
  85. (serialize-fail2ban-jail-action-configuration-arguments
  86. arguments)))))))
  87. (define fail2ban-backend->string
  88. (match-lambda
  89. ('auto "auto")
  90. ('pyinotify "pyinotify")
  91. ('gamin "gamin")
  92. ('polling "polling")
  93. ('systemd "systemd")
  94. (unknown
  95. (leave (G_ "fail2ban: '~a' is not a supported backend~%") unknown))))
  96. (define fail2ban-log-encoding->string
  97. (match-lambda
  98. ('auto "auto")
  99. ('utf-8 "utf-8")
  100. ('ascii "ascii")
  101. (unknown
  102. (leave (G_ "fail2ban: '~a' is not a supported log encoding~%") unknown))))
  103. (define (fail2ban-jail-configuration-serialize-field-name name)
  104. (cond ((symbol? name)
  105. (fail2ban-jail-configuration-serialize-field-name
  106. (symbol->string name)))
  107. ((string-suffix? "?" name)
  108. (fail2ban-jail-configuration-serialize-field-name
  109. (string-drop-right name 1)))
  110. ((string-prefix? "ban-time-" name)
  111. (fail2ban-jail-configuration-serialize-field-name
  112. (string-append "bantime." (substring name 9))))
  113. ((string-contains name "-")
  114. (fail2ban-jail-configuration-serialize-field-name
  115. (string-filter (lambda (c) (not (equal? c #\-))) name)))
  116. (else name)))
  117. (define (fail2ban-jail-configuration-serialize-string field-name value)
  118. #~(string-append
  119. #$(fail2ban-jail-configuration-serialize-field-name field-name)
  120. " = " #$value "\n"))
  121. (define (fail2ban-jail-configuration-serialize-integer field-name value)
  122. (fail2ban-jail-configuration-serialize-string
  123. field-name (number->string value)))
  124. (define (fail2ban-jail-configuration-serialize-boolean field-name value)
  125. (fail2ban-jail-configuration-serialize-string
  126. field-name (if value "true" "false")))
  127. (define (fail2ban-jail-configuration-serialize-backend field-name value)
  128. (if (maybe-value-set? value)
  129. (fail2ban-jail-configuration-serialize-string
  130. field-name (fail2ban-backend->string value))
  131. ""))
  132. (define (fail2ban-jail-configuration-serialize-fail2ban-ignore-cache-configuration field-name value)
  133. (fail2ban-jail-configuration-serialize-string
  134. field-name (serialize-fail2ban-ignore-cache-configuration value)))
  135. (define (fail2ban-jail-configuration-serialize-fail2ban-jail-filter-configuration field-name value)
  136. (fail2ban-jail-configuration-serialize-string
  137. field-name (serialize-fail2ban-jail-filter-configuration value)))
  138. (define (fail2ban-jail-configuration-serialize-log-encoding field-name value)
  139. (if (maybe-value-set? value)
  140. (fail2ban-jail-configuration-serialize-string
  141. field-name (fail2ban-log-encoding->string value))
  142. ""))
  143. (define (fail2ban-jail-configuration-serialize-list-of-strings field-name value)
  144. (if (null? value)
  145. ""
  146. (fail2ban-jail-configuration-serialize-string
  147. field-name (string-join value " "))))
  148. (define (fail2ban-jail-configuration-serialize-list-of-fail2ban-jail-actions field-name value)
  149. (if (null? value)
  150. ""
  151. (fail2ban-jail-configuration-serialize-string
  152. field-name (string-join
  153. (map serialize-fail2ban-jail-action-configuration value) "\n"))))
  154. (define (fail2ban-jail-configuration-serialize-symbol field-name value)
  155. (fail2ban-jail-configuration-serialize-string field-name (symbol->string value)))
  156. (define-maybe integer (prefix fail2ban-jail-configuration-))
  157. (define-maybe string (prefix fail2ban-jail-configuration-))
  158. (define-maybe boolean (prefix fail2ban-jail-configuration-))
  159. (define-maybe symbol (prefix fail2ban-jail-configuration-))
  160. (define-maybe fail2ban-ignore-cache-configuration (prefix fail2ban-jail-configuration-))
  161. (define-maybe fail2ban-jail-filter-configuration (prefix fail2ban-jail-configuration-))
  162. (define-configuration fail2ban-jail-configuration
  163. (name
  164. string
  165. "Required name of this jail configuration."
  166. empty-serializer)
  167. (enabled?
  168. (boolean #t)
  169. "Whether this jail is enabled.")
  170. (backend
  171. maybe-symbol
  172. "Backend to use to detect changes in the @code{log-path}. The default is
  173. 'auto. To consult the defaults of the jail configuration, refer to the
  174. @file{/etc/fail2ban/jail.conf} file of the @code{fail2ban} package."
  175. fail2ban-jail-configuration-serialize-backend)
  176. (max-retry
  177. maybe-integer
  178. "The number of failures before a host get banned
  179. (e.g. @code{(max-retry 5)}).")
  180. (max-matches
  181. maybe-integer
  182. "The number of matches stored in ticket (resolvable via
  183. tag @code{<matches>}) in action.")
  184. (find-time
  185. maybe-string
  186. "The time window during which the maximum retry count must be reached for
  187. an IP address to be banned. A host is banned if it has generated
  188. @code{max-retry} during the last @code{find-time}
  189. seconds (e.g. @code{(find-time \"10m\")}). It can be provided in seconds or
  190. using Fail2Ban's \"time abbreviation format\", as described in @command{man 5
  191. jail.conf}.")
  192. (ban-time
  193. maybe-string
  194. "The duration, in seconds or time abbreviated format, that a ban should last.
  195. (e.g. @code{(ban-time \"10m\")}).")
  196. (ban-time-increment?
  197. maybe-boolean
  198. "Whether to consider past bans to compute increases to the default ban time
  199. of a specific IP address.")
  200. (ban-time-factor
  201. maybe-string
  202. "The coefficient to use to compute an exponentially growing ban time.")
  203. (ban-time-formula
  204. maybe-string
  205. "This is the formula used to calculate the next value of a ban time.")
  206. (ban-time-multipliers
  207. maybe-string
  208. "Used to calculate next value of ban time instead of formula.")
  209. (ban-time-max-time
  210. maybe-string
  211. "The maximum number of seconds a ban should last.")
  212. (ban-time-rnd-time
  213. maybe-string
  214. "The maximum number of seconds a randomized ban time should last. This can
  215. be useful to stop ``clever'' botnets calculating the exact time an IP address
  216. can be unbanned again.")
  217. (ban-time-overall-jails?
  218. maybe-boolean
  219. "When true, it specifies the search of an IP address in the database should
  220. be made across all jails. Otherwise, only the current jail of the ban IP
  221. address is considered.")
  222. (ignore-self?
  223. maybe-boolean
  224. "Never ban the local machine's own IP address.")
  225. (ignore-ip
  226. (list-of-strings '())
  227. "A list of IP addresses, CIDR masks or DNS hosts to ignore.
  228. @code{fail2ban} will not ban a host which matches an address in this list.")
  229. (ignore-cache
  230. maybe-fail2ban-ignore-cache-configuration
  231. "Provide cache parameters for the ignore failure check.")
  232. (filter
  233. maybe-fail2ban-jail-filter-configuration
  234. "The filter to use by the jail, specified via a
  235. @code{<fail2ban-jail-filter-configuration>} object. By default, jails have
  236. names matching their filter name.")
  237. (log-time-zone
  238. maybe-string
  239. "The default time zone for log lines that do not have one.")
  240. (log-encoding
  241. maybe-symbol
  242. "The encoding of the log files handled by the jail.
  243. Possible values are: @code{'ascii}, @code{'utf-8} and @code{'auto}."
  244. fail2ban-jail-configuration-serialize-log-encoding)
  245. (log-path
  246. (list-of-strings '())
  247. "The file names of the log files to be monitored.")
  248. (action
  249. (list-of-fail2ban-jail-actions '())
  250. "A list of @code{<fail2ban-jail-action-configuration>}.")
  251. (extra-content
  252. (text-config '())
  253. "Extra content for the jail configuration, provided as a list of file-like
  254. objects."
  255. serialize-text-config)
  256. (prefix fail2ban-jail-configuration-))
  257. (define list-of-fail2ban-jail-configurations?
  258. (list-of fail2ban-jail-configuration?))
  259. (define (serialize-fail2ban-jail-configuration config)
  260. #~(string-append
  261. #$(format #f "[~a]\n" (fail2ban-jail-configuration-name config))
  262. #$(serialize-configuration
  263. config fail2ban-jail-configuration-fields)))
  264. (define-configuration/no-serialization fail2ban-configuration
  265. (fail2ban
  266. (package fail2ban)
  267. "The @code{fail2ban} package to use. It is used for both binaries and as
  268. base default configuration that is to be extended with
  269. @code{<fail2ban-jail-configuration>} objects.")
  270. (run-directory
  271. (string "/var/run/fail2ban")
  272. "The state directory for the @code{fail2ban} daemon.")
  273. (jails
  274. (list-of-fail2ban-jail-configurations '())
  275. "Instances of @code{<fail2ban-jail-configuration>} collected from
  276. extensions.")
  277. (extra-jails
  278. (list-of-fail2ban-jail-configurations '())
  279. "Instances of @code{<fail2ban-jail-configuration>} explicitly provided.")
  280. (extra-content
  281. (text-config '())
  282. "Extra raw content to add to the end of the @file{jail.local} file,
  283. provided as a list of file-like objects."))
  284. (define (serialize-fail2ban-configuration config)
  285. (let* ((jails (fail2ban-configuration-jails config))
  286. (extra-jails (fail2ban-configuration-extra-jails config))
  287. (extra-content (fail2ban-configuration-extra-content config)))
  288. (interpose
  289. (append (map serialize-fail2ban-jail-configuration
  290. (append jails extra-jails))
  291. (list (serialize-text-config 'extra-content extra-content))))))
  292. (define (config->fail2ban-etc-directory config)
  293. (let* ((fail2ban (fail2ban-configuration-fail2ban config))
  294. (jail-local (apply mixed-text-file "jail.local"
  295. (serialize-fail2ban-configuration config))))
  296. (directory-union
  297. "fail2ban-configuration"
  298. (list (computed-file
  299. "etc-fail2ban"
  300. (with-imported-modules '((guix build utils))
  301. #~(begin
  302. (use-modules (guix build utils))
  303. (let ((etc (string-append #$output "/etc")))
  304. (mkdir-p etc)
  305. (symlink #$(file-append fail2ban "/etc/fail2ban")
  306. (string-append etc "/fail2ban"))))))
  307. (computed-file
  308. "etc-fail2ban-jail.local"
  309. (with-imported-modules '((guix build utils))
  310. #~(begin
  311. (use-modules (guix build utils))
  312. (define etc/fail2ban (string-append #$output
  313. "/etc/fail2ban"))
  314. (mkdir-p etc/fail2ban)
  315. (symlink #$jail-local (string-append etc/fail2ban
  316. "/jail.local")))))))))
  317. (define (fail2ban-shepherd-service config)
  318. (match-record config <fail2ban-configuration>
  319. (fail2ban run-directory)
  320. (let* ((fail2ban-server (file-append fail2ban "/bin/fail2ban-server"))
  321. (pid-file (in-vicinity run-directory "fail2ban.pid"))
  322. (socket-file (in-vicinity run-directory "fail2ban.sock"))
  323. (config-dir (file-append (config->fail2ban-etc-directory config)
  324. "/etc/fail2ban"))
  325. (fail2ban-action (lambda args
  326. #~(lambda _
  327. (invoke #$fail2ban-server
  328. "-c" #$config-dir
  329. "-p" #$pid-file
  330. "-s" #$socket-file
  331. "-b"
  332. #$@args)))))
  333. ;; TODO: Add 'reload' action.
  334. (list (shepherd-service
  335. (provision '(fail2ban))
  336. (documentation "Run the fail2ban daemon.")
  337. (requirement '(user-processes))
  338. (modules `((ice-9 match)
  339. ,@%default-modules))
  340. (start (fail2ban-action "start"))
  341. (stop (fail2ban-action "stop")))))))
  342. (define fail2ban-service-type
  343. (service-type (name 'fail2ban)
  344. (extensions
  345. (list (service-extension shepherd-root-service-type
  346. fail2ban-shepherd-service)))
  347. (compose concatenate)
  348. (extend (lambda (config jails)
  349. (fail2ban-configuration
  350. (inherit config)
  351. (jails (append (fail2ban-configuration-jails config)
  352. jails)))))
  353. (default-value (fail2ban-configuration))
  354. (description "Run the fail2ban server.")))
  355. (define (fail2ban-jail-service svc-type jail)
  356. "Convenience procedure to add a fail2ban service extension to SVC-TYPE, a
  357. <service-type> object. The fail2ban extension is specified by JAIL, a
  358. <fail2ban-jail-configuration> object."
  359. (service-type
  360. (inherit svc-type)
  361. (extensions
  362. (append (service-type-extensions svc-type)
  363. (list (service-extension fail2ban-service-type
  364. (lambda _ (list jail))))))))
  365. ;;;
  366. ;;; Documentation generation.
  367. ;;;
  368. (define (generate-doc)
  369. (configuration->documentation 'fail2ban-configuration)
  370. (configuration->documentation 'fail2ban-ignore-cache-configuration)
  371. (configuration->documentation 'fail2ban-jail-action-configuration)
  372. (configuration->documentation 'fail2ban-jail-configuration)
  373. (configuration->documentation 'fail2ban-jail-filter-configuration))