For this to work, you need Volcane's extlookup function:
require 'csv'
module Puppet::Parser::Functions
newfunction(:extlookup, :type => :rvalue) do |args|
key = args[0]
default = "_ExtUNSET_"
datafile = "_ExtUNSET_"
default = args[1] if args[1]
datafile = args[2] if args[2]
extlookup_datadir = lookupvar('extlookup_datadir')
extlookup_precedence = Array.new
# precedence values can have variables embedded in them
# in the form %{fqdn}, you could for example do
#
# $extlookup_precedence = ["hosts/%{fqdn}", "common"]
#
# this will result in /path/to/extdata/hosts/your.box.com.csv
# being searched.
#
# we parse the precedence here because the best place to specify
# it would be in site.pp but site.pp is only evaluated at startup
# so $fqdn etc would have no meaning there, this way it gets evaluated
# each run and has access to the right variables for that run
lookupvar('extlookup_precedence').each do |prec|
while prec =~ /%\{(.+?)\}/
prec.gsub!(/%\{#{$1}\}/, lookupvar($1))
end
extlookup_precedence << prec
end
datafiles = Array.new
# if we got a custom data file, put it first in the array of search files
if datafile != ""
if File.exists?(extlookup_datadir + "/#{datafile}.csv")
datafiles << extlookup_datadir + "/#{datafile}.csv"
end
end
extlookup_precedence.each do |d|
datafiles << extlookup_datadir + "/#{d}.csv"
end
desired = "_ExtUNSET_"
datafiles.each do |file|
parser.watch_file(file) if File.exists?(file)
if desired == "_ExtUNSET_"
if File.exists?(file)
result = CSV.read(file).find_all do |r|
r[0] == key
end
# return just the single result if theres just one,
# else take all the fields in the csv and build an array
if result.length > 0
if result[0].length == 2
val = result[0][1].to_s
# parse %{}'s in the CSV into local variables using lookupvar()
while val =~ /%\{(.+?)\}/
val.gsub!(/%\{#{$1}\}/, lookupvar($1))
end
desired = val
elsif result[0].length > 1
length = result[0].length
cells = result[0][1,length]
desired = cells.map do |c|
c.to_s
while c =~ /%\{(.+?)\}/
c.gsub!(/%\{#{$1}\}/, lookupvar($1))
end
c
end
end
end
end
end
end
if desired == "_ExtUNSET_" && default == "_ExtUNSET_"
raise Puppet::ParseError, "No match found for '#{key}' in any data file during extlookup()"
else
desired = default if desired == "_ExtUNSET_"
end
desired
end
end
This is so you can look up trustednets and nodenets based on a location/domain/hostname basis via csv files. Extremely useful and should be in mainline puppet IMO. Ask in channel for more info on this piece.
iptables piece:
class iptables {
$nodenets = extlookup("nodenets")
$trustednets = extlookup("trustednets")
$puppetserver = extlookup("puppetserver")
package { "iptables": ensure => "present" }
exec { "/sbin/iptables-restore < /etc/iptables.rules":
require => [ File["/etc/iptables.rules"], Package["iptables"] ]
}
file { "/etc/iptables.rules":
content => template("iptables/iptables.rules.$domain.erb"),
ensure => "present",
group => "root",
mode => "440",
owner => "root",
require => Class["common::location"],
}
}
template for iptables.rules (this is a really anal firewall for boxes that sit on the edge). You will need .24.8 for the has_variable? function:
*filter
-P INPUT DROP
-P OUTPUT ACCEPT
-P FORWARD DROP
-A INPUT -s 127.0.0.1 -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p udp -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 13 -j DROP
-A INPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT
-A INPUT -p tcp ! --syn -m state --state NEW -j LOG --log-prefix "IPT NEW W/O SYN: "
-A INPUT -p tcp ! --syn -m state --state NEW -j DROP
-A INPUT -p tcp --tcp-flags ALL ALL -j LOG --log-prefix "IPT XMAS TREE PKT: "
-A INPUT -p tcp --tcp-flags ALL ALL -j DROP
-A INPUT -p tcp --tcp-flags ALL NONE -j LOG --log-prefix "IPT NULL PKT: "
-A INPUT -p tcp --tcp-flags ALL NONE -j DROP
-A INPUT -p tcp --tcp-flags ACK,FIN FIN -j LOG --log-prefix "IPT BAD TCP FLAGS: "
-A INPUT -p tcp --tcp-flags ACK,FIN FIN -j DROP
-A INPUT -p tcp --tcp-flags ACK,PSH PSH -j LOG --log-prefix "IPT BAD TCP FLAGS: "
-A INPUT -p tcp --tcp-flags ACK,PSH PSH -j DROP
-A INPUT -p tcp --tcp-flags ACK,URG URG -j LOG --log-prefix "IPT BAD TCP FLAGS: "
-A INPUT -p tcp --tcp-flags ACK,URG URG -j DROP
-A INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j LOG --log-prefix "IPT BAD TCP FLAGS: "
-A INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j DROP
-A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j LOG --log-prefix "IPT BAD TCP FLAGS: "
-A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
-A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j LOG --log-prefix "IPT BAD TCP FLAGS: "
-A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
-A INPUT -p tcp --tcp-flags ALL FIN,PSH,URG -j LOG --log-prefix "IPT BAD TCP FLAGS: "
-A INPUT -p tcp --tcp-flags ALL FIN,PSH,URG -j DROP
-A INPUT -p tcp --tcp-flags ALL SYN,FIN,PSH,URG -j LOG --log-prefix "IPT BAD TCP FLAGS: "
-A INPUT -p tcp --tcp-flags ALL SYN,FIN,PSH,URG -j DROP
-A INPUT -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -j LOG --log-prefix "IPT BAD TCP FLAGS: "
-A INPUT -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -j DROP
-A INPUT -f -j LOG --log-prefix "IPT FRAGMENTS: "
-A INPUT -f -j DROP
<% if has_variable? "open_tcp_ports" and !open_tcp_ports.empty? -%>
# These ports can be accessed via anyone
-A INPUT -p tcp -m multiport --dports <%= open_tcp_ports %> -j ACCEPT
<% end -%>
<% if has_variable? "open_udp_ports" and !open_udp_ports.empty? -%>
-A INPUT -p udp -m multiport --dports <%= open_udp_ports %> -j ACCEPT
<% end -%>
<% if (has_variable? "restricted_tcp_ports" and !restricted_tcp_ports.empty?) and (trustednets and ![ trustednets ].flatten.empty?) -%>
# These ports can only be accessed via trusted nets
<% [ trustednets ].flatten.each do |network| -%>
-A INPUT -s <%= network %> -p tcp -m multiport --dports <%= restricted_tcp_ports %> -j ACCEPT
<% end -%>
<% end -%>
<% if (has_variable? "restricted_udp_ports" and !restricted_udp_ports.empty?) and (trustednets and ![ trustednets ].flatten.empty?) -%>
# These ports can only be accessed via trusted nets
<% [ trustednets ].flatten.each do |network| -%>
-A INPUT -s <%= network %> -p udp -m multiport --dports <%= restricted_udp_ports %> -j ACCEPT
<% end -%>
<% end -%>
<% if (has_variable? "restricted_to_node_nets_tcp_ports" and !restricted_to_node_nets_tcp_ports.empty?) and (nodenets and ![ nodenets ].flatten.empty?) -%>
# These ports can only be accessed via local net clients
<% [ nodenets ].flatten.each do |network| -%>
-A INPUT -s <%= network %> -p tcp -m multiport --dports <%= restricted_to_node_nets_tcp_ports %> -j ACCEPT
<% end -%>
<% end -%>
<% if (has_variable? "restricted_to_node_nets_udp_ports" and !restricted_to_node_nets_udp_ports.empty?) and (nodenets and ![ nodenets ].flatten.empty?) -%>
# These ports can only be accessed via local net clients
<% [ nodenets ].flatten.each do |network| -%>
-A INPUT -s <%= network %> -p udp -m multiport --dports <%= restricted_to_node_nets_udp_ports %> -j ACCEPT
<% end -%>
<% end -%>
-A INPUT -s <%= puppetserver %> -p tcp -m tcp --dport 8139 -m comment --comment "Allow for puppetrunner" -j ACCEPT
# Log and Drop invalid states
-A INPUT -m state --state INVALID -j LOG --log-prefix "IPT INVALID STATE: "
-A INPUT -m state --state INVALID -j DROP
COMMIT
Sample node def:
node 'tomcat001' {
$open_tcp_ports = "443"
$restricted_tcp_ports = "22,80,8009,8180"
include basenode_tmpl
}
basenode_tmpl is just a class that includes a bunch of stuff that should be on all nodes.