aboutsummaryrefslogtreecommitdiff
path: root/src/property/firewalld.lisp
diff options
context:
space:
mode:
authorSean Whitton <spwhitton@spwhitton.name>2021-06-25 11:51:56 -0700
committerSean Whitton <spwhitton@spwhitton.name>2021-06-25 16:10:57 -0700
commitf3687fa69e3069eee9feaf2743c501df0322480f (patch)
tree39afdf9618cc9e5a008554941b01c82676a68cde /src/property/firewalld.lisp
parentce792f5e0a6aed1c4b8c284a1a142f22126127a7 (diff)
downloadconsfigurator-f3687fa69e3069eee9feaf2743c501df0322480f.tar.gz
add some firewalld properties
Signed-off-by: Sean Whitton <spwhitton@spwhitton.name>
Diffstat (limited to 'src/property/firewalld.lisp')
-rw-r--r--src/property/firewalld.lisp155
1 files changed, 155 insertions, 0 deletions
diff --git a/src/property/firewalld.lisp b/src/property/firewalld.lisp
new file mode 100644
index 0000000..4b75713
--- /dev/null
+++ b/src/property/firewalld.lisp
@@ -0,0 +1,155 @@
+;;; Consfigurator -- Lisp declarative configuration management system
+
+;;; Copyright (C) 2021 Sean Whitton <spwhitton@spwhitton.name>
+
+;;; This file 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, or (at your option)
+;;; any later version.
+
+;;; This file 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 this program. If not, see <http://www.gnu.org/licenses/>.
+
+(in-package :consfigurator.property.firewalld)
+(named-readtables:in-readtable :consfigurator)
+
+;; Setting the whole XML for the zone means that firewall configuration has to
+;; be in one place in the consfig, which is awkward when we might want
+;; different properties to be able to interact with the firewall depending on
+;; whether or not they are applied. So we take the approach of issuing single
+;; firewall-cmd(1) commands to set the permanent zone configuration.
+;;
+;; By contrast, for defining services, it should be fine to set the whole XML.
+
+(defproplist installed :posix ()
+ (:desc "firewalld installed")
+ (os:etypecase
+ (debianlike (apt:installed "firewalld"))))
+
+(defproplist service :posix (name xml)
+ (:desc #?"firewalld knows service ${name}")
+ (installed)
+ (file:exists-with-content
+ (merge-pathnames (strcat name ".xml") #P"/etc/firewalld/services/") xml))
+
+(defprop %firewall-cmd :posix (file warning &rest args)
+ (:apply
+ ;; --add-service will always tell us ALREADY_ENABLED if nothing was
+ ;; changed, but --set-target won't tell us whether a change was made, so we
+ ;; have to be prepared to look at whether the file changed, too.
+ ;;
+ ;; If we make no change to a builtin zone, or similar, then the
+ ;; corresponding .xml file may not exist either before or after running the
+ ;; command, and given how WITH-CHANGE-IF-CHANGES-FILE works, that means we
+ ;; fail to return :NO-CHANGE. However, we have enough :CHECK subroutines
+ ;; defined to avoid this situation actually arising.
+ (flet ((run ()
+ (let ((output (mrun "firewall-cmd" args)))
+ (and warning (search warning output) :no-change))))
+ (let ((result (if file
+ (with-change-if-changes-file
+ ((merge-pathnames file #P"/etc/firewalld/")) (run))
+ (run))))
+ (when (eql result :no-change)
+ (mrun "firewall-cmd" "--reload"))
+ result))))
+
+(defprop has-zone :posix (zone)
+ (:desc #?"firewalld zone ${zone} exists")
+ (:check (zerop (mrun :for-exit "firewall-cmd" "--permanent"
+ #?"--zone=${zone}" "--get-target")))
+ (:apply (mrun "firewall-cmd" "--permanent" #?"--new-zone=${zone}"))
+ (:unapply (mrun "firewall-cmd" "--permanent" #?"--delete-zone=${zone}")))
+
+(defproplist default-zone :posix (zone)
+ (:desc #?"firewalld default zone is ${zone}")
+ (installed)
+ (has-zone zone)
+ (%firewall-cmd "firewalld.conf" "ZONE_ALREADY_SET"
+ #?"--set-default-zone=${zone}"))
+
+(defproplist zone-target :posix (zone target)
+ (:desc #?"firewalld zone ${zone} has target ${target}")
+ (:check (string= target
+ (stripln (run :may-fail "firewall-cmd" "--permanent"
+ #?"--zone=${zone}" "--get-target"))))
+ (installed)
+ (has-zone zone)
+ (%firewall-cmd #?"zones/${zone}.xml" nil "--permanent"
+ #?"--zone=${zone}" #?"--set-target=${target}"))
+
+(defproplist has-service :posix (zone service)
+ (:desc #?"firewalld zone ${zone} has service ${service}")
+ (:check (zerop (mrun :for-exit "firewall-cmd" "--permanent"
+ #?"--zone=${zone}" #?"--query-service=${service}")))
+ (with-unapply
+ (installed)
+ (has-zone zone)
+ (%firewall-cmd #?"zones/${zone}.xml" "ALREADY_ENABLED"
+ "--permanent" #?"--zone=${zone}"
+ #?"--add-service=${service}")
+ :unapply (%firewall-cmd #?"zones/${zone}.xml" "NOT_ENABLED"
+ "--permanent" #?"--zone=${zone}"
+ #?"--remove-service=${service}")))
+
+(defproplist has-interface :posix (zone interface)
+ (:desc #?"firewalld zone ${zone} has interface ${interface}")
+ (:check (zerop (mrun :for-exit "firewall-cmd" "--permanent"
+ #?"--zone=${zone}"
+ #?"--query-interface=${interface}")))
+ (with-unapply
+ (installed)
+ (has-zone zone)
+ (%firewall-cmd #?"zones/${zone}.xml" nil
+ "--permanent" #?"--zone=${zone}"
+ #?"--change-interface=${interface}")
+ :unapply (%firewall-cmd #?"zones/${zone}.xml" nil
+ "--permanent" #?"--zone=${zone}"
+ #?"--remove-interface=${interface}")))
+
+(defproplist masquerade :posix (zone)
+ (:desc #?"firewalld zone ${zone} has masquerade")
+ (:check (zerop (mrun :for-exit "firewall-cmd" "--permanent"
+ #?"--zone=${zone}" "--query-masquerade")))
+ (with-unapply
+ (installed)
+ (has-zone zone)
+ (%firewall-cmd #?"zones/${zone}.xml" "ALREADY_ENABLED"
+ "--permanent"
+ #?"--zone=${zone}" "--add-masquerade")
+ :unapply (%firewall-cmd #?"zones/${zone}.xml" "NOT_ENABLED"
+ "--permanent"
+ #?"--zone=${zone}" "--remove-masquerade")))
+
+(defproplist rich-rule :posix (zone rule)
+ (:desc #?"firewalld zone ${zone} has rich rule \"${rule}\"")
+ (:check (zerop (mrun :for-exit "firewall-cmd"
+ "--permanent" #?"--zone=${zone}"
+ (strcat "--query-rich-rule=" (escape-sh-token rule)))))
+ (with-unapply
+ (installed)
+ (has-zone zone)
+ (%firewall-cmd #?"zones/${zone}.xml" "ALREADY_ENABLED"
+ "--permanent" #?"--zone=${zone}"
+ (strcat "--add-rich-rule=" (escape-sh-token rule)))
+ :unapply
+ (%firewall-cmd #?"zones/${zone}.xml" "NOT_ENABLED"
+ "--permanent" #?"--zone=${zone}"
+ (strcat "--remove-rich-rule=" (escape-sh-token rule)))))
+
+(defpropspec direct-rule :posix (&rest rule-args)
+ (:desc #?"firewalld has direct rule \"@{rule-args}\"")
+ (:check (zerop (mrun :for-exit "firewall-cmd"
+ "--permanent" "--direct" "--query-rule" rule-args)))
+ `(with-unapply
+ (installed)
+ (%firewall-cmd "direct.xml" "ALREADY_ENABLED"
+ "--permanent" "--direct" "--add-rule" ,@rule-args)
+ :unapply
+ (%firewall-cmd "direct.xml" "NOT_ENABLED"
+ "--permanent" "--direct" "--remove-rule" ,@rule-args)))