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

Support auditd configuration #143

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
59 changes: 59 additions & 0 deletions scripts/hostcfgd
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ FIPS_CONFIG_FILE = '/etc/sonic/fips.json'
OPENSSL_FIPS_CONFIG_FILE = '/etc/fips/fips_enable'
DEFAULT_FIPS_RESTART_SERVICES = ['ssh', 'telemetry.service', 'restapi']

# AUDIT
AUDIT_INIT_CONFIG_FILE = "/etc/sonic/audit/security-auditing.rules"
AUDIT_CONFIG_FILE = "/etc/audit/rules.d/security-auditing.rules"
AUDIT_SYSLOG_CONFIG_FILE = "/etc/audit/plugins.d/syslog.conf"
RESTART_AUDITD = ["sudo", "systemctl", "restart", "auditd"]

# MISC Constants
CFG_DB = "CONFIG_DB"
STATE_DB = "STATE_DB"
Expand Down Expand Up @@ -1170,6 +1176,49 @@ class KdumpCfg(object):
num_dumps = data.get("num_dumps")
run_cmd(["sonic-kdump-config", "--num_dumps", num_dumps])


class AuditCfg(object):
"""
Update Audit configuration in CONFIG_DB table
1) Copy the audit rules file when enablement or disablement
2) Restart auditd services to apply the changes

Args:
key: Triggered table's key. Should be always "config"
data: Configuration data
"""
def __init__(self, CfgDb):
self.config_db = CfgDb
self.audit_defaults = {"enabled": "true"}

def load(self, audit_table):
syslog.syslog(syslog.LOG_INFO, "AuditCfg init")
audit_conf = audit_table.get("config", {})
for row in self.audit_defaults:
value = self.audit_defaults.get(row)
if not audit_conf.get(row):
self.config_db.mod_entry("AUDIT", "config", {row: value})

def audit_update(self, key, data):
syslog.syslog(syslog.LOG_INFO, "Audit configuration update")
if key == "config":
audit_enabled = self.audit_defaults["enabled"]
if data.get("enabled") is not None:
audit_enabled = data.get("enabled")
if audit_enabled.lower() == "true":
enabled = True
else:
enabled = False
if enabled:
run_cmd(["sudo", "cp", AUDIT_INIT_CONFIG_FILE, AUDIT_CONFIG_FILE])
run_cmd(["sudo", "sed", "-i", "s/active = no/active = yes/", AUDIT_SYSLOG_CONFIG_FILE])
run_cmd(RESTART_AUDITD)
else:
run_cmd(["sudo", "rm", AUDIT_CONFIG_FILE])
run_cmd(["sudo", "sed", "-i", "s/active = yes/active = no/", AUDIT_SYSLOG_CONFIG_FILE])
run_cmd(RESTART_AUDITD)


