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.
- checksums.yaml +4 -4
- data/Berksfile +2 -1
- data/bin/mu-upload-chef-artifacts +3 -0
- data/cloud-mu.gemspec +2 -2
- data/cookbooks/firewall/CHANGELOG.md +295 -0
- data/cookbooks/firewall/CONTRIBUTING.md +2 -0
- data/cookbooks/firewall/MAINTAINERS.md +19 -0
- data/cookbooks/firewall/README.md +339 -0
- data/cookbooks/firewall/attributes/default.rb +5 -0
- data/cookbooks/firewall/attributes/firewalld.rb +1 -0
- data/cookbooks/firewall/attributes/iptables.rb +17 -0
- data/cookbooks/firewall/attributes/ufw.rb +12 -0
- data/cookbooks/firewall/attributes/windows.rb +8 -0
- data/cookbooks/firewall/libraries/helpers.rb +100 -0
- data/cookbooks/firewall/libraries/helpers_firewalld.rb +116 -0
- data/cookbooks/firewall/libraries/helpers_iptables.rb +112 -0
- data/cookbooks/firewall/libraries/helpers_ufw.rb +135 -0
- data/cookbooks/firewall/libraries/helpers_windows.rb +130 -0
- data/cookbooks/firewall/libraries/matchers.rb +30 -0
- data/cookbooks/firewall/libraries/provider_firewall_firewalld.rb +179 -0
- data/cookbooks/firewall/libraries/provider_firewall_iptables.rb +171 -0
- data/cookbooks/firewall/libraries/provider_firewall_iptables_ubuntu.rb +196 -0
- data/cookbooks/firewall/libraries/provider_firewall_iptables_ubuntu1404.rb +196 -0
- data/cookbooks/firewall/libraries/provider_firewall_rule.rb +34 -0
- data/cookbooks/firewall/libraries/provider_firewall_ufw.rb +138 -0
- data/cookbooks/firewall/libraries/provider_firewall_windows.rb +126 -0
- data/cookbooks/firewall/libraries/resource_firewall.rb +26 -0
- data/cookbooks/firewall/libraries/resource_firewall_rule.rb +52 -0
- data/cookbooks/firewall/metadata.json +1 -0
- data/cookbooks/firewall/recipes/default.rb +80 -0
- data/cookbooks/firewall/recipes/disable_firewall.rb +23 -0
- data/cookbooks/firewall/templates/default/ufw/default.erb +13 -0
- data/cookbooks/mu-firewall/metadata.rb +1 -1
- data/cookbooks/mu-master/recipes/default.rb +3 -1
- data/cookbooks/mu-master/recipes/init.rb +3 -1
- data/cookbooks/mu-master/templates/default/mu.rc.erb +3 -0
- data/cookbooks/mu-tools/recipes/apply_security.rb +2 -2
- data/cookbooks/mu-tools/recipes/aws_api.rb +1 -1
- data/cookbooks/mu-tools/recipes/base_repositories.rb +1 -1
- data/cookbooks/mu-tools/recipes/clamav.rb +1 -1
- data/cookbooks/mu-tools/recipes/cloudinit.rb +1 -1
- data/cookbooks/mu-tools/recipes/disable-requiretty.rb +2 -2
- data/cookbooks/mu-tools/recipes/eks.rb +1 -1
- data/cookbooks/mu-tools/recipes/gcloud.rb +1 -1
- data/cookbooks/mu-tools/recipes/nrpe.rb +1 -1
- data/cookbooks/mu-tools/recipes/rsyslog.rb +2 -2
- data/cookbooks/mu-tools/recipes/set_local_fw.rb +37 -28
- data/environments/dev.json +1 -1
- data/environments/prod.json +1 -1
- data/modules/mu/cleanup.rb +4 -0
- data/modules/mu/clouds/aws/container_cluster.rb +3 -0
- data/modules/mu/clouds/aws/role.rb +14 -2
- data/modules/mu/clouds/aws/userdata/linux.erb +2 -3
- data/modules/mu/clouds/aws.rb +30 -14
- data/modules/mu.rb +4 -0
- 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
|