Report abuse

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]

                            # Individual cells in a CSV result are a weird data type and throws
                            # puppets yaml parsing, so just map it all to plain old strings
                            desired = cells.map do |c|
                                c.to_s

                                # parse %{}'s in the CSV into local variables using lookupvar()
                                while c =~ /%\{(.+?)\}/
                                    c.gsub!(/%\{#{$1}\}/, lookupvar($1))
                                end

                                c
                            end
                        end
                    end
                end
            end
        end

        # don't accidently return nil's and such rather throw a parse error
        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

# Default DROP on input, default allow on out.
-P INPUT DROP
-P OUTPUT ACCEPT
-P FORWARD DROP

# Allow localhost in, and lo iface
-A INPUT -s 127.0.0.1 -j ACCEPT
-A INPUT  -i lo -j ACCEPT

# Allow established and related in
-A INPUT -p udp -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT

# Drop ICMP Info Leakage
-A INPUT -p icmp -m icmp --icmp-type 13 -j DROP

# Allow ping reply back in
-A INPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT

# Make sure NEW connections have SYN set
-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

# Log and Drop christmas tree packets
-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

# Log and Drop NULL packets
-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

# Log and drop all other non-specific malformed tcp packets
-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

# Log and drop IP Fragments to prevent fragmentation attack
-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? -%>
# These ports can be accessed via anyone
-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 -%>
# Allow puppetrunner
-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.