cloud-mu 2.0.1 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/Berksfile +2 -1
  3. data/bin/mu-upload-chef-artifacts +3 -0
  4. data/cloud-mu.gemspec +2 -2
  5. data/cookbooks/firewall/CHANGELOG.md +295 -0
  6. data/cookbooks/firewall/CONTRIBUTING.md +2 -0
  7. data/cookbooks/firewall/MAINTAINERS.md +19 -0
  8. data/cookbooks/firewall/README.md +339 -0
  9. data/cookbooks/firewall/attributes/default.rb +5 -0
  10. data/cookbooks/firewall/attributes/firewalld.rb +1 -0
  11. data/cookbooks/firewall/attributes/iptables.rb +17 -0
  12. data/cookbooks/firewall/attributes/ufw.rb +12 -0
  13. data/cookbooks/firewall/attributes/windows.rb +8 -0
  14. data/cookbooks/firewall/libraries/helpers.rb +100 -0
  15. data/cookbooks/firewall/libraries/helpers_firewalld.rb +116 -0
  16. data/cookbooks/firewall/libraries/helpers_iptables.rb +112 -0
  17. data/cookbooks/firewall/libraries/helpers_ufw.rb +135 -0
  18. data/cookbooks/firewall/libraries/helpers_windows.rb +130 -0
  19. data/cookbooks/firewall/libraries/matchers.rb +30 -0
  20. data/cookbooks/firewall/libraries/provider_firewall_firewalld.rb +179 -0
  21. data/cookbooks/firewall/libraries/provider_firewall_iptables.rb +171 -0
  22. data/cookbooks/firewall/libraries/provider_firewall_iptables_ubuntu.rb +196 -0
  23. data/cookbooks/firewall/libraries/provider_firewall_iptables_ubuntu1404.rb +196 -0
  24. data/cookbooks/firewall/libraries/provider_firewall_rule.rb +34 -0
  25. data/cookbooks/firewall/libraries/provider_firewall_ufw.rb +138 -0
  26. data/cookbooks/firewall/libraries/provider_firewall_windows.rb +126 -0
  27. data/cookbooks/firewall/libraries/resource_firewall.rb +26 -0
  28. data/cookbooks/firewall/libraries/resource_firewall_rule.rb +52 -0
  29. data/cookbooks/firewall/metadata.json +1 -0
  30. data/cookbooks/firewall/recipes/default.rb +80 -0
  31. data/cookbooks/firewall/recipes/disable_firewall.rb +23 -0
  32. data/cookbooks/firewall/templates/default/ufw/default.erb +13 -0
  33. data/cookbooks/mu-firewall/metadata.rb +1 -1
  34. data/cookbooks/mu-master/recipes/default.rb +3 -1
  35. data/cookbooks/mu-master/recipes/init.rb +3 -1
  36. data/cookbooks/mu-master/templates/default/mu.rc.erb +3 -0
  37. data/cookbooks/mu-tools/recipes/apply_security.rb +2 -2
  38. data/cookbooks/mu-tools/recipes/aws_api.rb +1 -1
  39. data/cookbooks/mu-tools/recipes/base_repositories.rb +1 -1
  40. data/cookbooks/mu-tools/recipes/clamav.rb +1 -1
  41. data/cookbooks/mu-tools/recipes/cloudinit.rb +1 -1
  42. data/cookbooks/mu-tools/recipes/disable-requiretty.rb +2 -2
  43. data/cookbooks/mu-tools/recipes/eks.rb +1 -1
  44. data/cookbooks/mu-tools/recipes/gcloud.rb +1 -1
  45. data/cookbooks/mu-tools/recipes/nrpe.rb +1 -1
  46. data/cookbooks/mu-tools/recipes/rsyslog.rb +2 -2
  47. data/cookbooks/mu-tools/recipes/set_local_fw.rb +37 -28
  48. data/environments/dev.json +1 -1
  49. data/environments/prod.json +1 -1
  50. data/modules/mu/cleanup.rb +4 -0
  51. data/modules/mu/clouds/aws/container_cluster.rb +3 -0
  52. data/modules/mu/clouds/aws/role.rb +14 -2
  53. data/modules/mu/clouds/aws/userdata/linux.erb +2 -3
  54. data/modules/mu/clouds/aws.rb +30 -14
  55. data/modules/mu.rb +4 -0
  56. metadata +30 -2
