diff --git a/.rubocop.yml b/.rubocop.yml
index 31e8248..04985fb 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -517,3 +517,7 @@ Style/RedundantArgument:
Enabled: false
Style/SwapValues:
Enabled: false
+
+# Discard multi-line chains of blocks
+Style/MultilineBlockChain:
+ Enabled: false
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 35a6957..2dde85b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,18 @@
All notable changes to this project will be documented in this file.
+## Release 1.4.2 (2023-01-22)
+
+[Full Changelog](https://github.com/webalexeu/puppet-windows_firewall/compare/v1.4.1...v1.4.2)
+
+**Features**
+
+- Bug fixes and performance improvements
+
+**Bugfixes**
+
+**Known Issues**
+
## Release 1.4.1 (2022-12-14)
[Full Changelog](https://github.com/webalexeu/puppet-windows_firewall/compare/v1.4.0...v1.4.1)
diff --git a/REFERENCE.md b/REFERENCE.md
index 6838f18..80b19c9 100644
--- a/REFERENCE.md
+++ b/REFERENCE.md
@@ -104,16 +104,16 @@ Configures how CRL checking is enforced
The following parameters are available in the `windows_firewall_global` type.
-* [`name`](#name)
-* [`provider`](#provider)
+* [`name`](#-windows_firewall_global--name)
+* [`provider`](#-windows_firewall_global--provider)
-##### `name`
+##### `name`
namevar
Not used (reference only)
-##### `provider`
+##### `provider`
The specific backend to use for this `windows_firewall_global` resource. You will seldom need to specify this --- Puppet
will usually discover the appropriate provider for your platform.
@@ -128,7 +128,7 @@ The following properties are available in the `windows_firewall_group` type.
##### `enabled`
-Valid values: ``true``, ``false``
+Valid values: `true`, `false`
Whether the rule group is enabled (`true` or `false`)
@@ -138,16 +138,16 @@ Default value: `true`
The following parameters are available in the `windows_firewall_group` type.
-* [`name`](#name)
-* [`provider`](#provider)
+* [`name`](#-windows_firewall_group--name)
+* [`provider`](#-windows_firewall_group--provider)
-##### `name`
+##### `name`
namevar
Name of the rule group to enable/disable
-##### `provider`
+##### `provider`
The specific backend to use for this `windows_firewall_group` resource. You will seldom need to specify this --- Puppet
will usually discover the appropriate provider for your platform.
@@ -176,7 +176,7 @@ Specifies the localized, user-facing name of the firewall rule being created
##### `enabled`
-Valid values: ``true``, ``false``
+Valid values: `true`, `false`
This parameter specifies that the rule object is administratively enabled or administratively disabled (`true` or `false`)
@@ -276,16 +276,16 @@ Default value: `any`
The following parameters are available in the `windows_firewall_ipsec_rule` type.
-* [`name`](#name)
-* [`provider`](#provider)
+* [`name`](#-windows_firewall_ipsec_rule--name)
+* [`provider`](#-windows_firewall_ipsec_rule--provider)
-##### `name`
+##### `name`
namevar
Name of this rule
-##### `provider`
+##### `provider`
The specific backend to use for this `windows_firewall_ipsec_rule` resource. You will seldom need to specify this ---
Puppet will usually discover the appropriate provider for your platform.
@@ -348,7 +348,7 @@ Allow remote management of Windows Firewall
##### `state`
-Valid values: `on`, `off`, ``true``, ``false``
+Valid values: `on`, `off`, `true`, `false`
State of this firewall profile
@@ -362,16 +362,16 @@ Control stateful unicast response to multicast
The following parameters are available in the `windows_firewall_profile` type.
-* [`name`](#name)
-* [`provider`](#provider)
+* [`name`](#-windows_firewall_profile--name)
+* [`provider`](#-windows_firewall_profile--provider)
-##### `name`
+##### `name`
namevar
Name of the profile to work on
-##### `provider`
+##### `provider`
The specific backend to use for this `windows_firewall_profile` resource. You will seldom need to specify this ---
Puppet will usually discover the appropriate provider for your platform.
@@ -428,7 +428,7 @@ Default value: `block`
##### `enabled`
-Valid values: ``true``, ``false``
+Valid values: `true`, `false`
Whether the rule is enabled (`true` or `false`)
@@ -499,10 +499,12 @@ Default value: `any`
##### `protocol`
-Valid values: `tcp`, `udp`, `icmpv4`, `icmpv6`, `/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/`
+Valid values: `any`, `tcp`, `udp`, `icmpv4`, `icmpv6`, `/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/`
the protocol the rule targets
+Default value: `any`
+
##### `remote_address`
the remote IP the rule targets (hostname not allowed), use an array to pass more then one
@@ -537,16 +539,16 @@ Default value: `any`
The following parameters are available in the `windows_firewall_rule` type.
-* [`name`](#name)
-* [`provider`](#provider)
+* [`name`](#-windows_firewall_rule--name)
+* [`provider`](#-windows_firewall_rule--provider)
-##### `name`
+##### `name`
namevar
Name of this rule
-##### `provider`
+##### `provider`
The specific backend to use for this `windows_firewall_rule` resource. You will seldom need to specify this --- Puppet
will usually discover the appropriate provider for your platform.
diff --git a/lib/puppet/provider/windows_firewall_global/ruby.rb b/lib/puppet/provider/windows_firewall_global/ruby.rb
index 99e43b2..dd6002a 100644
--- a/lib/puppet/provider/windows_firewall_global/ruby.rb
+++ b/lib/puppet/provider/windows_firewall_global/ruby.rb
@@ -1,16 +1,16 @@
require 'puppet_x'
require_relative '../../../puppet_x/windows_firewall'
-Puppet::Type.type(:windows_firewall_global).provide(:windows_firewall_global, :parent => Puppet::Provider) do
- confine :osfamily => :windows
+Puppet::Type.type(:windows_firewall_global).provide(:windows_firewall_global, parent: Puppet::Provider) do
+ confine osfamily: :windows
mk_resource_methods
desc 'Windows Firewall global settings'
- commands :cmd => 'netsh'
+ commands cmd: 'netsh'
def self.prefetch(resources)
instances.each do |prov|
- if resource = resources[prov.name]
+ if (resource = resources[prov.name])
resource.provider = prov
end
end
@@ -28,7 +28,7 @@ def create; end
def destroy; end
def self.instances
- PuppetX::WindowsFirewall.globals(command(:cmd)).collect { |hash| new(hash) }
+ PuppetX::WindowsFirewall.globals(command(:cmd)).map { |hash| new(hash) }
end
def flush
@@ -41,9 +41,8 @@ def flush
:boottimerulecategory,
:firewallrulecategory,
:stealthrulecategory,
- :consecrulecategory
- ].include?(property.name)
- }.each { |property|
+ :consecrulecategory].include?(property.name)
+ }.each do |property|
property_name = PuppetX::WindowsFirewall.global_argument_lookup(property.name)
property_value = property.value.instance_of?(Array) ? property.value.join(',') : property.value
@@ -53,7 +52,6 @@ def flush
cmd = "#{command(:cmd)} advfirewall set global #{arg}"
output = execute(cmd).to_s
Puppet.debug("...#{output}")
- }
+ end
end
-
end
diff --git a/lib/puppet/provider/windows_firewall_group/ruby.rb b/lib/puppet/provider/windows_firewall_group/ruby.rb
index cbdb3a7..99258ed 100644
--- a/lib/puppet/provider/windows_firewall_group/ruby.rb
+++ b/lib/puppet/provider/windows_firewall_group/ruby.rb
@@ -1,16 +1,16 @@
require 'puppet_x'
require_relative '../../../puppet_x/windows_firewall'
-Puppet::Type.type(:windows_firewall_group).provide(:windows_firewall_group, :parent => Puppet::Provider) do
- confine :osfamily => :windows
+Puppet::Type.type(:windows_firewall_group).provide(:windows_firewall_group, parent: Puppet::Provider) do
+ confine osfamily: :windows
mk_resource_methods
desc 'Windows Firewall group'
- commands :cmd => 'netsh'
+ commands cmd: 'netsh'
def self.prefetch(resources)
instances.each do |prov|
- if resource = resources[prov.name]
+ if (resource = resources[prov.name])
resource.provider = prov
end
end
@@ -28,7 +28,7 @@ def create; end
def destroy; end
def self.instances
- PuppetX::WindowsFirewall.groups.collect { |hash| new(hash) }
+ PuppetX::WindowsFirewall.groups.map { |hash| new(hash) }
end
def flush
@@ -37,12 +37,11 @@ def flush
# to inspect @resource instead
# careful its a label not a boolean...
- netsh_enabled = (@resource[:enabled] == :true)? 'yes': 'no'
+ netsh_enabled = (@resource[:enabled] == :true) ? 'yes' : 'no'
Puppet.notice("(windows_firewall) group '#{@resource[:name]}' enabled: #{@resource[:enabled]}")
cmd = "#{command(:cmd)} advfirewall firewall set rule group=\"#{@resource[:name]}\" new enable=\"#{netsh_enabled}\""
output = execute(cmd).to_s
Puppet.debug("...#{output}")
end
-
end
diff --git a/lib/puppet/provider/windows_firewall_ipsec_rule/ruby.rb b/lib/puppet/provider/windows_firewall_ipsec_rule/ruby.rb
index c5f68b2..c79b357 100644
--- a/lib/puppet/provider/windows_firewall_ipsec_rule/ruby.rb
+++ b/lib/puppet/provider/windows_firewall_ipsec_rule/ruby.rb
@@ -1,14 +1,14 @@
require 'puppet_x'
require_relative '../../../puppet_x/windows_firewall_ipsec'
-Puppet::Type.type(:windows_firewall_ipsec_rule).provide(:windows_firewall_ipsec_rule, :parent => Puppet::Provider) do
- confine :osfamily => :windows
+Puppet::Type.type(:windows_firewall_ipsec_rule).provide(:windows_firewall_ipsec_rule, parent: Puppet::Provider) do
+ confine osfamily: :windows
mk_resource_methods
desc 'Windows Firewall'
def self.prefetch(resources)
instances.each do |prov|
- if resource = resources[prov.name]
+ if (resource = resources[prov.name])
resource.provider = prov
end
end
@@ -27,16 +27,14 @@ def destroy
end
def self.instances
- PuppetX::WindowsFirewallIPSec.rules.collect { |hash| new(hash) }
+ PuppetX::WindowsFirewallIPSec.rules.map { |hash| new(hash) }
end
def flush
# Update rule
# Only if IS value ensure == SHOULD value ensure
# @property_hash contains the IS values (thanks Gary!). For new rules there is no IS, there is only the SHOULD
- if @property_hash[:ensure] == @resource[:ensure]
- PuppetX::WindowsFirewallIPSec.update_rule @resource
- end
+ return unless @property_hash[:ensure] == @resource[:ensure]
+ PuppetX::WindowsFirewallIPSec.update_rule @resource
end
-
end
diff --git a/lib/puppet/provider/windows_firewall_profile/ruby.rb b/lib/puppet/provider/windows_firewall_profile/ruby.rb
index 372c077..6a9984e 100644
--- a/lib/puppet/provider/windows_firewall_profile/ruby.rb
+++ b/lib/puppet/provider/windows_firewall_profile/ruby.rb
@@ -1,16 +1,16 @@
require 'puppet_x'
require_relative '../../../puppet_x/windows_firewall'
-Puppet::Type.type(:windows_firewall_profile).provide(:windows_firewall_profile, :parent => Puppet::Provider) do
- confine :osfamily => :windows
+Puppet::Type.type(:windows_firewall_profile).provide(:windows_firewall_profile, parent: Puppet::Provider) do
+ confine osfamily: :windows
mk_resource_methods
desc 'Windows Firewall profile'
- commands :cmd => 'netsh'
+ commands cmd: 'netsh'
def self.prefetch(resources)
instances.each do |prov|
- if resource = resources[prov.name]
+ if (resource = resources[prov.name])
resource.provider = prov
end
end
@@ -27,16 +27,15 @@ def create; end
# all work done in `flush()` method
def destroy; end
-
def self.instances
- PuppetX::WindowsFirewall.profiles(command(:cmd)).collect { |hash| new(hash) }
+ PuppetX::WindowsFirewall.profiles(command(:cmd)).map { |hash| new(hash) }
end
def flush
# @property_hash contains the `IS` values (thanks Gary!)... For new rules there is no `IS`, there is only the
# `SHOULD`. The setter methods from `mk_resource_methods` (or manually created) won't be called either. You have
# to inspect @resource instead
- @resource.properties.each { |property|
+ @resource.properties.each do |property|
property_name = PuppetX::WindowsFirewall.profile_argument_lookup(property.name)
property_value = property.value
@@ -45,7 +44,6 @@ def flush
cmd = "#{command(:cmd)} advfirewall set #{@resource[:name]}profile #{arg}"
output = execute(cmd).to_s
Puppet.debug("...#{output}")
- }
+ end
end
-
end
diff --git a/lib/puppet/provider/windows_firewall_rule/ruby.rb b/lib/puppet/provider/windows_firewall_rule/ruby.rb
index 792e013..75609d1 100644
--- a/lib/puppet/provider/windows_firewall_rule/ruby.rb
+++ b/lib/puppet/provider/windows_firewall_rule/ruby.rb
@@ -1,14 +1,14 @@
require 'puppet_x'
require_relative '../../../puppet_x/windows_firewall'
-Puppet::Type.type(:windows_firewall_rule).provide(:windows_firewall_rule, :parent => Puppet::Provider) do
- confine :osfamily => :windows
+Puppet::Type.type(:windows_firewall_rule).provide(:windows_firewall_rule, parent: Puppet::Provider) do
+ confine osfamily: :windows
mk_resource_methods
desc 'Windows Firewall'
def self.prefetch(resources)
instances.each do |prov|
- if resource = resources[prov.name]
+ if (resource = resources[prov.name])
resource.provider = prov
end
end
@@ -27,16 +27,14 @@ def destroy
end
def self.instances
- PuppetX::WindowsFirewall.rules.collect { |hash| new(hash) }
+ PuppetX::WindowsFirewall.rules.map { |hash| new(hash) }
end
def flush
# Update rule
# Only if IS value ensure == SHOULD value ensure
# @property_hash contains the IS values (thanks Gary!). For new rules there is no IS, there is only the SHOULD
- if @property_hash[:ensure] == @resource[:ensure]
- PuppetX::WindowsFirewall.update_rule @resource
- end
+ return unless @property_hash[:ensure] == @resource[:ensure]
+ PuppetX::WindowsFirewall.update_rule @resource
end
-
end
diff --git a/lib/puppet/type/windows_firewall_global.rb b/lib/puppet/type/windows_firewall_global.rb
index 49900c0..95ce729 100644
--- a/lib/puppet/type/windows_firewall_global.rb
+++ b/lib/puppet/type/windows_firewall_global.rb
@@ -14,7 +14,7 @@
newproperty(:strongcrlcheck) do
desc 'Configures how CRL checking is enforced'
validate do |value|
- if ! [0,1,2].include? value.to_i
+ unless [0, 1, 2].include? value.to_i
raise('Invalid value, allowed: 0,1,2')
end
end
@@ -25,13 +25,13 @@
validate do |value|
value = value.to_i
- if ! (value >= 5 && value <= 60)
- raise("Invalid value, allowed: 0,1,2")
+ unless value >= 5 && value <= 60
+ raise('Invalid value, allowed: 0,1,2')
end
end
end
- newproperty(:defaultexemptions, :array_matching => :all) do
+ newproperty(:defaultexemptions, array_matching: :all) do
desc 'Configures the default IPsec exemptions. Default is to exempt IPv6 neighbordiscovery protocol and DHCP from IPsec'
newvalues(:none, :neighbordiscovery, :icmp, :dhcp, :notconfigured)
@@ -41,7 +41,6 @@ def insync?(is)
# Element-wise comparison - http://ruby-doc.org/core-2.5.1/Array.html
(should.map { |e| e.to_s }.sort <=> is.sort) == 0
end
-
end
newproperty(:ipsecthroughnat) do
@@ -59,14 +58,14 @@ def insync?(is)
newproperty(:authzusergrptransport) do
desc 'Authz user group transport'
- validate do |value|
+ validate do |_value|
raise('property is read-only')
end
end
newproperty(:authzcomputergrptransport) do
desc 'Authz computer transport'
- validate do |value|
+ validate do |_value|
raise('property is read-only')
end
end
@@ -96,30 +95,29 @@ def insync?(is)
newproperty(:boottimerulecategory) do
desc 'Boot time rule category'
- validate do |value|
+ validate do |_value|
raise('property is read-only')
end
end
newproperty(:firewallrulecategory) do
desc 'Firewall rule category'
- validate do |value|
+ validate do |_value|
raise('property is read-only')
end
end
newproperty(:stealthrulecategory) do
desc 'Stealth rule category'
- validate do |value|
+ validate do |_value|
raise('property is read-only')
end
end
newproperty(:consecrulecategory) do
- desc'"con sec rule category'
- validate do |value|
+ desc '"con sec rule category'
+ validate do |_value|
raise('property is read-only')
end
end
-
end
diff --git a/lib/puppet/type/windows_firewall_group.rb b/lib/puppet/type/windows_firewall_group.rb
index 936e433..c373118 100644
--- a/lib/puppet/type/windows_firewall_group.rb
+++ b/lib/puppet/type/windows_firewall_group.rb
@@ -12,7 +12,7 @@
end
newproperty(:enabled) do
- desc "Whether the rule group is enabled (`true` or `false`)"
+ desc 'Whether the rule group is enabled (`true` or `false`)'
newvalues(:true, :false)
defaultto :true
@@ -25,5 +25,4 @@ def insync?(is)
is == should
end
end
-
end
diff --git a/lib/puppet/type/windows_firewall_ipsec_rule.rb b/lib/puppet/type/windows_firewall_ipsec_rule.rb
index 02ad8ba..3cdd17d 100644
--- a/lib/puppet/type/windows_firewall_ipsec_rule.rb
+++ b/lib/puppet/type/windows_firewall_ipsec_rule.rb
@@ -4,7 +4,7 @@
@doc = 'Manage Windows Firewall with Puppet'
ensurable do
- desc "How to ensure this firewall rule (`present` or `absent`)"
+ desc 'How to ensure this firewall rule (`present` or `absent`)'
defaultto :present
defaultvalues
@@ -15,7 +15,6 @@
def insync?(is)
(is == :present && should == :present) || (is == :absent && should == :absent)
end
-
end
# Resource validation
@@ -27,7 +26,7 @@ def insync?(is)
end
newproperty(:enabled) do
- desc "This parameter specifies that the rule object is administratively enabled or administratively disabled (`true` or `false`)"
+ desc 'This parameter specifies that the rule object is administratively enabled or administratively disabled (`true` or `false`)'
newvalues(:true, :false)
defaultto :true
end
@@ -36,7 +35,7 @@ def insync?(is)
desc 'Specifies the localized, user-facing name of the firewall rule being created'
defaultto { @resource[:name] }
validate do |value|
- unless value.kind_of?(String)
+ unless value.is_a?(String)
raise "Invalid value '#{value}'. Should be a string"
end
end
@@ -46,13 +45,13 @@ def insync?(is)
desc 'This parameter provides information about the firewall rule'
defaultto ''
validate do |value|
- unless value.kind_of?(String)
+ unless value.is_a?(String)
raise "Invalid value '#{value}'. Should be a string"
end
end
end
- newproperty(:profile, :array_matching=>:all) do
+ newproperty(:profile, array_matching: :all) do
desc 'Specifies one or more profiles to which the rule is assigned'
newvalues(:domain, :private, :public, :any)
@@ -65,12 +64,12 @@ def insync?(is)
newproperty(:display_group) do
desc 'This parameter specifies the source string for the DisplayGroup parameter (read-only)'
- validate do |value|
+ validate do |_value|
raise 'grouping is readonly: https://social.technet.microsoft.com/Forums/office/en-US/669a8eaf-13d1-4010-b2ac-30c800c4b152/2008r2-firewall-add-rules-to-group-create-new-group'
end
end
- newproperty(:local_address, :array_matching=>:all) do
+ newproperty(:local_address, array_matching: :all) do
desc 'Specifies that network packets with matching IP addresses match this rule (hostname not allowed), use an array to pass more then one'
# Checking that old syntax using comma is not used
@@ -93,7 +92,7 @@ def insync?(is)
defaultto 'any'
end
- newproperty(:remote_address, :array_matching=>:all) do
+ newproperty(:remote_address, array_matching: :all) do
desc 'Specifies that network packets with matching IP addresses match this rule (hostname not allowed), use an array to pass more then one'
# Checking that old syntax using comma is not used
@@ -119,14 +118,14 @@ def insync?(is)
newproperty(:protocol) do
desc 'This parameter specifies the protocol for an IPsec rule'
# Also accept 0-255 :/
- newvalues(:tcp, :udp, :icmpv4, :icmpv6, /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/)
+ newvalues(:tcp, :udp, :icmpv4, :icmpv6, %r{^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$})
isrequired
def insync?(is)
is.to_s == should.to_s
end
end
- newproperty(:local_port, :array_matching=>:all) do
+ newproperty(:local_port, array_matching: :all) do
desc 'Specifies that network packets with matching IP port numbers match this rule, use an array to pass more then one'
# Checking that old syntax using comma is not used
@@ -149,7 +148,7 @@ def insync?(is)
defaultto 'any'
end
- newproperty(:remote_port, :array_matching=>:all) do
+ newproperty(:remote_port, array_matching: :all) do
desc 'This parameter value is the second end point of an IPsec rule, use an array to pass more then one'
# Checking that old syntax using comma is not used
@@ -179,7 +178,7 @@ def insync?(is)
defaultto :transport
end
- newproperty(:interface_type, :array_matching=>:all) do
+ newproperty(:interface_type, array_matching: :all) do
desc 'Specifies that only network connections made through the indicated interface types are subject to the requirements of this rule'
newvalues(:any, :wired, :wireless, :remote_access)
@@ -222,7 +221,7 @@ def insync?(is)
newvalues(:none, :default, :userkerberos)
defaultto do
- if @resource[:inbound_security] == :require|| @resource[:inbound_security] == :request || @resource[:outbound_security] == :require || @resource[:outbound_security] == :request
+ if @resource[:inbound_security] == :require || @resource[:inbound_security] == :request || @resource[:outbound_security] == :require || @resource[:outbound_security] == :request
:default
else
:none
@@ -234,8 +233,7 @@ def insync?(is)
desc 'Name of this rule'
isnamevar
validate do |value|
- raise "it is not allowed to have a rule called 'any'" if value.downcase == 'any'
+ raise "it is not allowed to have a rule called 'any'" if value.casecmp('any').zero?
end
end
-
end
diff --git a/lib/puppet/type/windows_firewall_profile.rb b/lib/puppet/type/windows_firewall_profile.rb
index 2d62699..2fd7da9 100644
--- a/lib/puppet/type/windows_firewall_profile.rb
+++ b/lib/puppet/type/windows_firewall_profile.rb
@@ -17,13 +17,13 @@
desc 'State of this firewall profile'
newvalues(:on, :off, true, false)
munge do |value|
- if value == true
- munged = :on
- elsif value == false
- munged = :off
- else
- munged = value
- end
+ munged = if value == true
+ :on
+ elsif value == false
+ :off
+ else
+ value
+ end
munged
end
@@ -39,15 +39,15 @@
newproperty(:localfirewallrules) do
desc 'Merge local firewall rules with Group Policy rules. Valid when configuring a Group Policy store'
newvalues(:enable, :disable, :notconfigured)
- validate do |value|
+ validate do |_value|
raise("property is read-only because I'm not sure how to read the current value - pls open a ticket with info if you want this")
end
end
newproperty(:localconsecrules) do
- desc "Merge local connection security rules with Group Policy rules. Valid when configuring a Group Policy store"
+ desc 'Merge local connection security rules with Group Policy rules. Valid when configuring a Group Policy store'
newvalues(:enable, :disable, :notconfigured)
- validate do |value|
+ validate do |_value|
raise("property is read-only because I'm not sure how to read the current value - pls open a ticket with info if you want this")
end
end
diff --git a/lib/puppet/type/windows_firewall_rule.rb b/lib/puppet/type/windows_firewall_rule.rb
index 61e700c..7f1ba5a 100644
--- a/lib/puppet/type/windows_firewall_rule.rb
+++ b/lib/puppet/type/windows_firewall_rule.rb
@@ -4,7 +4,7 @@
@doc = 'Manage Windows Firewall with Puppet'
ensurable do
- desc "How to ensure this firewall rule (`present` or `absent`)"
+ desc 'How to ensure this firewall rule (`present` or `absent`)'
defaultto :present
defaultvalues
@@ -15,7 +15,6 @@
def insync?(is)
(is == :present && should == :present) || (is == :absent && should == :absent)
end
-
end
# Resource validation
@@ -29,7 +28,7 @@ def insync?(is)
end
newproperty(:enabled) do
- desc "Whether the rule is enabled (`true` or `false`)"
+ desc 'Whether the rule is enabled (`true` or `false`)'
newvalues(:true, :false)
defaultto :true
end
@@ -38,7 +37,7 @@ def insync?(is)
desc 'Display name for this rule'
defaultto { @resource[:name] }
validate do |value|
- unless value.kind_of?(String)
+ unless value.is_a?(String)
raise "Invalid value '#{value}'. Should be a string"
end
end
@@ -48,18 +47,18 @@ def insync?(is)
desc 'Description of this rule'
defaultto ''
validate do |value|
- unless value.kind_of?(String)
+ unless value.is_a?(String)
raise "Invalid value '#{value}'. Should be a string"
end
end
end
newproperty(:direction) do
- desc "Direction the rule applies to (`inbound`/`outbound`)"
+ desc 'Direction the rule applies to (`inbound`/`outbound`)'
newvalues(:inbound, :outbound)
isrequired
validate do |value|
- unless value.kind_of?(String)
+ unless value.is_a?(String)
raise "Invalid value '#{value}'. Should be a string"
end
unless ['inbound', 'outbound'].include?(value)
@@ -68,7 +67,7 @@ def insync?(is)
end
end
- newproperty(:profile, :array_matching=>:all) do
+ newproperty(:profile, array_matching: :all) do
desc 'Which profile(s) this rule belongs to, use an array to pass more then one'
newvalues(:domain, :private, :public, :any)
@@ -82,12 +81,12 @@ def insync?(is)
newproperty(:display_group) do
desc 'group that the rule belongs to (read-only)'
- validate do |value|
+ validate do |_value|
raise 'grouping is readonly: https://social.technet.microsoft.com/Forums/office/en-US/669a8eaf-13d1-4010-b2ac-30c800c4b152/2008r2-firewall-add-rules-to-group-create-new-group'
end
end
- newproperty(:local_address, :array_matching=>:all) do
+ newproperty(:local_address, array_matching: :all) do
desc 'the local IP the rule targets (hostname not allowed), use an array to pass more then one'
# Checking that old syntax using comma is not used
@@ -110,7 +109,7 @@ def insync?(is)
defaultto 'any'
end
- newproperty(:remote_address, :array_matching=>:all) do
+ newproperty(:remote_address, array_matching: :all) do
desc 'the remote IP the rule targets (hostname not allowed), use an array to pass more then one'
# Checking that old syntax using comma is not used
@@ -137,7 +136,7 @@ def insync?(is)
desc 'the protocol the rule targets'
# Also accept 0-255 :/
- newvalues(:any, :tcp, :udp, :icmpv4, :icmpv6, /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/)
+ newvalues(:any, :tcp, :udp, :icmpv4, :icmpv6, %r{^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$})
isrequired
def insync?(is)
is.to_s == should.to_s
@@ -163,7 +162,7 @@ def insync?(is)
end
end
- newproperty(:local_port, :array_matching=>:all) do
+ newproperty(:local_port, array_matching: :all) do
desc 'the local port the rule targets, use an array to pass more then one'
# Checking that old syntax using comma is not used
@@ -185,7 +184,7 @@ def insync?(is)
defaultto do
# Default is different when icmp_type is used
- if @resource[:icmp_type] != :any and !@resource[:icmp_type].nil?
+ if (@resource[:icmp_type] != :any) && !@resource[:icmp_type].nil?
'rpc'
else
'any'
@@ -193,7 +192,7 @@ def insync?(is)
end
end
- newproperty(:remote_port, :array_matching=>:all) do
+ newproperty(:remote_port, array_matching: :all) do
desc 'the remote port the rule targets, use an array to pass more then one'
# Checking that old syntax using comma is not used
@@ -233,13 +232,13 @@ def insync?(is)
desc 'Path to program this rule applies to'
def insync?(is)
- "#{is}".downcase == "#{should}".downcase
+ is.to_s.casecmp(should.to_s).zero?
end
defaultto :any
end
- newproperty(:interface_type, :array_matching=>:all) do
+ newproperty(:interface_type, array_matching: :all) do
desc 'Interface types this rule applies to'
newvalues(:any, :wired, :wireless, :remote_access)
@@ -254,7 +253,7 @@ def insync?(is)
desc 'service names this rule applies to'
def insync?(is)
- "#{is}".downcase == "#{should}".downcase
+ is.to_s.casecmp(should.to_s).zero?
end
defaultto :any
@@ -276,7 +275,7 @@ def insync?(is)
desc 'Specifies that matching IPsec rules of the indicated computer accounts are created'
def insync?(is)
- "#{is}".downcase == "#{should}".downcase
+ is.to_s.casecmp(should.to_s).zero?
end
defaultto :any
@@ -286,7 +285,7 @@ def insync?(is)
desc 'Specifies that matching IPsec rules of the indicated user accounts are created'
def insync?(is)
- "#{is}".downcase == "#{should}".downcase
+ is.to_s.casecmp(should.to_s).zero?
end
defaultto :any
@@ -296,7 +295,7 @@ def insync?(is)
desc 'Specifies that matching IPsec rules of the indicated user accounts are created'
def insync?(is)
- "#{is}".downcase == "#{should}".downcase
+ is.to_s.casecmp(should.to_s).zero?
end
defaultto :any
@@ -306,8 +305,7 @@ def insync?(is)
desc 'Name of this rule'
isnamevar
validate do |value|
- raise "it is not allowed to have a rule called 'any'" if value.downcase == "any"
+ raise "it is not allowed to have a rule called 'any'" if value.casecmp('any').zero?
end
end
-
end
diff --git a/lib/puppet_x/windows_firewall.rb b/lib/puppet_x/windows_firewall.rb
index 13e0c95..f42a268 100644
--- a/lib/puppet_x/windows_firewall.rb
+++ b/lib/puppet_x/windows_firewall.rb
@@ -2,405 +2,374 @@
require 'pp'
require 'puppet/util'
require 'puppet/util/windows'
-module PuppetX
- module WindowsFirewall
-
- MOD_DIR = 'windows_firewall/lib'
- SCRIPT_FILE = 'ps-bridge.ps1'
- SCRIPT_PATH = File.join('ps/windows_firewall', SCRIPT_FILE)
-
- # We need to be able to invoke the PS bridge script in both agent and apply
- # mode. In agent mode, the file will be found in LIBDIR, in apply mode it will
- # be found somewhere under CODEDIR. We need to read from the appropriate dir
- # for each mode to work in the most puppety way
- def self.resolve_ps_bridge
-
- case Puppet.run_mode.name
- when :user
- # AKA `puppet resource` - first scan modules then cache
- script = find_ps_bridge_in_modules || find_ps_bridge_in_cache
- when :apply
- # puppet apply demands local module install...
- script = find_ps_bridge_in_modules
- when :agent
- # agent mode would only look in cache
- script = find_ps_bridge_in_cache
- else
- raise("Don't know how to resolve #{SCRIPT_FILE} for windows_firewall in mode #{Puppet.run_mode.name}")
- end
- if ! script
- raise("windows_firewall unable to find #{SCRIPT_FILE} in expected location")
- end
+# This module manage Windows Firewall rules
+module PuppetX::WindowsFirewall
+ MOD_DIR = 'windows_firewall/lib'.freeze
+ SCRIPT_FILE = 'ps-bridge.ps1'.freeze
+ SCRIPT_PATH = File.join('ps/windows_firewall', SCRIPT_FILE)
+
+ # We need to be able to invoke the PS bridge script in both agent and apply
+ # mode. In agent mode, the file will be found in LIBDIR, in apply mode it will
+ # be found somewhere under CODEDIR. We need to read from the appropriate dir
+ # for each mode to work in the most puppety way
+ def self.resolve_ps_bridge
+ case Puppet.run_mode.name
+ when :user
+ # AKA `puppet resource` - first scan modules then cache
+ script = find_ps_bridge_in_modules || find_ps_bridge_in_cache
+ when :apply
+ # puppet apply demands local module install...
+ script = find_ps_bridge_in_modules
+ when :agent
+ # agent mode would only look in cache
+ script = find_ps_bridge_in_cache
+ else
+ raise("Don't know how to resolve #{SCRIPT_FILE} for windows_firewall in mode #{Puppet.run_mode.name}")
+ end
- cmd = ['powershell.exe', '-ExecutionPolicy', 'Bypass', '-File', script]
- cmd
+ unless script
+ raise("windows_firewall unable to find #{SCRIPT_FILE} in expected location")
end
- def self.find_ps_bridge_in_modules
- # 1st priority - environment
- check_for_script = File.join(
- Puppet.settings[:environmentpath],
- Puppet.settings[:environment],
- 'modules',
- MOD_DIR,
- SCRIPT_PATH,
- )
- Puppet.debug("Checking for #{SCRIPT_FILE} at #{check_for_script}")
- if File.exists? check_for_script
- script = check_for_script
- else
- # 2nd priority - custom module path, then basemodulepath
- full_module_path = "#{Puppet.settings[:modulepath]}#{File::PATH_SEPARATOR}#{Puppet.settings[:basemodulepath]}"
- full_module_path.split(File::PATH_SEPARATOR).reject do |path_element|
- path_element.empty?
- end.each do |path_element|
- check_for_script = File.join(path_element, MOD_DIR, SCRIPT_PATH)
- Puppet.debug("Checking for #{SCRIPT_FILE} at #{check_for_script}")
- if File.exists? check_for_script
- script = check_for_script
- break;
- end
+ cmd = ['powershell.exe', '-ExecutionPolicy', 'Bypass', '-File', script]
+ cmd
+ end
+
+ def self.find_ps_bridge_in_modules
+ # 1st priority - environment
+ check_for_script = File.join(
+ Puppet.settings[:environmentpath],
+ Puppet.settings[:environment],
+ 'modules',
+ MOD_DIR,
+ SCRIPT_PATH,
+ )
+ Puppet.debug("Checking for #{SCRIPT_FILE} at #{check_for_script}")
+ if File.exist? check_for_script
+ script = check_for_script
+ else
+ # 2nd priority - custom module path, then basemodulepath
+ full_module_path = "#{Puppet.settings[:modulepath]}#{File::PATH_SEPARATOR}#{Puppet.settings[:basemodulepath]}"
+ full_module_path.split(File::PATH_SEPARATOR).reject { |path_element|
+ path_element.empty?
+ }.each do |path_element|
+ check_for_script = File.join(path_element, MOD_DIR, SCRIPT_PATH)
+ Puppet.debug("Checking for #{SCRIPT_FILE} at #{check_for_script}")
+ if File.exist? check_for_script
+ script = check_for_script
+ break
end
end
-
- script
end
- def self.find_ps_bridge_in_cache
- check_for_script = File.join(Puppet.settings[:libdir], SCRIPT_PATH)
+ script
+ end
- Puppet.debug("Checking for #{SCRIPT_FILE} at #{check_for_script}")
- script = File.exists?(check_for_script) ? check_for_script : nil
- script
- end
+ def self.find_ps_bridge_in_cache
+ check_for_script = File.join(Puppet.settings[:libdir], SCRIPT_PATH)
- # convert a puppet type key name to the argument to use for `netsh` command
- def self.global_argument_lookup(key)
- {
- :keylifetime => 'mainmode mmkeylifetime',
- :secmethods => 'mainmode mmsecmethods',
- :forcedh => 'mainmode mmforcedh',
- :strongcrlcheck => 'ipsec strongcrlcheck',
- :saidletimemin => 'ipsec saidletimemin',
- :defaultexemptions => 'ipsec defaultexemptions',
- :ipsecthroughnat => 'ipsec ipsecthroughnat',
- :authzcomputergrp => 'ipsec authzcomputergrp',
- :authzusergrp => 'ipsec authzusergrp',
- }.fetch(key, key.to_s)
- end
+ Puppet.debug("Checking for #{SCRIPT_FILE} at #{check_for_script}")
+ script = File.exist?(check_for_script) ? check_for_script : nil
+ script
+ end
- # convert a puppet type key name to the argument to use for `netsh` command
- def self.profile_argument_lookup(key)
- {
- :localfirewallrules => 'settings localfirewallrules',
- :localconsecrules => 'settings localconsecrules',
- :inboundusernotification => 'settings inboundusernotification',
- :remotemanagement => 'settings remotemanagement',
- :unicastresponsetomulticast => 'settings unicastresponsetomulticast',
- :logallowedconnections => 'logging allowedconnections',
- :logdroppedconnections => 'logging droppedconnections',
- :filename => 'logging filename',
- :maxfilesize => 'logging maxfilesize',
- }.fetch(key, key.to_s)
- end
+ # convert a puppet type key name to the argument to use for `netsh` command
+ def self.global_argument_lookup(key)
+ {
+ keylifetime: 'mainmode mmkeylifetime',
+ secmethods: 'mainmode mmsecmethods',
+ forcedh: 'mainmode mmforcedh',
+ strongcrlcheck: 'ipsec strongcrlcheck',
+ saidletimemin: 'ipsec saidletimemin',
+ defaultexemptions: 'ipsec defaultexemptions',
+ ipsecthroughnat: 'ipsec ipsecthroughnat',
+ authzcomputergrp: 'ipsec authzcomputergrp',
+ authzusergrp: 'ipsec authzusergrp',
+ }.fetch(key, key.to_s)
+ end
- def self.to_ps(key)
- {
- :enabled => lambda { |x| camel_case(x) },
- :action => lambda { |x| camel_case(x) },
- :direction => lambda { |x| camel_case(x) },
- :description => lambda { |x| x.empty? == true ? "\"#{x}\"" : x },
- :interface_type => lambda { |x| x.map { |e| camel_case(e) }.join(',') },
- :profile => lambda { |x| x.map { |e| camel_case(e) }.join(',') },
- :protocol => lambda { |x| x.to_s.upcase.sub('V', 'v') },
- :icmp_type => lambda { |x| camel_case(x) },
- :edge_traversal_policy => lambda { |x| camel_case(x) },
- :local_port => lambda { |x| x.kind_of?(Array) ? (x.map { |e| camel_case(e) }).join(',') : camel_case(x) },
- :remote_port => lambda { |x| x.kind_of?(Array) ? (x.map { |e| camel_case(e) }).join(',') : camel_case(x) },
- :local_address => lambda { |x| x.kind_of?(Array) ? (x.map { |e| camel_case(e) }).join(',') : camel_case(x) },
- :remote_address => lambda { |x| x.kind_of?(Array) ? (x.map { |e| camel_case(e) }).join(',') : camel_case(x) },
- :program => lambda { |x| x.to_s == 'any' ? x : x.gsub(/\\/, '\\\\') },
- :authentication => lambda { |x| camel_case(x) },
- :encryption => lambda { |x| camel_case(x) },
- :remote_machine => lambda { |x| convert_to_sddl(x) },
- :local_user => lambda { |x| convert_to_sddl(x) },
- :remote_user => lambda { |x| convert_to_sddl(x) },
- }.fetch(key, lambda { |x| x })
- end
+ # convert a puppet type key name to the argument to use for `netsh` command
+ def self.profile_argument_lookup(key)
+ {
+ localfirewallrules: 'settings localfirewallrules',
+ localconsecrules: 'settings localconsecrules',
+ inboundusernotification: 'settings inboundusernotification',
+ remotemanagement: 'settings remotemanagement',
+ unicastresponsetomulticast: 'settings unicastresponsetomulticast',
+ logallowedconnections: 'logging allowedconnections',
+ logdroppedconnections: 'logging droppedconnections',
+ filename: 'logging filename',
+ maxfilesize: 'logging maxfilesize',
+ }.fetch(key, key.to_s)
+ end
- def self.to_ruby(key)
- {
- :enabled => lambda { |x| snake_case_sym(x) },
- :action => lambda { |x| snake_case_sym(x) },
- :direction => lambda { |x| snake_case_sym(x) },
- :interface_type => lambda { |x| x.split(',').map{ |e| snake_case_sym(e.strip) } },
- :profile => lambda { |x| x.split(',').map{ |e| snake_case_sym(e.strip) } },
- :protocol => lambda { |x| snake_case_sym(x) },
- :icmp_type => lambda { |x| x ? x.downcase : x },
- :edge_traversal_policy => lambda { |x| snake_case_sym(x) },
- :program => lambda { |x| x.to_s == 'Any' ? x.downcase : x.gsub(/\\\\/, '\\') },
- :remote_port => lambda { |x| x.kind_of?(Array) ? x.map { |e| e.downcase } : x.downcase.split },
- :local_port => lambda { |x| x.kind_of?(Array) ? x.map { |e| e.downcase } : x.downcase.split },
- :remote_address => lambda { |x| x.kind_of?(Array) ? x.map { |e| e.downcase } : x.downcase.split },
- :local_address => lambda { |x| x.kind_of?(Array) ? x.map { |e| e.downcase } : x.downcase.split },
- :authentication => lambda { |x| x.downcase },
- :encryption => lambda { |x| x.downcase },
- :remote_machine => lambda { |x| convert_from_sddl(x) },
- :local_user => lambda { |x| convert_from_sddl(x) },
- :remote_user => lambda { |x| convert_from_sddl(x) },
- :service => lambda { |x| x.downcase },
- }.fetch(key, lambda { |x| x })
- end
+ def self.to_ps(key)
+ {
+ enabled: ->(x) { camel_case(x) },
+ action: ->(x) { camel_case(x) },
+ direction: ->(x) { camel_case(x) },
+ description: ->(x) { (x.empty? == true) ? "\"#{x}\"" : x },
+ interface_type: ->(x) { x.map { |e| camel_case(e) }.join(',') },
+ profile: ->(x) { x.map { |e| camel_case(e) }.join(',') },
+ protocol: ->(x) { x.to_s.upcase.sub('V', 'v') },
+ icmp_type: ->(x) { camel_case(x) },
+ edge_traversal_policy: ->(x) { camel_case(x) },
+ local_port: ->(x) { x.is_a?(Array) ? (x.map { |e| camel_case(e) }).join(',') : camel_case(x) },
+ remote_port: ->(x) { x.is_a?(Array) ? (x.map { |e| camel_case(e) }).join(',') : camel_case(x) },
+ local_address: ->(x) { x.is_a?(Array) ? (x.map { |e| camel_case(e) }).join(',') : camel_case(x) },
+ remote_address: ->(x) { x.is_a?(Array) ? (x.map { |e| camel_case(e) }).join(',') : camel_case(x) },
+ program: ->(x) { (x.to_s == 'any') ? x : x.gsub(%r{\\}, '\\\\') },
+ authentication: ->(x) { camel_case(x) },
+ encryption: ->(x) { camel_case(x) },
+ remote_machine: ->(x) { convert_to_sddl(x) },
+ local_user: ->(x) { convert_to_sddl(x) },
+ remote_user: ->(x) { convert_to_sddl(x) },
+ }.fetch(key, ->(x) { x })
+ end
- # Convert name to SID and structure result as SDDL value
- def self.convert_to_sddl_acl(value, ace)
- # we need to convert users to sids first
- sids = []
- value.split(',').sort.each do |name|
- name.strip!
- sid = Puppet::Util::Windows::SID.name_to_sid(name)
- # If resolution failed, thrown a warning
- if sid.nil?
- warn("\"#{value}\" does not exist")
- else
- # Generate structured SSDL ACL
- cur_sid = '('+ ace +';;CC;;;' + sid + ')'
- end
- sids << cur_sid unless cur_sid.nil?
- end
- sids.sort.join('')
- end
+ def self.to_ruby(key)
+ {
+ enabled: ->(x) { snake_case_sym(x) },
+ action: ->(x) { snake_case_sym(x) },
+ direction: ->(x) { snake_case_sym(x) },
+ interface_type: ->(x) { x.split(',').map { |e| snake_case_sym(e.strip) } },
+ profile: ->(x) { x.split(',').map { |e| snake_case_sym(e.strip) } },
+ protocol: ->(x) { snake_case_sym(x) },
+ icmp_type: ->(x) { x ? x.downcase : x },
+ edge_traversal_policy: ->(x) { snake_case_sym(x) },
+ program: ->(x) { (x.to_s == 'Any') ? x.downcase : x.gsub(%r{\\\\}, '\\') },
+ remote_port: ->(x) { x.is_a?(Array) ? x.map { |e| e.downcase } : x.downcase.split },
+ local_port: ->(x) { x.is_a?(Array) ? x.map { |e| e.downcase } : x.downcase.split },
+ remote_address: ->(x) { x.is_a?(Array) ? x.map { |e| e.downcase } : x.downcase.split },
+ local_address: ->(x) { x.is_a?(Array) ? x.map { |e| e.downcase } : x.downcase.split },
+ authentication: ->(x) { x.downcase },
+ encryption: ->(x) { x.downcase },
+ remote_machine: ->(x) { convert_from_sddl(x) },
+ local_user: ->(x) { convert_from_sddl(x) },
+ remote_user: ->(x) { convert_from_sddl(x) },
+ service: ->(x) { x.downcase },
+ }.fetch(key, ->(x) { x })
+ end
- # Convert name to SID and structure result as SDDL value (Only if value is not any)
- def self.convert_to_sddl(value)
- if value.to_s == 'any'
- value
+ # Convert name to SID and structure result as SDDL value
+ def self.convert_to_sddl_acl(value, ace)
+ # we need to convert users to sids first
+ sids = []
+ value.split(',').sort.each do |name|
+ name.strip!
+ sid = Puppet::Util::Windows::SID.name_to_sid(name)
+ # If resolution failed, thrown a warning
+ if sid.nil?
+ warn("\"#{value}\" does not exist")
else
- 'O:LSD:' + (convert_to_sddl_acl(value['allow'], 'A') unless value['allow'].nil?).to_s + (convert_to_sddl_acl(value['block'], 'D') unless value['block'].nil?).to_s
+ # Generate structured SSDL ACL
+ cur_sid = '(' + ace + ';;CC;;;' + sid + ')'
end
+ sids << cur_sid unless cur_sid.nil?
end
+ sids.sort.join('')
+ end
- # Parse SDDL value and convert SID to name
- def self.convert_from_sddl(value)
- if value == 'Any'
- # Return value in lowercase
- value.downcase!
- else
- # we need to convert users to sids first
- # Delete prefix
- value.delete_prefix! 'O:LSD:'
- # Change ')(' to ',' to have a proper delimiter
- value.gsub! ')(', ','
- # Remove '()'
- value.delete! '()'
- # Define variables
- names = {}
- allow = []
- deny = []
- value.split(',').sort.each do |sid|
- # ACE is first character
- ace = sid.chr.upcase
- # Delete prefix on each user
- sid.delete_prefix! ace + ';;CC;;;'
- sid.strip!
- name = Puppet::Util::Windows::SID.sid_to_name(sid)
- # If resolution failed, return SID
- if name.nil?
- cur_name = sid.downcase!
- else
- cur_name = name.downcase!
- end
- case ace
- when 'A'
- allow << cur_name unless cur_name.nil?
- when 'D'
- deny << cur_name unless cur_name.nil?
- end
- end
- if !allow.empty?
- names['allow'] = allow.sort.join(',')
- end
- if !deny.empty?
- names['block'] = deny.sort.join(',')
- end
- names
- end
+ # Convert name to SID and structure result as SDDL value (Only if value is not any)
+ def self.convert_to_sddl(value)
+ if value.to_s == 'any'
+ value
+ else
+ 'O:LSD:' + (convert_to_sddl_acl(value['allow'], 'A') unless value['allow'].nil?).to_s + (convert_to_sddl_acl(value['block'], 'D') unless value['block'].nil?).to_s
end
+ end
- # create a normalised key name by:
- # 1. lowercasing input
- # 2. converting spaces to underscores
- # 3. convert to symbol
- def self.key_name(input)
- input.downcase.gsub(/\s/, '_').to_sym
+ # Parse SDDL value and convert SID to name
+ def self.convert_from_sddl(value)
+ if value == 'Any'
+ # Return value in lowercase
+ value.downcase!
+ else
+ # we need to convert users to sids first
+ # Delete prefix
+ value.delete_prefix! 'O:LSD:'
+ # Change ')(' to ',' to have a proper delimiter
+ value.gsub! ')(', ','
+ # Remove '()'
+ value.delete! '()'
+ # Define variables
+ names = {}
+ allow = []
+ deny = []
+ value.split(',').sort.each do |sid|
+ # ACE is first character
+ ace = sid.chr.upcase
+ # Delete prefix on each user
+ sid.delete_prefix! ace + ';;CC;;;'
+ sid.strip!
+ name = Puppet::Util::Windows::SID.sid_to_name(sid)
+ # If resolution failed, return SID
+ cur_name = if name.nil?
+ sid.downcase!
+ else
+ name.downcase!
+ end
+ case ace
+ when 'A'
+ allow << cur_name unless cur_name.nil?
+ when 'D'
+ deny << cur_name unless cur_name.nil?
+ end
+ end
+ unless allow.empty?
+ names['allow'] = allow.sort.join(',')
+ end
+ unless deny.empty?
+ names['block'] = deny.sort.join(',')
+ end
+ names
end
+ end
- # Convert input CamelCase to snake_case symbols
- def self.snake_case_sym(input)
- input.gsub(/([a-z])([A-Z])/, '\1_\2').downcase.to_sym
- end
+ # create a normalised key name by:
+ # 1. lowercasing input
+ # 2. converting spaces to underscores
+ # 3. convert to symbol
+ def self.key_name(input)
+ input.downcase.gsub(%r{\s}, '_').to_sym
+ end
- # Convert snake_case input symbol to CamelCase string
- def self.camel_case(input)
- # https://stackoverflow.com/a/24917606/3441106
- input.to_s.split('_').collect(&:capitalize).join
- end
+ # Convert input CamelCase to snake_case symbols
+ def self.snake_case_sym(input)
+ input.gsub(%r{([a-z])([A-Z])}, '\1_\2').downcase.to_sym
+ end
- def self.delete_rule(resource)
- Puppet.notice("(windows_firewall) deleting rule '#{resource[:display_name]}'")
- out = Puppet::Util::Execution.execute(resolve_ps_bridge + ["delete", resource[:name]]).to_s
- Puppet.debug out
- end
+ # Convert snake_case input symbol to CamelCase string
+ def self.camel_case(input)
+ # https://stackoverflow.com/a/24917606/3441106
+ input.to_s.split('_').map(&:capitalize).join
+ end
- def self.update_rule(resource)
- Puppet.notice("(windows_firewall) updating rule '#{resource[:display_name]}'")
+ def self.delete_rule(resource)
+ Puppet.notice("(windows_firewall) deleting rule '#{resource[:display_name]}'")
+ out = Puppet::Util::Execution.execute(resolve_ps_bridge + ['delete', resource[:name]]).to_s
+ Puppet.debug out
+ end
- # `Name` is mandatory and also a `parameter` not a `property`
- args = [ '-Name', resource[:name] ]
+ def self.update_rule(resource)
+ Puppet.notice("(windows_firewall) updating rule '#{resource[:display_name]}'")
- resource.properties.reject { |property|
- [:ensure, :protocol_type, :protocol_code].include?(property.name) ||
- property.value == :none
- }.each { |property|
- # All properties start `-`
- property_name = "-#{camel_case(property.name)}"
- property_value = to_ps(property.name).call(property.value)
+ # `Name` is mandatory and also a `parameter` not a `property`
+ args = [ '-Name', resource[:name] ]
- # protocol can optionally specify type and code, other properties are set very simply
- args << property_name
- args << property_value
- }
- Puppet.debug "Updating firewall rule with args: #{args}"
+ resource.properties.reject { |property|
+ [:ensure, :protocol_type, :protocol_code].include?(property.name) ||
+ property.value == :none
+ }.each do |property|
+ # All properties start `-`
+ property_name = "-#{camel_case(property.name)}"
+ property_value = to_ps(property.name).call(property.value)
- out = Puppet::Util::Execution.execute(resolve_ps_bridge + ['update'] + args)
- Puppet.debug out
+ # protocol can optionally specify type and code, other properties are set very simply
+ args << property_name
+ args << property_value
end
+ Puppet.debug "Updating firewall rule with args: #{args}"
- # Create a new firewall rule using powershell
- # @see https://docs.microsoft.com/en-us/powershell/module/netsecurity/new-netfirewallrule?view=win10-ps
- def self.create_rule(resource)
- Puppet.notice("(windows_firewall) adding rule '#{resource[:display_name]}'")
-
- # `Name` is mandatory and also a `parameter` not a `property`
- args = [ '-Name', resource[:name] ]
-
- resource.properties.reject { |property|
- [:ensure, :protocol_type, :protocol_code].include?(property.name) ||
- property.value == :none
- }.each { |property|
- # All properties start `-`
- property_name = "-#{camel_case(property.name)}"
- property_value = to_ps(property.name).call(property.value)
-
- # protocol can optionally specify type and code, other properties are set very simply
- args << property_name
- args << property_value
- }
- Puppet.debug "Creating firewall rule with args: #{args}"
-
- out = Puppet::Util::Execution.execute(resolve_ps_bridge + ['create'] + args)
- Puppet.debug out
- end
+ out = Puppet::Util::Execution.execute(resolve_ps_bridge + ['update'] + args)
+ Puppet.debug out
+ end
- def self.rules
- Puppet.debug('query all rules')
- rules = JSON.parse Puppet::Util::Execution.execute(resolve_ps_bridge + ['show']).to_s
-
- # Rules is an array of hash as-parsed and hash keys need converted to
- # lowercase ruby labels
- puppet_rules = rules.map { |e|
- Hash[e.map { |k, v|
- key = snake_case_sym(k)
- [key, to_ruby(key).call(v)]
- }].merge({ensure: :present})
- }
- Puppet.debug("Parsed rules: #{puppet_rules.size}")
- puppet_rules
+ # Create a new firewall rule using powershell
+ # @see https://docs.microsoft.com/en-us/powershell/module/netsecurity/new-netfirewallrule?view=win10-ps
+ def self.create_rule(resource)
+ Puppet.notice("(windows_firewall) adding rule '#{resource[:display_name]}'")
+
+ # `Name` is mandatory and also a `parameter` not a `property`
+ args = [ '-Name', resource[:name] ]
+
+ resource.properties.reject { |property|
+ [:ensure, :protocol_type, :protocol_code].include?(property.name) ||
+ property.value == :none
+ }.each do |property|
+ # All properties start `-`
+ property_name = "-#{camel_case(property.name)}"
+ property_value = to_ps(property.name).call(property.value)
+
+ # protocol can optionally specify type and code, other properties are set very simply
+ args << property_name
+ args << property_value
end
+ Puppet.debug "Creating firewall rule with args: #{args}"
- def self.groups
- Puppet.debug('query all groups')
- # get all individual firewall rules, then create a new hash containing the overall group
- # status for each group of rules
- g = {}
- rules.select { |e|
- # we are only interested in firewall rules that provide grouping information so bounce
- # anything that doesn't have it from the list
- ! e[:display_group].empty?
- }.each { |e|
- # extract the group information for each rule, use the value of :enabled to
- # build up an overall status for the whole group. Dont forget that the
- # value is a label :true or :false - to fit with puppet's newtype operator
- k = e[:display_group]
- current = g.fetch(k, e[:enabled])
-
- if current == :true && e[:enabled] == :true
- g[k] = :true
- else
- g[k] = :false
- end
-
- }
-
- # convert into puppet's preferred hash format which is an array of hashes
- # with each hash representing a distinct resource
- transformed = g.map { |k, v|
- { :name => k, :enabled => v}
- }
+ out = Puppet::Util::Execution.execute(resolve_ps_bridge + ['create'] + args)
+ Puppet.debug out
+ end
- Puppet.debug("group rules #{transformed}")
- transformed
+ def self.rules
+ Puppet.debug('query all rules')
+ rules = JSON.parse Puppet::Util::Execution.execute(resolve_ps_bridge + ['show']).to_s
+
+ # Rules is an array of hash as-parsed and hash keys need converted to
+ # lowercase ruby labels
+ puppet_rules = rules.map do |e|
+ Hash[e.map do |k, v|
+ key = snake_case_sym(k)
+ [key, to_ruby(key).call(v)]
+ end].merge({ ensure: :present })
end
+ Puppet.debug("Parsed rules: #{puppet_rules.size}")
+ puppet_rules
+ end
+ def self.groups
+ Puppet.debug('query all groups')
+ # get all individual firewall rules, then create a new hash containing the overall group
+ # status for each group of rules
+ g = {}
+ rules.reject { |e|
+ # we are only interested in firewall rules that provide grouping information so bounce
+ # anything that doesn't have it from the list
+ e[:display_group].empty?
+ }.each do |e|
+ # extract the group information for each rule, use the value of :enabled to
+ # build up an overall status for the whole group. Dont forget that the
+ # value is a label :true or :false - to fit with puppet's newtype operator
+ k = e[:display_group]
+ current = g.fetch(k, e[:enabled])
+
+ g[k] = if current == :true && e[:enabled] == :true
+ :true
+ else
+ :false
+ end
+ end
- # Each rule is se
- def self.parse_profile(input)
- profile = {}
- first_line = true
- profile_name = '__error__'
- input.split("\n").reject { |line|
- line =~ /---/ || line =~ /^\s*$/
- }.each { |line|
- if first_line
- # take the first word in the line - eg "public profile settings" -> "public"
- profile_name = line.split(" ")[0].downcase
- first_line = false
- else
- # nasty hack - "firewall policy" setting contains space and will break our
- # logic below. Also the setter in `netsh` to use is `firewallpolicy`. Just fix it...
- line = line.sub('Firewall Policy', 'firewallpolicy')
-
- # split each line at most twice by first glob of whitespace
- line_split = line.split(/\s+/, 2)
-
- if line_split.size == 2
- key = key_name(line_split[0].strip)
-
- # downcase all values for comparison purposes
- value = line_split[1].strip.downcase
-
- profile[key] = value
- end
- end
- }
-
- # if we see the rule then it must exist...
- profile[:name] = profile_name
-
- Puppet.debug "Parsed windows firewall profile: #{profile}"
- profile
+ # convert into puppet's preferred hash format which is an array of hashes
+ # with each hash representing a distinct resource
+ transformed = g.map do |k, v|
+ { name: k, enabled: v }
end
- # Each rule is se
- def self.parse_global(input)
- globals = {}
- input.split("\n").reject { |line|
- line =~ /---/ || line =~ /^\s*$/
- }.each { |line|
+ Puppet.debug("group rules #{transformed}")
+ transformed
+ end
+
+ # Each rule is se
+ def self.parse_profile(input)
+ profile = {}
+ first_line = true
+ profile_name = '__error__'
+ input.split("\n").reject { |line|
+ line.include?('---') || line =~ %r{^\s*$}
+ }.each do |line|
+ if first_line
+ # take the first word in the line - eg "public profile settings" -> "public"
+ profile_name = line.split(' ')[0].downcase
+ first_line = false
+ else
+ # nasty hack - "firewall policy" setting contains space and will break our
+ # logic below. Also the setter in `netsh` to use is `firewallpolicy`. Just fix it...
+ line = line.sub('Firewall Policy', 'firewallpolicy')
# split each line at most twice by first glob of whitespace
- line_split = line.split(/\s+/, 2)
+ line_split = line.split(%r{\s+}, 2)
if line_split.size == 2
key = key_name(line_split[0].strip)
@@ -408,58 +377,82 @@ def self.parse_global(input)
# downcase all values for comparison purposes
value = line_split[1].strip.downcase
- case key
- when :secmethods
- # secmethods are output with a hypen like this:
- # DHGroup2-AES128-SHA1,DHGroup2-3DES-SHA1
- # but must be input with a colon like this:
- # DHGroup2:AES128-SHA1,DHGroup2:3DES-SHA1
- safe_value = value.split(',').map { |e|
- e.sub('-', ':')
- }.join(',')
- when :strongcrlcheck
- safe_value = value.split(':')[0]
- when :defaultexemptions
- safe_value = value.split(',').sort
- when :saidletimemin
- safe_value = value.sub('min', '')
- when :ipsecthroughnat
- safe_value = value.gsub(' ', '')
- else
- safe_value = value
- end
-
- globals[key] = safe_value
+ profile[key] = value
end
- }
+ end
+ end
- globals[:name] = 'global'
+ # if we see the rule then it must exist...
+ profile[:name] = profile_name
- Puppet.debug "Parsed windows firewall globals: #{globals}"
- globals
- end
+ Puppet.debug "Parsed windows firewall profile: #{profile}"
+ profile
+ end
- # parse firewall profiles
- def self.profiles(cmd)
- profiles = []
- # the output of `show allprofiles` contains several blank lines that make parsing somewhat
- # harder so just run it for each of the three profiles to make life easy...
- ['publicprofile', 'domainprofile', 'privateprofile'].each { |profile|
- profiles << parse_profile(Puppet::Util::Execution.execute([cmd, 'advfirewall', 'show', profile]).to_s)
- }
- profiles
+ # Each rule is se
+ def self.parse_global(input)
+ globals = {}
+ input.split("\n").reject { |line|
+ line.include?('---') || line =~ %r{^\s*$}
+ }.each do |line|
+ # split each line at most twice by first glob of whitespace
+ line_split = line.split(%r{\s+}, 2)
+
+ next unless line_split.size == 2
+ key = key_name(line_split[0].strip)
+
+ # downcase all values for comparison purposes
+ value = line_split[1].strip.downcase
+
+ safe_value = case key
+ when :secmethods
+ # secmethods are output with a hypen like this:
+ # DHGroup2-AES128-SHA1,DHGroup2-3DES-SHA1
+ # but must be input with a colon like this:
+ # DHGroup2:AES128-SHA1,DHGroup2:3DES-SHA1
+ value.split(',').map { |e|
+ e.sub('-', ':')
+ }.join(',')
+ when :strongcrlcheck
+ value.split(':')[0]
+ when :defaultexemptions
+ value.split(',').sort
+ when :saidletimemin
+ value.sub('min', '')
+ when :ipsecthroughnat
+ value.delete(' ')
+ else
+ value
+ end
+
+ globals[key] = safe_value
end
+ globals[:name] = 'global'
+
+ Puppet.debug "Parsed windows firewall globals: #{globals}"
+ globals
+ end
+
+ # parse firewall profiles
+ def self.profiles(cmd)
+ profiles = []
+ # the output of `show allprofiles` contains several blank lines that make parsing somewhat
+ # harder so just run it for each of the three profiles to make life easy...
+ ['publicprofile', 'domainprofile', 'privateprofile'].each do |profile|
+ profiles << parse_profile(Puppet::Util::Execution.execute([cmd, 'advfirewall', 'show', profile]).to_s)
+ end
+ profiles
+ end
- # parse firewall profiles
- def self.globals(cmd)
- profiles = []
- # the output of `show allprofiles` contains several blank lines that make parsing somewhat
- # harder so just run it for each of the three profiles to make life easy...
- ['publicprofile', 'domainprofile', 'privateprofile'].each { |profile|
- profiles << parse_global(Puppet::Util::Execution.execute([cmd, 'advfirewall', 'show', 'global']).to_s)
- }
- profiles
+ # parse firewall profiles
+ def self.globals(cmd)
+ profiles = []
+ # the output of `show allprofiles` contains several blank lines that make parsing somewhat
+ # harder so just run it for each of the three profiles to make life easy...
+ ['publicprofile', 'domainprofile', 'privateprofile'].each do |_profile|
+ profiles << parse_global(Puppet::Util::Execution.execute([cmd, 'advfirewall', 'show', 'global']).to_s)
end
+ profiles
end
end
diff --git a/lib/puppet_x/windows_firewall_ipsec.rb b/lib/puppet_x/windows_firewall_ipsec.rb
index 29a484d..48d317b 100644
--- a/lib/puppet_x/windows_firewall_ipsec.rb
+++ b/lib/puppet_x/windows_firewall_ipsec.rb
@@ -1,253 +1,224 @@
require 'puppet_x'
require 'pp'
-module PuppetX
- module WindowsFirewallIPSec
-
- MOD_DIR = 'windows_firewall/lib'
- SCRIPT_FILE = 'ps-bridge-ipsec.ps1'
- SCRIPT_PATH = File.join('ps/windows_firewall', SCRIPT_FILE)
-
- # We need to be able to invoke the PS bridge script in both agent and apply
- # mode. In agent mode, the file will be found in LIBDIR, in apply mode it will
- # be found somewhere under CODEDIR. We need to read from the appropriate dir
- # for each mode to work in the most puppety way
- def self.resolve_ps_bridge
-
- case Puppet.run_mode.name
- when :user
- # AKA `puppet resource` - first scan modules then cache
- script = find_ps_bridge_in_modules || find_ps_bridge_in_cache
- when :apply
- # puppet apply demands local module install...
- script = find_ps_bridge_in_modules
- when :agent
- # agent mode would only look in cache
- script = find_ps_bridge_in_cache
- else
- raise("Don't know how to resolve #{SCRIPT_FILE} for windows_firewall in mode #{Puppet.run_mode.name}")
- end
- if ! script
- raise("windows_firewall unable to find #{SCRIPT_FILE} in expected location")
- end
+# This module manage Windows Firewall IPSec rules
+module PuppetX::WindowsFirewallIPSec
+ MOD_DIR = 'windows_firewall/lib'.freeze
+ SCRIPT_FILE = 'ps-bridge-ipsec.ps1'.freeze
+ SCRIPT_PATH = File.join('ps/windows_firewall', SCRIPT_FILE)
+
+ # We need to be able to invoke the PS bridge script in both agent and apply
+ # mode. In agent mode, the file will be found in LIBDIR, in apply mode it will
+ # be found somewhere under CODEDIR. We need to read from the appropriate dir
+ # for each mode to work in the most puppety way
+ def self.resolve_ps_bridge
+ case Puppet.run_mode.name
+ when :user
+ # AKA `puppet resource` - first scan modules then cache
+ script = find_ps_bridge_in_modules || find_ps_bridge_in_cache
+ when :apply
+ # puppet apply demands local module install...
+ script = find_ps_bridge_in_modules
+ when :agent
+ # agent mode would only look in cache
+ script = find_ps_bridge_in_cache
+ else
+ raise("Don't know how to resolve #{SCRIPT_FILE} for windows_firewall in mode #{Puppet.run_mode.name}")
+ end
- cmd = ['powershell.exe', '-ExecutionPolicy', 'Bypass', '-File', script]
- cmd
+ unless script
+ raise("windows_firewall unable to find #{SCRIPT_FILE} in expected location")
end
- def self.find_ps_bridge_in_modules
- # 1st priority - environment
- check_for_script = File.join(
- Puppet.settings[:environmentpath],
- Puppet.settings[:environment],
- 'modules',
- MOD_DIR,
- SCRIPT_PATH,
- )
- Puppet.debug("Checking for #{SCRIPT_FILE} at #{check_for_script}")
- if File.exists? check_for_script
- script = check_for_script
- else
- # 2nd priority - custom module path, then basemodulepath
- full_module_path = "#{Puppet.settings[:modulepath]}#{File::PATH_SEPARATOR}#{Puppet.settings[:basemodulepath]}"
- full_module_path.split(File::PATH_SEPARATOR).reject do |path_element|
- path_element.empty?
- end.each do |path_element|
- check_for_script = File.join(path_element, MOD_DIR, SCRIPT_PATH)
- Puppet.debug("Checking for #{SCRIPT_FILE} at #{check_for_script}")
- if File.exists? check_for_script
- script = check_for_script
- break;
- end
+ cmd = ['powershell.exe', '-ExecutionPolicy', 'Bypass', '-File', script]
+ cmd
+ end
+
+ def self.find_ps_bridge_in_modules
+ # 1st priority - environment
+ check_for_script = File.join(
+ Puppet.settings[:environmentpath],
+ Puppet.settings[:environment],
+ 'modules',
+ MOD_DIR,
+ SCRIPT_PATH,
+ )
+ Puppet.debug("Checking for #{SCRIPT_FILE} at #{check_for_script}")
+ if File.exist? check_for_script
+ script = check_for_script
+ else
+ # 2nd priority - custom module path, then basemodulepath
+ full_module_path = "#{Puppet.settings[:modulepath]}#{File::PATH_SEPARATOR}#{Puppet.settings[:basemodulepath]}"
+ full_module_path.split(File::PATH_SEPARATOR).reject { |path_element|
+ path_element.empty?
+ }.each do |path_element|
+ check_for_script = File.join(path_element, MOD_DIR, SCRIPT_PATH)
+ Puppet.debug("Checking for #{SCRIPT_FILE} at #{check_for_script}")
+ if File.exist? check_for_script
+ script = check_for_script
+ break
end
end
-
- script
end
- def self.find_ps_bridge_in_cache
- check_for_script = File.join(Puppet.settings[:libdir], SCRIPT_PATH)
+ script
+ end
- Puppet.debug("Checking for #{SCRIPT_FILE} at #{check_for_script}")
- script = File.exists?(check_for_script) ? check_for_script : nil
- script
- end
+ def self.find_ps_bridge_in_cache
+ check_for_script = File.join(Puppet.settings[:libdir], SCRIPT_PATH)
- def self.to_ps(key)
- {
- :enabled => lambda { |x| camel_case(x) },
- :action => lambda { |x| camel_case(x) },
- :description => lambda { |x| x.empty? == true ? "\"#{x}\"" : x },
- :interface_type => lambda { |x| x.map { |e| camel_case(e)}.join(',') },
- :profile => lambda { |x| x.map { |e| camel_case(e)}.join(',') },
- :protocol => lambda { |x| x.to_s.upcase.sub('V', 'v') },
- :local_port => lambda { |x| x.kind_of?(Array) ? (x.map { |e| camel_case(e) }).join(',') : camel_case(x) },
- :remote_port => lambda { |x| x.kind_of?(Array) ? (x.map { |e| camel_case(e) }).join(',') : camel_case(x) },
- :local_address => lambda { |x| x.kind_of?(Array) ? (x.map { |e| camel_case(e) }).join(',') : camel_case(x) },
- :remote_address => lambda { |x| x.kind_of?(Array) ? (x.map { |e| camel_case(e) }).join(',') : camel_case(x) },
- :mode => lambda { |x| camel_case(x) },
- :inbound_security => lambda { |x| camel_case(x) },
- :outbound_security => lambda { |x| camel_case(x) },
- :phase1auth_set => lambda { |x| camel_case(x) },
- :phase2auth_set => lambda { |x| camel_case(x) },
- }.fetch(key, lambda { |x| x })
- end
+ Puppet.debug("Checking for #{SCRIPT_FILE} at #{check_for_script}")
+ script = File.exist?(check_for_script) ? check_for_script : nil
+ script
+ end
- def self.to_ruby(key)
- {
- :enabled => lambda { |x| snake_case_sym(x) },
- :action => lambda { |x| snake_case_sym(x) },
- :interface_type => lambda { |x| x.split(',').map{ |e| snake_case_sym(e.strip) } },
- :profile => lambda { |x| x.split(',').map{ |e| snake_case_sym(e.strip) } },
- :protocol => lambda { |x| snake_case_sym(x) },
- :remote_port => lambda { |x| x.kind_of?(Array) ? x.map { |e| e.downcase } : x.downcase.split },
- :local_port => lambda { |x| x.kind_of?(Array) ? x.map { |e| e.downcase } : x.downcase.split },
- :remote_address => lambda { |x| x.kind_of?(Array) ? x.map { |e| e.downcase } : x.downcase.split },
- :local_address => lambda { |x| x.kind_of?(Array) ? x.map { |e| e.downcase } : x.downcase.split },
- :mode => lambda { |x| x.downcase },
- :inbound_security => lambda { |x| x.downcase },
- :outbound_security => lambda { |x| x.downcase },
- :phase1auth_set => lambda { |x| x.downcase },
- :phase2auth_set => lambda { |x| x.downcase },
- }.fetch(key, lambda { |x| x })
- end
+ def self.to_ps(key)
+ {
+ enabled: ->(x) { camel_case(x) },
+ action: ->(x) { camel_case(x) },
+ description: ->(x) { (x.empty? == true) ? "\"#{x}\"" : x },
+ interface_type: ->(x) { x.map { |e| camel_case(e) }.join(',') },
+ profile: ->(x) { x.map { |e| camel_case(e) }.join(',') },
+ protocol: ->(x) { x.to_s.upcase.sub('V', 'v') },
+ local_port: ->(x) { x.is_a?(Array) ? (x.map { |e| camel_case(e) }).join(',') : camel_case(x) },
+ remote_port: ->(x) { x.is_a?(Array) ? (x.map { |e| camel_case(e) }).join(',') : camel_case(x) },
+ local_address: ->(x) { x.is_a?(Array) ? (x.map { |e| camel_case(e) }).join(',') : camel_case(x) },
+ remote_address: ->(x) { x.is_a?(Array) ? (x.map { |e| camel_case(e) }).join(',') : camel_case(x) },
+ mode: ->(x) { camel_case(x) },
+ inbound_security: ->(x) { camel_case(x) },
+ outbound_security: ->(x) { camel_case(x) },
+ phase1auth_set: ->(x) { camel_case(x) },
+ phase2auth_set: ->(x) { camel_case(x) },
+ }.fetch(key, ->(x) { x })
+ end
- # create a normalised key name by:
- # 1. lowercasing input
- # 2. converting spaces to underscores
- # 3. convert to symbol
- def self.key_name(input)
- input.downcase.gsub(/\s/, '_').to_sym
- end
+ def self.to_ruby(key)
+ {
+ enabled: ->(x) { snake_case_sym(x) },
+ action: ->(x) { snake_case_sym(x) },
+ interface_type: ->(x) { x.split(',').map { |e| snake_case_sym(e.strip) } },
+ profile: ->(x) { x.split(',').map { |e| snake_case_sym(e.strip) } },
+ protocol: ->(x) { snake_case_sym(x) },
+ remote_port: ->(x) { x.is_a?(Array) ? x.map { |e| e.downcase } : x.downcase.split },
+ local_port: ->(x) { x.is_a?(Array) ? x.map { |e| e.downcase } : x.downcase.split },
+ remote_address: ->(x) { x.is_a?(Array) ? x.map { |e| e.downcase } : x.downcase.split },
+ local_address: ->(x) { x.is_a?(Array) ? x.map { |e| e.downcase } : x.downcase.split },
+ mode: ->(x) { x.downcase },
+ inbound_security: ->(x) { x.downcase },
+ outbound_security: ->(x) { x.downcase },
+ phase1auth_set: ->(x) { x.downcase },
+ phase2auth_set: ->(x) { x.downcase },
+ }.fetch(key, ->(x) { x })
+ end
- # Convert input CamelCase to snake_case symbols
- def self.snake_case_sym(input)
- input.gsub(/([a-z])([A-Z])/, '\1_\2').downcase.to_sym
- end
+ # create a normalised key name by:
+ # 1. lowercasing input
+ # 2. converting spaces to underscores
+ # 3. convert to symbol
+ def self.key_name(input)
+ input.downcase.gsub(%r{\s}, '_').to_sym
+ end
- # Convert snake_case input symbol to CamelCase string
- def self.camel_case(input)
- # https://stackoverflow.com/a/24917606/3441106
- input.to_s.split('_').collect(&:capitalize).join
- end
+ # Convert input CamelCase to snake_case symbols
+ def self.snake_case_sym(input)
+ input.gsub(%r{([a-z])([A-Z])}, '\1_\2').downcase.to_sym
+ end
- def self.delete_rule(resource)
- Puppet.notice("(windows_firewall) deleting ipsec rule '#{resource[:display_name]}'")
- out = Puppet::Util::Execution.execute(resolve_ps_bridge + ['delete', resource[:name]]).to_s
- Puppet.debug out
- end
+ # Convert snake_case input symbol to CamelCase string
+ def self.camel_case(input)
+ # https://stackoverflow.com/a/24917606/3441106
+ input.to_s.split('_').map(&:capitalize).join
+ end
- def self.update_rule(resource)
- Puppet.notice("(windows_firewall) updating ipsec rule '#{resource[:display_name]}'")
+ def self.delete_rule(resource)
+ Puppet.notice("(windows_firewall) deleting ipsec rule '#{resource[:display_name]}'")
+ out = Puppet::Util::Execution.execute(resolve_ps_bridge + ['delete', resource[:name]]).to_s
+ Puppet.debug out
+ end
- # `Name` is mandatory and also a `parameter` not a `property`
- args = [ '-Name', resource[:name] ]
+ def self.update_rule(resource)
+ Puppet.notice("(windows_firewall) updating ipsec rule '#{resource[:display_name]}'")
- resource.properties.reject { |property|
- [:ensure, :protocol_type, :protocol_code].include?(property.name)
- }.each { |property|
- # All properties start `-`
- property_name = "-#{camel_case(property.name)}"
- property_value = to_ps(property.name).call(property.value)
+ # `Name` is mandatory and also a `parameter` not a `property`
+ args = [ '-Name', resource[:name] ]
- # protocol can optionally specify type and code, other properties are set very simply
- args << property_name
- args << property_value
- }
- Puppet.debug "Updating firewall ipsec rule with args: #{args}"
+ resource.properties.reject { |property|
+ [:ensure, :protocol_type, :protocol_code].include?(property.name)
+ }.each do |property|
+ # All properties start `-`
+ property_name = "-#{camel_case(property.name)}"
+ property_value = to_ps(property.name).call(property.value)
- out = Puppet::Util::Execution.execute(resolve_ps_bridge + ['update'] + args)
- Puppet.debug out
+ # protocol can optionally specify type and code, other properties are set very simply
+ args << property_name
+ args << property_value
end
+ Puppet.debug "Updating firewall ipsec rule with args: #{args}"
- # Create a new firewall rule using powershell
- # @see https://docs.microsoft.com/en-us/powershell/module/netsecurity/new-netfirewallrule?view=win10-ps
- def self.create_rule(resource)
- Puppet.notice("(windows_firewall) adding ipsec rule '#{resource[:display_name]}'")
-
- # `Name` is mandatory and also a `parameter` not a `property`
- args = [ '-Name', resource[:name] ]
-
- resource.properties.reject { |property|
- [:ensure, :protocol_type, :protocol_code].include?(property.name)
- }.each { |property|
- # All properties start `-`
- property_name = "-#{camel_case(property.name)}"
- property_value = to_ps(property.name).call(property.value)
-
- # protocol can optionally specify type and code, other properties are set very simply
- args << property_name
- args << property_value
- }
- Puppet.debug "Creating firewall ipsec rule with args: #{args}"
-
- out = Puppet::Util::Execution.execute(resolve_ps_bridge + ['create'] + args)
- Puppet.debug out
- end
+ out = Puppet::Util::Execution.execute(resolve_ps_bridge + ['update'] + args)
+ Puppet.debug out
+ end
- def self.rules
- Puppet.debug('query all ipsec rules')
- rules = JSON.parse Puppet::Util::Execution.execute(resolve_ps_bridge + ['show']).to_s
-
- # Rules is an array of hash as-parsed and hash keys need converted to
- # lowercase ruby labels
- puppet_rules = rules.map { |e|
- Hash[e.map { |k, v|
- key = snake_case_sym(k)
- [key, to_ruby(key).call(v)]
- }].merge({ensure: :present})
- }
- Puppet.debug("Parsed ipsec rules: #{puppet_rules.size}")
- puppet_rules
- end
+ # Create a new firewall rule using powershell
+ # @see https://docs.microsoft.com/en-us/powershell/module/netsecurity/new-netfirewallrule?view=win10-ps
+ def self.create_rule(resource)
+ Puppet.notice("(windows_firewall) adding ipsec rule '#{resource[:display_name]}'")
- # Each rule is se
- def self.parse_profile(input)
- profile = {}
- first_line = true
- profile_name = '__error__'
- input.split("\n").reject { |line|
- line =~ /---/ || line =~ /^\s*$/
- }.each { |line|
- if first_line
- # take the first word in the line - eg "public profile settings" -> "public"
- profile_name = line.split(" ")[0].downcase
- first_line = false
- else
- # nasty hack - "firewall policy" setting contains space and will break our
- # logic below. Also the setter in `netsh` to use is `firewallpolicy`. Just fix it...
- line = line.sub('Firewall Policy', 'firewallpolicy')
-
- # split each line at most twice by first glob of whitespace
- line_split = line.split(/\s+/, 2)
-
- if line_split.size == 2
- key = key_name(line_split[0].strip)
-
- # downcase all values for comparison purposes
- value = line_split[1].strip.downcase
-
- profile[key] = value
- end
- end
- }
+ # `Name` is mandatory and also a `parameter` not a `property`
+ args = [ '-Name', resource[:name] ]
- # if we see the rule then it must exist...
- profile[:name] = profile_name
+ resource.properties.reject { |property|
+ [:ensure, :protocol_type, :protocol_code].include?(property.name)
+ }.each do |property|
+ # All properties start `-`
+ property_name = "-#{camel_case(property.name)}"
+ property_value = to_ps(property.name).call(property.value)
- Puppet.debug "Parsed windows firewall profile: #{profile}"
- profile
+ # protocol can optionally specify type and code, other properties are set very simply
+ args << property_name
+ args << property_value
end
+ Puppet.debug "Creating firewall ipsec rule with args: #{args}"
- # Each rule is se
- def self.parse_global(input)
- globals = {}
- input.split("\n").reject { |line|
- line =~ /---/ || line =~ /^\s*$/
- }.each { |line|
+ out = Puppet::Util::Execution.execute(resolve_ps_bridge + ['create'] + args)
+ Puppet.debug out
+ end
+
+ def self.rules
+ Puppet.debug('query all ipsec rules')
+ rules = JSON.parse Puppet::Util::Execution.execute(resolve_ps_bridge + ['show']).to_s
+
+ # Rules is an array of hash as-parsed and hash keys need converted to
+ # lowercase ruby labels
+ puppet_rules = rules.map do |e|
+ Hash[e.map do |k, v|
+ key = snake_case_sym(k)
+ [key, to_ruby(key).call(v)]
+ end].merge({ ensure: :present })
+ end
+ Puppet.debug("Parsed ipsec rules: #{puppet_rules.size}")
+ puppet_rules
+ end
+
+ # Each rule is se
+ def self.parse_profile(input)
+ profile = {}
+ first_line = true
+ profile_name = '__error__'
+ input.split("\n").reject { |line|
+ line.include?('---') || line =~ %r{^\s*$}
+ }.each do |line|
+ if first_line
+ # take the first word in the line - eg "public profile settings" -> "public"
+ profile_name = line.split(' ')[0].downcase
+ first_line = false
+ else
+ # nasty hack - "firewall policy" setting contains space and will break our
+ # logic below. Also the setter in `netsh` to use is `firewallpolicy`. Just fix it...
+ line = line.sub('Firewall Policy', 'firewallpolicy')
# split each line at most twice by first glob of whitespace
- line_split = line.split(/\s+/, 2)
+ line_split = line.split(%r{\s+}, 2)
if line_split.size == 2
key = key_name(line_split[0].strip)
@@ -255,35 +226,60 @@ def self.parse_global(input)
# downcase all values for comparison purposes
value = line_split[1].strip.downcase
- case key
- when :secmethods
- # secmethods are output with a hypen like this:
- # DHGroup2-AES128-SHA1,DHGroup2-3DES-SHA1
- # but must be input with a colon like this:
- # DHGroup2:AES128-SHA1,DHGroup2:3DES-SHA1
- safe_value = value.split(',').map { |e|
- e.sub('-', ':')
- }.join(',')
- when :strongcrlcheck
- safe_value = value.split(':')[0]
- when :defaultexemptions
- safe_value = value.split(',').sort
- when :saidletimemin
- safe_value = value.sub('min', '')
- when :ipsecthroughnat
- safe_value = value.gsub(' ', '')
- else
- safe_value = value
- end
-
- globals[key] = safe_value
+ profile[key] = value
end
- }
+ end
+ end
- globals[:name] = 'global'
+ # if we see the rule then it must exist...
+ profile[:name] = profile_name
- Puppet.debug "Parsed windows firewall globals: #{globals}"
- globals
+ Puppet.debug "Parsed windows firewall profile: #{profile}"
+ profile
+ end
+
+ # Each rule is se
+ def self.parse_global(input)
+ globals = {}
+ input.split("\n").reject { |line|
+ line.include?('---') || line =~ %r{^\s*$}
+ }.each do |line|
+ # split each line at most twice by first glob of whitespace
+ line_split = line.split(%r{\s+}, 2)
+
+ next unless line_split.size == 2
+ key = key_name(line_split[0].strip)
+
+ # downcase all values for comparison purposes
+ value = line_split[1].strip.downcase
+
+ safe_value = case key
+ when :secmethods
+ # secmethods are output with a hypen like this:
+ # DHGroup2-AES128-SHA1,DHGroup2-3DES-SHA1
+ # but must be input with a colon like this:
+ # DHGroup2:AES128-SHA1,DHGroup2:3DES-SHA1
+ value.split(',').map { |e|
+ e.sub('-', ':')
+ }.join(',')
+ when :strongcrlcheck
+ value.split(':')[0]
+ when :defaultexemptions
+ value.split(',').sort
+ when :saidletimemin
+ value.sub('min', '')
+ when :ipsecthroughnat
+ value.delete(' ')
+ else
+ value
+ end
+
+ globals[key] = safe_value
end
+
+ globals[:name] = 'global'
+
+ Puppet.debug "Parsed windows firewall globals: #{globals}"
+ globals
end
end
diff --git a/metadata.json b/metadata.json
index d2cfc61..cb0d49f 100644
--- a/metadata.json
+++ b/metadata.json
@@ -1,6 +1,6 @@
{
"name": "webalex-windows_firewall",
- "version": "1.4.1",
+ "version": "1.4.2",
"author": "webalex",
"summary": "Manage the windows firewall with Puppet",
"license": "Apache-2.0",