Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Move `create_rule()` to core library and rename it AFW

  • Loading branch information...
commit fe6bc60f465face12e7711608d6da5d27a956a20 1 parent 9aaab36
Julien Vehent authored
19 libraries/core.rb
View
@@ -1,4 +1,4 @@
-module AFWCore
+module AFW
# IP dummy regex, from `0.0.0.0` to `999.999.999.999`
IP_CIDR_VALID_REGEX = /\b(?:\d{1,3}\.){3}\d{1,3}\b(\/[0-3]?[0-9])?/
@@ -385,4 +385,21 @@ def build_rule_array(iptables_header, sources, destinations)
return iptables_array_destination
end
+
+
+ #
+ # Exported to other cookbooks ----
+ #
+ module_function
+ extend self
+ def create_rule(node, name, params)
+ node['afw']['rules'][name] = params
+ # Wrapper around `process_rule`
+ #
+ Chef::Log.info("AFW.create_rule(): processing '#{name}'")
+ if AFW.process_rule(node, name, params)
+ Chef::Log.info("AFW.create_rule(): finished processing '#{name}'")
+ end
+ return true
+ end
end
15 libraries/create_rule.rb
View
@@ -1,15 +0,0 @@
-module AFW
- extend AFWCore
- module_function
-
- def create_rule(node, name, params)
- node['afw']['rules'][name] = params
- # Wrapper around `process_rule`
- #
- Chef::Log.info("AFW.create_rule(): processing '#{name}'")
- if process_rule(node, name, params)
- Chef::Log.info("AFW.create_rule(): finished processing '#{name}'")
- end
- return true
- end
-end
2  metadata.json
View
@@ -1,7 +1,7 @@
{
"name": "afw",
"description": "Installs/Configures the Advanced FireWall",
- "long_description": "# The Advanced FireWall (afw) for Chef\n\n__A__dvanced __F__ire__W__all (AFW) for Chef and Linux that uses Iptables\nto dynamically configure inbound and outbound rules on nodes.\n\nAFW uses Chef searches extensively to open access to systems. Instead of\nspecifying the IP addresses of a set of sources or destinations, AFW allows you\nto specify searches in Chef syntax to list those systems. It is designed to be\na lot more dynamic and maintainable than regular firewalls, and allows for\nfiltering inbound and outbound traffic.\n\nAFW support `raw` rules, that are just straight `iptables` syntax passed to the\ntemplate. For these, Chef searches are not supported, but it gives you access\nto the full set of features `iptables` provides.\n\nAFW runs on any Linux 2.6 or 3.*. It does not rely on distribution specific\nwrapper, such as ufw for ubuntu, but calls `iptables-restore` directly.\n\n## Rules definitions\nRules must be added in roles or nodes attributes. A typical rule looks like:\n\n```\n:afw =>\n :rules =>\n 'MongoDB App Entry Point' => {\n :protocol => 'tcp',\n :direction => 'in',\n :interface => 'default',\n :user => 'mongodb',\n :source => ['(roles:*nodejs-app-node OR roles:*python-worker-node OR roles:*python-api-node) AND SAMETAG',\n '10.4.76.2',\n 'backup-server.example.net']\n :dport => '27017'\n },\n}\n\n```\n\nRules must be added into `node[:afw][:rules]`, and follow this syntax:\n\n```\n\n:afw =>\n :rules =>\n '<rule name>' =>\n :direction => '<in|out>',\n :protocol => '<udp|tcp|icmp>',\n :user => '<local user from /etc/passwd>',\n :interface => '<default|all|eth0|eth1|br0|...>',\n :source => '<ip|fqdn|chef search>|['<ip|fqdn|chef search>',...]'>',\n :sport => '<integer(:integer))>',\n :destination => '<ip|fqdn|chef search>|['<ip|fqdn|chef search>',...]'>',\n :dport => '<integer(:integer)>',\n :env => '<production|staging|...>',\n :options => ['disable_env_limit', 'disable_syntax_check', ...]\n}\n\n```\n\n* __Rule Name__ : A `string` that identifies the rule as uniquely as possible.\n\n* __direction__ (__mandatory__): `in` is for inbound firewall rules.\n`out` for outbound firewall rules.\nSelect whether the rule will apply to packets entering the system (`in`) or\nleaving it (`out`).\n\n* __protocol__ (__mandatory__):\nSelect the L4 protocol this rule applies to: `udp`, `tcp` or `icmp`.\n\n* __user__ (___mandatory___):\nSet the local user allowed to transmit packets. The user is checked only for\noutbound firewall rules (iptables limitation) but must be set for inbound\nrules as well, to ease auditing rules & systems.\nNote: if the local user doesn't exists, the provisioning will fail at the end,\nwhen the rules are loaded. If the user is installed by a package, the next chef\nrun will succeed and fix the issue.\n\n* __dport__ (__mandatory__):\nSet the destination port of the connections being filtered. This is mandatory.\nExcept when it's not (eg. icmp).\n\n* __interface__ (*optional*):\nSelect the network interface. If undef, the default interface will be used.\nif `all`, the interface parameter won't be set at all.\n\n* __source__ (__mandatory for `in` rules__):\nSet the source IP of the packets. This parameter can either be a single IP or\nnetwork (eg. `10.1.2.0/32`), a Fully Qualified Domain Name (eg. `bob.colo.lair`)\n or a Chef Search (eg. `roles:mongodb`).\nBy default, searches are limited to the same `chef_environment` (eg. staging),\nto allow for firewall rules that open connections between environments, you\nwill need an `heresy` parameter.\nIn a chef-search, you can also use they keyword `SAMETAG` that will limit the\nsearch to the nodes that share the same tags. This is useful if, for example,\nyou want to open connections to a database from all nodes within the same\nservice tag, but not beyond.\nThe syntax for a search would look like: 'roles:whatever-api AND SAMETAG'.\nIf you have multiple sources and destinations, you can set them in a array:\n```\n'AMQP Producers' => {\n :direction => 'in',\n :user => 'rabbitmq',\n :protocol => 'tcp',\n :interface => 'default',\n :source => ['producer1.internal.domain.com',\n 'producer2.internal.domain.com',\n '192.168.1.1',\n 'roles:megaserver'],\n :dport => '5672'\n}\n```\n\n* __destination__ (*Same as `source`*)\n\n* __sport__ (*optional*):\nSet the source port in the firewall rule. For `in` rules, that means the source\nport of the remote machine. For `out` rules, that means the source port of this\nnode when establishing a connection to a remote node.\n\n* __env__ (*optional*):\nThe env parameters can be used to limit the application of a rule to a specific\nenvironment. If `:env => 'staging'` is set, the rule will be applied to nodes in\nthe staging environment only.\n\n* __options__ (*optional*):\n- disable_env_limit:\n`disable_env_limit` can also used to cross environment.\nIf `:options => ['disable_env_limit']` is set, the source and destination searches\nwill return results from all environments, instead of limiting the result to the\nenvironment the node lives in.\nThe following rule will allow production workers to connect to staging\nBackendDB. Don't do that. Keep environments isolated as much as possible.\n```\n 'Production Worker to Staging BackendDB' => {\n :protocol => 'tcp',\n :direction => 'out',\n :user => 'application-user',\n :destination => 'roles:*backenddb* AND SAMETAG AND chef_environment:staging',\n :dport => '15017',\n :env => 'production',\n :options => ['disable_env_limit']\n }\n```\n- disable_syntax_check:\n`disable_syntax_check` will turn off the rule validation step for a specific rule.\nThis is useful if you want to define a rule without source/destination, and\nlet another recipe populate the source/destination arrays later on.\n```\n 'API calls from servers' => {\n :protocol => 'tcp',\n :direction => 'in',\n :user => 'www-data',\n :dport => '80',\n :options => ['disable_syntax_check']\n },\n```\nYou would then have a separate recipe that inserts IPs in this rule:\n```\nnode[:afw][:rules]['API calls from servers'][:source] = []\nip_list = ['1.2.3.4', '4.5.6.7', '12.32.43.54']\nip_list.each do |ip|\n node[:afw][:rules]['API calls from servers'][:source].push(ip)\nend\n```\n\n### Creating rules from external cookbooks\nIf you want a cookbook to create firewal rules directly, as opposed to storing\nthese rules in a roles, then you need to use the `create_rule()` function from\nthe `AFW` module.\nExample: create outbound firewall rule for haproxy in the haproxy cookbook\n#### depend in AFW in the metadata\n`cookbooks/haproxy/metadata.rb`\n```\n[...]\ndepends 'AFW'\n```\n#### create the rule from the recipe using ruby\n```\n # Call the AFW module to create the rule\n AFW.create_rule(\"Haproxy outbound to #{destination}:#{port}\",\n {'protocol' => 'tcp',\n 'direction' => 'out',\n 'user' => 'haproxy',\n 'destination' => \"#{destination}\",\n 'dport' => \"#{port}\"\n })\n```\nNote that `AFW.create_rule()` must be called from a normal section of ruby code\ndirectly (not from a `ruby_block`) to ensure that the rules are compiled at\nchef compile time. The AFW template will later (at runtime) populate these rules\ninto the `iptables-restore` file.\n\n### Predefined rules\nPredefined rules are iptables rules that are used directly by AFW. Those rules\nare used for specific purposes only, such as using a very particular module for\nwhich AFW wouldn't have any support.\nPredefined rules only support 2 arguments: `table` and `rule`.\n\n* __table__:\nthe netfilter table on which this rule must be applied. One of `nat`, `raw`,\n`mangle` or `filter.\n\n* __rule__: the firewall rule itself, in iptables-save format (do not specify\na table in this format, or it will fail).\n\nexample:\n```\n :afw => {\n :rules => {\n\n 'Accept all packets router through the bridge' => {\n :table => 'filter',\n :rule => '-I FORWARD -o br0 -m physdev --physdev-is-bridged -j ACCEPT'\n },\n\n 'Drop connection to the admin panel on the eth0 interface' => {\n :table => 'mangle',\n :rule => '-A INPUT -i eth0 -p tcp --dport 80 -m string --string \"get /admin http/1.1\" --icase --algo bm -m conntrack --ctstate ESTABLISHED -j DROP'\n },\n\n 'DNAT a source IP to change the destination port' => {\n :table => 'nat',\n :rule => '-A PREROUTING -i eth3 -s 201.23.72.3 -p tcp --dport 8008 -j DNAT --to-destination 127.0.0.1:1234'\n },\n\n 'Dont do conntrack on this specific user's UDP packets' => {\n :table => 'raw',\n :rule => '-A OUTPUT -o eth0 -p udp -m owner --uid-owner 105 -j NOTRACK'\n }\n }\n }\n```\n\n## Rules Generation\n\nThe recipe will generate a rule file in `/etc/firewall/rules.iptables` that\nconforms to the iptables-save/restore syntax.\nAt the end of the chef-run, and if the rules file has been modified during the\nrun, the `iptables-restore` command will reload the entire ruleset.\n\nHere's an example of templated ruleset. Notice how the rules are regrouped by\nsystem user, to make them easier to read.\n\n```\n# Generated by AFW on Tue Sep 25 02:55:32 +0000 2012\n\n*raw\n:PREROUTING ACCEPT [0:0]\n:OUTPUT ACCEPT [0:0]\n-A OUTPUT -o lo -j NOTRACK\n-A PREROUTING -i lo -j NOTRACK\nCOMMIT\n\n*mangle\n:PREROUTING ACCEPT [0:0]\n:INPUT ACCEPT [0:0]\n:FORWARD ACCEPT [0:0]\n:OUTPUT ACCEPT [0:0]\n:POSTROUTING ACCEPT [0:0]\nCOMMIT\n\n*nat\n:PREROUTING ACCEPT [0:0]\n:POSTROUTING ACCEPT [0:0]\n:OUTPUT ACCEPT [0:0]\nCOMMIT\n\n*filter\n:INPUT ACCEPT [0:0]\n:FORWARD ACCEPT [0:0]\n:OUTPUT ACCEPT [0:0]\n-A INPUT -i lo -j ACCEPT\n-A OUTPUT -o lo -j ACCEPT\n-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT\n-A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT\n# some default rules we want to open everywhere\n-A INPUT -p tcp --dport 22 -s 1.0.0.0/8 -j ACCEPT\n-A INPUT -p tcp --dport 22 -s 12.30.0.0/16 -j ACCEPT\n-A OUTPUT -p udp --dport 53 -d 1.0.0.0/8 -j ACCEPT\n\n:app-user - [0:0]\n-A OUTPUT -m owner --uid-owner 999 -m state --state NEW -j app-user\n-A app-user -o eth0 -p tcp --dport 2003 -d 1.2.3.4 -m conntrack --ctstate NEW -j ACCEPT\n-A app-user -o eth0 -p tcp --dport 27017 -d 1.1.4.3 -m conntrack --ctstate NEW -j ACCEPT\n-A app-user -o eth0 -p tcp --dport 5672 -d 1.1.4.9 -m conntrack --ctstate NEW -j ACCEPT\n-A app-user -o eth0 -p tcp --dport 80 -d 1.1.4.10 -m conntrack --ctstate NEW -j ACCEPT\n-A app-user -o eth0 -p udp --dport 8125 -d 1.1.4.74 -m conntrack --ctstate NEW -j ACCEPT\n-A app-user -j LOG --log-prefix \"DROP_AFW_OUTPUT_app-user \" --log-uid --log-tcp-sequence\n\n:haproxy - [0:0]\n-A OUTPUT -m owner --uid-owner 110 -m state --state NEW -j haproxy\n-A INPUT -i eth0 -p tcp --dport 10097 -s 0.0.0.0/0 -m conntrack --ctstate NEW -j ACCEPT\n-A haproxy -o eth0 -p tcp --dport 2003 -d 1.1.0.25 -m conntrack --ctstate NEW -j ACCEPT\n-A haproxy -o eth0 -p tcp --dport 2003 -d 1.1.0.37 -m conntrack --ctstate NEW -j ACCEPT\n-A haproxy -o eth0 -p tcp --dport 80 -d 1.1.4.58 -m conntrack --ctstate NEW -j ACCEPT\n-A haproxy -j LOG --log-prefix \"DROP_AFW_OUTPUT_haproxy \" --log-uid --log-tcp-sequence\n\n:nagios - [0:0]\n-A OUTPUT -m owner --uid-owner 105 -m state --state NEW -j nagios\n-A INPUT -i eth0 -p tcp --dport 5666 -s 1.1.0.33 -m conntrack --ctstate NEW -j ACCEPT\n-A nagios -j LOG --log-prefix \"DROP_AFW_OUTPUT_nagios \" --log-uid --log-tcp-sequence\n\n:ntp - [0:0]\n-A OUTPUT -m owner --uid-owner 104 -m state --state NEW -j ntp\n-A ntp -o eth0 -p udp --dport 123 -d ntp.example.net -m conntrack --ctstate NEW -j ACCEPT\n-A ntp -j LOG --log-prefix \"DROP_AFW_OUTPUT_ntp \" --log-uid --log-tcp-sequence\n\n:root - [0:0]\n-A OUTPUT -m owner --uid-owner 0 -m state --state NEW -j root\n-A INPUT -p icmp -s 0.0.0.0/0 -m conntrack --ctstate NEW -j ACCEPT\n-A root -o eth0 -p tcp --dport 514 -d 1.1.3.18 -m conntrack --ctstate NEW -j ACCEPT\n-A root -o eth0 -p udp --dport 514 -d 1.1.3.18 -m conntrack --ctstate NEW -j ACCEPT\n-A root -p icmp -d 0.0.0.0/0 -m conntrack --ctstate NEW -j ACCEPT\n-A root -j LOG --log-prefix \"DROP_AFW_OUTPUT_root \" --log-uid --log-tcp-sequence\n\n:snmp - [0:0]\n-A OUTPUT -m owner --uid-owner 108 -m state --state NEW -j snmp\n-A snmp -o eth0 -p udp --dport 162 -d 1.1.0.23 -m conntrack --ctstate NEW -j ACCEPT\n-A snmp -j LOG --log-prefix \"DROP_AFW_OUTPUT_snmp \" --log-uid --log-tcp-sequence\n\n:www-data - [0:0]\n-A OUTPUT -m owner --uid-owner 33 -m state --state NEW -j www-data\n-A INPUT -i eth0 -p tcp --dport 80 -s 1.1.4.4 -m conntrack --ctstate NEW -j ACCEPT\n-A INPUT -i eth0 -p tcp --dport 80 -s 1.1.4.1 -m conntrack --ctstate NEW -j ACCEPT\n-A www-data -j LOG --log-prefix \"DROP_AFW_OUTPUT_www-data \" --log-uid --log-tcp-sequence\n\n-A INPUT -j LOG --log-prefix \"DROP_AFW_INPUT \" --log-uid --log-tcp-sequence\n-A INPUT -j DROP\n\n-A OUTPUT -j LOG --log-prefix \"DROP_AFW_OUTPUT \" --log-uid --log-tcp-sequence\nCOMMIT\n```\n\nThe `INPUT` chain contains all of the rules for incoming connections. It does\nnot redirect packets to other chains, but accept or drop them directly.\n\nThe `OUTPUT` chain is a little different. Depending on the owner of the socket\nemitting packets, it will direct the packets to a different chain, named after\nthe socket owner.\nIn the example above, the packet from the `snmp` user will be directed to the\nchain named `snmp`. You can see in this chain that the first two rules accept\npacket, while the 3rd one will `LOG` to syslog when it is reached (it shouldn't\nbe). Eventually, a `DROP` will follow that log rule to drop packets that aren't\nsuppose to be sent.\n\n# ATTRIBUTES\n\n* `default[:afw][:enable] = true` : enable or disable the firewall restore\ncommand. If set the false, the rules will still be populated in \n`/etc/firewall/rules.iptables` but the restore command will not be issued.\n\n* `default[:afw][:enable_input_drop] = true` : DROP all input packets by defaut\n* `default[:afw][:enable_output_drop] = true` : DROP all output packets by defaut\n* `default[:afw][:enable_input_drop_log] = true` : LOG when DROP input packets\n* `default[:afw][:enable_output_drop_log] = true` : LOG when DROP output packets\n\n## Dependencies\n### Ohai network_addr\nThis cookbooks relies on the custom AWeber Ohai plugin which sets the two\nfollowing attributes:\n`node[:network][:lanip]` is set to the IP of the node on the LAN network\n`node[:network][:laniface]` is set to the network interface on the LAN\nWhile AFW can probably run with this plugin, it makes you life easier when you\nwant to open rules on your LAN interface. We typically enfore `eth0` to be the\nLAN interface, but feel free to use whatever you want.\n\nPlugin source:\n```\nprovides \"network\"\n\nrequire_plugin \"hostname\"\nrequire_plugin \"#{os}::network\"\n\nnetwork['interfaces'].each do |iface, addrs|\n\n addrs['addresses'].each do |ip, params|\n network[\"ipaddress_#{iface}\"] = ip if params['family'].eql?('inet')\n network[\"ipaddress6_#{iface}\"] = ip if params['family'].eql?('inet6')\n network[\"macaddress_#{iface}\"] = ip if params['family'].eql?('lladdr')\n end\n\nend\n\nlaniface = from(\"[ -e /vagrant ] && echo eth1 || echo eth0\")\n\nnetwork['lanip'] = network[\"ipaddress_#{laniface}\"]\nnetwork['laniface'] = laniface\n\nnetwork\n```\n\n# LICENSE\n```\n +-------------------------------------------------------------------------+\n | Advanced FireWall (AFW) Cookbook for Opscode Chef |\n | |\n | Copyright (C) 2012, AWeber, Julien Vehent |\n | |\n | This program is free software; you can redistribute it and/or modify |\n | it under the terms of the GNU General Public License version 2 |\n | as published by the Free Software Foundation. |\n | |\n | This program is distributed in the hope that it will be useful, |\n | but WITHOUT ANY WARRANTY; without even the implied warranty of |\n | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |\n | GNU General Public License for more details. |\n | |\n | You should have received a copy of the GNU General Public License along |\n | with this program; if not, write to the Free Software Foundation, Inc., |\n | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |\n | |\n +-------------------------------------------------------------------------+\n```\n",
+ "long_description": "# The Advanced FireWall (afw) for Chef\n\n__A__dvanced __F__ire__W__all (AFW) for Chef and Linux that uses Iptables\nto dynamically configure inbound and outbound rules on nodes.\n\nAFW uses Chef searches extensively to open access to systems. Instead of\nspecifying the IP addresses of a set of sources or destinations, AFW allows you\nto specify searches in Chef syntax to list those systems. It is designed to be\na lot more dynamic and maintainable than regular firewalls, and allows for\nfiltering inbound and outbound traffic.\n\nAFW support `raw` rules, that are just straight `iptables` syntax passed to the\ntemplate. For these, Chef searches are not supported, but it gives you access\nto the full set of features `iptables` provides.\n\nAFW runs on any Linux 2.6 or 3.*. It does not rely on distribution specific\nwrapper, such as ufw for ubuntu, but calls `iptables-restore` directly.\n\n## Rules definitions\nRules must be added in roles or nodes attributes. A typical rule looks like:\n\n```\n:afw =>\n :rules =>\n 'MongoDB App Entry Point' => {\n :protocol => 'tcp',\n :direction => 'in',\n :interface => 'default',\n :user => 'mongodb',\n :source => ['(roles:*nodejs-app-node OR roles:*python-worker-node OR roles:*python-api-node) AND SAMETAG',\n '10.4.76.2',\n 'backup-server.example.net']\n :dport => '27017'\n },\n}\n\n```\n\nRules must be added into `node[:afw][:rules]`, and follow this syntax:\n\n```\n\n:afw =>\n :rules =>\n '<rule name>' =>\n :direction => '<in|out>',\n :protocol => '<udp|tcp|icmp>',\n :user => '<local user from /etc/passwd>',\n :interface => '<default|all|eth0|eth1|br0|...>',\n :source => '<ip|fqdn|chef search>|['<ip|fqdn|chef search>',...]'>',\n :sport => '<integer(:integer))>',\n :destination => '<ip|fqdn|chef search>|['<ip|fqdn|chef search>',...]'>',\n :dport => '<integer(:integer)>',\n :env => '<production|staging|...>',\n :options => ['disable_env_limit', 'disable_syntax_check', ...]\n}\n\n```\n\n* __Rule Name__ : A `string` that identifies the rule as uniquely as possible.\n\n* __direction__ (__mandatory__): `in` is for inbound firewall rules.\n`out` for outbound firewall rules.\nSelect whether the rule will apply to packets entering the system (`in`) or\nleaving it (`out`).\n\n* __protocol__ (__mandatory__):\nSelect the L4 protocol this rule applies to: `udp`, `tcp` or `icmp`.\n\n* __user__ (___mandatory___):\nSet the local user allowed to transmit packets. The user is checked only for\noutbound firewall rules (iptables limitation) but must be set for inbound\nrules as well, to ease auditing rules & systems.\nNote: if the local user doesn't exists, the provisioning will fail at the end,\nwhen the rules are loaded. If the user is installed by a package, the next chef\nrun will succeed and fix the issue.\n\n* __dport__ (__mandatory__):\nSet the destination port of the connections being filtered. This is mandatory.\nExcept when it's not (eg. icmp).\n\n* __interface__ (*optional*):\nSelect the network interface. If undef, the default interface will be used.\nif `all`, the interface parameter won't be set at all.\n\n* __source__ (__mandatory for `in` rules__):\nSet the source IP of the packets. This parameter can either be a single IP or\nnetwork (eg. `10.1.2.0/32`), a Fully Qualified Domain Name (eg. `bob.colo.lair`)\n or a Chef Search (eg. `roles:mongodb`).\nBy default, searches are limited to the same `chef_environment` (eg. staging),\nto allow for firewall rules that open connections between environments, you\nwill need an `heresy` parameter.\nIn a chef-search, you can also use they keyword `SAMETAG` that will limit the\nsearch to the nodes that share the same tags. This is useful if, for example,\nyou want to open connections to a database from all nodes within the same\nservice tag, but not beyond.\nThe syntax for a search would look like: 'roles:whatever-api AND SAMETAG'.\nIf you have multiple sources and destinations, you can set them in a array:\n```\n'AMQP Producers' => {\n :direction => 'in',\n :user => 'rabbitmq',\n :protocol => 'tcp',\n :interface => 'default',\n :source => ['producer1.internal.domain.com',\n 'producer2.internal.domain.com',\n '192.168.1.1',\n 'roles:megaserver'],\n :dport => '5672'\n}\n```\n\n* __destination__ (*Same as `source`*)\n\n* __sport__ (*optional*):\nSet the source port in the firewall rule. For `in` rules, that means the source\nport of the remote machine. For `out` rules, that means the source port of this\nnode when establishing a connection to a remote node.\n\n* __env__ (*optional*):\nThe env parameters can be used to limit the application of a rule to a specific\nenvironment. If `:env => 'staging'` is set, the rule will be applied to nodes in\nthe staging environment only.\n\n* __options__ (*optional*):\n- disable_env_limit:\n`disable_env_limit` can also used to cross environment.\nIf `:options => ['disable_env_limit']` is set, the source and destination searches\nwill return results from all environments, instead of limiting the result to the\nenvironment the node lives in.\nThe following rule will allow production workers to connect to staging\nBackendDB. Don't do that. Keep environments isolated as much as possible.\n```\n 'Production Worker to Staging BackendDB' => {\n :protocol => 'tcp',\n :direction => 'out',\n :user => 'application-user',\n :destination => 'roles:*backenddb* AND SAMETAG AND chef_environment:staging',\n :dport => '15017',\n :env => 'production',\n :options => ['disable_env_limit']\n }\n```\n- disable_syntax_check:\n`disable_syntax_check` will turn off the rule validation step for a specific rule.\nThis is useful if you want to define a rule without source/destination, and\nlet another recipe populate the source/destination arrays later on.\n```\n 'API calls from servers' => {\n :protocol => 'tcp',\n :direction => 'in',\n :user => 'www-data',\n :dport => '80',\n :options => ['disable_syntax_check']\n },\n```\nYou would then have a separate recipe that inserts IPs in this rule:\n```\nnode[:afw][:rules]['API calls from servers'][:source] = []\nip_list = ['1.2.3.4', '4.5.6.7', '12.32.43.54']\nip_list.each do |ip|\n node[:afw][:rules]['API calls from servers'][:source].push(ip)\nend\n```\n\n### Creating rules from external cookbooks\nIf you want a cookbook to create firewal rules directly, as opposed to storing\nthese rules in a roles, then you need to use the `create_rule()` function from\nthe `AFW` module.\nExample: create outbound firewall rule for haproxy in the haproxy cookbook\n#### depend in AFW in the metadata\n`cookbooks/haproxy/metadata.rb`\n```\n[...]\ndepends 'AFW'\n```\n#### create the rule from the recipe using ruby\n```\n # Call the AFW module to create the rule\n AFW.create_rule(node,\n \"Haproxy outbound to #{destination}:#{port}\",\n {'protocol' => 'tcp',\n 'direction' => 'out',\n 'user' => 'haproxy',\n 'destination' => \"#{destination}\",\n 'dport' => \"#{port}\"\n })\n```\nNote that `AFW.create_rule()` must be called from a normal section of ruby code\ndirectly (not from a `ruby_block`) to ensure that the rules are compiled at\nchef compile time. The AFW template will later (at runtime) populate these rules\ninto the `iptables-restore` file.\n\n### Predefined rules\nPredefined rules are iptables rules that are used directly by AFW. Those rules\nare used for specific purposes only, such as using a very particular module for\nwhich AFW wouldn't have any support.\nPredefined rules only support 2 arguments: `table` and `rule`.\n\n* __table__:\nthe netfilter table on which this rule must be applied. One of `nat`, `raw`,\n`mangle` or `filter.\n\n* __rule__: the firewall rule itself, in iptables-save format (do not specify\na table in this format, or it will fail).\n\nexample:\n```\n :afw => {\n :rules => {\n\n 'Accept all packets router through the bridge' => {\n :table => 'filter',\n :rule => '-I FORWARD -o br0 -m physdev --physdev-is-bridged -j ACCEPT'\n },\n\n 'Drop connection to the admin panel on the eth0 interface' => {\n :table => 'mangle',\n :rule => '-A INPUT -i eth0 -p tcp --dport 80 -m string --string \"get /admin http/1.1\" --icase --algo bm -m conntrack --ctstate ESTABLISHED -j DROP'\n },\n\n 'DNAT a source IP to change the destination port' => {\n :table => 'nat',\n :rule => '-A PREROUTING -i eth3 -s 201.23.72.3 -p tcp --dport 8008 -j DNAT --to-destination 127.0.0.1:1234'\n },\n\n 'Dont do conntrack on this specific user's UDP packets' => {\n :table => 'raw',\n :rule => '-A OUTPUT -o eth0 -p udp -m owner --uid-owner 105 -j NOTRACK'\n }\n }\n }\n```\n\n## Rules Generation\n\nThe recipe will generate a rule file in `/etc/firewall/rules.iptables` that\nconforms to the iptables-save/restore syntax.\nAt the end of the chef-run, and if the rules file has been modified during the\nrun, the `iptables-restore` command will reload the entire ruleset.\n\nHere's an example of templated ruleset. Notice how the rules are regrouped by\nsystem user, to make them easier to read.\n\n```\n# Generated by AFW on Tue Sep 25 02:55:32 +0000 2012\n\n*raw\n:PREROUTING ACCEPT [0:0]\n:OUTPUT ACCEPT [0:0]\n-A OUTPUT -o lo -j NOTRACK\n-A PREROUTING -i lo -j NOTRACK\nCOMMIT\n\n*mangle\n:PREROUTING ACCEPT [0:0]\n:INPUT ACCEPT [0:0]\n:FORWARD ACCEPT [0:0]\n:OUTPUT ACCEPT [0:0]\n:POSTROUTING ACCEPT [0:0]\nCOMMIT\n\n*nat\n:PREROUTING ACCEPT [0:0]\n:POSTROUTING ACCEPT [0:0]\n:OUTPUT ACCEPT [0:0]\nCOMMIT\n\n*filter\n:INPUT ACCEPT [0:0]\n:FORWARD ACCEPT [0:0]\n:OUTPUT ACCEPT [0:0]\n-A INPUT -i lo -j ACCEPT\n-A OUTPUT -o lo -j ACCEPT\n-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT\n-A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT\n# some default rules we want to open everywhere\n-A INPUT -p tcp --dport 22 -s 1.0.0.0/8 -j ACCEPT\n-A INPUT -p tcp --dport 22 -s 12.30.0.0/16 -j ACCEPT\n-A OUTPUT -p udp --dport 53 -d 1.0.0.0/8 -j ACCEPT\n\n:app-user - [0:0]\n-A OUTPUT -m owner --uid-owner 999 -m state --state NEW -j app-user\n-A app-user -o eth0 -p tcp --dport 2003 -d 1.2.3.4 -m conntrack --ctstate NEW -j ACCEPT\n-A app-user -o eth0 -p tcp --dport 27017 -d 1.1.4.3 -m conntrack --ctstate NEW -j ACCEPT\n-A app-user -o eth0 -p tcp --dport 5672 -d 1.1.4.9 -m conntrack --ctstate NEW -j ACCEPT\n-A app-user -o eth0 -p tcp --dport 80 -d 1.1.4.10 -m conntrack --ctstate NEW -j ACCEPT\n-A app-user -o eth0 -p udp --dport 8125 -d 1.1.4.74 -m conntrack --ctstate NEW -j ACCEPT\n-A app-user -j LOG --log-prefix \"DROP_AFW_OUTPUT_app-user \" --log-uid --log-tcp-sequence\n\n:haproxy - [0:0]\n-A OUTPUT -m owner --uid-owner 110 -m state --state NEW -j haproxy\n-A INPUT -i eth0 -p tcp --dport 10097 -s 0.0.0.0/0 -m conntrack --ctstate NEW -j ACCEPT\n-A haproxy -o eth0 -p tcp --dport 2003 -d 1.1.0.25 -m conntrack --ctstate NEW -j ACCEPT\n-A haproxy -o eth0 -p tcp --dport 2003 -d 1.1.0.37 -m conntrack --ctstate NEW -j ACCEPT\n-A haproxy -o eth0 -p tcp --dport 80 -d 1.1.4.58 -m conntrack --ctstate NEW -j ACCEPT\n-A haproxy -j LOG --log-prefix \"DROP_AFW_OUTPUT_haproxy \" --log-uid --log-tcp-sequence\n\n:nagios - [0:0]\n-A OUTPUT -m owner --uid-owner 105 -m state --state NEW -j nagios\n-A INPUT -i eth0 -p tcp --dport 5666 -s 1.1.0.33 -m conntrack --ctstate NEW -j ACCEPT\n-A nagios -j LOG --log-prefix \"DROP_AFW_OUTPUT_nagios \" --log-uid --log-tcp-sequence\n\n:ntp - [0:0]\n-A OUTPUT -m owner --uid-owner 104 -m state --state NEW -j ntp\n-A ntp -o eth0 -p udp --dport 123 -d ntp.example.net -m conntrack --ctstate NEW -j ACCEPT\n-A ntp -j LOG --log-prefix \"DROP_AFW_OUTPUT_ntp \" --log-uid --log-tcp-sequence\n\n:root - [0:0]\n-A OUTPUT -m owner --uid-owner 0 -m state --state NEW -j root\n-A INPUT -p icmp -s 0.0.0.0/0 -m conntrack --ctstate NEW -j ACCEPT\n-A root -o eth0 -p tcp --dport 514 -d 1.1.3.18 -m conntrack --ctstate NEW -j ACCEPT\n-A root -o eth0 -p udp --dport 514 -d 1.1.3.18 -m conntrack --ctstate NEW -j ACCEPT\n-A root -p icmp -d 0.0.0.0/0 -m conntrack --ctstate NEW -j ACCEPT\n-A root -j LOG --log-prefix \"DROP_AFW_OUTPUT_root \" --log-uid --log-tcp-sequence\n\n:snmp - [0:0]\n-A OUTPUT -m owner --uid-owner 108 -m state --state NEW -j snmp\n-A snmp -o eth0 -p udp --dport 162 -d 1.1.0.23 -m conntrack --ctstate NEW -j ACCEPT\n-A snmp -j LOG --log-prefix \"DROP_AFW_OUTPUT_snmp \" --log-uid --log-tcp-sequence\n\n:www-data - [0:0]\n-A OUTPUT -m owner --uid-owner 33 -m state --state NEW -j www-data\n-A INPUT -i eth0 -p tcp --dport 80 -s 1.1.4.4 -m conntrack --ctstate NEW -j ACCEPT\n-A INPUT -i eth0 -p tcp --dport 80 -s 1.1.4.1 -m conntrack --ctstate NEW -j ACCEPT\n-A www-data -j LOG --log-prefix \"DROP_AFW_OUTPUT_www-data \" --log-uid --log-tcp-sequence\n\n-A INPUT -j LOG --log-prefix \"DROP_AFW_INPUT \" --log-uid --log-tcp-sequence\n-A INPUT -j DROP\n\n-A OUTPUT -j LOG --log-prefix \"DROP_AFW_OUTPUT \" --log-uid --log-tcp-sequence\nCOMMIT\n```\n\nThe `INPUT` chain contains all of the rules for incoming connections. It does\nnot redirect packets to other chains, but accept or drop them directly.\n\nThe `OUTPUT` chain is a little different. Depending on the owner of the socket\nemitting packets, it will direct the packets to a different chain, named after\nthe socket owner.\nIn the example above, the packet from the `snmp` user will be directed to the\nchain named `snmp`. You can see in this chain that the first two rules accept\npacket, while the 3rd one will `LOG` to syslog when it is reached (it shouldn't\nbe). Eventually, a `DROP` will follow that log rule to drop packets that aren't\nsuppose to be sent.\n\n# ATTRIBUTES\n\n* `default[:afw][:enable] = true` : enable or disable the firewall restore\ncommand. If set the false, the rules will still be populated in \n`/etc/firewall/rules.iptables` but the restore command will not be issued.\n\n* `default[:afw][:enable_input_drop] = true` : DROP all input packets by defaut\n* `default[:afw][:enable_output_drop] = true` : DROP all output packets by defaut\n* `default[:afw][:enable_input_drop_log] = true` : LOG when DROP input packets\n* `default[:afw][:enable_output_drop_log] = true` : LOG when DROP output packets\n\n## Dependencies\n### Ohai network_addr\nThis cookbooks relies on the custom AWeber Ohai plugin which sets the two\nfollowing attributes:\n`node[:network][:lanip]` is set to the IP of the node on the LAN network\n`node[:network][:laniface]` is set to the network interface on the LAN\nWhile AFW can probably run with this plugin, it makes you life easier when you\nwant to open rules on your LAN interface. We typically enfore `eth0` to be the\nLAN interface, but feel free to use whatever you want.\n\nPlugin source:\n```\nprovides \"network\"\n\nrequire_plugin \"hostname\"\nrequire_plugin \"#{os}::network\"\n\nnetwork['interfaces'].each do |iface, addrs|\n\n addrs['addresses'].each do |ip, params|\n network[\"ipaddress_#{iface}\"] = ip if params['family'].eql?('inet')\n network[\"ipaddress6_#{iface}\"] = ip if params['family'].eql?('inet6')\n network[\"macaddress_#{iface}\"] = ip if params['family'].eql?('lladdr')\n end\n\nend\n\nlaniface = from(\"[ -e /vagrant ] && echo eth1 || echo eth0\")\n\nnetwork['lanip'] = network[\"ipaddress_#{laniface}\"]\nnetwork['laniface'] = laniface\n\nnetwork\n```\n\n# LICENSE\n```\n +-------------------------------------------------------------------------+\n | Advanced FireWall (AFW) Cookbook for Opscode Chef |\n | |\n | Copyright (C) 2012, AWeber, Julien Vehent |\n | |\n | This program is free software; you can redistribute it and/or modify |\n | it under the terms of the GNU General Public License version 2 |\n | as published by the Free Software Foundation. |\n | |\n | This program is distributed in the hope that it will be useful, |\n | but WITHOUT ANY WARRANTY; without even the implied warranty of |\n | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |\n | GNU General Public License for more details. |\n | |\n | You should have received a copy of the GNU General Public License along |\n | with this program; if not, write to the Free Software Foundation, Inc., |\n | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |\n | |\n +-------------------------------------------------------------------------+\n```\n",
"maintainer": "Julien Vehent",
"maintainer_email": "julien@linuxwall.info",
"license": "All rights reserved",
2  recipes/default.rb
View
@@ -20,7 +20,7 @@
node['afw']['tables']['nat']['chains'] = []
class Chef::Recipe
- include AFWCore
+ include AFW
end
node['afw']['rules'].each do |name,params|
Please sign in to comment.
Something went wrong with that request. Please try again.