Skip to content

Commit

Permalink
[DPE-2909] Enable audit logging (#328)
Browse files Browse the repository at this point in the history
## Issue
In order to improve system security we need to enable audit logging for
MongoDB

## Solution
Add audit logging configuration parameters
  • Loading branch information
dmitry-ratushnyy authored Jan 11, 2024
1 parent 29e2705 commit 1048c2a
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 7 deletions.
3 changes: 3 additions & 0 deletions lib/charms/mongodb/v1/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ def get_mongod_args(
# for simplicity we run the mongod daemon on shards, configsvrs, and replicas on the same
# port
f"--port={Config.MONGODB_PORT}",
"--auditDestination=file",
f"--auditFormat={Config.AuditLog.FORMAT}",
f"--auditPath={full_data_dir}/{Config.AuditLog.FILE_NAME}",
logging_options,
]
if auth:
Expand Down
21 changes: 14 additions & 7 deletions src/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@ class Config:
MONGOD_CONF_FILE_PATH = f"{MONGOD_CONF_DIR}/mongod.conf"
SNAP_PACKAGES = [("charmed-mongodb", "6/edge", 87)]

class Role:
"""Role config names for MongoDB Charm."""

CONFIG_SERVER = "config-server"
REPLICATION = "replication"
SHARD = "shard"

# Keep these alphabetically sorted
class Actions:
"""Actions related config for MongoDB Charm."""

PASSWORD_PARAM_NAME = "password"
USERNAME_PARAM_NAME = "username"

class AuditLog:
"""Audit log related configuration."""

FORMAT = "JSON"
FILE_NAME = "audit.json"

class Backup:
"""Backup related config for MongoDB Charm."""

Expand Down Expand Up @@ -80,6 +80,13 @@ class Relations:
DB_RELATIONS = [OBSOLETE_RELATIONS_NAME, NAME]
Scopes = Literal[APP_SCOPE, UNIT_SCOPE]

class Role:
"""Role config names for MongoDB Charm."""

CONFIG_SERVER = "config-server"
REPLICATION = "replication"
SHARD = "shard"

class Secrets:
"""Secrets related constants."""

Expand Down
13 changes: 13 additions & 0 deletions tests/integration/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# See LICENSE file for licensing details.

import json
import logging
from pathlib import Path
from typing import Dict, Optional

Expand All @@ -18,6 +19,9 @@
SERIES = "jammy"


logger = logging.getLogger(__name__)


def unit_uri(ip_address: str, password, app=APP_NAME) -> str:
"""Generates URI that is used by MongoDB to connect to a single replica.
Expand Down Expand Up @@ -189,3 +193,12 @@ async def get_secret_content(ops_test, secret_id) -> Dict[str, str]:
_, stdout, _ = await ops_test.juju(*complete_command.split())
data = json.loads(stdout)
return data[secret_id]["content"]["Data"]


def audit_log_line_sanity_check(entry) -> bool:
fields = ["atype", "ts", "local", "remote", "users", "roles", "param", "result"]
for field in fields:
if entry.get(field) is None:
logger.error("Field '%s' not found in audit log entry \"%s\"", field, entry)
return False
return True
22 changes: 22 additions & 0 deletions tests/integration/test_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import json
import logging
import os
import subprocess
import time
from subprocess import check_output
from uuid import uuid4

import pytest
Expand All @@ -20,6 +22,7 @@
APP_NAME,
PORT,
UNIT_IDS,
audit_log_line_sanity_check,
count_primaries,
find_unit,
get_leader_id,
Expand Down Expand Up @@ -314,3 +317,22 @@ def juju_reports_one_primary(unit_messages):

# cleanup, remove killed unit
await ops_test.model.destroy_unit(target_unit)


async def test_audit_log(ops_test: OpsTest) -> None:
"""Test that audit log was created and contains actual audit data."""
leader_unit = await find_unit(ops_test, leader=True)
audit_log_snap_path = "/var/snap/charmed-mongodb/common/var/lib/mongodb/audit.json"
audit_log = check_output(
f"JUJU_MODEL={ops_test.model_full_name} juju ssh {leader_unit.name} 'sudo cat {audit_log_snap_path}'",
stderr=subprocess.PIPE,
shell=True,
universal_newlines=True,
)

for line in audit_log.splitlines():
if not len(line):
continue
item = json.loads(line)
# basic sanity check
assert audit_log_line_sanity_check(item), "Audit sanity log check failed for first line"
9 changes: 9 additions & 0 deletions tests/unit/test_mongodb_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ def test_get_mongod_args(self):
"--replSet=my_repl_set",
"--dbpath=/var/snap/charmed-mongodb/common/var/lib/mongodb",
"--port=27017",
"--auditDestination=file",
"--auditFormat=JSON",
"--auditPath=/var/snap/charmed-mongodb/common/var/lib/mongodb/audit.json",
"--auth",
"--clusterAuthMode=keyFile",
"--keyFile=/var/snap/charmed-mongodb/current/etc/mongod/keyFile",
Expand All @@ -36,6 +39,9 @@ def test_get_mongod_args(self):
"--replSet=my_repl_set",
"--dbpath=/var/snap/charmed-mongodb/common/var/lib/mongodb",
"--port=27017",
"--auditDestination=file",
"--auditFormat=JSON",
"--auditPath=/var/snap/charmed-mongodb/common/var/lib/mongodb/audit.json",
]

self.assertEqual(
Expand All @@ -50,6 +56,9 @@ def test_get_mongod_args(self):
"--replSet=my_repl_set",
"--dbpath=/var/lib/mongodb",
"--port=27017",
"--auditDestination=file",
"--auditFormat=JSON",
"--auditPath=/var/lib/mongodb/audit.json",
"--logpath=/var/lib/mongodb/mongodb.log",
]

Expand Down

0 comments on commit 1048c2a

Please sign in to comment.