1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
"""Integration tests for the user_groups module.
TODO:
* This module assumes that the "ubuntu" user will be created when "default" is
specified; this will need modification to run on other OSes.
"""
import re
import pytest
from tests.integration_tests.clouds import ImageSpecification
from tests.integration_tests.instances import IntegrationInstance
from tests.integration_tests.util import verify_clean_log
USER_DATA = """\
#cloud-config
# Add groups to the system
groups:
- secret: [root]
- cloud-users
# Add users to the system. Users are added after groups are added.
users:
- default
- name: foobar
gecos: Foo B. Bar
primary_group: foobar
groups: users
expiredate: '2038-01-19'
lock_passwd: false
passwd: $6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYe\
AHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/
- name: barfoo
gecos: Bar B. Foo
sudo: ALL=(ALL) NOPASSWD:ALL
groups: [cloud-users, secret]
lock_passwd: true
- name: cloudy
gecos: Magic Cloud App Daemon User
inactive: '0'
system: true
- name: eric
sudo: null
uid: 1742
- name: archivist
uid: 1743
"""
@pytest.mark.ci
@pytest.mark.user_data(USER_DATA)
class TestUsersGroups:
"""Test users and groups.
This test specifies a number of users and groups via user-data, and
confirms that they have been configured correctly in the system under test.
"""
@pytest.mark.ubuntu
@pytest.mark.parametrize(
"getent_args,regex",
[
# Test the ubuntu group
(["group", "ubuntu"], r"ubuntu:x:[0-9]{4}:"),
# Test the cloud-users group
(["group", "cloud-users"], r"cloud-users:x:[0-9]{4}:barfoo"),
# Test the ubuntu user
(
["passwd", "ubuntu"],
r"ubuntu:x:[0-9]{4}:[0-9]{4}:Ubuntu:/home/ubuntu:/bin/bash",
),
# Test the foobar user
(
["passwd", "foobar"],
r"foobar:x:[0-9]{4}:[0-9]{4}:Foo B. Bar:/home/foobar:",
),
# Test the barfoo user
(
["passwd", "barfoo"],
r"barfoo:x:[0-9]{4}:[0-9]{4}:Bar B. Foo:/home/barfoo:",
),
# Test the cloudy user
(["passwd", "cloudy"], r"cloudy:x:[0-9]{3,4}:"),
# Test str uid
(["passwd", "eric"], r"eric:x:1742:"),
# Test int uid
(["passwd", "archivist"], r"archivist:x:1743:"),
],
)
def test_users_groups(self, regex, getent_args, class_client):
"""Use getent to interrogate the various expected outcomes"""
result = class_client.execute(["getent"] + getent_args)
assert re.search(regex, result.stdout) is not None, (
"'getent {}' resulted in '{}', "
"but expected to match regex {}".format(
" ".join(getent_args), result.stdout, regex
)
)
def test_user_root_in_secret(self, class_client):
"""Test root user is in 'secret' group."""
log = class_client.read_from_file("/var/log/cloud-init.log")
verify_clean_log(log)
output = class_client.execute("groups root").stdout
_, groups_str = output.split(":", maxsplit=1)
groups = groups_str.split()
assert "secret" in groups
@pytest.mark.user_data(USER_DATA)
def test_sudoers_includedir(client: IntegrationInstance):
"""Ensure we don't add additional #includedir to sudoers.
Newer versions of /etc/sudoers will use @includedir rather than
#includedir. Ensure we handle that properly and don't include an
additional #includedir when one isn't warranted.
https://github.com/canonical/cloud-init/pull/783
"""
if ImageSpecification.from_os_image().release in [
"bionic",
"focal",
]:
raise pytest.skip(
"Test requires version of sudo installed on groovy and later"
)
client.execute("sed -i 's/#include/@include/g' /etc/sudoers")
sudoers = client.read_from_file("/etc/sudoers")
if "@includedir /etc/sudoers.d" not in sudoers:
client.execute("echo '@includedir /etc/sudoers.d' >> /etc/sudoers")
client.instance.clean()
client.restart()
sudoers = client.read_from_file("/etc/sudoers")
assert "#includedir" not in sudoers
assert sudoers.count("includedir /etc/sudoers.d") == 1
|