Skip to content

Commit a9fb3af

Browse files
davidpil2002ms-junyi
authored and
ms-junyi
committed
Password hardening test fix (sonic-net#6453)
What is the motivation for this PR? Fix the issue sonic-net#6428 How did you do it? explained in the summary. How did you verify/test it? Run the test with the sonic-builimage build from branch 202205 command line: py.test /sonic-mgmt/tests/passw_hardening/test_passw_hardening.py
1 parent f276d39 commit a9fb3af

File tree

10 files changed

+342
-283
lines changed

10 files changed

+342
-283
lines changed

tests/passw_hardening/conftest.py

+31-16
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import pytest
2-
import test_passw_hardening
2+
from tests.common.utilities import skip_release
3+
import passw_hardening_utils
34

45
def set_default_passw_hardening_policies(duthosts, enum_rand_one_per_hwsku_hostname):
56
duthost = duthosts[enum_rand_one_per_hwsku_hostname]
67

7-
passw_hardening_ob_dis = test_passw_hardening.PasswHardening(state='disabled',
8+
passw_hardening_ob_dis = passw_hardening_utils.PasswHardening(state='disabled',
89
expiration='100',
910
expiration_warning='15',
1011
history='12',
@@ -15,7 +16,21 @@ def set_default_passw_hardening_policies(duthosts, enum_rand_one_per_hwsku_hostn
1516
digit_class="true",
1617
special_class='true')
1718

18-
test_passw_hardening.config_and_review_policies(duthost, passw_hardening_ob_dis, test_passw_hardening.PAM_PASSWORD_CONF_DEFAULT_EXPECTED)
19+
passw_hardening_utils.config_and_review_policies(duthost, passw_hardening_ob_dis, passw_hardening_utils.PAM_PASSWORD_CONF_DEFAULT_EXPECTED)
20+
21+
@pytest.fixture(scope="module", autouse=True)
22+
def passw_version_required(duthosts, enum_rand_one_per_hwsku_hostname):
23+
"""Skips this test if the SONiC image installed on DUT is older than 202111
24+
25+
Args:
26+
duthost: DUT host object.
27+
28+
Returns:
29+
None.
30+
"""
31+
duthost = duthosts[enum_rand_one_per_hwsku_hostname]
32+
skip_release(duthost, ["201811", "201911", "202012", "202106", "202111"])
33+
1934

2035
@pytest.fixture(scope="function")
2136
def clean_passw_policies(duthosts, enum_rand_one_per_hwsku_hostname):
@@ -26,38 +41,38 @@ def clean_passw_policies(duthosts, enum_rand_one_per_hwsku_hostname):
2641
def clean_passw_one_policy_user(duthosts, enum_rand_one_per_hwsku_hostname):
2742
yield
2843
duthost = duthosts[enum_rand_one_per_hwsku_hostname]
29-
res_adduser_simple_0 = test_passw_hardening.config_user(duthost=duthost, username=test_passw_hardening.USERNAME_ONE_POLICY, mode='del')
44+
res_adduser_simple_0 = passw_hardening_utils.config_user(duthost=duthost, username=passw_hardening_utils.USERNAME_ONE_POLICY, mode='del')
3045

3146

3247
@pytest.fixture(scope="function")
3348
def clean_passw_len_min(duthosts, enum_rand_one_per_hwsku_hostname):
3449
yield
3550
duthost = duthosts[enum_rand_one_per_hwsku_hostname]
36-
test_passw_hardening.config_user(duthost=duthost, username=test_passw_hardening.USERNAME_LEN_MIN, mode='del')
37-
duthost.shell('sed -i /^'+test_passw_hardening.USERNAME_LEN_MIN+':/d /etc/security/opasswd')
51+
passw_hardening_utils.config_user(duthost=duthost, username=passw_hardening_utils.USERNAME_LEN_MIN, mode='del')
52+
duthost.shell('sed -i /^'+passw_hardening_utils.USERNAME_LEN_MIN+':/d /etc/security/opasswd')
3853

3954
@pytest.fixture(scope="function")
4055
def clean_passw_age(duthosts, enum_rand_one_per_hwsku_hostname):
4156
yield
4257
duthost = duthosts[enum_rand_one_per_hwsku_hostname]
43-
test_passw_hardening.config_user(duthost=duthost, username=test_passw_hardening.USERNAME_AGE, mode='del')
44-
duthost.shell('sed -i /^'+test_passw_hardening.USERNAME_AGE+':/d /etc/security/opasswd')
58+
passw_hardening_utils.config_user(duthost=duthost, username=passw_hardening_utils.USERNAME_AGE, mode='del')
59+
duthost.shell('sed -i /^'+passw_hardening_utils.USERNAME_AGE+':/d /etc/security/opasswd')
4560

4661

4762
@pytest.fixture(scope="function")
4863
def clean_passw_en_dis_policies(duthosts, enum_rand_one_per_hwsku_hostname):
4964
yield
5065
duthost = duthosts[enum_rand_one_per_hwsku_hostname]
51-
test_passw_hardening.config_user(duthost=duthost, username=test_passw_hardening.USERNAME_SIMPLE_0, mode='del')
52-
test_passw_hardening.config_user(duthost=duthost, username=test_passw_hardening.USERNAME_SIMPLE_1, mode='del')
53-
test_passw_hardening.config_user(duthost=duthost, username=test_passw_hardening.USERNAME_STRONG, mode='del')
54-
duthost.shell('sed -i /^'+test_passw_hardening.USERNAME_SIMPLE_0+':/d /etc/security/opasswd')
55-
duthost.shell('sed -i /^'+test_passw_hardening.USERNAME_SIMPLE_1+':/d /etc/security/opasswd')
56-
duthost.shell('sed -i /^'+test_passw_hardening.USERNAME_STRONG+':/d /etc/security/opasswd')
66+
passw_hardening_utils.config_user(duthost=duthost, username=passw_hardening_utils.USERNAME_SIMPLE_0, mode='del')
67+
passw_hardening_utils.config_user(duthost=duthost, username=passw_hardening_utils.USERNAME_SIMPLE_1, mode='del')
68+
passw_hardening_utils.config_user(duthost=duthost, username=passw_hardening_utils.USERNAME_STRONG, mode='del')
69+
duthost.shell('sed -i /^'+passw_hardening_utils.USERNAME_SIMPLE_0+':/d /etc/security/opasswd')
70+
duthost.shell('sed -i /^'+passw_hardening_utils.USERNAME_SIMPLE_1+':/d /etc/security/opasswd')
71+
duthost.shell('sed -i /^'+passw_hardening_utils.USERNAME_STRONG+':/d /etc/security/opasswd')
5772

5873
@pytest.fixture(scope="function")
5974
def clean_passw_history(duthosts, enum_rand_one_per_hwsku_hostname):
6075
yield
6176
duthost = duthosts[enum_rand_one_per_hwsku_hostname]
62-
test_passw_hardening.config_user(duthost=duthost, username=test_passw_hardening.USERNAME_HISTORY, mode='del')
63-
duthost.shell('sed -i /^'+test_passw_hardening.USERNAME_HISTORY+':/d /etc/security/opasswd')
77+
passw_hardening_utils.config_user(duthost=duthost, username=passw_hardening_utils.USERNAME_HISTORY, mode='del')
78+
duthost.shell('sed -i /^'+passw_hardening_utils.USERNAME_HISTORY+':/d /etc/security/opasswd')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import logging
2+
import os
3+
import difflib
4+
from tests.common.helpers.assertions import pytest_assert
5+
6+
CURR_DIR = os.path.dirname(os.path.abspath(__file__))
7+
8+
# Sample/Expected files
9+
PAM_PASSWORD_CONF_DEFAULT_EXPECTED = CURR_DIR + '/sample/passw_hardening_default/common-password'
10+
PAM_PASSWORD_CONF_EXPECTED = CURR_DIR + '/sample/passw_hardening_enable/common-password'
11+
PAM_PASSWORD_CONF_HISTORY_ONLY_EXPECTED = CURR_DIR + '/sample/passw_hardening_history/common-password'
12+
PAM_PASSWORD_CONF_REJECT_USER_PASSW_MATCH_EXPECTED = CURR_DIR + '/sample/passw_hardening_reject_user_passw_match/common-password'
13+
PAM_PASSWORD_CONF_DIGITS_ONLY_EXPECTED = CURR_DIR + '/sample/passw_hardening_digits/common-password'
14+
PAM_PASSWORD_CONF_LOWER_LETTER_ONLY_EXPECTED = CURR_DIR + '/sample/passw_hardening_lower_letter/common-password'
15+
PAM_PASSWORD_CONF_UPPER_LETTER_ONLY_EXPECTED = CURR_DIR + '/sample/passw_hardening_upper_letter/common-password'
16+
PAM_PASSWORD_CONF_SPECIAL_LETTER_ONLY_EXPECTED = CURR_DIR + '/sample/passw_hardening_special_letter/common-password'
17+
PAM_PASSWORD_CONF_LEN_MIN_ONLY_EXPECTED = CURR_DIR + '/sample/passw_hardening_min_len/common-password'
18+
PAM_PASSWORD_CONF_OUTPUT = CURR_DIR + '/output/login.def'
19+
PAM_PASSWORD_CONF = "/etc/pam.d/common-password"
20+
21+
# users
22+
USERNAME_STRONG = 'user_strong_test'
23+
USERNAME_SIMPLE_0 = 'user_simple_0_test'
24+
USERNAME_SIMPLE_1 = 'user_simple_1_test'
25+
USERNAME_ONE_POLICY = 'user_one_policy_test'
26+
USERNAME_AGE = 'user_test'
27+
USERNAME_HISTORY = 'user_history_test'
28+
USERNAME_LEN_MIN = 'user_test'
29+
30+
31+
class PasswHardening:
32+
def __init__(self, state='disabled', expiration='100', expiration_warning='15', history='12',
33+
len_min='8', reject_user_passw_match='true', lower_class='true',
34+
upper_class='true', digit_class='true', special_class='true'):
35+
self.policies = {
36+
"state": state,
37+
"expiration": expiration,
38+
"expiration-warning": expiration_warning,
39+
"history-cnt": history,
40+
"len-min": len_min,
41+
"reject-user-passw-match": reject_user_passw_match,
42+
"lower-class": lower_class,
43+
"upper-class": upper_class,
44+
"digits-class": digit_class,
45+
"special-class": special_class
46+
}
47+
48+
49+
def config_user(duthost, username, mode='add'):
50+
""" Function add or rm users using useradd/userdel tool. """
51+
52+
username = username.strip()
53+
command = "user{} {}".format(mode, username)
54+
user_cmd = duthost.shell(command, module_ignore_errors=True)
55+
return user_cmd
56+
57+
58+
def configure_passw_policies(duthost, passw_hardening_ob):
59+
for key, value in passw_hardening_ob.policies.items():
60+
logging.debug("configuration to be set: key={}, value={}".format(key, value))
61+
# cmd_config = 'sudo config passw-hardening policies ' + key + ' ' + value
62+
63+
cmd_config = 'sudo config passw-hardening policies {} {}'.format(key, value)
64+
65+
duthost.command(cmd_config)
66+
return True
67+
68+
69+
def compare_passw_policies_in_linux(duthost, pam_file_expected=PAM_PASSWORD_CONF_EXPECTED):
70+
"""Compare DUT common-password with the expected one."""
71+
72+
command_password_stdout = ''
73+
read_command_password = 'cat {}'.format(PAM_PASSWORD_CONF)
74+
75+
logging.debug('DUT command = {}'.format(read_command_password))
76+
read_command_password_cmd = duthost.command(read_command_password)
77+
command_password_stdout = read_command_password_cmd["stdout_lines"]
78+
command_password_stdout = [line.encode('utf-8') for line in command_password_stdout]
79+
80+
common_password_expected = []
81+
with open(pam_file_expected, 'r') as expected_common_password_file:
82+
for line in expected_common_password_file:
83+
line = line.strip()
84+
line = line.strip('\n')
85+
line = line.strip('\t')
86+
common_password_expected.append(line)
87+
88+
common_password_diff = [li for li in difflib.ndiff(command_password_stdout, common_password_expected) if
89+
li[0] != ' ']
90+
pytest_assert(len(common_password_diff) == 0, common_password_diff)
91+
92+
93+
def config_and_review_policies(duthost, passw_hardening_ob, pam_file_expected):
94+
"""
95+
1. Config passw hardening policies
96+
2. Show passw hardening policies
97+
3. Compare passw hardening polices from show cli to the expected (configured)
98+
4. Verify polices in PAM files was set according the configured
99+
"""
100+
FIRST_LINE = 0
101+
configure_passw_policies(duthost, passw_hardening_ob)
102+
103+
curr_show_policies = duthost.show_and_parse('show passw-hardening policies')[FIRST_LINE]
104+
exp_show_policies = dict((k.replace('-', ' '), v) for k, v in passw_hardening_ob.policies.items())
105+
106+
# ~~ test passw policies in show CLI ~~
107+
cli_passw_policies_cmp = cmp(exp_show_policies, curr_show_policies)
108+
pytest_assert(cli_passw_policies_cmp == 0, "Fail: exp_show_policies='{}',not equal to curr_show_policies='{}'"
109+
.format(exp_show_policies, curr_show_policies))
110+
111+
# ~~ test passw policies in PAM files ~~
112+
compare_passw_policies_in_linux(duthost, pam_file_expected)

tests/passw_hardening/sample/passw_hardening_digits/common-password

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
password requisite pam_cracklib.so retry=3 maxrepeat=0 minlen=1 ucredit=0 lcredit=0 dcredit=-1 ocredit=0 enforce_for_root
2828

29-
password required pam_pwhistory.so remember=0 use_authtok enforce_for_root
29+
password required pam_pwhistory.so remember=12 use_authtok enforce_for_root
3030

3131
password [success=1 default=ignore] pam_unix.so obscure yescrypt
3232
# here's the fallback if no module succeeds

tests/passw_hardening/sample/passw_hardening_history/common-password

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
# here are the per-package modules (the "Primary" block)
2626

27-
password requisite pam_cracklib.so retry=3 maxrepeat=0 minlen=1 ucredit=0 lcredit=0 dcredit=-1 ocredit=0 enforce_for_root
27+
password requisite pam_cracklib.so retry=3 maxrepeat=0 minlen=8 ucredit=0 lcredit=0 dcredit=-1 ocredit=0 enforce_for_root
2828

2929
password required pam_pwhistory.so remember=10 use_authtok enforce_for_root
3030

tests/passw_hardening/sample/passw_hardening_lower_letter/common-password

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
password requisite pam_cracklib.so retry=3 maxrepeat=0 minlen=1 ucredit=0 lcredit=-1 dcredit=0 ocredit=0 enforce_for_root
2828

29-
password required pam_pwhistory.so remember=0 use_authtok enforce_for_root
29+
password required pam_pwhistory.so remember=10 use_authtok enforce_for_root
3030

3131
password [success=1 default=ignore] pam_unix.so obscure yescrypt
3232
# here's the fallback if no module succeeds

tests/passw_hardening/sample/passw_hardening_min_len/common-password

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
password requisite pam_cracklib.so retry=3 maxrepeat=0 minlen=8 ucredit=0 lcredit=0 dcredit=-1 ocredit=0 enforce_for_root
2828

29-
password required pam_pwhistory.so remember=1 use_authtok enforce_for_root
29+
password required pam_pwhistory.so remember=10 use_authtok enforce_for_root
3030

3131
password [success=1 default=ignore] pam_unix.so obscure yescrypt
3232
# here's the fallback if no module succeeds

tests/passw_hardening/sample/passw_hardening_reject_user_passw_match/common-password

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
password requisite pam_cracklib.so retry=3 maxrepeat=0 minlen=1 ucredit=0 lcredit=0 dcredit=0 ocredit=0 reject_username enforce_for_root
2828

29-
password required pam_pwhistory.so remember=0 use_authtok enforce_for_root
29+
password required pam_pwhistory.so remember=10 use_authtok enforce_for_root
3030

3131
password [success=1 default=ignore] pam_unix.so obscure yescrypt
3232
# here's the fallback if no module succeeds

tests/passw_hardening/sample/passw_hardening_special_letter/common-password

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
password requisite pam_cracklib.so retry=3 maxrepeat=0 minlen=1 ucredit=0 lcredit=0 dcredit=0 ocredit=-1 enforce_for_root
2828

29-
password required pam_pwhistory.so remember=0 use_authtok enforce_for_root
29+
password required pam_pwhistory.so remember=10 use_authtok enforce_for_root
3030

3131
password [success=1 default=ignore] pam_unix.so obscure yescrypt
3232
# here's the fallback if no module succeeds

tests/passw_hardening/sample/passw_hardening_upper_letter/common-password

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
password requisite pam_cracklib.so retry=3 maxrepeat=0 minlen=1 ucredit=-1 lcredit=0 dcredit=0 ocredit=0 enforce_for_root
2828

29-
password required pam_pwhistory.so remember=0 use_authtok enforce_for_root
29+
password required pam_pwhistory.so remember=10 use_authtok enforce_for_root
3030

3131
password [success=1 default=ignore] pam_unix.so obscure yescrypt
3232
# here's the fallback if no module succeeds

0 commit comments

Comments
 (0)