diff options
author | Tim Smith <tsmith@chef.io> | 2020-07-15 14:11:18 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-15 14:11:18 -0700 |
commit | e01a00934565c40b053145b91bf97dc96e18d5d7 (patch) | |
tree | 29e548f68ad82e5980768210e933ceebe932d0c4 | |
parent | 6462439e3a7583b4a3a60c13456e5fdf8188bd04 (diff) | |
parent | 9bdf2b344af5b98905f8bc5b4744cc9d2830b08d (diff) | |
download | chef-e01a00934565c40b053145b91bf97dc96e18d5d7.tar.gz |
Merge pull request #9994 from chef-davin/windows_firewall_profile
Create the windows_firewall_profile resource for use with enabling/disabling and configuring the Windows firewall Domain, Private, and Public profiles
-rw-r--r-- | cspell.json | 3 | ||||
-rw-r--r-- | lib/chef/provider/package/snap.rb | 2 | ||||
-rw-r--r-- | lib/chef/resource/windows_firewall_profile.rb | 197 | ||||
-rw-r--r-- | lib/chef/resources.rb | 1 | ||||
-rw-r--r-- | spec/unit/resource/windows_firewall_profile_spec.rb | 77 |
5 files changed, 279 insertions, 1 deletions
diff --git a/cspell.json b/cspell.json index c900adbc84..30f26af8b0 100644 --- a/cspell.json +++ b/cspell.json @@ -302,6 +302,7 @@ "cpio", "cpjones", "CPPFLAGS", + "Crae", "Cragun", "CREAT", "createhomedir", @@ -1988,6 +1989,8 @@ "unformatter", "Unformatter", "unhold", + "unicast", + "Unicast", "unignored", "uninst", "unintuitive", diff --git a/lib/chef/provider/package/snap.rb b/lib/chef/provider/package/snap.rb index 921227ad06..77b33d5c7f 100644 --- a/lib/chef/provider/package/snap.rb +++ b/lib/chef/provider/package/snap.rb @@ -137,7 +137,7 @@ class Chef # while it is expected to allow clients to connect using https over # a tcp socket, at this point only a unix socket is supported. the - # socket is /run/snapd.socket note - unix socket is not defined on + # socket is /run/snapd.socket note - UNIXSocket is not defined on # windows systems if defined?(::UNIXSocket) UNIXSocket.open("/run/snapd.socket") do |socket| diff --git a/lib/chef/resource/windows_firewall_profile.rb b/lib/chef/resource/windows_firewall_profile.rb new file mode 100644 index 0000000000..c17572a0bd --- /dev/null +++ b/lib/chef/resource/windows_firewall_profile.rb @@ -0,0 +1,197 @@ +# +# Author:: John McCrae (<jmccrae@chef.io>) +# Author:: Davin Taddeo (<davin@chef.io>) +# Copyright:: Copyright (c) Chef Software Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class Chef + class Resource + class WindowsFirewallProfile < Chef::Resource + provides :windows_firewall_profile + description "Use the **windows_firewall_profile** resource to enable, disable, and configure the Windows firewall." + introduced "16.3" + + examples <<~DOC + **Enable and Configure the Private Profile of the Windows Profile**: + + ```ruby + windows_firewall_profile 'Private' do + default_inbound_action 'Block' + default_outbound_action 'Allow' + allow_inbound_rules 'True' + display_notification 'False' + action :enable + end + ``` + + **Enable and Configure the Public Profile of the Windows Firewall**: + + ```ruby + windows_firewall_profile 'Public' do + default_inbound_action 'Block' + default_outbound_action 'Allow' + allow_inbound_rules 'False' + display_notification 'False' + action :enable + end + ``` + + **Disable the Domain Profile of the Windows Firewall**: + + ```ruby + windows_firewall_profile 'Disable the Domain Profile of the Windows Firewall' do + profile 'Domain' + action :disable + end + ``` + DOC + + unified_mode true + + property :profile, String, + name_property: true, + equal_to: %w{ Domain Public Private }, + description: "Set the Windows Profile being configured" + + property :default_inbound_action, [String, nil], + equal_to: %w{ Allow Block NotConfigured }, + description: "Set the default policy for inbound network traffic" + + property :default_outbound_action, [String, nil], + equal_to: %w{ Allow Block NotConfigured }, + description: "Set the default policy for outbound network traffic" + + property :allow_inbound_rules, [true, false, String], equal_to: [true, false, "NotConfigured"], description: "Allow users to set inbound firewall rules" + property :allow_local_firewall_rules, [true, false, String], equal_to: [true, false, "NotConfigured"], description: "Merges inbound firewall rules into the policy" + property :allow_local_ipsec_rules, [true, false, String], equal_to: [true, false, "NotConfigured"], description: "Allow users to manage local connection security rules" + property :allow_user_apps, [true, false, String], equal_to: [true, false, "NotConfigured"], description: "Allow user applications to manage firewall" + property :allow_user_ports, [true, false, String], equal_to: [true, false, "NotConfigured"], description: "Allow users to manage firewall port rules" + property :allow_unicast_response, [true, false, String], equal_to: [true, false, "NotConfigured"], description: "Allow unicast responses to multicast and broadcast messages" + property :display_notification, [true, false, String], equal_to: [true, false, "NotConfigured"], description: "Display a notification when firewall blocks certain activity" + + load_current_value do |desired| + ps_get_net_fw_profile = load_firewall_state(desired.profile) + output = powershell_out(ps_get_net_fw_profile) + if output.stdout.empty? + current_value_does_not_exist! + else + state = Chef::JSONCompat.from_json(output.stdout) + end + + default_inbound_action state["default_inbound_action"] + default_outbound_action state["default_outbound_action"] + allow_inbound_rules convert_to_ruby(state["allow_inbound_rules"]) + allow_local_firewall_rules convert_to_ruby(state["allow_local_firewall_rules"]) + allow_local_ipsec_rules convert_to_ruby(state["allow_local_ipsec_rules"]) + allow_user_apps convert_to_ruby(state["allow_user_apps"]) + allow_user_ports convert_to_ruby(state["allow_user_ports"]) + allow_unicast_response convert_to_ruby(state["allow_unicast_response"]) + display_notification convert_to_ruby(state["display_notification"]) + end + + def convert_to_ruby(obj) + if obj.to_s.downcase == "true" + true + elsif obj.to_s.downcase == "false" + false + elsif obj.to_s.downcase == "notconfigured" + "NotConfigured" + end + end + + def convert_to_powershell(obj) + if obj.to_s.downcase == "true" + "True" + elsif obj.to_s.downcase == "false" + "False" + elsif obj.to_s.downcase == "notconfigured" + "NotConfigured" + end + end + + action :enable do + converge_if_changed :default_inbound_action, :default_outbound_action, :allow_inbound_rules, :allow_local_firewall_rules, + :allow_local_ipsec_rules, :allow_user_apps, :allow_user_ports, :allow_unicast_response, :display_notification do + fw_cmd = firewall_command(new_resource.profile) + powershell_exec!(fw_cmd) + end + unless firewall_enabled?(new_resource.profile) + converge_by "Enable the #{new_resource.profile} Firewall Profile" do + cmd = "Set-NetFirewallProfile -Profile #{new_resource.profile} -Enabled \"True\"" + powershell_out!(cmd) + end + end + end + + action :disable do + if firewall_enabled?(new_resource.profile) + converge_by "Disable the #{new_resource.profile} Firewall Profile" do + cmd = "Set-NetFirewallProfile -Profile #{new_resource.profile} -Enabled \"False\"" + powershell_out!(cmd) + end + end + end + + action_class do + def firewall_command(fw_profile) + cmd = "Set-NetFirewallProfile -Profile \"#{fw_profile}\"" + cmd << " -DefaultInboundAction \"#{new_resource.default_inbound_action}\"" unless new_resource.default_inbound_action.nil? + cmd << " -DefaultOutboundAction \"#{new_resource.default_outbound_action}\"" unless new_resource.default_outbound_action.nil? + cmd << " -AllowInboundRules \"#{convert_to_powershell(new_resource.allow_inbound_rules)}\"" unless new_resource.allow_inbound_rules.nil? + cmd << " -AllowLocalFirewallRules \"#{convert_to_powershell(new_resource.allow_local_firewall_rules)}\"" unless new_resource.allow_local_firewall_rules.nil? + cmd << " -AllowLocalIPsecRules \"#{convert_to_powershell(new_resource.allow_local_ipsec_rules)}\"" unless new_resource.allow_local_ipsec_rules.nil? + cmd << " -AllowUserApps \"#{convert_to_powershell(new_resource.allow_user_apps)}\"" unless new_resource.allow_user_apps.nil? + cmd << " -AllowUserPorts \"#{convert_to_powershell(new_resource.allow_user_ports)}\"" unless new_resource.allow_user_ports.nil? + cmd << " -AllowUnicastResponseToMulticast \"#{convert_to_powershell(new_resource.allow_unicast_response)}\"" unless new_resource.allow_unicast_response.nil? + cmd << " -NotifyOnListen \"#{convert_to_powershell(new_resource.display_notification)}\"" unless new_resource.display_notification.nil? + cmd + end + + def load_firewall_state(profile_name) + <<-EOH + Remove-TypeData System.Array # workaround for PS bug here: https://bit.ly/2SRMQ8M + $#{profile_name} = Get-NetFirewallProfile -Profile #{profile_name} + ([PSCustomObject]@{ + default_inbound_action = $#{profile_name}.DefaultInboundAction.ToString() + default_outbound_action = $#{profile_name}.DefaultOutboundAction.ToString() + allow_inbound_rules = $#{profile_name}.AllowInboundRules.ToString() + allow_local_firewall_rules = $#{profile_name}.AllowLocalFirewallRules.ToString() + allow_local_ipsec_rules = $#{profile_name}.AllowLocalIPsecRules.ToString() + allow_user_apps = $#{profile_name}.AllowUserApps.ToString() + allow_user_ports = $#{profile_name}.AllowUserPorts.ToString() + allow_unicast_response = $#{profile_name}.AllowUnicastResponseToMulticast.ToString() + display_notification = $#{profile_name}.NotifyOnListen.ToString() + }) | ConvertTo-Json + EOH + end + + def firewall_enabled?(profile_name) + cmd = <<~CODE + $#{profile_name} = Get-NetFirewallProfile -Profile #{profile_name} + if ($#{profile_name}.Enabled) { + return $true + } else {return $false} + CODE + firewall_status = powershell_out(cmd).stdout + if firewall_status =~ /True/ + true + elsif firewall_status =~ /False/ + false + end + end + end + end + end +end diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb index 619ffe6f6f..a47b96cb5c 100644 --- a/lib/chef/resources.rb +++ b/lib/chef/resources.rb @@ -153,6 +153,7 @@ require_relative "resource/windows_dns_zone" require_relative "resource/windows_feature" require_relative "resource/windows_feature_dism" require_relative "resource/windows_feature_powershell" +require_relative "resource/windows_firewall_profile" require_relative "resource/windows_firewall_rule" require_relative "resource/windows_font" require_relative "resource/windows_pagefile" diff --git a/spec/unit/resource/windows_firewall_profile_spec.rb b/spec/unit/resource/windows_firewall_profile_spec.rb new file mode 100644 index 0000000000..086ac78998 --- /dev/null +++ b/spec/unit/resource/windows_firewall_profile_spec.rb @@ -0,0 +1,77 @@ +# +# Copyright:: Copyright (c) Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "spec_helper" + +describe Chef::Resource::WindowsFirewallProfile do + let(:resource) { Chef::Resource::WindowsFirewallProfile.new("fakey_fakerton") } + + it "sets resource name as :windows_firewall_profile" do + expect(resource.resource_name).to eql(:windows_firewall_profile) + end + + %w{ Domain Private Public }.each do |this_profile| + it "The profile accepts values for the \"#{this_profile}\" Profile" do + expect { resource.profile this_profile }.not_to raise_error + end + end + + it "the profile property does not accept bad profile names" do + expect { resource.profile "Special" }.to raise_error(Chef::Exceptions::ValidationFailed) + end + + it "the resource's default_inbound_action property only strings Block, Allow, or NotConfigured" do + expect { resource.default_inbound_action "AllowSome" }.to raise_error(ArgumentError) + expect { resource.default_inbound_action "Block" }.not_to raise_error + end + it "the resource's default_outbound_action property only accepts strings Block, Allow, or NotConfigured" do + expect { resource.default_outbound_action "BlockMost" }.to raise_error(ArgumentError) + expect { resource.default_outbound_action "Allow" }.not_to raise_error + end + it "the resource's allow_inbound_rules property only accepts strings true, false, or NotConfigured" do + expect { resource.allow_inbound_rules "Yes" }.to raise_error(ArgumentError) + expect { resource.allow_inbound_rules true }.not_to raise_error + end + it "the resource's allow_local_firewall_rules property only accepts strings true, false, or NotConfigured" do + expect { resource.allow_local_firewall_rules "No" }.to raise_error(ArgumentError) + expect { resource.allow_local_firewall_rules false }.not_to raise_error + end + it "the resource's allow_local_ipsec_rules property only accepts strings true, false, or NotConfigured" do + expect { resource.allow_local_ipsec_rules "Yes" }.to raise_error(ArgumentError) + expect { resource.allow_local_ipsec_rules true }.not_to raise_error + end + it "the resource's allow_user_apps property only accepts strings true, false, or NotConfigured" do + expect { resource.allow_user_apps "No" }.to raise_error(ArgumentError) + expect { resource.allow_user_apps false }.not_to raise_error + end + it "the resource's allow_user_ports property only accepts strings true, false, or NotConfigured" do + expect { resource.allow_user_ports "Nope" }.to raise_error(ArgumentError) + expect { resource.allow_user_ports "NotConfigured" }.not_to raise_error + end + it "the resource's allow_unicast_response property only accepts strings true, false, or NotConfigured" do + expect { resource.allow_unicast_response "True" }.to raise_error(ArgumentError) + expect { resource.allow_unicast_response true }.not_to raise_error + end + it "the resource's display_notification property only accepts strings true, false, or NotConfigured" do + expect { resource.display_notification "False" }.to raise_error(ArgumentError) + expect { resource.display_notification false }.not_to raise_error + end + + it "sets the default action as :configure" do + expect(resource.action).to eql([:enable]) + end +end |