Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NAS-133112 / 25.04 / Add auditd rules to middleware configuration #15219

Merged
merged 1 commit into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/middlewared/middlewared/etc_files/audit_setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from middlewared.utils.auditd import set_audit_rules


def render(service, middleware, render_ctx):
set_audit_rules(render_ctx['system.security.config']['enable_gpos_stig'])
7 changes: 7 additions & 0 deletions src/middlewared/middlewared/plugins/etc.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ async def render(self, path, ctx):
class EtcService(Service):

GROUPS = {
'audit': {
'ctx': [{'method': 'system.security.config'}],
'entries': [
{'type': 'py', 'path': 'audit_setup'},
]

},
'docker': [
{'type': 'py', 'path': 'docker/daemon.json'},
],
Expand Down
46 changes: 46 additions & 0 deletions src/middlewared/middlewared/utils/auditd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import enum
import os
import stat
import subprocess


AUDIT_DIR = '/etc/audit'
AUDIT_RULES_DIR = os.path.join(AUDIT_DIR, 'rules.d')
AUDIT_PLUGINS_DIR = os.path.join(AUDIT_DIR, 'plugins.d')
CONF_AUDIT_RULES_DIR = '/conf/audit_rules'


class AUDITRules(enum.StrEnum):
BASE = '10-base-config.rules'
STIG = '30-stig.rules'
PRIVILEGED = '31-privileged.rules'
MODULE = '43-module-load.rules'
FINALIZE = '99-finalize.rules'


STIG_AUDIT_RULES = frozenset([rules for rules in AUDITRules])
NOSTIG_AUDIT_RULES = frozenset([AUDITRules.BASE])


def set_audit_rules(gpos_stig_enabled: bool) -> None:
rules_set = STIG_AUDIT_RULES if gpos_stig_enabled else NOSTIG_AUDIT_RULES

# first remove all files that shouldn't be there
for rules_file in os.listdir(AUDIT_RULES_DIR):
full_path = os.path.join(AUDIT_RULES_DIR, rules_file)
if rules_file not in rules_set:
os.unlink(full_path)
elif not stat.S_ISLNK(os.lstat(full_path).st_mode):
os.unlink(full_path)
elif os.readlink(full_path) != os.path.join(CONF_AUDIT_RULES_DIR, rules_file):
os.unlink(full_path)

for rules_file in rules_set:
conf_path = os.path.join(CONF_AUDIT_RULES_DIR, rules_file)
audit_path = os.path.join(AUDIT_RULES_DIR, rules_file)
if os.path.exists(audit_path):
continue

os.symlink(conf_path, audit_path)

subprocess.run(['augenrules', '--load'])
40 changes: 40 additions & 0 deletions tests/unit/test_auditd_rules.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import os
import pytest
import subprocess

from middlewared.utils import auditd


@pytest.fixture(scope='function')
def auditd_gpos_stig_enable():
auditd.set_audit_rules(True)
try:
yield
finally:
auditd.set_audit_rules(False)


@pytest.fixture(scope='function')
def auditd_gpos_stig_disable():
# make extra-sure we're disabled
auditd.set_audit_rules(False)


@pytest.mark.parametrize('ruleset', auditd.AUDITRules)
def test__auditd_conf_rules_exist(ruleset):
assert os.path.exists(os.path.join(auditd.CONF_AUDIT_RULES_DIR, ruleset))


def test__auditd_enable_gpos_stig(auditd_gpos_stig_enable):
assert set(os.listdir(auditd.AUDIT_RULES_DIR)) == auditd.STIG_AUDIT_RULES
rules = subprocess.run(['auditctl', '-l'], capture_output=True)
data = rules.stdout.decode().strip()
assert data != 'No rules'


def test__auditd_disable_gpos_stig(auditd_gpos_stig_disable):
assert set(os.listdir(auditd.AUDIT_RULES_DIR)) == auditd.NOSTIG_AUDIT_RULES

rules = subprocess.run(['auditctl', '-l'], capture_output=True)
data = rules.stdout.decode().strip()
assert data == 'No rules'
Loading