Skip to content

Commit

Permalink
split oscap-debug to host OS sysctl-only scan + full VM scan
Browse files Browse the repository at this point in the history
The sysctl-only scan is much simpler and faster, but doesn't
seem to reliably reproduce the freeze.

The nested VM test runs much faster than a full host OS scan,
and is able to easily reproduce it.

Signed-off-by: Jiri Jaburek <[email protected]>
  • Loading branch information
comps committed Sep 27, 2024
1 parent 986884d commit dc4f413
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 15 deletions.
8 changes: 8 additions & 0 deletions scanning/oscap-debug/helgrind.fmf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
summary: Runs oscap via valgrind - helgrind
test: python3 -m lib.runtest ./helgrind.py
result: custom
environment+:
PYTHONPATH: ../..
duration: 4h
require+:
- valgrind
26 changes: 26 additions & 0 deletions scanning/oscap-debug/helgrind.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/python3

from lib import util, results


profile = 'cis_workstation_l1'

extra_debuginfos = [
'glibc',
'openscap-scanner',
'xmlsec1',
'xmlsec1-openssl',
'libtool-ltdl',
'openssl-libs',
]

util.subprocess_run(['dnf', '-y', 'debuginfo-install', *extra_debuginfos], check=True)

oscap_cmd = [
'valgrind', '--tool=helgrind', '--',
'oscap', 'xccdf', 'eval', '--profile', profile, '--progress',
util.get_datastream(),
]
util.subprocess_run(oscap_cmd)

results.report_and_exit()
12 changes: 12 additions & 0 deletions scanning/oscap-debug/sysctl-only.fmf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
summary: Runs oscap many times to hopefully reproduce a freeze
test: python3 -m lib.runtest ./sysctl-only.py
result: custom
environment+:
PYTHONPATH: ../..
duration: 4h
require+:
- gdb
adjust:
- when: distro < rhel-9
enabled: false
because: we need a fairly modern gdb
105 changes: 105 additions & 0 deletions scanning/oscap-debug/sysctl-only.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/python3

import time
import signal
import subprocess

from lib import util, results, oscap


start_time = time.monotonic()

profile = 'anssi_bp28_high'

# sysctl rules only take about 1-2 seconds
oscap_timeout = 10

# unselect all rules in the specified profile, except for
# sysctl_* rules
ds = oscap.global_ds()
rules = ds.profiles[profile].rules
rules = {rule for rule in rules if not rule.startswith('sysctl_')}
oscap.unselect_rules(util.get_datastream(), 'scan-ds.xml', rules)

extra_debuginfos = [
'glibc',
'openscap-scanner',
'xmlsec1',
'xmlsec1-openssl',
'libtool-ltdl',
'openssl-libs',
]

util.subprocess_run(['dnf', '-y', 'debuginfo-install', *extra_debuginfos], check=True)

with open('gdb.script', 'w') as f:
f.write(util.dedent('''
generate-core-file oscap.core
set logging file oscap-bt.txt
set logging overwrite on
set logging redirect on
set logging enabled on
thread apply all bt
set logging enabled off
'''))

oscap_cmd = [
'oscap', 'xccdf', 'eval', '--profile', profile, '--progress', 'scan-ds.xml',
]

# run for all of the configured test duration, minus 600 seconds for safety
# (running gdb, compressing corefile which takes forever, etc.)
attempt = 1
metadata = util.TestMetadata()
duration = metadata.duration_seconds() - oscap_timeout - 600
util.log(f"trying to freeze oscap for {duration} total seconds")

while time.monotonic() - start_time < duration:
oscap_proc = util.subprocess_Popen(oscap_cmd)

try:
returncode = oscap_proc.wait(oscap_timeout)
if returncode not in [0,2]:
results.report(
'fail', f'attempt:{attempt}', f"oscap failed with {returncode}",
)
continue

except subprocess.TimeoutExpired:
# figure out oscap PID on the remote system
pgrep = util.subprocess_run(
['pgrep', '-n', 'oscap'],
stdout=subprocess.PIPE, universal_newlines=True,
)
if pgrep.returncode != 0:
results.report(
'warn',
f'attempt:{attempt}',
f"pgrep returned {pgrep.returncode}, oscap probably just finished "
"and we hit a rare race, moving on",
)
continue

oscap_pid = pgrep.stdout.strip()

# attach gdb to that PID
util.subprocess_run(
['gdb', '-n', '-batch', '-x', 'gdb.script', '-p', oscap_pid],
check=True,
)

util.subprocess_run(['xz', '-e', '-9', 'oscap.core'], check=True)
results.report(
'fail', f'attempt:{attempt}', "oscap froze, gdb output available",
logs=['oscap.core.xz', 'oscap-bt.txt'],
)
break

finally:
oscap_proc.send_signal(signal.SIGKILL)
oscap_proc.wait()

results.report('pass', f'attempt:{attempt}')
attempt += 1

results.report_and_exit()
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
summary: Runs oscap many times to hopefully reproduce a freeze
test: python3 -m lib.runtest ./test.py
test: python3 -m lib.runtest ./vm-scan.py
result: custom
environment+:
PYTHONPATH: ../..
duration: 4h
tag:
- needs-param
require+:
# virt library dependencies
- libvirt-daemon
Expand All @@ -25,3 +23,6 @@ adjust:
- when: arch != x86_64
enabled: false
because: we want to run virtualization on x86_64 only
- when: distro < rhel-9
enabled: false
because: we need a fairly modern gdb
21 changes: 9 additions & 12 deletions scanning/oscap-debug/test.py → scanning/oscap-debug/vm-scan.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
#!/usr/bin/python3

import os
import time
import subprocess
import tempfile

from lib import util, results, virt, oscap
from conf import remediation, partitions
from lib import util, results, virt


profile = os.environ.get('PROFILE')
if not profile:
raise RuntimeError("specify PROFILE via env variable, consider also TIMEOUT")
profile = 'cis_workstation_l1'

# should be set to approximate the profile scan time
oscap_timeout = int(os.environ.get('TIMEOUT', 600))
# cis_workstation_l1 takes about 4-5 seconds to scan
oscap_timeout = 30

extra_packages = [
'gdb',
Expand All @@ -26,6 +22,7 @@
'xmlsec1',
'xmlsec1-openssl',
'libtool-ltdl',
'openssl-libs',
]

start_time = time.monotonic()
Expand Down Expand Up @@ -65,10 +62,10 @@
oscap_cmd = f'oscap xccdf eval --profile {profile} --progress scan-ds.xml'

while time.monotonic() - start_time < duration:
oscap = g.ssh(oscap_cmd, func=util.subprocess_Popen)
oscap_proc = g.ssh(oscap_cmd, func=util.subprocess_Popen)

try:
returncode = oscap.wait(oscap_timeout)
returncode = oscap_proc.wait(oscap_timeout)
if returncode not in [0,2]:
results.report(
'fail', f'attempt:{attempt}', f"oscap failed with {returncode}",
Expand Down Expand Up @@ -103,8 +100,8 @@
break

finally:
oscap.terminate()
oscap.wait()
oscap_proc.terminate()
oscap_proc.wait()

results.report('pass', f'attempt:{attempt}')
attempt += 1
Expand Down

0 comments on commit dc4f413

Please sign in to comment.