abide_dev_utils 0.6.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.rubocop.yml +1 -1
- data/CODEOWNERS +1 -0
- data/abide_dev_utils.gemspec +7 -6
- data/itests.rb +138 -0
- data/lib/abide_dev_utils/cli/comply.rb +23 -7
- data/lib/abide_dev_utils/cli/puppet.rb +18 -0
- data/lib/abide_dev_utils/cli/xccdf.rb +26 -7
- data/lib/abide_dev_utils/comply.rb +225 -168
- data/lib/abide_dev_utils/errors/comply.rb +4 -0
- data/lib/abide_dev_utils/errors/general.rb +5 -0
- data/lib/abide_dev_utils/errors/xccdf.rb +8 -0
- data/lib/abide_dev_utils/gcloud.rb +2 -1
- data/lib/abide_dev_utils/output.rb +7 -3
- data/lib/abide_dev_utils/ppt/api.rb +219 -0
- data/lib/abide_dev_utils/ppt/score_module.rb +162 -0
- data/lib/abide_dev_utils/ppt.rb +22 -19
- data/lib/abide_dev_utils/validate.rb +5 -1
- data/lib/abide_dev_utils/version.rb +1 -1
- data/lib/abide_dev_utils/xccdf.rb +565 -12
- metadata +30 -14
- data/lib/abide_dev_utils/xccdf/cis/hiera.rb +0 -166
- data/lib/abide_dev_utils/xccdf/cis.rb +0 -3
- data/lib/abide_dev_utils/xccdf/utils.rb +0 -85
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3978f655b9053e54d4fd63d456d3d65b9079c7684c6fcc88a3f764ead1aa012a
|
4
|
+
data.tar.gz: 746265daf86c8095a0f4ddbf9909d85a30ba2495b2b449e19896584c6e88da2b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6bbb36782aff2d06e4fd55ea66582cf64c4414c2e13f1c3cca76c8b1c33c420f76474b058c2e2ce49bb294efc01b677588957fa8f745d3b2fa022683253281f
|
7
|
+
data.tar.gz: 78f92a917f547ba04f17610e0bca9cc5816efaadf7b066645cc75e40c303173a9b0d3e27ea0d7a741219f88c5e479810e14f180fc813cd5a31e3d6f0ee4bf65e
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/CODEOWNERS
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
* @puppetlabs/abide-team
|
data/abide_dev_utils.gemspec
CHANGED
@@ -7,14 +7,14 @@ require "abide_dev_utils/version"
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
8
|
spec.name = "abide_dev_utils"
|
9
9
|
spec.version = AbideDevUtils::VERSION
|
10
|
-
spec.authors = ["
|
11
|
-
spec.email = ["
|
10
|
+
spec.authors = ["abide-team"]
|
11
|
+
spec.email = ["abide-team@puppet.com"]
|
12
12
|
|
13
|
-
spec.summary = "Helper utilities for developing
|
14
|
-
spec.description = "Provides a CLI with helpful utilities for developing
|
15
|
-
spec.homepage = "https://github.com/
|
13
|
+
spec.summary = "Helper utilities for developing compliance Puppet code"
|
14
|
+
spec.description = "Provides a CLI with helpful utilities for developing compliance Puppet code"
|
15
|
+
spec.homepage = "https://github.com/puppetlabs/abide_dev_utils"
|
16
16
|
spec.license = "MIT"
|
17
|
-
spec.required_ruby_version = Gem::Requirement.new(">= 2.
|
17
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
|
18
18
|
|
19
19
|
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
20
20
|
|
@@ -39,6 +39,7 @@ Gem::Specification.new do |spec|
|
|
39
39
|
spec.add_dependency 'ruby-progressbar', '~> 1.11'
|
40
40
|
spec.add_dependency 'selenium-webdriver', '~> 4.0.0.beta4'
|
41
41
|
spec.add_dependency 'google-cloud-storage', '~> 1.34'
|
42
|
+
spec.add_dependency 'hashdiff', '~> 1.0'
|
42
43
|
|
43
44
|
# Dev dependencies
|
44
45
|
spec.add_development_dependency 'bundler'
|
data/itests.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'json'
|
5
|
+
require 'yaml'
|
6
|
+
require 'abide_dev_utils/comply'
|
7
|
+
require 'abide_dev_utils/ppt/api'
|
8
|
+
|
9
|
+
OS_BENCHMARK_MAP = {
|
10
|
+
'centos-7' => 'CIS_CentOS_Linux_7_Benchmark_v3.1.1-xccdf.xml',
|
11
|
+
'centos-8' => 'CIS_CentOS_Linux_8_Benchmark_v1.0.1-xccdf.xml',
|
12
|
+
'rhel-7' => 'CIS_Red_Hat_Enterprise_Linux_7_Benchmark_v3.1.1-xccdf.xml',
|
13
|
+
'rhel-8' => 'CIS_Red_Hat_Enterprise_Linux_8_Benchmark_v1.0.1-xccdf.xml',
|
14
|
+
'serv-2016' => 'CIS_Microsoft_Windows_Server_2016_RTM_(Release_1607)_Benchmark_v1.3.0-xccdf.xml',
|
15
|
+
'serv-2019' => 'CIS_Microsoft_Windows_Server_2019_Benchmark_v1.2.1-xccdf.xml'
|
16
|
+
}
|
17
|
+
EL_PROFILE_1_SERVER = 'xccdf_org.cisecurity.benchmarks_profile_Level_1_-_Server'
|
18
|
+
WIN_PROFILE_1_MS = 'xccdf_org.cisecurity.benchmarks_profile_Level_1_-_Member_Server'
|
19
|
+
NIX_SCAN_HASH = {
|
20
|
+
'nix-centos-7.c.team-sse.internal' => {
|
21
|
+
'benchmark' => OS_BENCHMARK_MAP['centos-7'],
|
22
|
+
'profile' => EL_PROFILE_1_SERVER
|
23
|
+
},
|
24
|
+
'nix-centos-8.c.team-sse.internal' => {
|
25
|
+
'benchmark' => OS_BENCHMARK_MAP['centos-8'],
|
26
|
+
'profile' => EL_PROFILE_1_SERVER
|
27
|
+
},
|
28
|
+
'nix-rhel-7.c.team-sse.internal' => {
|
29
|
+
'benchmark' => OS_BENCHMARK_MAP['rhel-7'],
|
30
|
+
'profile' => EL_PROFILE_1_SERVER
|
31
|
+
},
|
32
|
+
'nix-rhel-8.c.team-sse.internal' => {
|
33
|
+
'benchmark' => OS_BENCHMARK_MAP['rhel-8'],
|
34
|
+
'profile' => EL_PROFILE_1_SERVER
|
35
|
+
}
|
36
|
+
}.freeze
|
37
|
+
WIN_SCAN_HASH = {
|
38
|
+
'win-server-2016.c.team-sse.internal' => {
|
39
|
+
'benchmark' => OS_BENCHMARK_MAP['serv-2016'],
|
40
|
+
'profile' => EL_PROFILE_1_SERVER
|
41
|
+
},
|
42
|
+
'win-serv-2019.c.team-sse.internal' => {
|
43
|
+
'benchmark' => OS_BENCHMARK_MAP['serv-2019'],
|
44
|
+
'profile' => WIN_PROFILE_1_MS
|
45
|
+
}
|
46
|
+
}.freeze
|
47
|
+
|
48
|
+
scan_hash = ENV['ABIDE_OS'] == 'nix' ? NIX_SCAN_HASH : WIN_SCAN_HASH
|
49
|
+
node_group_name = ENV['ABIDE_OS'] == 'nix' ? 'CEM Linux Nodes' : 'CEM Windows Nodes'
|
50
|
+
|
51
|
+
puts 'Creating client...'
|
52
|
+
client = AbideDevUtils::Ppt::ApiClient.new(ENV['PUPPET_HOST'], auth_token: ENV['PE_ACCESS_TOKEN'])
|
53
|
+
puts 'Starting code deploy...'
|
54
|
+
code_manager_deploy = client.post_codemanager_deploys('environments' => ['production'], 'wait' => true)
|
55
|
+
raise 'Code manager deployment failed!' unless code_manager_deploy['status'] == 'complete'
|
56
|
+
|
57
|
+
puts 'Code deploy successful...'
|
58
|
+
puts 'Gathering node group ID...'
|
59
|
+
node_groups = client.get_classifier1_groups
|
60
|
+
node_group_id = nil
|
61
|
+
node_groups.each { |x| node_group_id = x['id'] if x['name'] == node_group_name }
|
62
|
+
raise 'Failed to find requested node group!' if node_group_id.nil?
|
63
|
+
|
64
|
+
puts 'Running Puppet on nodes...'
|
65
|
+
puppet_run = client.post_orchestrator_command_deploy('environment' => 'production', 'scope' => { 'node_group' => node_group_id })
|
66
|
+
puts "Started job #{puppet_run['job']['name']}..."
|
67
|
+
timeout = 0
|
68
|
+
run_complete = false
|
69
|
+
until run_complete || timeout >= 30
|
70
|
+
puts "Waiting on job #{puppet_run['job']['name']} to complete..."
|
71
|
+
status = client.get_orchestrator_jobs(puppet_run['job']['name'])
|
72
|
+
case status['state']
|
73
|
+
when 'failed'
|
74
|
+
raise "Job #{puppet_run['job']['name']} finished with failures!"
|
75
|
+
when 'finished'
|
76
|
+
run_complete = true
|
77
|
+
break
|
78
|
+
else
|
79
|
+
timeout += 1
|
80
|
+
sleep(10)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
raise 'Job timed out waiting for completion' unless run_complete
|
84
|
+
|
85
|
+
puts 'Starting node scans...'
|
86
|
+
scan_job = client.post_orchestrator_command_task(
|
87
|
+
'environment' => 'production',
|
88
|
+
'task' => 'comply::ciscat_scan',
|
89
|
+
'params' => {
|
90
|
+
'comply_port' => '443',
|
91
|
+
'comply_server' => ENV['COMPLY_FQDN'],
|
92
|
+
'ssl_verify_mode' => 'none',
|
93
|
+
'scan_type' => 'desired',
|
94
|
+
'scan_hash' => JSON.generate(scan_hash)
|
95
|
+
},
|
96
|
+
'scope' => {
|
97
|
+
'node_group' => node_group_id
|
98
|
+
}
|
99
|
+
)
|
100
|
+
puts "Started scan #{scan_job['job']['name']}..."
|
101
|
+
timeout = 0
|
102
|
+
scan_complete = false
|
103
|
+
until scan_complete || timeout >= 30
|
104
|
+
puts "Waiting on scan #{scan_job['job']['name']} to complete..."
|
105
|
+
status = client.get_orchestrator_jobs(scan_job['job']['name'])
|
106
|
+
case status['state']
|
107
|
+
when 'failed'
|
108
|
+
raise "Task #{scan_job['job']['name']} finished with failures!"
|
109
|
+
when 'finished'
|
110
|
+
scan_complete = true
|
111
|
+
break
|
112
|
+
else
|
113
|
+
timeout += 1
|
114
|
+
sleep(10)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
raise 'Job timed out waiting for completion' unless scan_complete
|
118
|
+
|
119
|
+
puts 'Collecting scan report from Comply...'
|
120
|
+
onlylist = scan_hash.keys
|
121
|
+
scan_report = AbideDevUtils::Comply.build_report("https://#{ENV['COMPLY_FQDN']}", ENV['COMPLY_PASSWORD'], nil, onlylist: onlylist)
|
122
|
+
puts 'Saving report to nix_report.yaml...'
|
123
|
+
File.open('nix_report.yaml', 'w') { |f| f.write(scan_report.to_yaml) }
|
124
|
+
|
125
|
+
puts 'Comparing current report to last report...'
|
126
|
+
opts = {
|
127
|
+
report_name: 'nix_report.yaml',
|
128
|
+
remote_storage: 'gcloud',
|
129
|
+
upload: true
|
130
|
+
}
|
131
|
+
result = AbideDevUtils::Comply.compare_reports(File.expand_path('./nix_report.yaml'), 'nix_report.yaml', opts)
|
132
|
+
if result
|
133
|
+
puts 'Success!'
|
134
|
+
exit(0)
|
135
|
+
else
|
136
|
+
puts 'Failure!'
|
137
|
+
exit(1)
|
138
|
+
end
|
@@ -12,6 +12,7 @@ module Abide
|
|
12
12
|
def initialize
|
13
13
|
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: true)
|
14
14
|
add_command(ComplyReportCommand.new)
|
15
|
+
add_command(ComplyCompareReportCommand.new)
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
@@ -57,23 +58,19 @@ module Abide
|
|
57
58
|
options.on('-t [SECONDS]', '--timeout [SECONDS]', OPT_TIMEOUT_DESC) do |t|
|
58
59
|
@data[:timeout] = t
|
59
60
|
end
|
60
|
-
options.on('-s
|
61
|
+
options.on('-s [X,Y,Z]', '--status [X,Y,Z]',
|
61
62
|
%w[pass fail error notapplicable notchecked unknown informational],
|
62
63
|
Array,
|
63
64
|
OPT_STATUS_DESC) do |s|
|
64
65
|
s&.map! { |i| i == 'notchecked' ? 'not checked' : i }
|
65
66
|
@data[:status] = s
|
66
67
|
end
|
67
|
-
options.on('--only
|
68
|
+
options.on('--only [X,Y,Z]', Array, OPT_ONLY_NODES) do |o|
|
68
69
|
@data[:onlylist] = o
|
69
70
|
end
|
70
|
-
options.on('--ignore
|
71
|
+
options.on('--ignore [X,Y,Z]', Array, OPT_IGNORE_NODES) do |i|
|
71
72
|
@data[:ignorelist] = i
|
72
73
|
end
|
73
|
-
# options.on('-R', '--[no-]regression-test', OPT_REGRESSION_TEST) do |r|
|
74
|
-
# @data[:regression] = r
|
75
|
-
# end
|
76
|
-
# options.on('--')
|
77
74
|
end
|
78
75
|
|
79
76
|
def help_arguments
|
@@ -95,5 +92,24 @@ module Abide
|
|
95
92
|
Abide::CLI::OUTPUT.yaml(report, file: outfile)
|
96
93
|
end
|
97
94
|
end
|
95
|
+
|
96
|
+
class ComplyCompareReportCommand < AbideCommand
|
97
|
+
CMD_NAME = 'compare-report'
|
98
|
+
CMD_SHORT = 'Compare two Comply reports and get the differences.'
|
99
|
+
CMD_LONG = 'Compare two Comply reports and get the differences. Report A is compared to report B, showing what changes it would take for A to equal B.'
|
100
|
+
CMD_REPORT_A = 'The current Comply report yaml file'
|
101
|
+
CMD_REPORT_B = 'The old Comply report yaml file name or full path'
|
102
|
+
def initialize
|
103
|
+
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: false)
|
104
|
+
argument_desc(REPORT_A: CMD_REPORT_A, REPORT_B: CMD_REPORT_B)
|
105
|
+
options.on('-u', '--upload-new', 'If you want to upload the new scan report') { @data[:upload] = true }
|
106
|
+
options.on('-s [STORAGE]', '--remote-storage [STORAGE]', 'Remote storage to upload the report to. (Only supports "gcloud")') { |x| @data[:remote_storage] = x }
|
107
|
+
options.on('-r [NAME]', '--name [NAME]', 'The name to upload the report as') { |x| @data[:report_name] = x }
|
108
|
+
end
|
109
|
+
|
110
|
+
def execute(report_a, report_b)
|
111
|
+
AbideDevUtils::Comply.compare_reports(report_a, report_b, @data)
|
112
|
+
end
|
113
|
+
end
|
98
114
|
end
|
99
115
|
end
|
@@ -217,5 +217,23 @@ module Abide
|
|
217
217
|
AbideDevUtils::Ppt.add_cis_comment(path, xccdf, number_format: @data.fetch(:number_format, false))
|
218
218
|
end
|
219
219
|
end
|
220
|
+
|
221
|
+
class PuppetScoreModuleCommand < AbideCommand
|
222
|
+
CMD_NAME = 'score'
|
223
|
+
CMD_SHORT = 'Scores a Puppet module just like Puppet Forge'
|
224
|
+
CMD_LONG = 'Scores a Puppet module just like Puppet Forge. This is a useful quality-check before publishing a module.'
|
225
|
+
def initialize
|
226
|
+
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: false)
|
227
|
+
options.on('-o [PATH]', '--outfile [PATH]', 'Save results to a file') { |x| @data[:outfile] = x }
|
228
|
+
options.on('-q', '--quiet', FalseClass, 'Do not print results to console') { |x| @data[:quiet] = x }
|
229
|
+
options.on('-c', '--checks', Array, 'Comma-separated list of individual checks to run. Defaults to running all checks.') { |x| @data[:check] = x }
|
230
|
+
options.on('-m [PATH]', '--module [PATH]', 'Path to a Puppet module to score. Defaults to using the current directory.') { |x| @data[:module] = x }
|
231
|
+
end
|
232
|
+
|
233
|
+
def execute
|
234
|
+
module_path = @data.fetch(:module, nil)
|
235
|
+
AbideDevUtils::Ppt.score_module(module_path, **@data)
|
236
|
+
end
|
237
|
+
end
|
220
238
|
end
|
221
239
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'abide_dev_utils/cli/abstract'
|
3
4
|
require 'abide_dev_utils/xccdf'
|
4
5
|
|
5
6
|
module Abide
|
@@ -14,11 +15,12 @@ module Abide
|
|
14
15
|
long_desc(CMD_LONG)
|
15
16
|
add_command(CmdParse::HelpCommand.new, default: true)
|
16
17
|
add_command(XccdfToHieraCommand.new)
|
18
|
+
add_command(XccdfDiffCommand.new)
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
20
22
|
class XccdfToHieraCommand < CmdParse::Command
|
21
|
-
CMD_NAME = '
|
23
|
+
CMD_NAME = 'to-hiera'
|
22
24
|
CMD_SHORT = 'Generates control coverage report'
|
23
25
|
CMD_LONG = 'Generates report of valid Puppet classes that match with Hiera controls'
|
24
26
|
def initialize
|
@@ -37,15 +39,32 @@ module Abide
|
|
37
39
|
|
38
40
|
def execute(xccdf_file)
|
39
41
|
@data[:type] = 'cis' if @data[:type].nil?
|
40
|
-
|
41
|
-
|
42
|
+
hfile = AbideDevUtils::XCCDF.to_hiera(xccdf_file, @data)
|
43
|
+
AbideDevUtils::Output.yaml(hfile, console: @data[:file].nil?, file: @data[:file])
|
42
44
|
end
|
45
|
+
end
|
43
46
|
|
44
|
-
|
47
|
+
class XccdfDiffCommand < AbideCommand
|
48
|
+
CMD_NAME = 'diff'
|
49
|
+
CMD_SHORT = 'Generates a diff report between two XCCDF files'
|
50
|
+
CMD_LONG = 'Generates a diff report between two XCCDF files'
|
51
|
+
CMD_FILE1_ARG = 'path to first XCCDF file'
|
52
|
+
CMD_FILE2_ARG = 'path to second XCCDF file'
|
53
|
+
def initialize
|
54
|
+
super(CMD_NAME, CMD_SHORT, CMD_LONG, takes_commands: false)
|
55
|
+
argument_desc(FILE1: CMD_FILE1_ARG, FILE2: CMD_FILE2_ARG)
|
56
|
+
options.on('-o [PATH]', '--out-file', 'Save the report as a yaml file') { |x| @data[:outfile] = x }
|
57
|
+
options.on('-p [PROFILE]', '--profile', 'Only diff and specific profile in the benchmarks') do |x|
|
58
|
+
@data[:profile] = x
|
59
|
+
end
|
60
|
+
options.on('-q', '--quiet', 'Show no output in the terminal') { @data[:quiet] = false }
|
61
|
+
options.on('--no-diff-profiles', 'Do not diff the profiles in the XCCDF files') { @data[:diff_profiles] = false }
|
62
|
+
options.on('--no-diff-controls', 'Do not diff the controls in the XCCDF files') { @data[:diff_controls] = false }
|
63
|
+
end
|
45
64
|
|
46
|
-
def
|
47
|
-
|
48
|
-
|
65
|
+
def execute(file1, file2)
|
66
|
+
diffreport = AbideDevUtils::XCCDF.diff(file1, file2, @data)
|
67
|
+
AbideDevUtils::Output.yaml(diffreport, console: @data.fetch(:quiet, true), file: @data.fetch(:outfile, nil))
|
49
68
|
end
|
50
69
|
end
|
51
70
|
end
|