From f3687fa69e3069eee9feaf2743c501df0322480f Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Fri, 25 Jun 2021 11:51:56 -0700 Subject: add some firewalld properties Signed-off-by: Sean Whitton --- src/property/firewalld.lisp | 155 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 src/property/firewalld.lisp (limited to 'src/property/firewalld.lisp') 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 + +;;; 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 . + +(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))) -- cgit v1.2.3