cem_acpt 0.11.2 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +93 -0
- data/docs/ARCHITECTURE.md +10 -16
- data/exe/cem_acpt_scan +16 -0
- data/lib/cem_acpt/cli.rb +24 -0
- data/lib/cem_acpt/config/base.rb +10 -3
- data/lib/cem_acpt/config/cem_acpt_scan.rb +112 -0
- data/lib/cem_acpt/config.rb +1 -0
- data/lib/cem_acpt/platform/gcp.rb +6 -9
- data/lib/cem_acpt/provision/terraform/linux.rb +52 -6
- data/lib/cem_acpt/provision/terraform.rb +147 -10
- data/lib/cem_acpt/scan/daemon_client.rb +91 -0
- data/lib/cem_acpt/scan/errors.rb +44 -0
- data/lib/cem_acpt/scan/result.rb +89 -0
- data/lib/cem_acpt/scan.rb +17 -0
- data/lib/cem_acpt/test_data.rb +59 -3
- data/lib/cem_acpt/test_runner/log_formatter/scan_result_formatter.rb +72 -0
- data/lib/cem_acpt/test_runner/log_formatter.rb +4 -1
- data/lib/cem_acpt/test_runner.rb +103 -5
- data/lib/cem_acpt/version.rb +1 -1
- data/lib/cem_acpt.rb +18 -0
- data/lib/terraform/gcp/linux/main.tf +129 -1
- data/lib/terraform/gcp/linux/scan/scan_service.rb +148 -0
- data/lib/terraform/gcp/linux/scan/scan_service.service +12 -0
- data/lib/terraform/gcp/windows/main.tf +1 -1
- data/lib/terraform/image/gcp/linux/main.tf +1 -1
- data/specifications/CEM-6511.md +286 -0
- data/specifications/CEM-6720.md +187 -0
- data/specifications/CEM-6759.md +168 -0
- data/specifications/CEM-6760.md +120 -0
- data/specifications/CEM-6761.md +136 -0
- data/specifications/CEM-6762.md +163 -0
- data/specifications/CEM-6765.md +101 -0
- metadata +23 -4
- data/lib/cem_acpt/provision.rb +0 -20
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b82bc3c9709dc1486de6675c33508a532f8c036fde480e6fd967e5a2e7dfdae8
|
|
4
|
+
data.tar.gz: 4fad921e96175030f521ac21a898ee3488a57b109e309f696980e044efc28123
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b711d5b4ee1cf1f4c712d38b12c68fd3e20939a69e161004b1b2ecc0256838c04473bf089b86e1c78097db848197b6218617cbc31fb53ba52a2bd7dc84910b07
|
|
7
|
+
data.tar.gz: bd425c7655b89ce251c32d239b0dd7ac1333d0d17f262e08a8c997b388a065a900f6849404e31fbdd969d3e8042aeb934b276b2b002611c72e190e4094245a83
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -27,6 +27,7 @@ cem_acpt --config ./cem_acpt_config.yaml
|
|
|
27
27
|
```bash
|
|
28
28
|
cem_acpt -h | --help
|
|
29
29
|
cem_acpt_image -h | --help
|
|
30
|
+
cem_acpt_scan -h | --help
|
|
30
31
|
```
|
|
31
32
|
|
|
32
33
|
> If you do not delete Gemfile.lock before running `bundle install`, you may encounter dependency version errors. Just delete Gemfile.lock and run `bundle install` again to get past these.
|
|
@@ -297,6 +298,98 @@ Much like `name_pattern_vars`, specifying the `image_name_builder` top-level key
|
|
|
297
298
|
* Destroy the test nodes
|
|
298
299
|
* Report the results of the tests
|
|
299
300
|
|
|
301
|
+
## Automated benchmark scans (`cem_acpt_scan`)
|
|
302
|
+
|
|
303
|
+
`cem_acpt_scan` is a third binary that provisions test nodes the same way `cem_acpt` does, applies the acceptance test case's `manifest.pp`, then runs a real benchmark scan against the node — OpenSCAP for STIG profiles, CIS-CAT Pro for CIS profiles — instead of executing Goss assertions. It exists for AI-driven and human workflows that need to validate a manifest change against the actual published benchmark, not just developer-authored Goss tests.
|
|
304
|
+
|
|
305
|
+
Linux only in this version. A Windows test case requested via `-t` causes the run to fail before any node is provisioned.
|
|
306
|
+
|
|
307
|
+
### Quickstart
|
|
308
|
+
|
|
309
|
+
From the root of the consuming module (e.g. `sce_linux`):
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
cem_acpt_scan --config ./cem_acpt_scan_config.yaml -t cis_rhel-8_firewalld_server_2
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
`cem_acpt_scan` discovers test cases the same way `cem_acpt` does, but only `manifest.pp` is required in each test directory — `goss.yaml` is optional.
|
|
316
|
+
|
|
317
|
+
### CLI flags
|
|
318
|
+
|
|
319
|
+
In addition to the shared flags (`-c/--config`, `-I/--CI`, `-q/--quiet`, `-v/--verbose`, `-L/--log-file`, `-Y`, `-X`, `-D/--no-destroy-nodes`, etc.), `cem_acpt_scan` adds:
|
|
320
|
+
|
|
321
|
+
| Flag | Effect |
|
|
322
|
+
| --- | --- |
|
|
323
|
+
| `-m, --module-dir DIR` | Path to the consuming module root. Same semantics as `cem_acpt`. |
|
|
324
|
+
| `-t, --tests TESTS` | Comma-separated list of acceptance-test case names to scan. |
|
|
325
|
+
| `--scanner SCANNER` | Override the scanner. One of `openscap`, `ciscat`. Default: derived from the test-case framework (`stig_*` → openscap, `cis_*` → ciscat). |
|
|
326
|
+
| `--threshold N` | Override the global pass/fail score threshold (float, 0–100). |
|
|
327
|
+
| `--scan-output FILE` | Save the normalized JSON report to `FILE`. Multi-case runs fan out to `FILE.<test_case>.json` so reports don't collide. Without this flag, the JSON is printed to stdout only. |
|
|
328
|
+
|
|
329
|
+
### Configuration
|
|
330
|
+
|
|
331
|
+
Scan-mode settings live under the top-level `cem_acpt_scan` config key:
|
|
332
|
+
|
|
333
|
+
```yaml
|
|
334
|
+
cem_acpt_scan:
|
|
335
|
+
# Global pass/fail threshold (scanner-native pass-rate, 0-100).
|
|
336
|
+
threshold: 80.0
|
|
337
|
+
|
|
338
|
+
# Per-test-case threshold overrides. Optional.
|
|
339
|
+
test_thresholds:
|
|
340
|
+
cis_rhel-8_firewalld_server_2: 75.0
|
|
341
|
+
|
|
342
|
+
# Required for any test case that resolves to the ciscat scanner.
|
|
343
|
+
# Local file path or gs:// URI; gs:// URIs are downloaded to a per-run
|
|
344
|
+
# cache file before being passed to Terraform.
|
|
345
|
+
cis_cat_pro_source: gs://my-bucket/cis-cat-pro-4.x.tar.gz
|
|
346
|
+
# cis_cat_pro_source: /opt/cis-cat-pro/Assessor-CLI-4.5.0.tar.gz
|
|
347
|
+
|
|
348
|
+
# Scanner-native profile id mapping, keyed by full acceptance-test
|
|
349
|
+
# directory name. Required: a missing entry fails before provisioning.
|
|
350
|
+
profiles:
|
|
351
|
+
openscap:
|
|
352
|
+
stig_rhel-8_firewalld_public_3: xccdf_org.ssgproject.content_profile_stig
|
|
353
|
+
cis_cat:
|
|
354
|
+
cis_rhel-8_firewalld_server_2: xccdf_org.cisecurity.benchmarks_profile_Level_2_-_Server
|
|
355
|
+
|
|
356
|
+
# On-node scan daemon (a small WebRick service installed at provision time).
|
|
357
|
+
daemon:
|
|
358
|
+
port: 8084
|
|
359
|
+
ready_timeout: 60
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Output
|
|
363
|
+
|
|
364
|
+
For each scanned test case, `cem_acpt_scan` prints a normalized JSON document to stdout:
|
|
365
|
+
|
|
366
|
+
```json
|
|
367
|
+
{
|
|
368
|
+
"test_case": "cis_rhel-8_firewalld_server_2",
|
|
369
|
+
"scanner": "ciscat",
|
|
370
|
+
"profile": "xccdf_org.cisecurity.benchmarks_profile_Level_2_-_Server",
|
|
371
|
+
"score": 87.4,
|
|
372
|
+
"threshold": 80.0,
|
|
373
|
+
"passed_count": 187,
|
|
374
|
+
"failed_count": 27,
|
|
375
|
+
"not_applicable_count": 14,
|
|
376
|
+
"error_count": 0,
|
|
377
|
+
"rules": [
|
|
378
|
+
{ "id": "...", "title": "...", "severity": "high", "result": "pass" }
|
|
379
|
+
],
|
|
380
|
+
"pass": true
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
`--scan-output FILE` writes the same JSON to disk. The exit code is `0` if every test case scored at or above its threshold, `1` if any scored below.
|
|
385
|
+
|
|
386
|
+
### How it differs from `cem_acpt`
|
|
387
|
+
|
|
388
|
+
* The runner registers a single `:scan` action group instead of `:goss` and `:bolt`.
|
|
389
|
+
* The acceptance-test discovery predicate flips from `goss.yaml` to `manifest.pp` — scan-only test directories don't need a Goss file.
|
|
390
|
+
* The on-node Goss systemd services are skipped; in their place an on-node scan daemon is installed (mirroring the Goss server pattern but serving `/scan` and `/health` endpoints).
|
|
391
|
+
* Pass/fail is decided by the configured threshold, not by per-rule assertions.
|
|
392
|
+
|
|
300
393
|
## Generating acceptance test node images
|
|
301
394
|
|
|
302
395
|
CemAcpt provides the command `cem_acpt_image` that allows you to generate new acceptance test node images that are then used with the `cem_acpt` command. This is useful for when you want to test a new version of Puppet or a new OS.
|
data/docs/ARCHITECTURE.md
CHANGED
|
@@ -62,7 +62,6 @@ cem_acpt/
|
|
|
62
62
|
│ │ ├── logging/ # log line formatters
|
|
63
63
|
│ │ ├── platform.rb # dynamic loader for platform implementations
|
|
64
64
|
│ │ ├── platform/ # base + GCP platform module
|
|
65
|
-
│ │ ├── provision.rb # provisioner factory
|
|
66
65
|
│ │ ├── provision/ # Terraform driver + OS-specific backends
|
|
67
66
|
│ │ ├── test_runner.rb # main TestRunner::Runner
|
|
68
67
|
│ │ ├── test_runner/ # log_formatter/, test_results.rb
|
|
@@ -170,9 +169,9 @@ ordering visible in code (`base.rb:125-155`) is:
|
|
|
170
169
|
6. **`add_static_options!`** runs last as a single phase; internally
|
|
171
170
|
it does two things:
|
|
172
171
|
- 6a. Sets the framework-owned keys `user_config.dir`,
|
|
173
|
-
`user_config.file`, `
|
|
174
|
-
|
|
175
|
-
|
|
172
|
+
`user_config.file`, and `terraform.dir`. Only these three keys
|
|
173
|
+
clobber anything earlier; the rest of the merged config is
|
|
174
|
+
untouched.
|
|
176
175
|
- 6b. Calls `set_third_party_env_vars!`: `RUNNER_DEBUG=1` forces
|
|
177
176
|
`log_level=debug` and `verbose=true`; `GITHUB_ACTIONS=true` or
|
|
178
177
|
`CI=true` forces `ci_mode=true`.
|
|
@@ -402,12 +401,11 @@ name, test name).
|
|
|
402
401
|
|
|
403
402
|
## 7. Provisioner: Terraform
|
|
404
403
|
|
|
405
|
-
There is currently one provisioner. `
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
fixed today.
|
|
404
|
+
There is currently one provisioner. `TestRunner#provision_test_nodes`
|
|
405
|
+
instantiates `Provision::Terraform` directly. A factory was removed
|
|
406
|
+
in CEM-6720 because it signaled an extension surface that did not
|
|
407
|
+
exist; re-introduce one when there is a concrete second
|
|
408
|
+
implementation.
|
|
411
409
|
|
|
412
410
|
### `Provision::Terraform`
|
|
413
411
|
|
|
@@ -1017,17 +1015,13 @@ These are things the exploration surfaced that don't seem load-bearing
|
|
|
1017
1015
|
in the current code but would warrant a quick decision before
|
|
1018
1016
|
spec-driven refactors.
|
|
1019
1017
|
|
|
1020
|
-
1.
|
|
1021
|
-
`Config::Base#add_static_options!`, even though the `Provision`
|
|
1022
|
-
factory branches on it. If we want a non-Terraform path in the
|
|
1023
|
-
future the static set would need to become a default.
|
|
1024
|
-
2. **Platform constant cache.** `Platform.platform_class` caches the
|
|
1018
|
+
1. **Platform constant cache.** `Platform.platform_class` caches the
|
|
1025
1019
|
created class under `CemAcpt::Platform` (e.g.
|
|
1026
1020
|
`CemAcpt::Platform::Gcp`) and uses `const_defined?(name, false)`,
|
|
1027
1021
|
so unrelated same-named constants elsewhere in the graph cannot
|
|
1028
1022
|
collide. Mixins live alongside under `CemAcpt::Platform::Mixin`.
|
|
1029
1023
|
Resolved by [CEM-6717](rfcs/0008-namespace-platform-classes.md).
|
|
1030
|
-
|
|
1024
|
+
2. **`lib/terraform/image/gcp/windows/` is an empty directory.** It
|
|
1031
1025
|
contains only `.keep` — no `main.tf`. As of the fix for
|
|
1032
1026
|
[CEM-6712](rfcs/0003-windows-image-builder-template.md),
|
|
1033
1027
|
`TerraformBuilder#assert_template_present!` raises
|
data/exe/cem_acpt_scan
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'dotenv/load'
|
|
5
|
+
require 'cem_acpt'
|
|
6
|
+
require 'cem_acpt/cli'
|
|
7
|
+
|
|
8
|
+
command, options = CemAcpt::Cli.parse_opts_for(:cem_acpt_scan)
|
|
9
|
+
# Set CLI defaults
|
|
10
|
+
options[:module_dir] = Dir.pwd unless options[:module_dir]
|
|
11
|
+
if (options[:log_level] == 'debug' || options[:verbose]) && !options[:quiet]
|
|
12
|
+
puts '############################### RUNNING ###############################'
|
|
13
|
+
puts "Command #{File.basename(__FILE__)}:#{command} using options from command line: #{options}"
|
|
14
|
+
puts '#######################################################################'
|
|
15
|
+
end
|
|
16
|
+
CemAcpt.run(command, File.basename(__FILE__).to_sym, options)
|
data/lib/cem_acpt/cli.rb
CHANGED
|
@@ -92,6 +92,30 @@ module CemAcpt
|
|
|
92
92
|
opts.on('-F', '--filter FILTER', 'Filter images to build by name with regex. Example: -F "(alma|rhel)-8"') do |f|
|
|
93
93
|
options[:image_name_filter] = Regexp.new(f)
|
|
94
94
|
end
|
|
95
|
+
when :cem_acpt_scan
|
|
96
|
+
opts.on('-m', '--module-dir DIR', 'Set module directory. Example: -m "/tmp/module"') do |m|
|
|
97
|
+
options[:module_dir] = m
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
opts.on('-t', '--tests TESTS', 'Set tests. Example: -t "test1,test2"') do |t|
|
|
101
|
+
options[:tests] = t.split(',')
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
opts.on('--scanner SCANNER', %w[openscap ciscat],
|
|
105
|
+
'Override scanner choice. One of: openscap, ciscat. Default: derived from test-case framework.') do |s|
|
|
106
|
+
options[:cem_acpt_scan] ||= {}
|
|
107
|
+
options[:cem_acpt_scan][:scanner] = s
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
opts.on('--threshold N', Float, 'Override the global pass/fail score threshold (0-100).') do |n|
|
|
111
|
+
options[:cem_acpt_scan] ||= {}
|
|
112
|
+
options[:cem_acpt_scan][:threshold] = n
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
opts.on('--scan-output FILE', 'Save normalized JSON report to FILE. Multi-case runs fan out to FILE.<test_case>.json.') do |f|
|
|
116
|
+
options[:cem_acpt_scan] ||= {}
|
|
117
|
+
options[:cem_acpt_scan][:scan_output] = f
|
|
118
|
+
end
|
|
95
119
|
end
|
|
96
120
|
|
|
97
121
|
opts.on('-D', '--debug', 'Enable debug logging') do
|
data/lib/cem_acpt/config/base.rb
CHANGED
|
@@ -52,7 +52,6 @@ module CemAcpt
|
|
|
52
52
|
no_destroy_nodes
|
|
53
53
|
no_ephemeral_ssh_key
|
|
54
54
|
platform
|
|
55
|
-
provisioner
|
|
56
55
|
puppet
|
|
57
56
|
quiet
|
|
58
57
|
secrets
|
|
@@ -190,6 +189,16 @@ module CemAcpt
|
|
|
190
189
|
end
|
|
191
190
|
alias ci? ci_mode?
|
|
192
191
|
|
|
192
|
+
# Whether this config represents a scan run (cem_acpt_scan).
|
|
193
|
+
# Subclasses that drive a scan workflow override to true.
|
|
194
|
+
def scan_mode?
|
|
195
|
+
false
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def scan?
|
|
199
|
+
scan_mode?
|
|
200
|
+
end
|
|
201
|
+
|
|
193
202
|
def debug_mode?
|
|
194
203
|
get('log_level') == 'debug'
|
|
195
204
|
end
|
|
@@ -262,8 +271,6 @@ module CemAcpt
|
|
|
262
271
|
add_config_explanation('user_config.dir', 'static value')
|
|
263
272
|
config.dset('user_config.file', user_config_file)
|
|
264
273
|
add_config_explanation('user_config.file', 'static value')
|
|
265
|
-
config.dset('provisioner', 'terraform')
|
|
266
|
-
add_config_explanation('provisioner', 'static value')
|
|
267
274
|
config.dset('terraform.dir', terraform_dir)
|
|
268
275
|
add_config_explanation('terraform.dir', 'static value')
|
|
269
276
|
set_third_party_env_vars!(config)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module CemAcpt
|
|
6
|
+
module Config
|
|
7
|
+
# Holds the configuration for cem_acpt_scan.
|
|
8
|
+
#
|
|
9
|
+
# Mirrors the {Config::CemAcpt} pattern: a hardcoded VALID_KEYS list and a
|
|
10
|
+
# {#defaults} hash. The new top-level key {cem_acpt_scan} carries
|
|
11
|
+
# scan-specific settings (scanner choice, threshold, profile mapping,
|
|
12
|
+
# daemon port, CIS-CAT Pro source); the rest of the schema reuses keys
|
|
13
|
+
# already understood by other commands so node provisioning code does not
|
|
14
|
+
# have to special-case scan mode.
|
|
15
|
+
class CemAcptScan < Base
|
|
16
|
+
VALID_KEYS = %i[
|
|
17
|
+
cem_acpt_scan
|
|
18
|
+
actions
|
|
19
|
+
image_name_builder
|
|
20
|
+
module_dir
|
|
21
|
+
node_data
|
|
22
|
+
test_data
|
|
23
|
+
tests
|
|
24
|
+
].freeze
|
|
25
|
+
|
|
26
|
+
def env_var_prefix
|
|
27
|
+
'CEM_ACPT_SCAN'
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def scan_mode?
|
|
31
|
+
true
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# The default configuration
|
|
35
|
+
def defaults
|
|
36
|
+
{
|
|
37
|
+
actions: {},
|
|
38
|
+
cem_acpt_scan: {
|
|
39
|
+
scanner: nil, # nil = derive from test-case framework (cis -> ciscat, stig -> openscap)
|
|
40
|
+
threshold: 80.0,
|
|
41
|
+
test_thresholds: {},
|
|
42
|
+
scan_output: nil, # nil = stdout only; otherwise a file path
|
|
43
|
+
cis_cat_pro_source: nil, # local path or gs:// URI; required when any test resolves to ciscat
|
|
44
|
+
cis_cat_pro_license: nil, # local path or gs:// URI to license bundle (.tar.gz or .zip); required when any test resolves to ciscat. Rotates independently of cis_cat_pro_source.
|
|
45
|
+
daemon: {
|
|
46
|
+
port: 8084,
|
|
47
|
+
ready_timeout: 60,
|
|
48
|
+
},
|
|
49
|
+
profiles: {
|
|
50
|
+
openscap: {},
|
|
51
|
+
cis_cat: {},
|
|
52
|
+
},
|
|
53
|
+
benchmarks: {
|
|
54
|
+
cis_cat: {},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
ci_mode: false,
|
|
58
|
+
config_file: nil,
|
|
59
|
+
image_name_builder: {
|
|
60
|
+
character_substitutions: [['_', '-']],
|
|
61
|
+
parts: ['cem-acpt', '$image_fam', '$collection', '$firewall'],
|
|
62
|
+
join_with: '-',
|
|
63
|
+
},
|
|
64
|
+
log_level: 'info',
|
|
65
|
+
log_file: nil,
|
|
66
|
+
log_format: 'text',
|
|
67
|
+
module_dir: Dir.pwd,
|
|
68
|
+
node_data: {},
|
|
69
|
+
no_destroy_nodes: false,
|
|
70
|
+
no_ephemeral_ssh_key: false,
|
|
71
|
+
platform: {
|
|
72
|
+
name: 'gcp',
|
|
73
|
+
gcp: {
|
|
74
|
+
windows_bucket: 'win_cem_acpt',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
quiet: false,
|
|
78
|
+
test_data: {
|
|
79
|
+
for_each: {
|
|
80
|
+
collection: %w[puppet8],
|
|
81
|
+
},
|
|
82
|
+
vars: {},
|
|
83
|
+
name_pattern_vars: %r{^(?<framework>[a-z]+)_(?<image_fam>[a-z0-9-]+)_(?<firewall>[a-z]+)_(?<framework_vars>[-_a-z0-9]+)$},
|
|
84
|
+
vars_post_processing: {
|
|
85
|
+
new_vars: [
|
|
86
|
+
{
|
|
87
|
+
name: 'profile',
|
|
88
|
+
string_split: {
|
|
89
|
+
from: 'framework_vars',
|
|
90
|
+
using: '_',
|
|
91
|
+
part: 0,
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'level',
|
|
96
|
+
string_split: {
|
|
97
|
+
from: 'framework_vars',
|
|
98
|
+
using: '_',
|
|
99
|
+
part: 1,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
delete_vars: %w[framework_vars],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
tests: [],
|
|
107
|
+
verbose: false,
|
|
108
|
+
}
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
data/lib/cem_acpt/config.rb
CHANGED
|
@@ -37,19 +37,16 @@ module CemAcpt
|
|
|
37
37
|
@gcp_username = @config.get('platform.username')
|
|
38
38
|
return @gcp_username if @gcp_username
|
|
39
39
|
|
|
40
|
-
stdout, stderr, status = Open3.capture3('gcloud
|
|
41
|
-
raise "gcloud
|
|
40
|
+
stdout, stderr, status = Open3.capture3('gcloud auth list --format json')
|
|
41
|
+
raise "gcloud auth list failed (exit #{status.exitstatus}): #{stderr.chomp}" unless status.success?
|
|
42
42
|
|
|
43
43
|
parsed = JSON.parse(stdout)
|
|
44
|
-
|
|
45
|
-
raise
|
|
44
|
+
active_account = parsed.find { |account| account['status'] == 'ACTIVE' }
|
|
45
|
+
raise 'No active account found in gcloud auth list output' unless active_account
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
raise "gcloud os-login posixAccounts entry has no username. stdout: #{stdout.chomp}" unless username
|
|
49
|
-
|
|
50
|
-
@gcp_username = username
|
|
47
|
+
@gcp_username = active_account['account'].split('@').first
|
|
51
48
|
rescue JSON::ParserError => e
|
|
52
|
-
raise "Failed to parse gcloud
|
|
49
|
+
raise "Failed to parse gcloud auth output: #{e.message}. stdout: #{stdout.chomp}"
|
|
53
50
|
end
|
|
54
51
|
|
|
55
52
|
def gcp_credentials_file
|
|
@@ -47,31 +47,77 @@ module CemAcpt
|
|
|
47
47
|
commands << apply_command
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
+
# Commands to run on a scan-mode node. Skips Goss installation and the
|
|
51
|
+
# Goss systemd units (they are not used in scan mode), installs the
|
|
52
|
+
# scan daemon and its scanners (including a Java JRE because CIS-CAT
|
|
53
|
+
# Pro's Assessor-CLI is a Java app), then applies the puppet manifest.
|
|
54
|
+
# The CIS-CAT Pro tarball is uploaded *and* extracted by the
|
|
55
|
+
# cis_cat_pro_upload null_resource in main.tf, so this method does not
|
|
56
|
+
# touch /opt/cis-cat-pro/.
|
|
57
|
+
# @return [Array<String>] commands for `provisioner "remote-exec"`.
|
|
58
|
+
def scan_provision_commands
|
|
59
|
+
commands = [
|
|
60
|
+
"sudo /opt/puppetlabs/puppet/bin/puppet module install #{destination_provision_directory}/#{remote_module_package_name}",
|
|
61
|
+
'sudo /opt/puppetlabs/puppet/bin/gem install webrick',
|
|
62
|
+
'sudo chmod +x /opt/cem_acpt/log_service/log_service.rb',
|
|
63
|
+
'sudo /opt/cem_acpt/log_service/log_service.rb',
|
|
64
|
+
install_scanner_packages_command,
|
|
65
|
+
install_java_command,
|
|
66
|
+
'sudo chmod +x /opt/cem_acpt/scan/scan_service.rb',
|
|
67
|
+
'sudo cp /opt/cem_acpt/scan/scan_service.service /etc/systemd/system/scan_service.service',
|
|
68
|
+
'sudo systemctl daemon-reload',
|
|
69
|
+
'sudo systemctl start scan_service.service && sudo systemctl enable scan_service.service',
|
|
70
|
+
]
|
|
71
|
+
commands << apply_command
|
|
72
|
+
commands
|
|
73
|
+
end
|
|
74
|
+
|
|
50
75
|
# A wrapper around provision_commands that allows for extra commands to be added for a specific OS version(i.e EL 8)
|
|
51
76
|
# @param image_name [String] The name of the OS image being provisioned.
|
|
77
|
+
# @param scan_mode [Boolean] When true, returns the scan-mode command list
|
|
78
|
+
# (Goss-free, scan-daemon-installed) wrapped with the same OS-specific prefixes.
|
|
52
79
|
# @return [Array<String>] An array of shell commands to be run on the provisioned nodes to set up the necessary
|
|
53
80
|
# environment for running the Puppet manifest, including any additional commands needed for specific OS
|
|
54
81
|
# versions.
|
|
55
|
-
def provision_commands_wrapper(image_name)
|
|
82
|
+
def provision_commands_wrapper(image_name, scan_mode: false)
|
|
83
|
+
base = scan_mode ? scan_provision_commands : provision_commands
|
|
56
84
|
if ['rhel-8', 'oel-8', 'alma-8', 'rocky-8'].any? { |el8| image_name.include?(el8) }
|
|
57
85
|
commands = [
|
|
58
86
|
'sudo dnf upgrade --refresh -y rpm glibc',
|
|
59
87
|
'sudo rm /var/lib/rpm/.rpm.lock',
|
|
60
88
|
'sudo dnf upgrade -y dnf',
|
|
61
89
|
]
|
|
62
|
-
|
|
63
|
-
commands
|
|
90
|
+
(commands << base).flatten
|
|
64
91
|
elsif image_name.include?('ubuntu')
|
|
65
92
|
commands = ['sudo apt purge -y unattended-upgrades', 'sudo apt-get update -y']
|
|
66
|
-
|
|
67
|
-
commands
|
|
93
|
+
(commands << base).flatten
|
|
68
94
|
else
|
|
69
|
-
|
|
95
|
+
base
|
|
70
96
|
end
|
|
71
97
|
end
|
|
72
98
|
|
|
73
99
|
private
|
|
74
100
|
|
|
101
|
+
# Returns the OS-package install command for OpenSCAP. Both EL and Ubuntu
|
|
102
|
+
# ship `oscap`; the SCAP content package differs by distro family. The
|
|
103
|
+
# command is intentionally OR-chained so it succeeds on whichever package
|
|
104
|
+
# manager is present without needing fact-based dispatch.
|
|
105
|
+
# @return [String] shell command to install the OpenSCAP scanner.
|
|
106
|
+
def install_scanner_packages_command
|
|
107
|
+
# Try dnf first (RHEL/Alma/Rocky/Oracle), fall back to apt (Ubuntu).
|
|
108
|
+
'sudo dnf install -y openscap-scanner scap-security-guide || sudo apt-get install -y libopenscap8 ssg-base ssg-debderived'
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Returns the OS-package install command for a headless Java runtime.
|
|
112
|
+
# CIS-CAT Pro's Assessor-CLI.sh is a Java application, so the JRE must
|
|
113
|
+
# be present on every scan-mode node regardless of which scanner the
|
|
114
|
+
# test case resolves to (installing unconditionally is cheap, and
|
|
115
|
+
# threading scanner-resolution into the provisioner is not worth it).
|
|
116
|
+
# @return [String] shell command to install a headless JRE.
|
|
117
|
+
def install_java_command
|
|
118
|
+
'sudo dnf install -y java-11-openjdk-headless || sudo apt-get install -y default-jre-headless'
|
|
119
|
+
end
|
|
120
|
+
|
|
75
121
|
# @return [String] The command to apply the Puppet manifest on the provisioned nodes, including any necessary
|
|
76
122
|
# options for logging and debugging. This command is constructed based on the configuration settings for the
|
|
77
123
|
# provisioner, and may include additional options for debug and verbose logging if those settings are enabled.
|