class NtpCfg(object):
"""
NtpCfg Config Daemon
Expand Down Expand Up @@ -1709,6 +1758,9 @@ class HostConfigDaemon:
# Initialize KDump Config and set the config to default if nothing is provided
self.kdumpCfg = KdumpCfg(self.config_db)

# Initialize Audit Config and set the config to default if nothing is provided
self.auditCfg = AuditCfg(self.config_db)

# Initialize IpTables
self.iptables = Iptables()

Expand Down Expand Up @@ -1755,6 +1807,7 @@ class HostConfigDaemon:
ldap_server = init_data['LDAP_SERVER']
lpbk_table = init_data['LOOPBACK_INTERFACE']
kdump = init_data['KDUMP']
audit = init_data['AUDIT']
passwh = init_data['PASSW_HARDENING']
ssh_server = init_data['SSH_SERVER']
dev_meta = init_data.get(swsscommon.CFG_DEVICE_METADATA_TABLE_NAME, {})
Expand All @@ -1771,6 +1824,7 @@ class HostConfigDaemon:
self.aaacfg.load(aaa, tacacs_global, tacacs_server, radius_global, radius_server, ldap_global, ldap_server)
self.iptables.load(lpbk_table)
self.kdumpCfg.load(kdump)
self.auditCfg.load(audit)
self.passwcfg.load(passwh)
self.sshscfg.load(ssh_server)
self.devmetacfg.load(dev_meta)
Expand Down Expand Up @@ -1897,6 +1951,10 @@ class HostConfigDaemon:
syslog.syslog(syslog.LOG_INFO, 'Kdump handler...')
self.kdumpCfg.kdump_update(key, data)

def audit_handler (self, key, op, data):
syslog.syslog(syslog.LOG_INFO, 'Audit handler')
self.auditCfg.audit_update(key, data)

def device_metadata_handler(self, key, op, data):
syslog.syslog(syslog.LOG_INFO, 'DeviceMeta handler...')
self.devmetacfg.hostname_update(data)
Expand Down Expand Up @@ -1945,6 +2003,7 @@ class HostConfigDaemon:
return callback

self.config_db.subscribe('KDUMP', make_callback(self.kdump_handler))
self.config_db.subscribe('AUDIT', make_callback(self.audit_handler))
# Handle AAA, TACACS and RADIUS related tables
self.config_db.subscribe('AAA', make_callback(self.aaa_handler))
self.config_db.subscribe('TACPLUS', make_callback(self.tacacs_global_handler))
Expand Down
90 changes: 90 additions & 0 deletions tests/hostcfgd/hostcfgd_audit_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import os
import sys
import pytest
import importlib.util
import importlib.machinery
from swsscommon import swsscommon
from unittest.mock import call, patch, Mock

from sonic_py_common.general import getstatusoutput_noshell
from tests.common.mock_configdb import MockConfigDb, MockDBConnector

test_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
modules_path = os.path.dirname(test_path)
scripts_path = os.path.join(modules_path, "scripts")
sys.path.insert(0, modules_path)

# Load the file under test
hostcfgd_path = os.path.join(scripts_path, 'hostcfgd')
loader = importlib.machinery.SourceFileLoader('hostcfgd', hostcfgd_path)
spec = importlib.util.spec_from_loader(loader.name, loader)
hostcfgd = importlib.util.module_from_spec(spec)
loader.exec_module(hostcfgd)
sys.modules['hostcfgd'] = hostcfgd
original_syslog = hostcfgd.syslog

# Mock swsscommon classes
hostcfgd.ConfigDBConnector = MockConfigDb
hostcfgd.DBConnector = MockDBConnector
hostcfgd.Table = Mock()


AUDIT_INIT_CONFIG_FILE = "/etc/sonic/audit/security-auditing.rules"
AUDIT_CONFIG_FILE = "/etc/audit/rules.d/security-auditing.rules"
AUDIT_SYSLOG_CONFIG_FILE = "/etc/audit/plugins.d/syslog.conf"
RESTART_AUDITD = ['sudo', 'systemctl', 'restart', 'auditd']


class TestHostcfgdAudit(object):
"""
Test hostcfd daemon - AUDIT
"""
def setup_method(self):
print("SETUP")
self.test_data = {'enabled': 'true'}

@patch('hostcfgd.subprocess.check_call')
@patch('syslog.syslog')
def test_audit_enable(self, mock_syslog, mock_subprocess):
self.test_data['enabled'] = 'true'
MockConfigDb.set_config_db(self.test_data)
host_config_daemon = hostcfgd.HostConfigDaemon()
host_config_daemon.audit_handler("config", "", self.test_data)

expected_syslog = [
call(hostcfgd.syslog.LOG_INFO, "Audit handler"),
call(hostcfgd.syslog.LOG_INFO, "Audit configuration update")
]
mock_syslog.assert_has_calls(expected_syslog)

expected_subprocess = [
call(['sudo', 'cp', AUDIT_INIT_CONFIG_FILE, AUDIT_CONFIG_FILE]),
call(["sudo", "sed", "-i", "s/active = no/active = yes/", AUDIT_SYSLOG_CONFIG_FILE]),
call(RESTART_AUDITD)
]
mock_subprocess.assert_has_calls(expected_subprocess)

@patch('hostcfgd.subprocess.check_call')
@patch('syslog.syslog')
def test_audit_disable(self, mock_syslog, mock_subprocess):
self.test_data['enabled'] = 'false'
print(self.test_data)
MockConfigDb.set_config_db(self.test_data)
host_config_daemon = hostcfgd.HostConfigDaemon()
host_config_daemon.audit_handler("config", "", self.test_data)

expected_syslog = [
call(hostcfgd.syslog.LOG_INFO, "Audit handler"),
call(hostcfgd.syslog.LOG_INFO, "Audit configuration update")
]
mock_syslog.assert_has_calls(expected_syslog)

expected_subprocess = [
call(["sudo", "rm", AUDIT_CONFIG_FILE]),
call(["sudo", "sed", "-i", "s/active = yes/active = no/", AUDIT_SYSLOG_CONFIG_FILE]),
call(RESTART_AUDITD)
]
mock_subprocess.assert_has_calls(expected_subprocess)

def teardown_method(self):
print("TEARDOWN")
6 changes: 6 additions & 0 deletions tests/hostcfgd/test_vectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"PASSW_HARDENING": {},
"SSH_SERVER": {},
"KDUMP": {},
"AUDIT": {},
"NTP": {},
"NTP_SERVER": {},
"LOOPBACK_INTERFACE": {},
Expand Down Expand Up @@ -45,6 +46,11 @@

}
},
"AUDIT": {
"config": {
"enabled": "true"
}
},
"NTP": {
"global": {
"vrf": "default",
Expand Down
Loading