[202205] Check config file not empty after modify it in hostcfgd. (#14115)

What I did
Check /etc/pam.d/sshd integrity after modify it in hostcfgd.

Why I did it
Found some incident that /etc/pam.d/sshd become empty file during OR upgrade.

How I verified it
Pass all UT.
Add new UT to cover new code.
This commit is contained in:
Hua Liu 2023-03-08 09:51:06 -08:00 committed by GitHub
parent 1ccad8f0b3
commit b715894dc8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 101 additions and 6 deletions

View File

@ -780,11 +780,28 @@ class AaaCfg(object):
interface_ip = ipv4_addr
return interface_ip
def check_file_not_empty(self, filename):
exists = os.path.exists(filename)
if not exists:
syslog.syslog(syslog.LOG_ERR, "file size check failed: {} is missing".format(filename))
return
size = os.path.getsize(filename)
if size == 0:
syslog.syslog(syslog.LOG_ERR, "file size check failed: {} is empty, file corrupted".format(filename))
return
syslog.syslog(syslog.LOG_INFO, "file size check pass: {} size is ({}) bytes".format(filename, size))
def modify_single_file(self, filename, operations=None):
if operations:
cmd = "sed -e {0} {1} > {1}.new; mv -f {1} {1}.old; mv -f {1}.new {1}".format(' -e '.join(operations), filename)
os.system(cmd)
self.check_file_not_empty(filename)
def modify_conf_file(self):
authentication = self.authentication_default.copy()
authentication.update(self.authentication)

View File

@ -42,13 +42,10 @@ class TestHostcfgdTACACS(TestCase):
return subprocess.check_output('diff -uR {} {} || true'.format(file1, file2), shell=True)
"""
Check different config
Mock hostcfgd
"""
def check_config(self, test_name, test_data, config_name):
def mock_hostcfgd(self, test_data, config_name, op_path, sop_path):
t_path = templates_path
op_path = output_path + "/" + test_name + "_" + config_name
sop_path = sample_output_path + "/" + test_name + "_" + config_name
hostcfgd.PAM_AUTH_CONF_TEMPLATE = t_path + "/common-auth-sonic.j2"
hostcfgd.NSS_TACPLUS_CONF_TEMPLATE = t_path + "/tacplus_nss.conf.j2"
hostcfgd.NSS_RADIUS_CONF_TEMPLATE = t_path + "/radius_nss.conf.j2"
@ -68,8 +65,12 @@ class TestHostcfgdTACACS(TestCase):
shutil.copyfile( sop_path + "/login.old", op_path + "/login")
MockConfigDb.set_config_db(test_data[config_name])
host_config_daemon = hostcfgd.HostConfigDaemon()
return hostcfgd.HostConfigDaemon()
"""
Render different config
"""
def render_config_file(self, host_config_daemon):
aaa = host_config_daemon.config_db.get_table('AAA')
try:
@ -83,6 +84,17 @@ class TestHostcfgdTACACS(TestCase):
tacacs_server = []
host_config_daemon.aaacfg.load(aaa,tacacs_global,tacacs_server,[],[])
"""
Check different config
"""
def check_config(self, test_name, test_data, config_name):
op_path = output_path + "/" + test_name + "_" + config_name
sop_path = sample_output_path + "/" + test_name + "_" + config_name
host_config_daemon = self.mock_hostcfgd(test_data, config_name, op_path, sop_path)
self.render_config_file(host_config_daemon)
dcmp = filecmp.dircmp(sop_path, op_path)
diff_output = ""
for name in dcmp.diff_files:
@ -114,3 +126,68 @@ class TestHostcfgdTACACS(TestCase):
self.check_config(test_name, test_data, "config_db_local_and_tacacs")
# test disable accounting
self.check_config(test_name, test_data, "config_db_disable_accounting")
@parameterized.expand(HOSTCFGD_TEST_TACACS_VECTOR)
def test_hostcfgd_sshd_not_empty(self, test_name, test_data):
"""
Test hostcfd sshd config file not empty check
Args:
test_name(str): test name
test_data(dict): test data which contains initial Config Db tables, and expected results
Returns:
None
"""
config_name = "config_db_local"
op_path = output_path + "/" + test_name + "_" + config_name
sop_path = sample_output_path + "/" + test_name + "_" + config_name
host_config_daemon = self.mock_hostcfgd(test_data, config_name, op_path, sop_path)
# test sshd empty case
hostcfgd.ETC_PAMD_SSHD = op_path + "/sshd_empty"
shutil.copyfile( sop_path + "/sshd_empty.old", op_path + "/sshd_empty")
# render with empty sshd config file and check error log
original_syslog = hostcfgd.syslog
with mock.patch('hostcfgd.syslog.syslog') as mocked_syslog:
mocked_syslog.LOG_ERR = original_syslog.LOG_ERR
self.render_config_file(host_config_daemon)
# check sys log
expected = [
mock.call(mocked_syslog.LOG_ERR, "file size check failed: {} is empty, file corrupted".format(hostcfgd.ETC_PAMD_SSHD))
]
mocked_syslog.assert_has_calls(expected)
# test sshd missing case
hostcfgd.ETC_PAMD_SSHD = op_path + "/sshd_missing"
with mock.patch('hostcfgd.syslog.syslog') as mocked_syslog:
mocked_syslog.LOG_ERR = original_syslog.LOG_ERR
# missing file can't test by render config file,
# because missing file case difficult to reproduce: code always generate a empty file.
host_config_daemon.aaacfg.check_file_not_empty(hostcfgd.ETC_PAMD_SSHD)
# check sys log
expected = [
mock.call(mocked_syslog.LOG_ERR, "file size check failed: {} is missing".format(hostcfgd.ETC_PAMD_SSHD))
]
mocked_syslog.assert_has_calls(expected)
# test sshd exist and not empty case
hostcfgd.ETC_PAMD_SSHD = op_path + "/sshd_not_empty"
shutil.copyfile( sop_path + "/sshd_not_empty.old", op_path + "/sshd_not_empty")
# render with empty sshd config file and check error log
original_syslog = hostcfgd.syslog
with mock.patch('hostcfgd.syslog.syslog') as mocked_syslog:
mocked_syslog.LOG_INFO = original_syslog.LOG_INFO
self.render_config_file(host_config_daemon)
# check sys log
expected = [
mock.call(mocked_syslog.LOG_INFO, "file size check pass: {} size is ({}) bytes".format(hostcfgd.ETC_PAMD_SSHD, 21))
]
mocked_syslog.assert_has_calls(expected)