cloud-mu 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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