summaryrefslogtreecommitdiff
path: root/tests/integration_tests/modules/test_set_password.py
diff options
context:
space:
mode:
authorzdc <zdc@users.noreply.github.com>2020-12-25 18:57:19 +0200
committerGitHub <noreply@github.com>2020-12-25 18:57:19 +0200
commit27c317f83d8e393254b6766b34fdf8d29148ea8f (patch)
treeea824de28fa639ba6ba8b212efaf53b5df2e90d9 /tests/integration_tests/modules/test_set_password.py
parent66dc53b1b3f8786f3bbb25e914c1dc8161af0494 (diff)
parentc6bcb8df28daa234686a563549681082eb3283a1 (diff)
downloadvyos-cloud-init-27c317f83d8e393254b6766b34fdf8d29148ea8f.tar.gz
vyos-cloud-init-27c317f83d8e393254b6766b34fdf8d29148ea8f.zip
Merge pull request #28 from zdc/T2117-equuleus-20.4
T2117: Cloud-init updated to 20.4
Diffstat (limited to 'tests/integration_tests/modules/test_set_password.py')
-rw-r--r--tests/integration_tests/modules/test_set_password.py151
1 files changed, 151 insertions, 0 deletions
diff --git a/tests/integration_tests/modules/test_set_password.py b/tests/integration_tests/modules/test_set_password.py
new file mode 100644
index 00000000..b13f76fb
--- /dev/null
+++ b/tests/integration_tests/modules/test_set_password.py
@@ -0,0 +1,151 @@
+"""Integration test for the set_password module.
+
+This test specifies a combination of user/password pairs, and ensures that the
+system has the correct passwords set.
+
+There are two tests run here: one tests chpasswd's list being a YAML list, the
+other tests chpasswd's list being a string. Both expect the same results, so
+they use a mixin to share their test definitions, because we can (of course)
+only specify one user-data per instance.
+"""
+import crypt
+
+import pytest
+import yaml
+
+
+COMMON_USER_DATA = """\
+#cloud-config
+ssh_pwauth: yes
+users:
+ - default
+ - name: tom
+ # md5 gotomgo
+ passwd: "$1$S7$tT1BEDIYrczeryDQJfdPe0"
+ lock_passwd: false
+ - name: dick
+ # md5 gocubsgo
+ passwd: "$1$ssisyfpf$YqvuJLfrrW6Cg/l53Pi1n1"
+ lock_passwd: false
+ - name: harry
+ # sha512 goharrygo
+ passwd: "$6$LF$9Z2p6rWK6TNC1DC6393ec0As.18KRAvKDbfsGJEdWN3sRQRwpdfoh37EQ3y\
+Uh69tP4GSrGW5XKHxMLiKowJgm/"
+ lock_passwd: false
+ - name: jane
+ # sha256 gojanego
+ passwd: "$5$iW$XsxmWCdpwIW8Yhv.Jn/R3uk6A4UaicfW5Xp7C9p9pg."
+ lock_passwd: false
+ - name: "mikey"
+ lock_passwd: false
+"""
+
+LIST_USER_DATA = COMMON_USER_DATA + """
+chpasswd:
+ list:
+ - tom:mypassword123!
+ - dick:RANDOM
+ - harry:RANDOM
+ - mikey:$5$xZ$B2YGGEx2AOf4PeW48KC6.QyT1W2B4rZ9Qbltudtha89
+"""
+
+STRING_USER_DATA = COMMON_USER_DATA + """
+chpasswd:
+ list: |
+ tom:mypassword123!
+ dick:RANDOM
+ harry:RANDOM
+ mikey:$5$xZ$B2YGGEx2AOf4PeW48KC6.QyT1W2B4rZ9Qbltudtha89
+"""
+
+USERS_DICTS = yaml.safe_load(COMMON_USER_DATA)["users"]
+USERS_PASSWD_VALUES = {
+ user_dict["name"]: user_dict["passwd"]
+ for user_dict in USERS_DICTS
+ if "name" in user_dict and "passwd" in user_dict
+}
+
+
+class Mixin:
+ """Shared test definitions."""
+
+ def _fetch_and_parse_etc_shadow(self, class_client):
+ """Fetch /etc/shadow and parse it into Python data structures
+
+ Returns: ({user: password}, [duplicate, users])
+ """
+ shadow_content = class_client.read_from_file("/etc/shadow")
+ users = {}
+ dupes = []
+ for line in shadow_content.splitlines():
+ user, encpw = line.split(":")[0:2]
+ if user in users:
+ dupes.append(user)
+ users[user] = encpw
+ return users, dupes
+
+ def test_no_duplicate_users_in_shadow(self, class_client):
+ """Confirm that set_passwords has not added duplicate shadow entries"""
+ _, dupes = self._fetch_and_parse_etc_shadow(class_client)
+
+ assert [] == dupes
+
+ def test_password_in_users_dict_set_correctly(self, class_client):
+ """Test that the password specified in the users dict is set."""
+ shadow_users, _ = self._fetch_and_parse_etc_shadow(class_client)
+ assert USERS_PASSWD_VALUES["jane"] == shadow_users["jane"]
+
+ def test_password_in_chpasswd_list_set_correctly(self, class_client):
+ """Test that a chpasswd password overrides one in the users dict."""
+ shadow_users, _ = self._fetch_and_parse_etc_shadow(class_client)
+ mikey_hash = "$5$xZ$B2YGGEx2AOf4PeW48KC6.QyT1W2B4rZ9Qbltudtha89"
+ assert mikey_hash == shadow_users["mikey"]
+
+ def test_random_passwords_set_correctly(self, class_client):
+ """Test that RANDOM chpasswd entries replace users dict passwords."""
+ shadow_users, _ = self._fetch_and_parse_etc_shadow(class_client)
+
+ # These should have been changed
+ assert shadow_users["harry"] != USERS_PASSWD_VALUES["harry"]
+ assert shadow_users["dick"] != USERS_PASSWD_VALUES["dick"]
+
+ # To random passwords
+ assert shadow_users["harry"].startswith("$")
+ assert shadow_users["dick"].startswith("$")
+
+ # Which are not the same
+ assert shadow_users["harry"] != shadow_users["dick"]
+
+ def test_explicit_password_set_correctly(self, class_client):
+ """Test that an explicitly-specified password is set correctly."""
+ shadow_users, _ = self._fetch_and_parse_etc_shadow(class_client)
+
+ fmt_and_salt = shadow_users["tom"].rsplit("$", 1)[0]
+ expected_value = crypt.crypt("mypassword123!", fmt_and_salt)
+
+ assert expected_value == shadow_users["tom"]
+
+ def test_shadow_expected_users(self, class_client):
+ """Test that the right set of users is in /etc/shadow."""
+ shadow = class_client.read_from_file("/etc/shadow")
+ for user_dict in USERS_DICTS:
+ if "name" in user_dict:
+ assert "{}:".format(user_dict["name"]) in shadow
+
+ def test_sshd_config(self, class_client):
+ """Test that SSH password auth is enabled."""
+ sshd_config = class_client.read_from_file("/etc/ssh/sshd_config")
+ # We look for the exact line match, to avoid a commented line matching
+ assert "PasswordAuthentication yes" in sshd_config.splitlines()
+
+
+@pytest.mark.ci
+@pytest.mark.user_data(LIST_USER_DATA)
+class TestPasswordList(Mixin):
+ """Launch an instance with LIST_USER_DATA, ensure Mixin tests pass."""
+
+
+@pytest.mark.ci
+@pytest.mark.user_data(STRING_USER_DATA)
+class TestPasswordListString(Mixin):
+ """Launch an instance with STRING_USER_DATA, ensure Mixin tests pass."""