Skip to content

Commit

Permalink
Install and configure syslog-ng
Browse files Browse the repository at this point in the history
- Added syslog-ng to SNAC configuration tool.
- Created a test event to verify logging.

Ensures compliance with NIST SP 800-171 auditing and logging requirements, enhancing system security and reliability.

Signed-off-by: Eli Engelhardt <[email protected]>
  • Loading branch information
eli-engelhardt committed Feb 24, 2025
1 parent 464b2a2 commit 79235bb
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 27 deletions.
4 changes: 4 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
* Install and configure `auditd` in order to log system activites.
<<<<<<< HEAD
=======
* Install and configure `syslog-ng` in order to log system activites.
>>>>>>> f2f5ff5 (Install and configure syslog-ng)
### Changed
* Restricted write access to system logs in `/var/log` to System Maintainers (root) and Auditors via the `adm` group.
Expand Down
31 changes: 30 additions & 1 deletion nilrt_snac/_common.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,33 @@
import grp
import os
import pathlib
import stat
import subprocess


def _check_group_ownership(path: str, group: str) -> bool:
"Checks if the group ownership of a file or directory matches the specified group."
stat_info = os.stat(path)
gid = stat_info.st_gid
group_info = grp.getgrgid(gid)

return group_info.gr_name == group

def _check_owner(path: str, owner: str) -> bool:
"Checks if the owner of a file or directory matches the specified owner."
stat_info = os.stat(path)
uid = stat_info.st_uid
owner_info = grp.getgrgid(uid)
return owner_info.gr_name == owner

def _check_permissions(path: str, expected_mode: int) -> bool:
"Checks if the permissions of a file or directory match the expected mode."
stat_info = os.stat(path)
return stat.S_IMODE(stat_info.st_mode) == expected_mode

def _cmd(*args: str):
"Syntactic sugar for running shell commands."
subprocess.run(args, check=True)

def get_distro():
try:
Expand All @@ -9,4 +38,4 @@ def get_distro():
if line.startswith("ID="):
return line.split("=")[1].strip()
except NameError:
return None
return None
2 changes: 2 additions & 0 deletions nilrt_snac/_configs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from nilrt_snac._configs._ssh_config import _SshConfig
from nilrt_snac._configs._sudo_config import _SudoConfig
from nilrt_snac._configs._sysapi_config import _SysAPIConfig
from nilrt_snac._configs._syslog_ng_config import _SyslogConfig
from nilrt_snac._configs._tmux_config import _TmuxConfig
from nilrt_snac._configs._wifi_config import _WIFIConfig
from nilrt_snac._configs._wireguard_config import _WireguardConfig
Expand All @@ -38,4 +39,5 @@
_SudoConfig(),
_FirewallConfig(),
_AuditdConfig(),
_SyslogConfig(),
]
27 changes: 1 addition & 26 deletions nilrt_snac/_configs/_auditd_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,14 @@
import os
import re
import socket
import stat
import subprocess
from typing import List

from nilrt_snac import logger
from nilrt_snac._configs._base_config import _BaseConfig
from nilrt_snac._common import _check_group_ownership, _check_owner, _check_permissions, _cmd
from nilrt_snac._configs._config_file import EqualsDelimitedConfigFile, _ConfigFile
from nilrt_snac.opkg import opkg_helper

def _check_group_ownership(path: str, group: str) -> bool:
"Checks if the group ownership of a file or directory matches the specified group."
stat_info = os.stat(path)
gid = stat_info.st_gid
group_info = grp.getgrgid(gid)

return group_info.gr_name == group

def _check_owner(path: str, owner: str) -> bool:
"Checks if the owner of a file or directory matches the specified owner."
stat_info = os.stat(path)
uid = stat_info.st_uid
owner_info = grp.getgrgid(uid)
return owner_info.gr_name == owner

def _check_permissions(path: str, expected_mode: int) -> bool:
"Checks if the permissions of a file or directory match the expected mode."
stat_info = os.stat(path)
return stat.S_IMODE(stat_info.st_mode) == expected_mode

def _cmd(*args: str):
"Syntactic sugar for running shell commands."
subprocess.run(args, check=True)

def ensure_groups_exist(groups: List[str]) -> None:
"Ensures the specified groups exist on the system."
for group in groups:
Expand Down
56 changes: 56 additions & 0 deletions nilrt_snac/_configs/_syslog_ng_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import argparse

from nilrt_snac import logger
from nilrt_snac._configs._base_config import _BaseConfig
from nilrt_snac._configs._config_file import _ConfigFile
from nilrt_snac._common import _check_group_ownership, _check_owner, _check_permissions, _cmd
from nilrt_snac.opkg import opkg_helper


class _SyslogConfig(_BaseConfig):
def __init__(self):
self._opkg_helper = opkg_helper
self.syslog_conf_path = '/etc/syslog-ng/syslog-ng.conf'

def configure(self, args: argparse.Namespace) -> None:
print("Configuring syslog-ng...")
dry_run: bool = args.dry_run
if dry_run:
return

# Check if syslog-ng is already installed
if not self._opkg_helper.is_installed("syslog-ng"):
self._opkg_helper.install("syslog-ng")

# Enable persistent storage
_cmd('nirtcfg', '--set', 'section=SystemSettings,token=PersistentLogs.enabled,value="True"')

# Restart syslog-ng service
_cmd('/etc/init.d/syslog', 'restart')



def verify(self, args: argparse.Namespace) -> bool:
print("Verifying syslog-ng configuration...")
valid: bool = True


# Check if syslog-ng is setup to log in /var/log
if not self._opkg_helper.is_installed("syslog-ng"):
logger.error("Required syslog-ng package is not installed.")
valid = False

# Check group ownership and permissions of syslog.conf
if not _check_group_ownership(self.syslog_conf_path, "adm"):
logger.error(f"ERROR: {self.syslog_conf_path} is not owned by the 'adm' group.")
valid = False
if not _check_permissions(self.syslog_conf_path, 0o640):
logger.error(f"ERROR: {self.syslog_conf_path} does not have 640 permissions.")
valid = False
if not _check_owner(self.syslog_conf_path, "root"):
logger.error(f"ERROR: {self.syslog_conf_path} is not owned by 'root'.")
valid = False



return valid

0 comments on commit 79235bb

Please sign in to comment.