Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Password hardening test fix #6453

Merged
merged 3 commits into from
Oct 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 31 additions & 16 deletions tests/passw_hardening/conftest.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import pytest
import test_passw_hardening
from tests.common.utilities import skip_release
import passw_hardening_utils

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

passw_hardening_ob_dis = test_passw_hardening.PasswHardening(state='disabled',
passw_hardening_ob_dis = passw_hardening_utils.PasswHardening(state='disabled',
expiration='100',
expiration_warning='15',
history='12',
Expand All @@ -15,7 +16,21 @@ def set_default_passw_hardening_policies(duthosts, enum_rand_one_per_hwsku_hostn
digit_class="true",
special_class='true')

test_passw_hardening.config_and_review_policies(duthost, passw_hardening_ob_dis, test_passw_hardening.PAM_PASSWORD_CONF_DEFAULT_EXPECTED)
passw_hardening_utils.config_and_review_policies(duthost, passw_hardening_ob_dis, passw_hardening_utils.PAM_PASSWORD_CONF_DEFAULT_EXPECTED)

@pytest.fixture(scope="module", autouse=True)
def passw_version_required(duthosts, enum_rand_one_per_hwsku_hostname):
"""Skips this test if the SONiC image installed on DUT is older than 202111

Args:
duthost: DUT host object.

Returns:
None.
"""
duthost = duthosts[enum_rand_one_per_hwsku_hostname]
skip_release(duthost, ["201811", "201911", "202012", "202106", "202111"])
ZhaohuiS marked this conversation as resolved.
Show resolved Hide resolved


@pytest.fixture(scope="function")
def clean_passw_policies(duthosts, enum_rand_one_per_hwsku_hostname):
Expand All @@ -26,38 +41,38 @@ def clean_passw_policies(duthosts, enum_rand_one_per_hwsku_hostname):
def clean_passw_one_policy_user(duthosts, enum_rand_one_per_hwsku_hostname):
yield
duthost = duthosts[enum_rand_one_per_hwsku_hostname]
res_adduser_simple_0 = test_passw_hardening.config_user(duthost=duthost, username=test_passw_hardening.USERNAME_ONE_POLICY, mode='del')
res_adduser_simple_0 = passw_hardening_utils.config_user(duthost=duthost, username=passw_hardening_utils.USERNAME_ONE_POLICY, mode='del')


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

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


@pytest.fixture(scope="function")
def clean_passw_en_dis_policies(duthosts, enum_rand_one_per_hwsku_hostname):
yield
duthost = duthosts[enum_rand_one_per_hwsku_hostname]
test_passw_hardening.config_user(duthost=duthost, username=test_passw_hardening.USERNAME_SIMPLE_0, mode='del')
test_passw_hardening.config_user(duthost=duthost, username=test_passw_hardening.USERNAME_SIMPLE_1, mode='del')
test_passw_hardening.config_user(duthost=duthost, username=test_passw_hardening.USERNAME_STRONG, mode='del')
duthost.shell('sed -i /^'+test_passw_hardening.USERNAME_SIMPLE_0+':/d /etc/security/opasswd')
duthost.shell('sed -i /^'+test_passw_hardening.USERNAME_SIMPLE_1+':/d /etc/security/opasswd')
duthost.shell('sed -i /^'+test_passw_hardening.USERNAME_STRONG+':/d /etc/security/opasswd')
passw_hardening_utils.config_user(duthost=duthost, username=passw_hardening_utils.USERNAME_SIMPLE_0, mode='del')
passw_hardening_utils.config_user(duthost=duthost, username=passw_hardening_utils.USERNAME_SIMPLE_1, mode='del')
passw_hardening_utils.config_user(duthost=duthost, username=passw_hardening_utils.USERNAME_STRONG, mode='del')
duthost.shell('sed -i /^'+passw_hardening_utils.USERNAME_SIMPLE_0+':/d /etc/security/opasswd')
duthost.shell('sed -i /^'+passw_hardening_utils.USERNAME_SIMPLE_1+':/d /etc/security/opasswd')
duthost.shell('sed -i /^'+passw_hardening_utils.USERNAME_STRONG+':/d /etc/security/opasswd')

@pytest.fixture(scope="function")
def clean_passw_history(duthosts, enum_rand_one_per_hwsku_hostname):
yield
duthost = duthosts[enum_rand_one_per_hwsku_hostname]
test_passw_hardening.config_user(duthost=duthost, username=test_passw_hardening.USERNAME_HISTORY, mode='del')
duthost.shell('sed -i /^'+test_passw_hardening.USERNAME_HISTORY+':/d /etc/security/opasswd')
passw_hardening_utils.config_user(duthost=duthost, username=passw_hardening_utils.USERNAME_HISTORY, mode='del')
duthost.shell('sed -i /^'+passw_hardening_utils.USERNAME_HISTORY+':/d /etc/security/opasswd')
112 changes: 112 additions & 0 deletions tests/passw_hardening/passw_hardening_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import logging
import os
import difflib
from tests.common.helpers.assertions import pytest_assert

CURR_DIR = os.path.dirname(os.path.abspath(__file__))

# Sample/Expected files
PAM_PASSWORD_CONF_DEFAULT_EXPECTED = CURR_DIR + '/sample/passw_hardening_default/common-password'
PAM_PASSWORD_CONF_EXPECTED = CURR_DIR + '/sample/passw_hardening_enable/common-password'
PAM_PASSWORD_CONF_HISTORY_ONLY_EXPECTED = CURR_DIR + '/sample/passw_hardening_history/common-password'
PAM_PASSWORD_CONF_REJECT_USER_PASSW_MATCH_EXPECTED = CURR_DIR + '/sample/passw_hardening_reject_user_passw_match/common-password'
PAM_PASSWORD_CONF_DIGITS_ONLY_EXPECTED = CURR_DIR + '/sample/passw_hardening_digits/common-password'
PAM_PASSWORD_CONF_LOWER_LETTER_ONLY_EXPECTED = CURR_DIR + '/sample/passw_hardening_lower_letter/common-password'
PAM_PASSWORD_CONF_UPPER_LETTER_ONLY_EXPECTED = CURR_DIR + '/sample/passw_hardening_upper_letter/common-password'
PAM_PASSWORD_CONF_SPECIAL_LETTER_ONLY_EXPECTED = CURR_DIR + '/sample/passw_hardening_special_letter/common-password'
PAM_PASSWORD_CONF_LEN_MIN_ONLY_EXPECTED = CURR_DIR + '/sample/passw_hardening_min_len/common-password'
PAM_PASSWORD_CONF_OUTPUT = CURR_DIR + '/output/login.def'
PAM_PASSWORD_CONF = "/etc/pam.d/common-password"

# users
USERNAME_STRONG = 'user_strong_test'
USERNAME_SIMPLE_0 = 'user_simple_0_test'
USERNAME_SIMPLE_1 = 'user_simple_1_test'
USERNAME_ONE_POLICY = 'user_one_policy_test'
USERNAME_AGE = 'user_test'
USERNAME_HISTORY = 'user_history_test'
USERNAME_LEN_MIN = 'user_test'


class PasswHardening:
def __init__(self, state='disabled', expiration='100', expiration_warning='15', history='12',
len_min='8', reject_user_passw_match='true', lower_class='true',
upper_class='true', digit_class='true', special_class='true'):
self.policies = {
"state": state,
"expiration": expiration,
"expiration-warning": expiration_warning,
"history-cnt": history,
"len-min": len_min,
"reject-user-passw-match": reject_user_passw_match,
"lower-class": lower_class,
"upper-class": upper_class,
"digits-class": digit_class,
"special-class": special_class
}


def config_user(duthost, username, mode='add'):
""" Function add or rm users using useradd/userdel tool. """

username = username.strip()
command = "user{} {}".format(mode, username)
user_cmd = duthost.shell(command, module_ignore_errors=True)
return user_cmd


def configure_passw_policies(duthost, passw_hardening_ob):
for key, value in passw_hardening_ob.policies.items():
logging.debug("configuration to be set: key={}, value={}".format(key, value))
# cmd_config = 'sudo config passw-hardening policies ' + key + ' ' + value

cmd_config = 'sudo config passw-hardening policies {} {}'.format(key, value)

duthost.command(cmd_config)
return True


def compare_passw_policies_in_linux(duthost, pam_file_expected=PAM_PASSWORD_CONF_EXPECTED):
"""Compare DUT common-password with the expected one."""

command_password_stdout = ''
read_command_password = 'cat {}'.format(PAM_PASSWORD_CONF)

logging.debug('DUT command = {}'.format(read_command_password))
read_command_password_cmd = duthost.command(read_command_password)
command_password_stdout = read_command_password_cmd["stdout_lines"]
command_password_stdout = [line.encode('utf-8') for line in command_password_stdout]

common_password_expected = []
with open(pam_file_expected, 'r') as expected_common_password_file:
for line in expected_common_password_file:
line = line.strip()
line = line.strip('\n')
line = line.strip('\t')
common_password_expected.append(line)

common_password_diff = [li for li in difflib.ndiff(command_password_stdout, common_password_expected) if
li[0] != ' ']
pytest_assert(len(common_password_diff) == 0, common_password_diff)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

common_password_diff

The 2nd parameter should be a string, not an array.



def config_and_review_policies(duthost, passw_hardening_ob, pam_file_expected):
"""
1. Config passw hardening policies
2. Show passw hardening policies
3. Compare passw hardening polices from show cli to the expected (configured)
4. Verify polices in PAM files was set according the configured
"""
FIRST_LINE = 0
configure_passw_policies(duthost, passw_hardening_ob)

curr_show_policies = duthost.show_and_parse('show passw-hardening policies')[FIRST_LINE]
exp_show_policies = dict((k.replace('-', ' '), v) for k, v in passw_hardening_ob.policies.items())

# ~~ test passw policies in show CLI ~~
cli_passw_policies_cmp = cmp(exp_show_policies, curr_show_policies)
pytest_assert(cli_passw_policies_cmp == 0, "Fail: exp_show_policies='{}',not equal to curr_show_policies='{}'"
.format(exp_show_policies, curr_show_policies))

# ~~ test passw policies in PAM files ~~
compare_passw_policies_in_linux(duthost, pam_file_expected)
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

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

password required pam_pwhistory.so remember=0 use_authtok enforce_for_root
password required pam_pwhistory.so remember=12 use_authtok enforce_for_root

password [success=1 default=ignore] pam_unix.so obscure yescrypt
# here's the fallback if no module succeeds
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

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

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

password required pam_pwhistory.so remember=10 use_authtok enforce_for_root

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

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

password required pam_pwhistory.so remember=0 use_authtok enforce_for_root
password required pam_pwhistory.so remember=10 use_authtok enforce_for_root

password [success=1 default=ignore] pam_unix.so obscure yescrypt
# here's the fallback if no module succeeds
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

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

password required pam_pwhistory.so remember=1 use_authtok enforce_for_root
password required pam_pwhistory.so remember=10 use_authtok enforce_for_root

password [success=1 default=ignore] pam_unix.so obscure yescrypt
# here's the fallback if no module succeeds
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

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

password required pam_pwhistory.so remember=0 use_authtok enforce_for_root
password required pam_pwhistory.so remember=10 use_authtok enforce_for_root

password [success=1 default=ignore] pam_unix.so obscure yescrypt
# here's the fallback if no module succeeds
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

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

password required pam_pwhistory.so remember=0 use_authtok enforce_for_root
password required pam_pwhistory.so remember=10 use_authtok enforce_for_root

password [success=1 default=ignore] pam_unix.so obscure yescrypt
# here's the fallback if no module succeeds
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

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

password required pam_pwhistory.so remember=0 use_authtok enforce_for_root
password required pam_pwhistory.so remember=10 use_authtok enforce_for_root

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