Skip to content

Commit

Permalink
Add OSX support
Browse files Browse the repository at this point in the history
  • Loading branch information
nmburgan committed Jan 6, 2022
1 parent 97180aa commit dd1897f
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 15 deletions.
22 changes: 21 additions & 1 deletion README.md
Expand Up @@ -59,12 +59,30 @@ Run a basic scan from the command line:
```bash
puppet task run log4jscanner::run_scan --nodes <nodes> directories=/opt,/var skip=/opt/puppetlabs
```
#### Running the task on macOS machines
The task supports macOS, but runs slightly differently. You can either run
the `log4jscanner:run_scan_osx` task, or you can create an [inventory file](https://puppet.com/docs/bolt/latest/inventory_files.html)
that defines the `darwin` feature for all the macOS machines you want to run on. The file could look similar to

```yaml
groups:
- name: macos
targets:
- 192.168.0.122
features:
- darwin
```

Then you can run the same `log4jscanner::run_scan` task with Bolt,

```bash
bolt task run log4jscanner::run_scan directories=/opt,/var skip=/opt/puppetlabs --targets macos
```
## Reference
### Manifest Parameters
- ensure: Set to 'absent' to remove artifacts (cron/scheduled tasks, files) from nodes. (default 'present')
- linux_directories: Array of directories to scan on Linux nodes. (default \['/'\])
- linux_skip: Array of glob patterns to skip scanning on Linux nodes. (default \['/proc','/sys'\])
- linux_skip: Array of glob patterns to skip scanning on Linux nodes. (default \['/proc','/sys','/tmp'\])
- scan_data_owner: User to own log4jscanner files. (default 'root')
- scan_data_group: Group to own log4jscanner files. (default 'root')
- cron_user: User to run the cron job for scanning. (default 'root')
Expand All @@ -76,6 +94,8 @@ puppet task run log4jscanner::run_scan --nodes <nodes> directories=/opt,/var ski
- windows_directories: Array of directories to scan on Windows nodes. (default \['C:'\])
- windows_skip: Array of glob patterns to skip scanning on Windows nodes. (default \["C:\\Windows\\Temp"\])
- scheduled_task_every: Run the scheduled task every X days. (default 1)
- osx_directories: Array of directories to scan on OSX nodes (default \['/'\])
- osx_skip: Array of glob patterns to skip scanning on OSX nodes (default \['/tmp', '/Users/osx', '/dev', '/private/var/db', '/private/var/folders', '/System/Volumes/Data/private/var/db', '/System/Volumes/Data/private/var/folders'\])

### Task Parameters
- directories: Comma-separated list of directories to search for vulnerable log4j jars
Expand Down
4 changes: 2 additions & 2 deletions lib/facter/log4jscanner.rb
@@ -1,13 +1,13 @@
Facter.add('log4jscanner') do
confine { ['Linux', 'windows'].include?(Facter.value(:kernel)) }
confine { ['Linux', 'Darwin', 'windows'].include?(Facter.value(:kernel)) }
setcode do
errors = []
warnings = {}
last_runtime = ''
data = {}

cache_dir = case Facter.value(:kernel)
when 'Linux'
when 'Linux', 'Darwin'
'/opt/puppetlabs/log4jscanner'
when 'windows'
'C:\ProgramData\PuppetLabs\log4jscanner'
Expand Down
54 changes: 50 additions & 4 deletions manifests/init.pp
Expand Up @@ -20,10 +20,14 @@
# Which directories to skip on Windows nodes
# @param scheduled_task_every
# How often the task should run, as a number of days
# @param osx_directories
# Which directories to scan on OSX nodes
# @param osx_skip
# Which directories to skip on OSX nodes
class log4jscanner (
Enum['present', 'absent'] $ensure = 'present',
Array[String] $linux_directories = ['/'],
Array[String] $linux_skip = ['/proc','/sys'],
Array[String] $linux_skip = ['/proc', '/sys', '/tmp'],
String $scan_data_owner = 'root',
String $scan_data_group = 'root',
String $cron_user = 'root',
Expand All @@ -35,6 +39,8 @@
Array[String] $windows_directories = ['C:'],
Array[String] $windows_skip = ['C:\Windows\Temp'],
Integer $scheduled_task_every = 1,
Array[String] $osx_directories = ['/'],
Array[String] $osx_skip = ['/tmp', '/Users/osx', '/dev', '/private/var/db', '/private/var/folders', '/System/Volumes/Data/private/var/db', '/System/Volumes/Data/private/var/folders'],
) {

# Run things/set up files, or clean up when ensure=>absent?
Expand Down Expand Up @@ -67,9 +73,9 @@
case $facts['kernel'] {
'Linux': {
$fact_upload_cmd = "/opt/puppetlabs/bin/puppet facts upload --environment ${environment}"
$cache_dir = '/opt/puppetlabs/log4jscanner'
$scan_script = 'scan_data_generation.sh'
$scan_script_mode = '0700'
$cache_dir = '/opt/puppetlabs/log4jscanner'
$scan_script = 'scan_data_generation.sh'
$scan_script_mode = '0700'
File {
owner => $scan_data_owner,
group => $scan_data_group,
Expand Down Expand Up @@ -104,6 +110,45 @@
require => File[$scan_cmd],
}
}
'Darwin': {
$fact_upload_cmd = "/opt/puppetlabs/bin/puppet facts upload --environment ${environment}"
$cache_dir = '/opt/puppetlabs/log4jscanner'
$scan_script = 'scan_data_generation.sh'
$scan_script_mode = '0700'
File {
owner => $scan_data_owner,
group => $scan_data_group,
mode => '0644',
}
$dirs = $osx_directories
$skip_dirs = $osx_skip
$scan_bin = 'log4jscanner_osx'
$checksum = 'b81bb538179909213aea0ae414492dd4a5f05e4a243b55894d8507dffcb9d23a'
$scan_cmd = "${cache_dir}/${scan_script}"

if $generate_scan_data_exec {
exec { $generate_scan_data_exec:
command => $scan_cmd,
user => $scan_data_owner,
group => $scan_data_group,
refreshonly => true,
require => File[$scan_cmd],
timeout => 0,
}
}

cron { 'Log4jscanner - Cache scan data':
ensure => $ensure,
command => $scan_cmd,
user => $cron_user,
hour => $cron_hour,
minute => $cron_minute,
month => $cron_month,
monthday => $cron_monthday,
weekday => $cron_weekday,
require => File[$scan_cmd],
}
}
'windows': {
$fact_upload_cmd = "\"${windows_puppet_install_path}\\bin\\puppet.bat\" facts upload --environment ${environment}"
$cache_dir = 'C:/ProgramData/PuppetLabs/log4jscanner'
Expand Down Expand Up @@ -160,6 +205,7 @@
'skip' => $skip_dirs,
'cache_dir' => $cache_dir,
'fact_upload_cmd' => $fact_upload_cmd,
'scan_bin' => $scan_bin,
}
file { $scan_cmd:
ensure => $ensure_file,
Expand Down
9 changes: 9 additions & 0 deletions metadata.json
Expand Up @@ -97,6 +97,15 @@
"2019",
"10"
]
},
{
"operatingsystem": "OSX",
"operatingsystemrelease": [
"10.13",
"10.14",
"10.15",
"11"
]
}
],
"requirements": [
Expand Down
43 changes: 36 additions & 7 deletions spec/classes/log4jscanner_spec.rb
@@ -1,12 +1,9 @@
require 'spec_helper'
describe 'log4jscanner' do
# I don't think rspec-puppet-facts supports OSX, but the code is here
# in case that gets added at some point.
on_supported_os.each do |os, os_facts|
context "on #{os}" do
let(:facts) { os_facts }
let(:environment) { 'production' }
let(:params) { { 'cron_minute' => 11 } }
let(:scan_cmd) { "#{cache_dir}/#{scan_script}" }

case os_facts[:kernel]
when 'Linux'
let(:fact_upload_cmd) { '/opt/puppetlabs/bin/puppet facts upload --environment production' }
Expand All @@ -16,6 +13,14 @@
let(:scan_bin) { 'log4jscanner_nix' }
let(:checksum) { '1e8d28e53cde54b3b81c66401afd4485adfecdf6cbaf622ff0324fe2b3a1649b' }
let(:default_script_regex) { %r{CACHEDIR=#{cache_dir}} }
when 'Darwin'
let(:fact_upload_cmd) { '/opt/puppetlabs/bin/puppet facts upload --environment production' }
let(:cache_dir) { '/opt/puppetlabs/log4jscanner' }
let(:scan_script) { 'scan_data_generation.sh' }
let(:scan_script_mode) { '0700' }
let(:scan_bin) { 'log4jscanner_osx' }
let(:checksum) { 'b81bb538179909213aea0ae414492dd4a5f05e4a243b55894d8507dffcb9d23a' }
let(:default_script_regex) { %r{CACHEDIR=#{cache_dir}} }
when 'windows'
let(:fact_upload_cmd) { '"C:\Program Files\Puppet Labs\Puppet\bin\puppet.bat" facts upload --environment production' }
let(:cache_dir) { 'C:/ProgramData/PuppetLabs/log4jscanner' }
Expand All @@ -26,6 +31,11 @@
let(:default_script_regex) { %r{\$CacheDir = "#{cache_dir}"} }
end

let(:facts) { os_facts }
let(:environment) { 'production' }
let(:params) { { 'cron_minute' => 11 } }
let(:scan_cmd) { "#{cache_dir}/#{scan_script}" }

context 'with default parameters' do
it do
is_expected.to compile.with_all_deps
Expand All @@ -49,7 +59,7 @@
.with_refreshonly(true)
.with_subscribe(["File[#{scan_cmd}]", "File[#{cache_dir}]"])
case os_facts[:kernel]
when 'Linux'
when 'Linux', 'Darwin'
is_expected.to contain_exec('Log4jscanner generate scan data')
.with_command(scan_cmd)
.with_user('root')
Expand Down Expand Up @@ -101,7 +111,7 @@
is_expected.not_to contain_exec('Log4jscanner generate scan data')
is_expected.not_to contain_exec('Log4jscanner fact upload')
case os_facts[:kernel]
when 'Linux'
when 'Linux', 'Darwin'
is_expected.to contain_cron('Log4jscanner - Cache scan data').with_ensure('absent')
when 'windows'
is_expected.to contain_scheduled_task('Log4jscanner - Cache scan data').with_ensure('absent')
Expand All @@ -123,6 +133,8 @@
params['windows_directories'] = ['C:\LiveLong', 'C:\AndProsper']
params['windows_skip'] = ['C:\PeaceAnd', 'C:\LongLife']
params['scheduled_task_every'] = 2
params['osx_directories'] = ['/livelongosx', '/andprosperosx']
params['osx_skip'] = ['/peaceandosx', '/longlifeosx']

is_expected.to compile.with_all_deps
case os_facts[:kernel]
Expand All @@ -143,6 +155,23 @@
.with_month(2)
.with_monthday(3)
.with_weekday(4)
when 'Darwin'
is_expected.to contain_file(scan_cmd)
.with_ensure('file')
.with_content(%r{--skip /peaceandosx.*--skip /longlifeosx.*/livelongosx /andprosperosx}m)
is_expected.to contain_exec('Log4jscanner generate scan data')
.with_command(scan_cmd)
.with_user('picard')
.with_group('starfleet')
is_expected.to contain_cron('Log4jscanner - Cache scan data')
.with_ensure('present')
.with_command(scan_cmd)
.with_user('riker')
.with_hour(1)
.with_minute(11)
.with_month(2)
.with_monthday(3)
.with_weekday(4)
when 'windows'
is_expected.to contain_file(scan_cmd)
.with_ensure('file')
Expand Down
27 changes: 27 additions & 0 deletions tasks/osx.sh
@@ -0,0 +1,27 @@
#!/usr/bin/env bash

set -e

skip=""
if [ -n "${PT_skip}" ]; then
while IFS=',' read -ra skipdirs; do
for glob in "${skipdirs[@]}"; do
skip="${skip} --skip ${glob}"
done
done <<< "${PT_skip}"
fi

rewrite=""
if [ -n "${PT_rewrite}" ] && [ "${PT_rewrite}" = "true" ]; then
rewrite="--rewrite"
fi

dirs=""
while IFS=',' read -ra directories; do
for d in "${directories[@]}"; do
dirs="${dirs} ${d}"
done
done <<< "${PT_directories}"

echo "Command: ${PT__installdir}/log4jscanner/files/log4jscanner_osx ${skip} ${rewrite} ${dirs}"
${PT__installdir}/log4jscanner/files/log4jscanner_osx ${skip} ${rewrite} ${dirs} 2>&1
1 change: 1 addition & 0 deletions tasks/run_scan.json
Expand Up @@ -16,6 +16,7 @@
}
},
"implementations": [
{ "name": "osx.sh", "requirements": ["darwin"], "files": ["log4jscanner/files/log4jscanner_osx"], "input_method": "environment" },
{ "name": "nix.sh", "requirements": ["shell"], "files": ["log4jscanner/files/log4jscanner_nix"], "input_method": "environment" },
{ "name": "win.ps1", "requirements": ["powershell"], "files": ["log4jscanner/files/log4jscanner.exe"], "input_method": "powershell" }
]
Expand Down
21 changes: 21 additions & 0 deletions tasks/run_scan_osx.json
@@ -0,0 +1,21 @@
{
"description": "Runs Google's log4jscanner on an OSX target node",
"support_noop": false,
"parameters": {
"directories": {
"description": "Comma-separated list of directories to search for vulnerable log4j JARs",
"type": "String"
},
"skip": {
"description": "Comma-separated list of glob patterns to skip when scanning (e.g. '/var/run/*')",
"type": "Optional[String]"
},
"rewrite": {
"description": "Rewrite vulnerable JARs as they are detected",
"type": "Optional[Boolean]"
}
},
"implementations": [
{ "name": "osx.sh", "requirements": ["shell"], "files": ["log4jscanner/files/log4jscanner_osx"], "input_method": "environment" }
]
}
2 changes: 1 addition & 1 deletion templates/scan_data_generation.sh.epp
Expand Up @@ -31,7 +31,7 @@ else
touch "${UPDATEFILE}.previous"
fi

${CACHEDIR}/log4jscanner_nix ${skip} ${dirs} 1>${UPDATEFILE} 2>${ERRORFILE}
${CACHEDIR}/<%= $scan_bin %> ${skip} ${dirs} 1>${UPDATEFILE} 2>${ERRORFILE}
diff=$(diff -y --suppress-common "${UPDATEFILE}" "${UPDATEFILE}.previous" | wc -l)
rm -f "${UPDATEFILE}.previous"
if [ "${diff}" != "0" ]; then
Expand Down

0 comments on commit dd1897f

Please sign in to comment.