From 0979ae020c02d8d193f22d4ffc4738a1f6842f3f Mon Sep 17 00:00:00 2001 From: xumia <59720581+xumia@users.noreply.github.com> Date: Fri, 21 Jul 2023 16:52:52 +0800 Subject: [PATCH] Support to config fips state (#69) Support to config fips state, allow to enable or enforce the FIPS. When FIPS is enforced, you can only disable the FIPS by changing to non-enforced state and reboot the DUT. HLD: https://github.com/sonic-net/SONiC/blob/master/doc/fips/SONiC-OpenSSL-FIPS-140-3-deployment.md --- azure-pipelines.yml | 25 ++--- scripts/hostcfgd | 130 +++++++++++++++++++++++++- scripts/procdockerstatsd | 20 +++- setup.py | 2 +- tests/common/mock_bootloader.py | 13 +++ tests/common/mock_configdb.py | 14 ++- tests/hostcfgd/hostcfgd_fips_test.py | 133 +++++++++++++++++++++++++++ tests/mock_connector.py | 5 + tests/procdockerstatsd_test.py | 6 ++ 9 files changed, 327 insertions(+), 21 deletions(-) create mode 100644 tests/common/mock_bootloader.py create mode 100644 tests/hostcfgd/hostcfgd_fips_test.py diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 924acd77..e41e0efc 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -75,30 +75,21 @@ stages: sudo dpkg -i libnl-nf-3-200_*.deb sudo dpkg -i libhiredis0.14_*.deb sudo dpkg -i libyang_1.0.73_*.deb - workingDirectory: $(Pipeline.Workspace)/target/debs/bullseye/ - displayName: 'Install Debian dependencies' - - - task: DownloadPipelineArtifact@2 - inputs: - source: specific - project: build - pipeline: 9 - artifact: sonic-swss-common - runVersion: 'latestFromBranch' - runBranch: 'refs/heads/$(BUILD_BRANCH)' - displayName: "Download sonic swss common deb packages" - - - script: | - set -xe sudo dpkg -i libswsscommon_1.0.0_amd64.deb sudo dpkg -i python3-swsscommon_1.0.0_amd64.deb - workingDirectory: $(Pipeline.Workspace)/ - displayName: 'Install swss-common dependencies' + workingDirectory: $(Pipeline.Workspace)/target/debs/bullseye/ + displayName: 'Install Debian dependencies' - script: | set -xe + sudo pip3 install enum34 sudo pip3 install swsssdk-2.0.1-py3-none-any.whl sudo pip3 install sonic_py_common-1.0-py3-none-any.whl + sudo pip3 install sonic_yang_mgmt-1.0-py3-none-any.whl + sudo pip3 install sonic_yang_models-1.0-py3-none-any.whl + sudo pip3 install sonic_config_engine-1.0-py3-none-any.whl + sudo pip3 install sonic_platform_common-1.0-py3-none-any.whl + sudo pip3 install sonic_utilities-1.2-py3-none-any.whl workingDirectory: $(Pipeline.Workspace)/target/python-wheels/bullseye/ displayName: 'Install Python dependencies' diff --git a/scripts/hostcfgd b/scripts/hostcfgd index 3a3bbece..d2a0e768 100644 --- a/scripts/hostcfgd +++ b/scripts/hostcfgd @@ -10,11 +10,14 @@ import syslog import signal import re import jinja2 +import json import threading +from datetime import datetime from sonic_py_common import device_info from sonic_py_common.general import check_output_pipe from swsscommon.swsscommon import ConfigDBConnector, DBConnector, Table, SonicDBConfig from swsscommon import swsscommon +from sonic_installer import bootloader # FILE PAM_AUTH_CONF = "/etc/pam.d/common-auth-sonic" @@ -57,12 +60,18 @@ RADIUS_SERVER_TIMEOUT_DEFAULT = "5" RADIUS_SERVER_AUTH_TYPE_DEFAULT = "pap" RADIUS_PAM_AUTH_CONF_DIR = "/etc/pam_radius_auth.d/" +# FIPS +FIPS_CONFIG_FILE = '/etc/sonic/fips.json' +OPENSSL_FIPS_CONFIG_FILE = '/etc/fips/fips_enable' +DEFAULT_FIPS_RESTART_SERVICES = ['ssh', 'telemetry.service', 'restapi'] + # MISC Constants CFG_DB = "CONFIG_DB" STATE_DB = "STATE_DB" HOSTCFGD_MAX_PRI = 10 # Used to enforce ordering b/w daemons under Hostcfgd DEFAULT_SELECT_TIMEOUT = 1000 PORT_INIT_TIMEOUT_SEC = 180 +PROC_CMDLINE = '/proc/cmdline' def safe_eval(val, default_value=False): @@ -107,6 +116,18 @@ def run_cmd_pipe(cmd0, cmd1, cmd2, log_err=True, raise_exception=False): if raise_exception: raise +def run_cmd_output(cmd, log_err=True, raise_exception=False): + output = '' + try: + output = subprocess.check_output(cmd) + except Exception as err: + if log_err: + syslog.syslog(syslog.LOG_ERR, "{} - failed: return code - {}, output:\n{}" + .format(err.cmd, err.returncode, err.output)) + if raise_exception: + raise + return output + def is_true(val): if val == 'True' or val == 'true': @@ -1660,6 +1681,101 @@ class RSyslogCfg(object): self.cache['config'] = rsyslog_config self.cache['servers'] = rsyslog_servers +class FipsCfg(object): + """ + FipsCfg Config Daemon + Handles the changes in FIPS table. + """ + + def __init__(self, state_db_conn): + self.enable = False + self.enforce = False + self.restart_services = DEFAULT_FIPS_RESTART_SERVICES + self.state_db_conn = state_db_conn + + def read_config(self): + if os.path.exists(FIPS_CONFIG_FILE): + with open(FIPS_CONFIG_FILE) as f: + conf = json.load(f) + self.restart_services = conf.get(RESTART_SERVICES_KEY, []) + + with open(PROC_CMDLINE) as f: + kernel_cmdline = f.read().strip().split(' ') + self.cur_enforced = 'sonic_fips=1' in kernel_cmdline or 'fips=1' in kernel_cmdline + + def load(self, data={}): + common_config = data.get('global', {}) + if not common_config: + syslog.syslog(syslog.LOG_INFO, f'FipsCfg: skipped the FIPS config, the FIPS setting is empty.') + return + self.read_config() + self.enforce = is_true(common_config.get('enforce', 'false')) + self.enable = self.enforce or is_true(common_config.get('enable', 'false')) + self.update() + + def fips_handler(self, data): + self.load(data) + + def update(self): + syslog.syslog(syslog.LOG_DEBUG, f'FipsCfg: update fips option enable: {self.enable}, enforce: {self.enforce}.') + self.update_enforce_config() + self.update_noneenforce_config() + self.state_db_conn.hset('FIPS_STATS|state', 'config_datetime', datetime.utcnow().isoformat()) + syslog.syslog(syslog.LOG_DEBUG, f'FipsCfg: update fips option complete.') + + def update_noneenforce_config(self): + cur_fips_enabled = '0' + if os.path.exists(OPENSSL_FIPS_CONFIG_FILE): + with open(OPENSSL_FIPS_CONFIG_FILE) as f: + cur_fips_enabled = f.read().strip() + + expected_fips_enabled = '0' + if self.enable: + expected_fips_enabled = '1' + + # If the runtime config is not as expected, change the config + if cur_fips_enabled != expected_fips_enabled: + os.makedirs(os.path.dirname(OPENSSL_FIPS_CONFIG_FILE), exist_ok=True) + with open(OPENSSL_FIPS_CONFIG_FILE, 'w') as f: + f.write(expected_fips_enabled) + + self.restart() + + def restart(self): + if self.cur_enforced: + syslog.syslog(syslog.LOG_INFO, f'FipsCfg: skipped to restart services, since FIPS enforced.') + return + + modified_time = datetime.utcfromtimestamp(0) + if os.path.exists(OPENSSL_FIPS_CONFIG_FILE): + modified_time = datetime.fromtimestamp(os.path.getmtime(OPENSSL_FIPS_CONFIG_FILE)) + timestamp = self.state_db_conn.hget('FIPS_STATS|state', 'config_datetime') + if timestamp and datetime.fromisoformat(timestamp).replace(tzinfo=None) > modified_time.replace(tzinfo=None): + syslog.syslog(syslog.LOG_INFO, f'FipsCfg: skipped to restart services, since the services have alread been restarted.') + return + + # Restart the services required and in the running state + output = run_cmd_output(['sudo', 'systemctl', '-t', 'service', '--state=running', '--no-pager', '-o', 'json']) + if not output: + return + + services = [s['unit'] for s in json.loads(output)] + for service in self.restart_services: + if service in services or service + '.service' in services: + syslog.syslog(syslog.LOG_INFO, f'FipsCfg: restart service {service}.') + run_cmd(['sudo', 'systemctl', 'restart', service]) + + + def update_enforce_config(self): + loader = bootloader.get_bootloader() + image = loader.get_next_image() + next_enforced = loader.get_fips(image) + if next_enforced == self.enforce: + syslog.syslog(syslog.LOG_INFO, f'FipsCfg: skipped to configure the enforce option {self.enforce}, since the config has already been set.') + return + syslog.syslog(syslog.LOG_INFO, f'FipsCfg: update the FIPS enforce option {self.enforce}.') + loader.set_fips(image, self.enforce) + class HostConfigDaemon: def __init__(self): self.state_db_conn = DBConnector(STATE_DB, 0) @@ -1713,6 +1829,9 @@ class HostConfigDaemon: # Initialize RSyslogCfg self.rsyslogcfg = RSyslogCfg() + # Initialize FipsCfg + self.fipscfg = FipsCfg(self.state_db_conn) + def load(self, init_data): features = init_data['FEATURE'] aaa = init_data['AAA'] @@ -1730,7 +1849,7 @@ class HostConfigDaemon: mgmt_vrf = init_data.get(swsscommon.CFG_MGMT_VRF_CONFIG_TABLE_NAME, {}) syslog_cfg = init_data.get(swsscommon.CFG_SYSLOG_CONFIG_TABLE_NAME, {}) syslog_srv = init_data.get(swsscommon.CFG_SYSLOG_SERVER_TABLE_NAME, {}) - + fips_cfg = init_data.get('FIPS', {}) self.feature_handler.sync_state_field(features) self.aaacfg.load(aaa, tacacs_global, tacacs_server, radius_global, radius_server) @@ -1741,6 +1860,7 @@ class HostConfigDaemon: self.devmetacfg.load(dev_meta) self.mgmtifacecfg.load(mgmt_ifc, mgmt_vrf) self.rsyslogcfg.load(syslog_cfg, syslog_srv) + self.fipscfg.load(fips_cfg) # Update AAA with the hostname self.aaacfg.hostname_update(self.devmetacfg.hostname) @@ -1856,6 +1976,11 @@ class HostConfigDaemon: syslog.syslog(syslog.LOG_INFO, 'SYSLOG_CONFIG table handler...') self.rsyslog_handler() + def fips_config_handler(self, key, op, data): + syslog.syslog(syslog.LOG_INFO, 'FIPS table handler...') + data = self.config_db.get_table("FIPS") + self.fipscfg.fips_handler(data) + def wait_till_system_init_done(self): # No need to print the output in the log file so using the "--quiet" # flag @@ -1909,6 +2034,9 @@ class HostConfigDaemon: self.config_db.subscribe(swsscommon.CFG_SYSLOG_SERVER_TABLE_NAME, make_callback(self.rsyslog_server_handler)) + # Handle FIPS changes + self.config_db.subscribe('FIPS', make_callback(self.fips_config_handler)) + syslog.syslog(syslog.LOG_INFO, "Waiting for systemctl to finish initialization") self.wait_till_system_init_done() diff --git a/scripts/procdockerstatsd b/scripts/procdockerstatsd index c2f5a706..19e579ae 100755 --- a/scripts/procdockerstatsd +++ b/scripts/procdockerstatsd @@ -13,7 +13,7 @@ from datetime import datetime from sonic_py_common import daemon_base from swsscommon import swsscommon -from sonic_py_common.general import getstatusoutput_noshell_pipe +from sonic_py_common.general import getstatusoutput_noshell_pipe, getstatusoutput_noshell VERSION = '1.0' @@ -168,6 +168,22 @@ class ProcDockerStats(daemon_base.DaemonBase): cmd = row.get('CMD') self.update_state_db(value, 'CMD', cmd) + def update_fipsstats_command(self): + fips_db_key = 'FIPS_STATS|state' + + # Check if FIPS enforced in the current kernel cmdline + with open('/proc/cmdline') as f: + kernel_cmdline = f.read().strip().split(' ') + enforced = 'sonic_fips=1' in kernel_cmdline or 'fips=1' in kernel_cmdline + + # Check if FIPS runtime status + exitcode, _ = getstatusoutput_noshell_pipe(['sudo', 'openssl', 'engine', '-vv'], ['grep', '-i', 'symcryp']) + enabled = not any(exitcode) + + self.update_state_db(fips_db_key, 'timestamp', datetime.utcnow().isoformat()) + self.update_state_db(fips_db_key, 'enforced', str(enforced)) + self.update_state_db(fips_db_key, 'enabled', str(enabled)) + def update_state_db(self, key1, key2, value2): self.state_db.set('STATE_DB', key1, key2, value2) @@ -186,6 +202,8 @@ class ProcDockerStats(daemon_base.DaemonBase): self.update_state_db('DOCKER_STATS|LastUpdateTime', 'lastupdate', str(datetimeobj)) self.update_processstats_command() self.update_state_db('PROCESS_STATS|LastUpdateTime', 'lastupdate', str(datetimeobj)) + self.update_fipsstats_command() + self.update_state_db('FIPS_STATS|LastUpdateTime', 'lastupdate', str(datetimeobj)) # Data need to be updated every 2 mins. hence adding delay of 120 seconds time.sleep(120) diff --git a/setup.py b/setup.py index 5c9898f9..dc0787d9 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ from packaging import version # sonic_dependencies, version requirement only supports '>=' -sonic_dependencies = ['sonic-py-common'] +sonic_dependencies = ['sonic-py-common', 'sonic-utilities'] for package in sonic_dependencies: try: package_dist = pkg_resources.get_distribution(package.split(">=")[0]) diff --git a/tests/common/mock_bootloader.py b/tests/common/mock_bootloader.py new file mode 100644 index 00000000..37781ad1 --- /dev/null +++ b/tests/common/mock_bootloader.py @@ -0,0 +1,13 @@ +class MockBootloader(object): + + def __init__(self, enforce=False): + self.enforce = enforce + + def get_next_image(self): + return "" + + def set_fips(self, image, enable): + self.enforce = enable + + def get_fips(self, image): + return self.enforce diff --git a/tests/common/mock_configdb.py b/tests/common/mock_configdb.py index 7c7a7510..15abc00b 100644 --- a/tests/common/mock_configdb.py +++ b/tests/common/mock_configdb.py @@ -139,4 +139,16 @@ def pop(self): class MockDBConnector(): def __init__(self, db, val, tcpFlag=False, name=None): - pass + self.data = {} + + def hget(self, key, field): + if key not in self.data: + return None + if field not in self.data[key]: + return None + return self.data[key][field] + + def hset(self, key, field, value): + if key not in self.data: + self.data[key] = {} + self.data[key][field] = value diff --git a/tests/hostcfgd/hostcfgd_fips_test.py b/tests/hostcfgd/hostcfgd_fips_test.py new file mode 100644 index 00000000..029e77d3 --- /dev/null +++ b/tests/hostcfgd/hostcfgd_fips_test.py @@ -0,0 +1,133 @@ +import importlib.machinery +import importlib.util +import filecmp +import json +import shutil +import os +import sys +from swsscommon import swsscommon + +from parameterized import parameterized +from unittest import TestCase, mock +from tests.common.mock_configdb import MockConfigDb, MockDBConnector +from tests.common.mock_bootloader import MockBootloader +from sonic_py_common.general import getstatusoutput_noshell + +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.Mock() +running_services = [{"unit":"ssh.service","load":"loaded","active":"active","sub":"running","description":"OpenBSD Secure Shell server"}, + {"unit":"restapi.service","load":"loaded","active":"active","sub":"running","description":"SONiC Restful API Service"}] + +class TestHostcfgdFIPS(TestCase): + """ + Test hostcfd daemon - FIPS + """ + def run_diff(self, file1, file2): + _, output = getstatusoutput_noshell(['diff', '-uR', file1, file2]) + return output + + def setUp(self): + self._workPath =os.path.join('/tmp/test_fips/', self._testMethodName) + self.test_data = {'DEVICE_METADATA':{},'FIPS': {}} + self.test_data['DEVICE_METADATA'] = {'localhost': {'hostname': 'fips'}} + self.test_data['FIPS']['global'] = {'enable': 'false', 'enforce': 'false'} + hostcfgd.FIPS_CONFIG_FILE = os.path.join(self._workPath + 'eips.json') + hostcfgd.OPENSSL_FIPS_CONFIG_FILE = os.path.join(self._workPath, 'fips_enabled') + hostcfgd.PROC_CMDLINE = os.path.join(self._workPath, 'cmdline') + os.makedirs(self._workPath, exist_ok=True) + with open(hostcfgd.PROC_CMDLINE, 'w') as f: + f.write('swiotlb=65536 sonic_fips=0') + with open(hostcfgd.OPENSSL_FIPS_CONFIG_FILE, 'w') as f: + f.write('0') + + def tearDown(self): + shutil.rmtree(self._workPath, ignore_errors=True) + + def assert_fips_runtime_config(self, result='1'): + with open(hostcfgd.OPENSSL_FIPS_CONFIG_FILE) as f: + assert f.read() == result + + @mock.patch('sonic_installer.bootloader.get_bootloader', side_effect=[MockBootloader()]) + @mock.patch('syslog.syslog') + @mock.patch('subprocess.check_output', side_effect=[json.dumps(running_services)]) + @mock.patch('subprocess.check_call') + def test_hostcfgd_fips_enable(self, mock_check_call, mock_check_output, mock_syslog, mock_get_bootloader): + with open(hostcfgd.PROC_CMDLINE, 'w') as f: + f.write('swiotlb=65536 sonic_fips=0') + self.test_data['FIPS']['global']['enable'] = 'true' + MockConfigDb.set_config_db(self.test_data) + host_config_daemon = hostcfgd.HostConfigDaemon() + host_config_daemon.fips_config_handler("FIPS", '', self.test_data) + mock_syslog.assert_any_call(original_syslog.LOG_INFO, 'FipsCfg: restart service ssh.') + mock_syslog.assert_any_call(original_syslog.LOG_INFO, 'FipsCfg: restart service restapi.') + mock_syslog.assert_any_call(original_syslog.LOG_INFO, 'FipsCfg: skipped to configure the enforce option False, since the config has already been set.') + mock_syslog.assert_called_with(original_syslog.LOG_DEBUG, 'FipsCfg: update fips option complete.') + self.assert_fips_runtime_config() + + @mock.patch('sonic_installer.bootloader.get_bootloader', side_effect=[MockBootloader()]) + @mock.patch('syslog.syslog') + @mock.patch('subprocess.check_output', side_effect=[json.dumps(running_services)]) + @mock.patch('subprocess.check_call') + def test_hostcfgd_fips_disable(self, mock_check_call, mock_check_output, mock_syslog, mock_get_bootloader): + with open(hostcfgd.PROC_CMDLINE, 'w') as f: + f.write('swiotlb=65536 sonic_fips=0') + with open(hostcfgd.OPENSSL_FIPS_CONFIG_FILE, 'w') as f: + f.write('1') + self.test_data['FIPS']['global']['enable'] = 'false' + MockConfigDb.set_config_db(self.test_data) + host_config_daemon = hostcfgd.HostConfigDaemon() + host_config_daemon.fips_config_handler("FIPS", '', self.test_data) + mock_syslog.assert_any_call(original_syslog.LOG_INFO, 'FipsCfg: restart service ssh.') + mock_syslog.assert_any_call(original_syslog.LOG_INFO, 'FipsCfg: restart service restapi.') + mock_syslog.assert_any_call(original_syslog.LOG_INFO, 'FipsCfg: skipped to configure the enforce option False, since the config has already been set.') + mock_syslog.assert_called_with(original_syslog.LOG_DEBUG, 'FipsCfg: update fips option complete.') + self.assert_fips_runtime_config('0') + + @mock.patch('sonic_installer.bootloader.get_bootloader', return_value=MockBootloader()) + @mock.patch('syslog.syslog') + @mock.patch('subprocess.check_output', side_effect=[json.dumps(running_services)]) + @mock.patch('subprocess.check_call') + def test_hostcfgd_fips_enforce(self, mock_check_call, mock_check_output, mock_syslog, mock_get_bootloader): + with open(hostcfgd.PROC_CMDLINE, 'w') as f: + f.write('swiotlb=65536 sonic_fips=0') + self.test_data['FIPS']['global']['enforce'] = 'true' + MockConfigDb.set_config_db(self.test_data) + host_config_daemon = hostcfgd.HostConfigDaemon() + host_config_daemon.fips_config_handler("FIPS", '', self.test_data) + mock_syslog.assert_any_call(original_syslog.LOG_INFO, 'FipsCfg: restart service ssh.') + mock_syslog.assert_any_call(original_syslog.LOG_INFO, 'FipsCfg: restart service restapi.') + mock_syslog.assert_any_call(original_syslog.LOG_INFO, 'FipsCfg: update the FIPS enforce option True.') + mock_syslog.assert_called_with(original_syslog.LOG_DEBUG, 'FipsCfg: update fips option complete.') + self.assert_fips_runtime_config() + + @mock.patch('sonic_installer.bootloader.get_bootloader', return_value=MockBootloader(True)) + @mock.patch('syslog.syslog') + @mock.patch('subprocess.check_output', side_effect=[json.dumps(running_services)]) + @mock.patch('subprocess.check_call') + def test_hostcfgd_fips_enforce_reconf(self, mock_check_call, mock_check_output, mock_syslog, mock_get_bootloader): + with open(hostcfgd.PROC_CMDLINE, 'w') as f: + f.write('swiotlb=65536 sonic_fips=1') + self.test_data['FIPS']['global']['enforce'] = 'true' + MockConfigDb.set_config_db(self.test_data) + host_config_daemon = hostcfgd.HostConfigDaemon() + host_config_daemon.fips_config_handler("FIPS", '', self.test_data) + mock_syslog.assert_any_call(original_syslog.LOG_INFO, 'FipsCfg: skipped to restart services, since FIPS enforced.') + mock_syslog.assert_any_call(original_syslog.LOG_INFO, 'FipsCfg: skipped to configure the enforce option True, since the config has already been set.') + mock_syslog.assert_called_with(original_syslog.LOG_DEBUG, 'FipsCfg: update fips option complete.') + self.assert_fips_runtime_config() diff --git a/tests/mock_connector.py b/tests/mock_connector.py index 29b47119..71490796 100644 --- a/tests/mock_connector.py +++ b/tests/mock_connector.py @@ -11,6 +11,11 @@ def connect(self, db_id): def get(self, db_id, key, field): return MockConnector.data[key][field] + def set(self, db_id, key, field, value): + if key not in MockConnector.data: + MockConnector.data[key] = {} + MockConnector.data[key][field] = value + def keys(self, db_id, pattern): match = pattern.split('*')[0] ret = [] diff --git a/tests/procdockerstatsd_test.py b/tests/procdockerstatsd_test.py index cdb31d22..40b222db 100644 --- a/tests/procdockerstatsd_test.py +++ b/tests/procdockerstatsd_test.py @@ -57,3 +57,9 @@ def test_update_processstats_command(self): pdstatsd.update_processstats_command() mock_cmd.assert_has_calls(expected_calls) + @patch('procdockerstatsd.getstatusoutput_noshell_pipe', return_value=([0, 0], '')) + def test_update_fipsstats_command(self, mock_cmd): + pdstatsd = procdockerstatsd.ProcDockerStats(procdockerstatsd.SYSLOG_IDENTIFIER) + pdstatsd.update_fipsstats_command() + assert pdstatsd.state_db.get('STATE_DB', 'FIPS_STATS|state', 'enforced') == "False" + assert pdstatsd.state_db.get('STATE_DB', 'FIPS_STATS|state', 'enabled') == "True"