@@ -0,0 +1,112 @@
1
+ module FirewallCookbook
2
+ module Helpers
3
+ module Iptables
4
+ include FirewallCookbook::Helpers
5
+ include Chef::Mixin::ShellOut
6
+
7
+ CHAIN = { in: 'INPUT', out: 'OUTPUT', pre: 'PREROUTING', post: 'POSTROUTING' }.freeze unless defined? CHAIN # , nil => "FORWARD"}
8
+ TARGET = { allow: 'ACCEPT', reject: 'REJECT', deny: 'DROP', masquerade: 'MASQUERADE', redirect: 'REDIRECT', log: 'LOG --log-prefix "iptables: " --log-level 7' }.freeze unless defined? TARGET
9
+
10
+ def build_firewall_rule(current_node, rule_resource, ipv6 = false)
11
+ el5 = current_node['platform_family'] == 'rhel' && Gem::Dependency.new('', '~> 5.0').match?('', current_node['platform_version'])
12
+
13
+ return rule_resource.raw.strip if rule_resource.raw
14
+ firewall_rule = if rule_resource.direction
15
+ "-A #{CHAIN[rule_resource.direction.to_sym]} "
16
+ else
17
+ '-A FORWARD '
18
+ end
19
+
20
+ if [:pre, :post].include?(rule_resource.direction)
21
+ firewall_rule << '-t nat '
22
+ end
23
+
24
+ # Iptables order of prameters is important here see example output below:
25
+ # -A INPUT -s 1.2.3.4/32 -d 5.6.7.8/32 -i lo -p tcp -m tcp -m state --state NEW -m comment --comment "hello" -j DROP
26
+ firewall_rule << "-s #{ip_with_mask(rule_resource, rule_resource.source)} " if rule_resource.source && rule_resource.source != '0.0.0.0/0'
27
+ firewall_rule << "-d #{rule_resource.destination} " if rule_resource.destination
28
+
29
+ firewall_rule << "-i #{rule_resource.interface} " if rule_resource.interface
30
+ firewall_rule << "-o #{rule_resource.dest_interface} " if rule_resource.dest_interface
31
+
32
+ firewall_rule << "-p #{rule_resource.protocol} " if rule_resource.protocol && rule_resource.protocol.to_s.to_sym != :none
33
+ firewall_rule << '-m tcp ' if rule_resource.protocol && rule_resource.protocol.to_s.to_sym == :tcp
34
+
35
+ # using multiport here allows us to simplify our greps and rule building
36
+ firewall_rule << "-m multiport --sports #{port_to_s(rule_resource.source_port)} " if rule_resource.source_port
37
+ firewall_rule << "-m multiport --dports #{port_to_s(dport_calc(rule_resource))} " if dport_calc(rule_resource)
38
+
39
+ firewall_rule << "-m state --state #{rule_resource.stateful.is_a?(Array) ? rule_resource.stateful.join(',').upcase : rule_resource.stateful.upcase} " if rule_resource.stateful
40
+ # the comments extension is not available for ip6tables on rhel/centos 5
41
+ unless el5 && ipv6
42
+ firewall_rule << "-m comment --comment \"#{rule_resource.description}\" " if rule_resource.include_comment
43
+ end
44
+
45
+ firewall_rule << "-j #{TARGET[rule_resource.command.to_sym]} "
46
+ firewall_rule << "--to-ports #{rule_resource.redirect_port} " if rule_resource.command == :redirect
47
+ firewall_rule.strip!
48
+ firewall_rule
49
+ end
50
+
51
+ def iptables_packages(new_resource)
52
+ packages = if ipv6_enabled?(new_resource)
53
+ %w(iptables iptables-ipv6)
54
+ else
55
+ %w(iptables)
56
+ end
57
+
58
+ # centos 7 requires extra service
59
+ if !debian?(node) && node['platform_family'] != "amazon" && node['platform_version'].to_i >= 7
60
+ packages << %w(iptables-services)
61
+ end
62
+
63
+ packages.flatten
64
+ end
65
+
66
+ def iptables_commands(new_resource)
67
+ if ipv6_enabled?(new_resource)
68
+ %w(iptables ip6tables)
69
+ else
70
+ %w(iptables)
71
+ end
72
+ end
73
+
74
+ def log_iptables(new_resource)
75
+ iptables_commands(new_resource).each do |cmd|
76
+ shell_out!("#{cmd} -L -n")
77
+ end
78
+ rescue
79
+ Chef::Log.info('log_iptables failed!')
80
+ end
81
+
82
+ def iptables_flush!(new_resource)
83
+ iptables_commands(new_resource).each do |cmd|
84
+ shell_out!("#{cmd} -F")
85
+ end
86
+ end
87
+
88
+ def iptables_default_allow!(new_resource)
89
+ iptables_commands(new_resource).each do |cmd|
90
+ shell_out!("#{cmd} -P INPUT ACCEPT")
91
+ shell_out!("#{cmd} -P OUTPUT ACCEPT")
92
+ shell_out!("#{cmd} -P FORWARD ACCEPT")
93
+ end
94
+ end
95
+
96
+ def default_ruleset(current_node)
97
+ current_node['firewall']['iptables']['defaults'][:ruleset].to_h
98
+ end
99
+
100
+ def ensure_default_rules_exist(current_node, new_resource)
101
+ input = new_resource.rules
102
+
103
+ # don't use iptables_commands here since we do populate the
104
+ # hash regardless of ipv6 status
105
+ %w(iptables ip6tables).each do |name|
106
+ input[name] = {} unless input[name]
107
+ input[name].merge!(default_ruleset(current_node).to_h)
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,135 @@
1
+ module FirewallCookbook
2
+ module Helpers
3
+ module Ufw
4
+ include FirewallCookbook::Helpers
5
+ include Chef::Mixin::ShellOut
6
+
7
+ def ufw_rules_filename
8
+ '/etc/default/ufw-chef.rules'
9
+ end
10
+
11
+ def ufw_active?
12
+ cmd = shell_out!('ufw', 'status')
13
+ cmd.stdout =~ /^Status:\sactive/
14
+ end
15
+
16
+ def ufw_disable!
17
+ shell_out!('ufw', 'disable', input: 'yes')
18
+ end
19
+
20
+ def ufw_enable!
21
+ shell_out!('ufw', 'enable', input: 'yes')
22
+ end
23
+
24
+ def ufw_reset!
25
+ shell_out!('ufw', 'reset', input: 'yes')
26
+ end
27
+
28
+ def ufw_logging!(param)
29
+ shell_out!('ufw', 'logging', param.to_s)
30
+ end
31
+
32
+ def ufw_rule!(cmd)
33
+ shell_out!(cmd, input: 'yes')
34
+ end
35
+
36
+ def build_rule(new_resource)
37
+ Chef::Log.info("#{new_resource.name} apply_rule #{new_resource.command}")
38
+
39
+ # if we don't do this, we may see some bugs where traffic is opened on all ports to all hosts when only RELATED,ESTABLISHED was intended
40
+ if new_resource.stateful
41
+ msg = ''
42
+ msg << "firewall_rule[#{new_resource.name}] was asked to "
43
+ msg << "#{new_resource.command} a stateful rule using #{new_resource.stateful} "
44
+ msg << 'but ufw does not support this kind of rule. Consider guarding by platform_family.'
45
+ raise msg
46
+ end
47
+
48
+ # if we don't do this, ufw will fail as it does not support protocol numbers, so we'll only allow it to run if specifying icmp/tcp/udp protocol types
49
+ if new_resource.protocol && !new_resource.protocol.to_s.downcase.match('^(tcp|udp|esp|ah|ipv6|none)$')
50
+ msg = ''
51
+ msg << "firewall_rule[#{new_resource.name}] was asked to "
52
+ msg << "#{new_resource.command} a rule using protocol #{new_resource.protocol} "
53
+ msg << 'but ufw does not support this kind of rule. Consider guarding by platform_family.'
54
+ raise msg
55
+ end
56
+
57
+ # some examples:
58
+ # ufw allow from 192.168.0.4 to any port 22
59
+ # ufw deny proto tcp from 10.0.0.0/8 to 192.168.0.1 port 25
60
+ # ufw insert 1 allow proto tcp from 0.0.0.0/0 to 192.168.0.1 port 25
61
+
62
+ if new_resource.raw
63
+ "ufw #{new_resource.raw.strip}"
64
+ else
65
+ "ufw #{rule(new_resource)}"
66
+ end
67
+ end
68
+
69
+ def rule(new_resource)
70
+ rule = ''
71
+ rule << "#{new_resource.command} "
72
+ rule << rule_interface(new_resource)
73
+ rule << rule_logging(new_resource)
74
+ rule << rule_proto(new_resource)
75
+ rule << rule_dest_port(new_resource)
76
+ rule << rule_source_port(new_resource)
77
+ rule = rule.strip
78
+
79
+ if rule == 'ufw allow in proto tcp to any from any'
80
+ Chef::Log.warn("firewall_rule[#{new_resource.name}] produced a rule that opens all traffic. This may be a logic error in your cookbook.")
81
+ end
82
+
83
+ rule
84
+ end
85
+
86
+ def rule_interface(new_resource)
87
+ rule = ''
88
+ rule << "#{new_resource.direction} " if new_resource.direction
89
+ rule << "on #{new_resource.interface} " if new_resource.interface && new_resource.direction
90
+ rule << "in on #{new_resource.interface} " if new_resource.interface && !new_resource.direction
91
+ rule
92
+ end
93
+
94
+ def rule_proto(new_resource)
95
+ rule = ''
96
+ rule << "proto #{new_resource.protocol} " if new_resource.protocol && new_resource.protocol.to_s.to_sym != :none
97
+ rule
98
+ end
99
+
100
+ def rule_dest_port(new_resource)
101
+ rule = if new_resource.destination
102
+ "to #{new_resource.destination} "
103
+ else
104
+ 'to any '
105
+ end
106
+ rule << "port #{port_to_s(dport_calc(new_resource))} " if dport_calc(new_resource)
107
+ rule
108
+ end
109
+
110
+ def rule_source_port(new_resource)
111
+ rule = if new_resource.source
112
+ "from #{new_resource.source} "
113
+ else
114
+ 'from any '
115
+ end
116
+
117
+ if new_resource.source_port
118
+ rule << "port #{port_to_s(new_resource.source_port)} "
119
+ end
120
+ rule
121
+ end
122
+
123
+ def rule_logging(new_resource)
124
+ case new_resource.logging && new_resource.logging.to_sym
125
+ when :connections
126
+ 'log '
127
+ when :packets
128
+ 'log-all '
129
+ else
130
+ ''
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,130 @@
1
+ module FirewallCookbook
2
+ module Helpers
3
+ module Windows
4
+ include FirewallCookbook::Helpers
5
+ include Chef::Mixin::ShellOut
6
+
7
+ def fixup_cidr(str)
8
+ newstr = str.clone
9
+ newstr.gsub!('0.0.0.0/0', 'any') if newstr.include?('0.0.0.0/0')
10
+ newstr.gsub!('/0', '') if newstr.include?('/0')
11
+ newstr
12
+ end
13
+
14
+ def windows_rules_filename
15
+ "#{ENV['HOME']}/windows-chef.rules"
16
+ end
17
+
18
+ def active?
19
+ @active ||= begin
20
+ cmd = shell_out!('netsh advfirewall show currentprofile')
21
+ cmd.stdout =~ /^State\sON/
22
+ end
23
+ end
24
+
25
+ def enable!
26
+ shell_out!('netsh advfirewall set currentprofile state on')
27
+ end
28
+
29
+ def disable!
30
+ shell_out!('netsh advfirewall set currentprofile state off')
31
+ end
32
+
33
+ def reset!
34
+ shell_out!('netsh advfirewall reset')
35
+ end
36
+
37
+ def add_rule!(params)
38
+ shell_out!("netsh advfirewall #{params}")
39
+ end
40
+
41
+ def delete_all_rules!
42
+ shell_out!('netsh advfirewall firewall delete rule name=all')
43
+ end
44
+
45
+ def to_type(new_resource)
46
+ cmd = new_resource.command
47
+ type = if cmd == :reject || cmd == :deny
48
+ :block
49
+ else
50
+ :allow
51
+ end
52
+ type
53
+ end
54
+
55
+ def build_rule(new_resource)
56
+ type = to_type(new_resource)
57
+ parameters = {}
58
+
59
+ parameters['description'] = "\"#{new_resource.description}\""
60
+ parameters['dir'] = new_resource.direction
61
+
62
+ new_resource.program && parameters['program'] = new_resource.program
63
+ new_resource.service && parameters['service'] = new_resource.service
64
+ parameters['protocol'] = new_resource.protocol
65
+
66
+ if new_resource.direction.to_sym == :out
67
+ parameters['localip'] = new_resource.source ? fixup_cidr(new_resource.source) : 'any'
68
+ parameters['localport'] = new_resource.source_port ? port_to_s(new_resource.source_port) : 'any'
69
+ parameters['interfacetype'] = new_resource.interface ? new_resource.interface : 'any'
70
+ parameters['remoteip'] = new_resource.destination ? fixup_cidr(new_resource.destination) : 'any'
71
+ parameters['remoteport'] = new_resource.dest_port ? port_to_s(new_resource.dest_port) : 'any'
72
+ else
73
+ parameters['localip'] = new_resource.destination ? new_resource.destination : 'any'
74
+ parameters['localport'] = dport_calc(new_resource) ? port_to_s(dport_calc(new_resource)) : 'any'
75
+ parameters['interfacetype'] = new_resource.dest_interface ? new_resource.dest_interface : 'any'
76
+ parameters['remoteip'] = new_resource.source ? fixup_cidr(new_resource.source) : 'any'
77
+ parameters['remoteport'] = new_resource.source_port ? port_to_s(new_resource.source_port) : 'any'
78
+ end
79
+
80
+ parameters['action'] = type.to_s
81
+
82
+ partial_command = parameters.map { |k, v| "#{k}=#{v}" }.join(' ')
83
+ "firewall add rule name=\"#{new_resource.name}\" #{partial_command}"
84
+ end
85
+
86
+ def rule_exists?(name)
87
+ @exists ||= begin
88
+ cmd = shell_out!("netsh advfirewall firewall show rule name=\"#{name}\"", returns: [0, 1])
89
+ cmd.stdout !~ /^No rules match the specified criteria/
90
+ end
91
+ end
92
+
93
+ def show_all_rules!
94
+ cmd = shell_out!('netsh advfirewall firewall show rule name=all')
95
+ cmd.stdout.each_line do |line|
96
+ Chef::Log.warn(line)
97
+ end
98
+ end
99
+
100
+ def rule_up_to_date?(name, type)
101
+ @up_to_date ||= begin
102
+ desired_parameters = rule_parameters(type)
103
+ current_parameters = {}
104
+
105
+ cmd = shell_out!("netsh advfirewall firewall show rule name=\"#{name}\" verbose")
106
+ cmd.stdout.each_line do |line|
107
+ current_parameters['description'] = "\"#{Regexp.last_match(1).chomp}\"" if line =~ /^Description:\s+(.*)$/
108
+ current_parameters['dir'] = Regexp.last_match(1).chomp if line =~ /^Direction:\s+(.*)$/
109
+ current_parameters['program'] = Regexp.last_match(1).chomp if line =~ /^Program:\s+(.*)$/
110
+ current_parameters['service'] = Regexp.last_match(1).chomp if line =~ /^Service:\s+(.*)$/
111
+ current_parameters['protocol'] = Regexp.last_match(1).chomp if line =~ /^Protocol:\s+(.*)$/
112
+ current_parameters['localip'] = Regexp.last_match(1).chomp if line =~ /^LocalIP:\s+(.*)$/
113
+ current_parameters['localport'] = Regexp.last_match(1).chomp if line =~ /^LocalPort:\s+(.*)$/
114
+ current_parameters['interfacetype'] = Regexp.last_match(1).chomp if line =~ /^InterfaceTypes:\s+(.*)$/
115
+ current_parameters['remoteip'] = Regexp.last_match(1).chomp if line =~ /^RemoteIP:\s+(.*)$/
116
+ current_parameters['remoteport'] = Regexp.last_match(1).chomp if line =~ /^RemotePort:\s+(.*)$/
117
+ current_parameters['action'] = Regexp.last_match(1).chomp if line =~ /^Action:\s+(.*)$/
118
+ end
119
+
120
+ up_to_date = true
121
+ desired_parameters.each do |k, v|
122
+ up_to_date = false if current_parameters[k] !~ /^["]?#{v}["]?$/i
123
+ end
124
+
125
+ up_to_date
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,30 @@
1
+ if defined?(ChefSpec)
2
+ ChefSpec.define_matcher(:firewall)
3
+ ChefSpec.define_matcher(:firewall_rule)
4
+
5
+ # actions(:install, :restart, :disable, :flush, :save)
6
+
7
+ def install_firewall(resource)
8
+ ChefSpec::Matchers::ResourceMatcher.new(:firewall, :install, resource)
9
+ end
10
+
11
+ def restart_firewall(resource)
12
+ ChefSpec::Matchers::ResourceMatcher.new(:firewall, :restart, resource)
13
+ end
14
+
15
+ def disable_firewall(resource)
16
+ ChefSpec::Matchers::ResourceMatcher.new(:firewall, :disable, resource)
17
+ end
18
+
19
+ def flush_firewall(resource)
20
+ ChefSpec::Matchers::ResourceMatcher.new(:firewall, :flush, resource)
21
+ end
22
+
23
+ def save_firewall(resource)
24
+ ChefSpec::Matchers::ResourceMatcher.new(:firewall, :save, resource)
25
+ end
26
+
27
+ def create_firewall_rule(resource)
28
+ ChefSpec::Matchers::ResourceMatcher.new(:firewall_rule, :create, resource)
29
+ end
30
+ end
@@ -0,0 +1,179 @@
1
+ #
2
+ # Author:: Ronald Doorn (<rdoorn@schubergphilis.com>)
3
+ # Cookbook:: firewall
4
+ # Resource:: default
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ class Chef
19
+ class Provider::FirewallFirewalld < Chef::Provider::LWRPBase
20
+ include FirewallCookbook::Helpers::Firewalld
21
+
22
+ provides :firewall, os: 'linux', platform_family: %w(rhel fedora) do |node|
23
+ node['platform_version'].to_f >= 7.0 && !node['firewall']['redhat7_iptables']
24
+ end
25
+
26
+ def whyrun_supported?
27
+ false
28
+ end
29
+
30
+ def action_install
31
+ return if disabled?(new_resource)
32
+
33
+ firewalld_package = package 'firewalld' do
34
+ action :nothing
35
+ options new_resource.package_options
36
+ end
37
+ firewalld_package.run_action(:install)
38
+ new_resource.updated_by_last_action(firewalld_package.updated_by_last_action?)
39
+
40
+ unless ::File.exist?(firewalld_rules_filename)
41
+ rules_file = lookup_or_create_rulesfile
42
+ rules_file.content '# created by chef to allow service to start'
43
+ rules_file.run_action(:create)
44
+ new_resource.updated_by_last_action(rules_file.updated_by_last_action?)
45
+ end
46
+
47
+ firewalld_service = lookup_or_create_service
48
+ [:enable, :start].each do |a|
49
+ firewalld_service.run_action(a)
50
+ new_resource.updated_by_last_action(firewalld_service.updated_by_last_action?)
51
+ end
52
+ end
53
+
54
+ def action_restart
55
+ return if disabled?(new_resource)
56
+
57
+ # ensure it's initialized
58
+ new_resource.rules({}) unless new_resource.rules
59
+ new_resource.rules['firewalld'] = {} unless new_resource.rules['firewalld']
60
+
61
+ # this populates the hash of rules from firewall_rule resources
62
+ firewall_rules = Chef.run_context.resource_collection.select { |item| item.is_a?(Chef::Resource::FirewallRule) }
63
+ firewall_rules.each do |firewall_rule|
64
+ next unless firewall_rule.action.include?(:create) && !firewall_rule.should_skip?(:create)
65
+
66
+ ip_versions(firewall_rule).each do |ip_version|
67
+ # build rules to apply with weight
68
+ k = "firewall-cmd --direct --add-rule #{build_firewall_rule(firewall_rule, ip_version)}"
69
+ v = firewall_rule.position
70
+
71
+ # unless we're adding them for the first time.... bail out.
72
+ next if new_resource.rules['firewalld'].key?(k) && new_resource.rules['firewalld'][k] == v
73
+ new_resource.rules['firewalld'][k] = v
74
+
75
+ # If persistent rules is enabled (default) make sure we add a permanent rule at the same time
76
+ perm_rules = node && node['firewall'] && node['firewall']['firewalld'] && node['firewall']['firewalld']['permanent']
77
+ if firewall_rule.permanent || perm_rules
78
+ k = "firewall-cmd --permanent --direct --add-rule #{build_firewall_rule(firewall_rule, ip_version)}"
79
+ new_resource.rules['firewalld'][k] = v
80
+ end
81
+ end
82
+ end
83
+
84
+ # ensure a file resource exists with the current firewalld rules
85
+ rules_file = lookup_or_create_rulesfile
86
+ rules_file.content build_rule_file(new_resource.rules['firewalld'])
87
+ rules_file.run_action(:create)
88
+
89
+ # ensure the service is running without waiting.
90
+ firewalld_service = lookup_or_create_service
91
+ [:enable, :start].each do |a|
92
+ firewalld_service.run_action(a)
93
+ new_resource.updated_by_last_action(firewalld_service.updated_by_last_action?)
94
+ end
95
+
96
+ # mark updated if we changed the zone
97
+ unless firewalld_default_zone?(new_resource.enabled_zone)
98
+ firewalld_default_zone!(new_resource.enabled_zone)
99
+ new_resource.updated_by_last_action(true)
100
+ end
101
+
102
+ # if the file was changed, load new ruleset
103
+ return unless rules_file.updated_by_last_action?
104
+ firewalld_flush!
105
+ # TODO: support logging
106
+
107
+ new_resource.rules['firewalld'].sort_by { |_k, v| v }.map { |k, _v| k }.each do |cmd|
108
+ firewalld_rule!(cmd)
109
+ end
110
+
111
+ new_resource.updated_by_last_action(true)
112
+ end
113
+
114
+ def action_disable
115
+ return if disabled?(new_resource)
116
+
117
+ if firewalld_active?
118
+ firewalld_flush!
119
+ firewalld_default_zone!(new_resource.disabled_zone)
120
+ new_resource.updated_by_last_action(true)
121
+ end
122
+
123
+ # ensure the service is stopped without waiting.
124
+ firewalld_service = lookup_or_create_service
125
+ [:disable, :stop].each do |a|
126
+ firewalld_service.run_action(a)
127
+ new_resource.updated_by_last_action(firewalld_service.updated_by_last_action?)
128
+ end
129
+
130
+ rules_file = lookup_or_create_rulesfile
131
+ rules_file.content '# created by chef to allow service to start'
132
+ rules_file.run_action(:create)
133
+ new_resource.updated_by_last_action(rules_file.updated_by_last_action?)
134
+ end
135
+
136
+ def action_flush
137
+ return if disabled?(new_resource)
138
+ return unless firewalld_active?
139
+
140
+ firewalld_flush!
141
+ new_resource.updated_by_last_action(true)
142
+
143
+ rules_file = lookup_or_create_rulesfile
144
+ rules_file.content '# created by chef to allow service to start'
145
+ rules_file.run_action(:create)
146
+ new_resource.updated_by_last_action(rules_file.updated_by_last_action?)
147
+ end
148
+
149
+ def action_save
150
+ return if disabled?(new_resource)
151
+ return if firewalld_all_rules_permanent!
152
+
153
+ firewalld_save!
154
+ new_resource.updated_by_last_action(true)
155
+ end
156
+
157
+ def lookup_or_create_service
158
+ begin
159
+ firewalld_service = Chef.run_context.resource_collection.find(service: 'firewalld')
160
+ rescue
161
+ firewalld_service = service 'firewalld' do
162
+ action :nothing
163
+ end
164
+ end
165
+ firewalld_service
166
+ end
167
+
168
+ def lookup_or_create_rulesfile
169
+ begin
170
+ firewalld_file = Chef.run_context.resource_collection.find(file: firewalld_rules_filename)
171
+ rescue
172
+ firewalld_file = file firewalld_rules_filename do
173
+ action :nothing
174
+ end
175
+ end
176
+ firewalld_file
177
+ end
178
+ end
179
+ end