diff --git a/modules/shorewall/files/interfaces b/modules/shorewall/files/interfaces new file mode 100644 index 00000000..c15d01f4 --- /dev/null +++ b/modules/shorewall/files/interfaces @@ -0,0 +1,11 @@ +# +# Shorewall -- /etc/shorewall/interfaces +# +# For information about entries in this file, type "man shorewall-interfaces" +# +# The manpage is also online at +# https://shorewall.org/manpages/shorewall-interfaces.html +# +?FORMAT 2 +############################################################################### +#ZONE INTERFACE OPTIONS diff --git a/modules/shorewall/files/interfaces.frag.tmpl b/modules/shorewall/files/interfaces.frag.tmpl new file mode 100644 index 00000000..fbf4de6d --- /dev/null +++ b/modules/shorewall/files/interfaces.frag.tmpl @@ -0,0 +1,10 @@ +{{/* +############################################################################### +#ZONE INTERFACE OPTIONS +*/ -}} +{{ if .comment -}} +# +# {{ .comment }} +# +{{ end -}} +{{ .zone }} {{ .interface }} physical={{ .physical }}{{ if .options }},{{ golang_strings_join .options "," }}{{ end }} diff --git a/modules/shorewall/files/policy b/modules/shorewall/files/policy new file mode 100644 index 00000000..3799319b --- /dev/null +++ b/modules/shorewall/files/policy @@ -0,0 +1,10 @@ +# +# Shorewall -- /etc/shorewall/policy +# +# For information about entries in this file, type "man shorewall-policy" +# +# The manpage is also online at +# https://shorewall.org/manpages/shorewall-policy.html +# +############################################################################### +#SOURCE DEST POLICY LOGLEVEL RATE CONNLIMIT diff --git a/modules/shorewall/files/policy.frag.tmpl b/modules/shorewall/files/policy.frag.tmpl new file mode 100644 index 00000000..13eae0bd --- /dev/null +++ b/modules/shorewall/files/policy.frag.tmpl @@ -0,0 +1,10 @@ +{{/* +############################################################################### +#SOURCE DEST POLICY LOGLEVEL RATE CONNLIMIT +*/ -}} +{{ if .comment -}} +# +# {{ .comment }} +# +{{ end -}} +{{ .source }} {{ .dest }} {{ .policy }}{{ if .log_level }} $LOG_LEVEL{{ end }} diff --git a/modules/shorewall/files/rules b/modules/shorewall/files/rules new file mode 100644 index 00000000..615f4d95 --- /dev/null +++ b/modules/shorewall/files/rules @@ -0,0 +1,17 @@ +# +# Shorewall -- /etc/shorewall/rules +# +# For information on the settings in this file, type "man shorewall-rules" +# +# The manpage is also online at +# https://shorewall.org/manpages/shorewall-rules.html +# +############################################################################################################################################################## +#ACTION SOURCE DEST PROTO DPORT SPORT ORIGDEST RATE USER MARK CONNLIMIT TIME HEADERS SWITCH HELPER + +?SECTION ALL +?SECTION ESTABLISHED +?SECTION RELATED +?SECTION INVALID +?SECTION UNTRACKED +?SECTION NEW diff --git a/modules/shorewall/files/rules.frag.tmpl b/modules/shorewall/files/rules.frag.tmpl new file mode 100644 index 00000000..f478d85a --- /dev/null +++ b/modules/shorewall/files/rules.frag.tmpl @@ -0,0 +1,12 @@ +{{/* +############################################################################################################################################################## +#ACTION SOURCE DEST PROTO DPORT SPORT ORIGDEST RATE USER MARK CONNLIMIT TIME HEADERS SWITCH HELPER +*/ -}} +{{ if .comment -}} +# +# {{ .comment }} +# +{{ end -}} +{{ if .rule -}} +{{ .rule }} +{{ end -}} diff --git a/modules/shorewall/files/shorewall.conf.tmpl b/modules/shorewall/files/shorewall.conf.tmpl new file mode 100644 index 00000000..2dafd6c5 --- /dev/null +++ b/modules/shorewall/files/shorewall.conf.tmpl @@ -0,0 +1,300 @@ +############################################################################### +# +# Shorewall Version 5 -- /etc/shorewall/shorewall.conf +# +# For information about the settings in this file, type "man shorewall.conf" +# +# Manpage also online at https://shorewall.org/manpages/shorewall.conf.html +############################################################################### +# S T A R T U P E N A B L E D +############################################################################### + +STARTUP_ENABLED=Yes + +############################################################################### +# V E R B O S I T Y +############################################################################### + +VERBOSITY=1 + +############################################################################### +# P A G E R +############################################################################### + +PAGER= + +############################################################################### +# F I R E W A L L +############################################################################### + +FIREWALL= + +############################################################################### +# L O G G I N G +############################################################################### + +LOG_LEVEL="info" + +BLACKLIST_LOG_LEVEL= + +INVALID_LOG_LEVEL= + +LOG_BACKEND= + +LOG_MARTIANS=Yes + +LOG_VERBOSITY=2 + +LOG_ZONE=Both + +LOGALLNEW= + +LOGFILE=/var/log/messages + +LOGFORMAT="%s %s " + +LOGTAGONLY=No + +LOGLIMIT="s:1/sec:10" + +MACLIST_LOG_LEVEL="$LOG_LEVEL" + +RELATED_LOG_LEVEL= + +RPFILTER_LOG_LEVEL="$LOG_LEVEL" + +SFILTER_LOG_LEVEL="$LOG_LEVEL" + +SMURF_LOG_LEVEL="$LOG_LEVEL" + +STARTUP_LOG=/var/log/shorewall-init.log + +TCP_FLAGS_LOG_LEVEL="$LOG_LEVEL" + +UNTRACKED_LOG_LEVEL= + +############################################################################### +# L O C A T I O N O F F I L E S A N D D I R E C T O R I E S +############################################################################### + +ARPTABLES= + +CONFIG_PATH=":${CONFDIR}/shorewall:${SHAREDIR}/shorewall" + +GEOIPDIR=/usr/share/xt_geoip/LE + +IPTABLES= + +IP= + +IPSET= + +LOCKFILE= + +MODULESDIR= + +NFACCT= + +PATH="/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin:/usr/local/sbin" + +PERL=/usr/bin/perl + +RESTOREFILE=restore + +SHOREWALL_SHELL=/bin/sh + +SUBSYSLOCK=/var/lock/subsys/shorewall + +TC= + +############################################################################### +# D E F A U L T A C T I O N S / M A C R O S +############################################################################### + +ACCEPT_DEFAULT="none" +BLACKLIST_DEFAULT="Broadcast(DROP),Multicast(DROP),dropNotSyn:$LOG_LEVEL,dropInvalid:$LOG_LEVEL,DropDNSrep:$LOG_LEVEL" +DROP_DEFAULT="Broadcast(DROP),Multicast(DROP)" +NFQUEUE_DEFAULT="none" +QUEUE_DEFAULT="none" +REJECT_DEFAULT="Broadcast(DROP),Multicast(DROP)" + +############################################################################### +# R S H / R C P C O M M A N D S +############################################################################### + +RCP_COMMAND='scp ${files} ${root}@${system}:${destination}' +RSH_COMMAND='ssh ${root}@${system} ${command}' + +############################################################################### +# F I R E W A L L O P T I O N S +############################################################################### + +ACCOUNTING=Yes + +ACCOUNTING_TABLE=filter + +ADD_IP_ALIASES=No + +ADD_SNAT_ALIASES=No + +ADMINISABSENTMINDED=Yes + +AUTOCOMMENT=Yes + +AUTOHELPERS=Yes + +AUTOMAKE=Yes + +BALANCE_PROVIDERS=No + +BASIC_FILTERS=No + +BLACKLIST="NEW,INVALID,UNTRACKED" + +CLAMPMSS=No + +CLEAR_TC=Yes + +COMPLETE=No + +DEFER_DNS_RESOLUTION=Yes + +DELETE_THEN_ADD=Yes + +DETECT_DNAT_IPADDRS=No + +DISABLE_IPV6=No + +DOCKER=No + +DOCKER_BRIDGE=docker0 + +DONT_LOAD= + +DYNAMIC_BLACKLIST=Yes + +EXPAND_POLICIES=Yes + +EXPORTMODULES=Yes + +FASTACCEPT=No + +FORWARD_CLEAR_MARK= + +HELPERS= + +IGNOREUNKNOWNVARIABLES=No + +IMPLICIT_CONTINUE=No + +IPSET_WARNINGS=Yes + +IP_FORWARDING=Keep + +KEEP_RT_TABLES=No + +MACLIST_TABLE=filter + +MACLIST_TTL= + +MANGLE_ENABLED=Yes + +MARK_IN_FORWARD_CHAIN=No + +MINIUPNPD=No + +MULTICAST=No + +MUTEX_TIMEOUT=60 + +NULL_ROUTE_RFC1918=No + +OPTIMIZE=All + +OPTIMIZE_ACCOUNTING=No + +PERL_HASH_SEED=0 + +REJECT_ACTION= + +RENAME_COMBINED=Yes + +REQUIRE_INTERFACE=No + +RESTART=restart + +RESTORE_DEFAULT_ROUTE=Yes + +RESTORE_ROUTEMARKS=Yes + +RETAIN_ALIASES=No + +ROUTE_FILTER=No + +SAVE_ARPTABLES=No + +SAVE_IPSETS=No + +TC_ENABLED=Internal + +TC_EXPERT=No + +TC_PRIOMAP="2 3 3 3 2 3 1 1 2 2 2 2 2 2 2 2" + +TRACK_PROVIDERS=Yes + +TRACK_RULES=No + +USE_DEFAULT_RT=Yes + +USE_NFLOG_SIZE=No + +USE_PHYSICAL_NAMES=No + +USE_RT_NAMES=No + +VERBOSE_MESSAGES=Yes + +WARNOLDCAPVERSION=Yes + +WORKAROUNDS=No + +ZERO_MARKS=No + +ZONE2ZONE=- + +############################################################################### +# P A C K E T D I S P O S I T I O N +############################################################################### + +BLACKLIST_DISPOSITION=DROP + +INVALID_DISPOSITION=CONTINUE + +MACLIST_DISPOSITION=REJECT + +RELATED_DISPOSITION=ACCEPT + +RPFILTER_DISPOSITION=DROP + +SMURF_DISPOSITION=DROP + +SFILTER_DISPOSITION=DROP + +TCP_FLAGS_DISPOSITION=DROP + +UNTRACKED_DISPOSITION=CONTINUE + +################################################################################ +# P A C K E T M A R K L A Y O U T +################################################################################ + +TC_BITS= + +PROVIDER_BITS= + +PROVIDER_OFFSET= + +MASK_BITS= + +ZONE_BITS=0 diff --git a/modules/shorewall/files/snat b/modules/shorewall/files/snat new file mode 100644 index 00000000..d4e359c6 --- /dev/null +++ b/modules/shorewall/files/snat @@ -0,0 +1,10 @@ +# +# Shorewall -- /etc/shorewall/snat +# +# For information about entries in this file, type "man shorewall-snat" +# +# See https://shorewall.org/manpages/shorewall-snat.html for more information +# +?FORMAT 2 +################################################################################################################################################### +#ACTION SOURCE DEST PROTO DPORT SPORT IPSEC MARK USER SWITCH ORIGDEST PROBABILITY diff --git a/modules/shorewall/files/snat.frag.tmpl b/modules/shorewall/files/snat.frag.tmpl new file mode 100644 index 00000000..fd42a19c --- /dev/null +++ b/modules/shorewall/files/snat.frag.tmpl @@ -0,0 +1,10 @@ +{{/* +################################################################################################################################################### +#ACTION SOURCE DEST PROTO DPORT SPORT IPSEC MARK USER SWITCH ORIGDEST PROBABILITY +*/ -}} +{{ if .comment -}} +# +# {{ .comment }} +# +{{ end -}} +{{ .action }} {{ .source }} {{ .dest }} diff --git a/modules/shorewall/files/stoppedrule.frag.tmpl b/modules/shorewall/files/stoppedrule.frag.tmpl new file mode 100644 index 00000000..69b350f1 --- /dev/null +++ b/modules/shorewall/files/stoppedrule.frag.tmpl @@ -0,0 +1,12 @@ +{{/* +############################################################################### +#ACTION SOURCE DEST PROTO DPORT SPORT +*/ -}} +{{ if .comment -}} +# +# {{ .comment }} +# +{{ end -}} +{{ if .rule -}} +{{ .rule }} +{{ end -}} diff --git a/modules/shorewall/files/stoppedrules b/modules/shorewall/files/stoppedrules new file mode 100644 index 00000000..cb447287 --- /dev/null +++ b/modules/shorewall/files/stoppedrules @@ -0,0 +1,13 @@ +# +# Shorewall -- /etc/shorewall/stoppedrules +# +# For information about entries in this file, type "man shorewall-stoppedrules" +# +# The manpage is also online at +# https://shorewall.org/manpages/shorewall-stoppedrules.html +# +# See https://shorewall.org/starting_and_stopping_shorewall.htm for additional +# information. +# +############################################################################### +#ACTION SOURCE DEST PROTO DPORT SPORT diff --git a/modules/shorewall/files/zones b/modules/shorewall/files/zones new file mode 100644 index 00000000..62e6aadb --- /dev/null +++ b/modules/shorewall/files/zones @@ -0,0 +1,10 @@ +# +# Shorewall -- /etc/shorewall/zones +# +# For information about this file, type "man shorewall-zones" +# +# The manpage is also online at +# https://shorewall.org/manpages/shorewall-zones.html +# +############################################################################### +#ZONE TYPE OPTIONS IN_OPTIONS OUT_OPTIONS diff --git a/modules/shorewall/files/zones.frag.tmpl b/modules/shorewall/files/zones.frag.tmpl new file mode 100644 index 00000000..3dd61c14 --- /dev/null +++ b/modules/shorewall/files/zones.frag.tmpl @@ -0,0 +1,10 @@ +{{/* +############################################################################### +#ZONE TYPE OPTIONS IN_OPTIONS OUT_OPTIONS +*/ -}} +{{ if .comment -}} +# +# {{ .comment }} +# +{{ end -}} +{{ .name }} {{ .type }} diff --git a/modules/shorewall/main.mcl b/modules/shorewall/main.mcl new file mode 100644 index 00000000..b5f0f4cb --- /dev/null +++ b/modules/shorewall/main.mcl @@ -0,0 +1,529 @@ +# Mgmt +# Copyright (C) 2013-2024+ James Shubin and the project contributors +# Written by James Shubin and the project contributors +# +# This program 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. +# +# This program 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 . +# +# Additional permission under GNU GPL version 3 section 7 +# +# If you modify this program, or any covered work, by linking or combining it +# with embedded mcl code and modules (and that the embedded mcl code and +# modules which link with this program, contain a copy of their source code in +# the authoritative form) containing parts covered by the terms of any other +# license, the licensors of this program grant you additional permission to +# convey the resulting work. Furthermore, the licensors of this program grant +# the original author, James Shubin, additional permission to update this +# additional permission if he deems it necessary to achieve the goals of this +# additional permission. + +import "deploy" +import "fmt" +import "golang" +import "local" +import "golang/strings" + +# XXX: The templates need a padding function to line up columns. +class firewall() { + pkg "shorewall" { + state => "installed", + + Before => File["/etc/shorewall/"], + } + + file "/etc/shorewall/" { + state => $const.res.file.state.exists, + recurse => true, + purge => true, + owner => "root", + group => "root", + mode => "u=rwx,go=", # dir + } + + file "/etc/shorewall/shorewall.conf" { + state => $const.res.file.state.exists, + content => golang.template(deploy.readfile("/files/shorewall.conf.tmpl")), + owner => "root", + group => "root", + mode => "u=rw,go=", + + Notify => Svc["shorewall"], + } + + svc "shorewall" { + state => "running", + startup => "enabled", + } + + $vardir = local.vardir("shorewall/") + + # Add the default fw zone. + include zone("fw", struct{ + type => "firewall", + }) + + include params_base # TODO: Do we need the base file present? +} + +class firewall:zone_base() { + file "${vardir}zones.d/" { + state => $const.res.file.state.exists, + recurse => true, + purge => true, + owner => "root", + group => "root", + mode => "u=rwx,go=", # dir + } + + file "${vardir}zones.header" { + state => $const.res.file.state.exists, + content => deploy.readfile("/files/zones"), # static, no template! + owner => "root", + group => "root", + mode => "u=rw,go=", + } + + file "/etc/shorewall/zones" { + state => $const.res.file.state.exists, + fragments => [ + "${vardir}zones.header", # also pull this one file + "${vardir}zones.d/", # pull from this dir + ], + owner => "root", + group => "root", + mode => "u=rw,go=", + + Notify => Svc["shorewall"], + } +} + +# NOTE: the firewall type is added automatically by this module +class firewall:zone($name, $st) { + print "zone: ${name}" {} + # XXX: document why this is named zone_base instead of base:zone_base or + # change the compiler to use the second version? + include zone_base + + $type = $st->type || "ipv4" + #$options = [] # TODO: add option validation? + $comment = $st->comment || "" + + # TODO: Test type is valid from: + #$valid_types = [ + # "bport", + # "bport4", + # "bport6", + # "firewall", + # "ip", + # "ipsec", + # "ipsec4", + # "ipsec6", + # "ipv4", + # "ipv6", + # "local" + # "loopback" + # "vserver", + #] + + $tmpl = struct{ + name => "${name}", + type => "${type}", + comment => "${comment}", + } + file "${vardir}zones.d/${name}.zone" { + state => $const.res.file.state.exists, + content => golang.template(deploy.readfile("/files/zones.frag.tmpl"), $tmpl), + owner => "root", + group => "root", + mode => "u=rw,go=", + } +} + +class firewall:interface_base() { + file "${vardir}interfaces.d/" { + state => $const.res.file.state.exists, + recurse => true, + purge => true, + owner => "root", + group => "root", + mode => "u=rwx,go=", # dir + } + + file "${vardir}interfaces.header" { + state => $const.res.file.state.exists, + content => deploy.readfile("/files/interfaces"), # static, no template! + owner => "root", + group => "root", + mode => "u=rw,go=", + } + + file "/etc/shorewall/interfaces" { + state => $const.res.file.state.exists, + fragments => [ + "${vardir}interfaces.header", # also pull this one file + "${vardir}interfaces.d/", # pull from this dir + ], + owner => "root", + group => "root", + mode => "u=rw,go=", + + Notify => Svc["shorewall"], + } +} + +class firewall:interface($name, $zone, $st) { + print "interface: ${name}" {} + include interface_base + + $interface = $st->interface || (strings.to_upper($zone) + "_IF") # eg: NET_IF + $physical = $st->physical || $name + $options []str = $st->options || [] # TODO: add option validation? + $comment = $st->comment || "" + + $tmpl = struct{ + zone => "${zone}", + interface => "${interface}", + physical => "${physical}", + options => $options, + comment => "${comment}", + } + file "${vardir}interfaces.d/${name}.interface" { + state => $const.res.file.state.exists, + content => golang.template(deploy.readfile("/files/interfaces.frag.tmpl"), $tmpl), + owner => "root", + group => "root", + mode => "u=rw,go=", + } +} + +class firewall:policy_base() { + file "${vardir}policy.d/" { + state => $const.res.file.state.exists, + recurse => true, + purge => true, + owner => "root", + group => "root", + mode => "u=rwx,go=", # dir + } + + file "${vardir}policy.header" { + state => $const.res.file.state.exists, + content => deploy.readfile("/files/policy"), # static, no template! + owner => "root", + group => "root", + mode => "u=rw,go=", + } + + file "/etc/shorewall/policy" { + state => $const.res.file.state.exists, + fragments => [ + "${vardir}policy.header", # also pull this one file + "${vardir}policy.d/", # pull from this dir + ], + owner => "root", + group => "root", + mode => "u=rw,go=", + + Notify => Svc["shorewall"], + } +} + +class firewall:policy($name, $st) { + print "policy: ${name}" {} + include policy_base + + $source = $st->source + $dest = $st->dest + $policy = $st->policy + $log = $st->log || false + $comment = $st->comment || "" + + $tmpl = struct{ + source => "${source}", + dest => "${dest}", + policy => "${policy}", + log_level => $log, + comment => "${comment}", + } + file "${vardir}policy.d/${name}.policy" { + state => $const.res.file.state.exists, + content => golang.template(deploy.readfile("/files/policy.frag.tmpl"), $tmpl), + owner => "root", + group => "root", + mode => "u=rw,go=", + } +} + +class firewall:rule_base() { + file "${vardir}rules.d/" { + state => $const.res.file.state.exists, + recurse => true, + purge => true, + owner => "root", + group => "root", + mode => "u=rwx,go=", # dir + } + + file "${vardir}rules.header" { + state => $const.res.file.state.exists, + content => deploy.readfile("/files/rules"), # static, no template! + owner => "root", + group => "root", + mode => "u=rw,go=", + } + + file "/etc/shorewall/rules" { + state => $const.res.file.state.exists, + fragments => [ + "${vardir}rules.header", # also pull this one file + "${vardir}rules.d/", # pull from this dir + ], + owner => "root", + group => "root", + mode => "u=rw,go=", + + Notify => Svc["shorewall"], + } +} + +class firewall:rule($name, $st) { + print "rule: ${name}" {} + include rule_base + + $rule = $st->rule || "" # entire rule contents OR use the below values + + $action = $st->action # REJECT or SSH(ACCEPT) or Ping(DROP) + $source = $st->source # source zone + $source_ips []str = $st->source_ips || [] + $dest = $st->dest || "" # dest zone + $dest_ips []str = $st->dest_ips || [] + $proto = $st->proto || "" # protocol + # TODO: port doesn't support ranges atm + $port = $st->port || 0 + #$sport = $st->sport || 0 # TODO + #$original = $st->original || [] # TODO + $comment = $st->comment || "" + + $source_ips_joined = strings.join($source_ips, ",") + $valid_source = if $source_ips_joined == "" { + "${source}" + } else { + "${source}:${source_ips_joined}" + } + + $dest_ips_joined = strings.join($dest_ips, ",") + $valid_dest = if $dest_ips_joined == "" { + "${dest}" + } else { + "${dest}:${dest_ips_joined}" + } + + $valid_proto = if $proto == "" { + "-" + } else { + "${proto}" + } + + # TODO: type switch here if we ever support doing that + $valid_port = if $port == 0 { + "-" + } else { + fmt.printf("%d", $port) + } + + # TODO: tabs for beautifying, replace with a padding function eventually. + $full_rule = if $proto == "" and $port == 0 { + "${action}\t${valid_source}\t\t${valid_dest}" + } else { + "${action}\t${valid_source}\t\t${valid_dest}\t\t${valid_proto}\t${valid_port}" + } + + $valid_rule = if $rule == "" { + $full_rule + } else { + $rule + } + + $tmpl = struct{ + rule => "${valid_rule}", + comment => "${comment}", + } + file "${vardir}rules.d/${name}.rule" { + state => $const.res.file.state.exists, + content => golang.template(deploy.readfile("/files/rules.frag.tmpl"), $tmpl), + owner => "root", + group => "root", + mode => "u=rw,go=", + } +} + +class firewall:stoppedrule_base() { + file "${vardir}stoppedrules.d/" { + state => $const.res.file.state.exists, + recurse => true, + purge => true, + owner => "root", + group => "root", + mode => "u=rwx,go=", # dir + } + + file "${vardir}stoppedrules.header" { + state => $const.res.file.state.exists, + content => deploy.readfile("/files/stoppedrules"), # static, no template! + owner => "root", + group => "root", + mode => "u=rw,go=", + } + + file "/etc/shorewall/stoppedrules" { + state => $const.res.file.state.exists, + fragments => [ + "${vardir}stoppedrules.header", # also pull this one file + "${vardir}stoppedrules.d/", # pull from this dir + ], + owner => "root", + group => "root", + mode => "u=rw,go=", + + Notify => Svc["shorewall"], + } +} + +class firewall:stoppedrule($name, $st) { + print "stoppedrule: ${name}" {} + include stoppedrule_base + + $rule = $st->rule || "" # entire rule contents OR use the below values + + $action = $st->action # REJECT or SSH(ACCEPT) or Ping(DROP) + $source = $st->source # source zone + $dest = $st->dest # dest zone + + $comment = $st->comment || "" + + # TODO: tabs for beautifying, replace with a padding function eventually. + $valid_rule = if $rule == "" { + "${action}\t${source}\t\t${dest}" + } else { + $rule + } + + $tmpl = struct{ + rule => "${valid_rule}", + comment => "${comment}", + } + file "${vardir}stoppedrules.d/${name}.stoppedrule" { + state => $const.res.file.state.exists, + content => golang.template(deploy.readfile("/files/stoppedrules.frag.tmpl"), $tmpl), + owner => "root", + group => "root", + mode => "u=rw,go=", + } +} + +class firewall:snat_base() { + file "${vardir}snat.d/" { + state => $const.res.file.state.exists, + recurse => true, + purge => true, + owner => "root", + group => "root", + mode => "u=rwx,go=", # dir + } + + file "${vardir}snat.header" { + state => $const.res.file.state.exists, + content => deploy.readfile("/files/snat"), # static, no template! + owner => "root", + group => "root", + mode => "u=rw,go=", + } + + file "/etc/shorewall/snat" { + state => $const.res.file.state.exists, + fragments => [ + "${vardir}snat.header", # also pull this one file + "${vardir}snat.d/", # pull from this dir + ], + owner => "root", + group => "root", + mode => "u=rw,go=", + + Notify => Svc["shorewall"], + } +} + +class firewall:snat($name, $st) { + print "snat: ${name}" {} + include snat_base + + $action = $st->action # "MASQUERADE" usually + $source = $st->source # list of ip/cidr + $dest = $st->dest + $comment = $st->comment || "" + + $valid_source = strings.join($source, ",") + + $tmpl = struct{ + action => "${action}", + source => "${valid_source}", + dest => "${dest}", + comment => "${comment}", + } + file "${vardir}snat.d/${name}.snat" { + state => $const.res.file.state.exists, + content => golang.template(deploy.readfile("/files/snat.frag.tmpl"), $tmpl), + owner => "root", + group => "root", + mode => "u=rw,go=", + } +} + +class firewall:params_base() { + file "${vardir}params.d/" { + state => $const.res.file.state.exists, + recurse => true, + purge => true, + owner => "root", + group => "root", + mode => "u=rwx,go=", # dir + } + + file "${vardir}params.header" { + state => $const.res.file.state.exists, + content => deploy.readfile("/files/params"), # static, no template! + owner => "root", + group => "root", + mode => "u=rw,go=", + } + + file "/etc/shorewall/params" { + state => $const.res.file.state.exists, + fragments => [ + "${vardir}params.header", # also pull this one file + "${vardir}params.d/", # pull from this dir + ], + owner => "root", + group => "root", + mode => "u=rw,go=", + + Notify => Svc["shorewall"], + } +} + +class firewall:params($name, $st) { + print "params: ${name}" {} + include params_base + + # TODO: add params +} diff --git a/modules/shorewall/metadata.yaml b/modules/shorewall/metadata.yaml new file mode 100644 index 00000000..e69de29b