Skip to content

Commit

Permalink
Add tolerance as hiera lookup, store in options. Upon confinement if … (
Browse files Browse the repository at this point in the history
#152)

* Add tolerance as hiera lookup, store in options. Upon confinement if tolerance is set, use it to determine which CEs should be removed if they haven't already been by the confine process.

* Add readme, changelog, version bump. Begin adding spec test changes

* Add unit tests for enforcement_tolerance

* Add Gemfile changes so pipeline doesn't fail

---------

Co-authored-by: Garrett Adams <[email protected]>
  • Loading branch information
CyrodiilSavior and Garrett Adams authored Feb 22, 2023
1 parent d847216 commit cc0405d
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 5 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
* Thu Feb 16 2023 Garrett Adams <[email protected]> - 3.3.0
- Add tolerance as hiera lookup. Confine checks that can be enforced
based on the tolerance setting.

* Fri Jun 03 2022 Mike Riddle <[email protected]> - 3.2.3
- Updated reporting to account for escaping knockout prefixes

Expand Down
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ group :test do
major_puppet_version = puppet_version.scan(/(\d+)(?:\.|\Z)/).flatten.first.to_i
gem 'rake'
gem 'puppet', puppet_version
gem 'facter', ENV['FACTER_GEM_VERSION'] || '~> 4.2.0'
gem 'rspec'
gem 'rspec-puppet'
gem 'hiera-puppet-helper'
Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,25 @@ or specfic os version.
compliance_markup::enforcement: [ 'disa_stig', 'nist_800_53_rev4' ]
```

### Configuring enforcement tolerance

Certain checks have a built-in safety mechanism built in called 'enforcement tolerance' this allows the user to define what level of risk they wish to enfoce and based on their tolerance level.

The default enforcement tolerance is `40`. This will allow enforcement of any checks that can not cause any access restrictions or breakages to a system. If you wish to enforce checks that are more dangerous or less dangerous, you can override this default value but using:

```yaml
---
compliance_markup::enforcement_tolerance_level: 40
```

Valid levels are:

- 20: Remediation works fine on all systems, changes will be made.
- 40: Remediation works, minor issues may arise in special cases
- 60: May cause login or access issues on system
- 80: Breaking remediation changes will be enforced


### Debugging the Hiera Backend

The Hiera backend exposes a debug interface to users via `lookup`. These can be used to query the library for data or metrics.
Expand Down
19 changes: 19 additions & 0 deletions lib/puppetx/simp/compliance_mapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def enforcement(key, context=self, options={"mode" => "value"}, &block)

begin
profile_list = Array(call_function('lookup', 'compliance_markup::enforcement', { 'default_value' => [] }))
options[:tolerance_setting] = Integer(call_function('lookup', 'compliance_markup::enforcement_tolerance_level', { 'default_value' => 40 }).to_i)

unless profile_list == []
debug("debug: compliance_markup::enforcement set to #{profile_list}, attempting to enforce")
Expand Down Expand Up @@ -229,6 +230,7 @@ def load(options={}, &block)
@compliance_data.each do |filename, map|
if map.key?("version")
version = SemanticPuppet::Version.parse(map["version"])
map["tolerance_setting"] = options[:tolerance_setting]

if version.major == 2
v2.import(filename, map)
Expand Down Expand Up @@ -367,6 +369,22 @@ def apply_confinement(value)
end
end
end
if specification.key?('remediation') && !delete_item
remediation_hash = specification['remediation']
if remediation_hash && @tolerance_setting
highest_saved_risk = 0
remediation_hash = remediation_hash.first if remediation_hash.instance_of?(Array)
next if remediation_hash.nil?
delete_item = true if remediation_hash.keys.include?('disabled')
next unless remediation_hash.keys.include?('risk')

risk_level = remediation_hash['risk'].first['level']
if risk_level >= highest_saved_risk && (@tolerance_setting <= risk_level)
highest_saved_risk = risk_level
delete_item = true
end
end
end
end

delete_item
Expand All @@ -377,6 +395,7 @@ def apply_confinement(value)

def import(filename, data)
data.each do |key, value|
@tolerance_setting = data["tolerance_setting"] if data.key? "tolerance_setting"
apply_confinement(value) if value.is_a?(Hash)

case key
Expand Down
2 changes: 1 addition & 1 deletion metadata.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "simp-compliance_markup",
"version": "3.2.3",
"version": "3.3.0",
"author": "SIMP Team",
"summary": "Compliance-mapping annotation for Puppet code",
"license": "Apache-2.0",
Expand Down
3 changes: 2 additions & 1 deletion spec/classes/init_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,8 @@ class test4 (
let(:facts) {
os_facts.merge(
{
:target_compliance_profile => profile_name
:target_compliance_profile => profile_name,
:target_enforcement_tolerance => 40
}
)
}
Expand Down
2 changes: 2 additions & 0 deletions spec/fixtures/hieradata/compliance-engine.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ compliance_markup::report_types:
# do something different based on your test requirements
compliance_markup::enforcement:
- "%{facts.target_compliance_profile}"

compliance_markup::enforcement_tolerance_level: "%{facts.target_enforcement_tolerance}"
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ version: 2.0.0
compliance_markup::enforcement:
- test_profile

compliance_markup::enforcement_tolerance_level: 40

profiles:
test_profile:
controls:
Expand Down
134 changes: 131 additions & 3 deletions spec/functions/compliance_markup/01_enforcement_confine_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,82 @@
'os.family' => '!RedHat',
},
},
'01_el7_check' => {
'01_disabled_check' => {
'type' => 'puppet-class-parameter',
'settings' => {
'parameter' => 'test_module_01::is_disabled',
'value' => true,
},
'ces' => [
'01_ce2',
],
'remediation' => {
'disabled' => [
{'reason'=> "This is the reason this check is disabled."}
]
},
},
'01_level_21_check' => {
'type' => 'puppet-class-parameter',
'settings' => {
'parameter' => 'test_module_01::is_level_21',
'value' => true,
},
'ces' => [
'01_ce2',
],
'remediation' => {
'risk' => [
{'level'=> 21}
]
},
},
'01_level_41_check' => {
'type' => 'puppet-class-parameter',
'settings' => {
'parameter' => 'test_module_01::is_level_41',
'value' => true,
},
'ces' => [
'01_ce2',
],
'remediation' => {
'risk' => [
{'level'=> 41, 'reason'=> 'this is the reason for level 41'}
]
},
},
'01_level_61_check' => {
'type' => 'puppet-class-parameter',
'settings' => {
'parameter' => 'test_module_01::is_level_61',
'value' => true,
},
'ces' => [
'01_ce2',
],
'remediation' => {
'risk' => [
{'level'=> 61, 'reason'=> 'this is the reason for level 61'}
]
},
},
'01_level_81_check' => {
'type' => 'puppet-class-parameter',
'settings' => {
'parameter' => 'test_module_01::is_level_81',
'value' => true,
},
'ces' => [
'01_ce2',
],
'remediation' => {
'risk' => [
{'level'=> 81, 'reason'=> 'this is the reason for level 81'}
]
},
},
'01_el7_check' => {
'type' => 'puppet-class-parameter',
'settings' => {
'parameter' => 'test_module_01::el_version',
Expand All @@ -87,7 +162,7 @@
'01_ce2',
],
'confine' => {
'os.name' => [
'os.name' => [
'RedHat',
'CentOS',
],
Expand Down Expand Up @@ -164,7 +239,6 @@
end

let(:hieradata) { 'compliance-engine' }

# Test for confine on a single fact in checks.
if os_facts[:osfamily] == 'RedHat'
it { is_expected.to run.with_params('test_module_01::is_el').and_return(true) }
Expand Down Expand Up @@ -207,6 +281,60 @@

# Test for confine on module name & module version in ce.
it { is_expected.to run.with_params('test_module_01::fixed_confines').and_raise_error(Puppet::DataBinding::LookupError, "Function lookup() did not find a value for the name 'test_module_01::fixed_confines'") }


end

context "on #{os} with compliance_markup::::enforcement and an existing profile using tolerance above level 21" do
let(:facts) do
os_facts.merge('target_compliance_profile' => '01_profile_test', 'target_enforcement_tolerance' => "22")
end
let(:hieradata) { 'compliance-engine' }

it { is_expected.to run.with_params('test_module_01::is_disabled').and_raise_error(Puppet::DataBinding::LookupError, "Function lookup() did not find a value for the name 'test_module_01::is_disabled'") }
it { is_expected.to run.with_params('test_module_01::is_level_21').and_return(true) }
it { is_expected.to run.with_params('test_module_01::is_level_41').and_raise_error(Puppet::DataBinding::LookupError, "Function lookup() did not find a value for the name 'test_module_01::is_level_41'") }
it { is_expected.to run.with_params('test_module_01::is_level_61').and_raise_error(Puppet::DataBinding::LookupError, "Function lookup() did not find a value for the name 'test_module_01::is_level_61'") }
it { is_expected.to run.with_params('test_module_01::is_level_81').and_raise_error(Puppet::DataBinding::LookupError, "Function lookup() did not find a value for the name 'test_module_01::is_level_81'") }
end

context "on #{os} with compliance_markup::::enforcement and an existing profile using tolerance above level 41" do
let(:facts) do
os_facts.merge('target_compliance_profile' => '01_profile_test', 'target_enforcement_tolerance' => "42")
end
let(:hieradata) { 'compliance-engine' }

it { is_expected.to run.with_params('test_module_01::is_disabled').and_raise_error(Puppet::DataBinding::LookupError, "Function lookup() did not find a value for the name 'test_module_01::is_disabled'") }
it { is_expected.to run.with_params('test_module_01::is_level_21').and_return(true) }
it { is_expected.to run.with_params('test_module_01::is_level_41').and_return(true) }
it { is_expected.to run.with_params('test_module_01::is_level_61').and_raise_error(Puppet::DataBinding::LookupError, "Function lookup() did not find a value for the name 'test_module_01::is_level_61'") }
it { is_expected.to run.with_params('test_module_01::is_level_81').and_raise_error(Puppet::DataBinding::LookupError, "Function lookup() did not find a value for the name 'test_module_01::is_level_81'") }
end

context "on #{os} with compliance_markup::::enforcement and an existing profile using tolerance above level 61" do
let(:facts) do
os_facts.merge('target_compliance_profile' => '01_profile_test', 'target_enforcement_tolerance' => "62")
end
let(:hieradata) { 'compliance-engine' }

it { is_expected.to run.with_params('test_module_01::is_disabled').and_raise_error(Puppet::DataBinding::LookupError, "Function lookup() did not find a value for the name 'test_module_01::is_disabled'") }
it { is_expected.to run.with_params('test_module_01::is_level_21').and_return(true) }
it { is_expected.to run.with_params('test_module_01::is_level_41').and_return(true) }
it { is_expected.to run.with_params('test_module_01::is_level_61').and_return(true) }
it { is_expected.to run.with_params('test_module_01::is_level_81').and_raise_error(Puppet::DataBinding::LookupError, "Function lookup() did not find a value for the name 'test_module_01::is_level_81'") }
end

context "on #{os} with compliance_markup::::enforcement and an existing profile using tolerance above level 81" do
let(:facts) do
os_facts.merge('target_compliance_profile' => '01_profile_test', 'target_enforcement_tolerance' => "82")
end
let(:hieradata) { 'compliance-engine' }

it { is_expected.to run.with_params('test_module_01::is_disabled').and_raise_error(Puppet::DataBinding::LookupError, "Function lookup() did not find a value for the name 'test_module_01::is_disabled'") }
it { is_expected.to run.with_params('test_module_01::is_level_21').and_return(true) }
it { is_expected.to run.with_params('test_module_01::is_level_41').and_return(true) }
it { is_expected.to run.with_params('test_module_01::is_level_61').and_return(true) }
it { is_expected.to run.with_params('test_module_01::is_level_81').and_return(true) }
end
end
end

0 comments on commit cc0405d

Please sign in to comment.