Merge ~danilogondolfo/network-manager/+git/network-manager:new_autopkgtests into network-manager:ubuntu/master
- Git
- lp:~danilogondolfo/network-manager/+git/network-manager
- new_autopkgtests
- Merge into ubuntu/master
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Merge reported by: | Lukas Märdian | ||||||||
Merged at revision: | c358f28a92bcb4aec37d7d82c403331fcb29ddda | ||||||||
Proposed branch: | ~danilogondolfo/network-manager/+git/network-manager:new_autopkgtests | ||||||||
Merge into: | network-manager:ubuntu/master | ||||||||
Diff against target: |
514 lines (+317/-31) 3 files modified
debian/tests/control (+1/-1) debian/tests/nm.py (+173/-20) debian/tests/nm_netplan.py (+143/-10) |
||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Lukas Märdian | Approve | ||
Sebastien Bacher | Pending | ||
Review via email:
|
This proposal supersedes a proposal from 2023-05-11.
Commit message
Description of the change
New autopkgtests targeting the network manager + netplan integration.
I did some refactoring in nm.py so it's a bit easier to implement new tests that rely on wifi authentication.
The new tests are related to https:/

Lukas Märdian (slyon) wrote : Posted in a previous version of this proposal | # |

Lukas Märdian (slyon) wrote : | # |
Thank you very much, Danilo! LGTM.
praise: I really like how you enhanced the generic NetworkManager tests in nm.py along the way and resolved an old FIXME!
I left two little (non-blocking) comments inline.
I moved the MP state to "Work in progress" as it's still pending the corresponding update to libnetplan. IMO this can be merged as soon as the new Netplan version hits mantic-proposed! This is the point where the new tests should start passing.

Danilo Egea Gondolfo (danilogondolfo) wrote : | # |
Thanks, Lukas. I addressed your comments and implemented two new test cases related to Wireguard.

Lukas Märdian (slyon) wrote : | # |
I'd like us to make sure that this test-case (from bug #1952967) is also supported (doesn't need to be using "nmtui", though):
> To test one can launch nmtui with the following command:
> network-
> - Proceed to add new connection
> - Select "IP tunnel"
> - enter a profile name e.g. "IP tunnel connection 1"
> - enter a device e.g. "gre10"
> - select mode "GRE"
> - enter local IP e.g. "10.20.20.1"
> - enter remote IP e.g. "10.20.20.2"
> - Press "OK" to save the connection

Danilo Egea Gondolfo (danilogondolfo) wrote : | # |
Now that netplan.io 0.106.1 is in mantic-release, all these new tests will pass, see https:/
May I ask someone from Desktop to take a look at it?

Lukas Märdian (slyon) wrote : | # |
I talked to @seb128 and he agreed we can go ahead with uploading these new tests.
Rebased, merged and sponsored!
Preview Diff
1 | diff --git a/debian/tests/control b/debian/tests/control |
2 | index 1985292..e22c775 100644 |
3 | --- a/debian/tests/control |
4 | +++ b/debian/tests/control |
5 | @@ -3,7 +3,7 @@ Depends: python3, hostapd, dnsmasq-base, wpasupplicant, isc-dhcp-client, iw |
6 | Restrictions: needs-root allow-stderr isolation-machine skippable |
7 | |
8 | Tests: nm.py |
9 | -Depends: python3, dnsmasq-base, isc-dhcp-client, gir1.2-nm-1.0, network-manager, hostapd, iw, python3-dbusmock, python3-netaddr, wpasupplicant |
10 | +Depends: python3, dnsmasq-base, isc-dhcp-client, gir1.2-nm-1.0, network-manager, hostapd, iw, python3-dbusmock, python3-netaddr, wpasupplicant, easy-rsa |
11 | Restrictions: needs-root isolation-machine skippable |
12 | |
13 | Tests: killswitches-no-urfkill |
14 | diff --git a/debian/tests/nm.py b/debian/tests/nm.py |
15 | index f7a03b7..2e59104 100755 |
16 | --- a/debian/tests/nm.py |
17 | +++ b/debian/tests/nm.py |
18 | @@ -16,6 +16,7 @@ import socket |
19 | import netaddr |
20 | import unittest |
21 | import ctypes |
22 | +import shutil |
23 | |
24 | try: |
25 | from dbusmock import DBusTestCase |
26 | @@ -44,6 +45,102 @@ os.environ["GSETTINGS_BACKEND"] = "memory" |
27 | os.dup2(sys.stdout.fileno(), sys.stderr.fileno()) |
28 | |
29 | |
30 | +class WifiAuthentication(): |
31 | + def __init__(self): |
32 | + self.wpa_settings = None |
33 | + self.wpa_eap_settings = None |
34 | + self.setting_name = None |
35 | + self.needed_secrets = None |
36 | + |
37 | + self.tmpdir = '/tmp/hostapd' |
38 | + shutil.rmtree(self.tmpdir, ignore_errors=True) |
39 | + |
40 | + @property |
41 | + def auth_settings(self): |
42 | + return self.wpa_settings |
43 | + |
44 | + @property |
45 | + def auth_eap_settings(self): |
46 | + return self.wpa_eap_settings |
47 | + |
48 | + |
49 | +class WifiAuthenticationWPAPSK(WifiAuthentication): |
50 | + def __init__(self, psk): |
51 | + super().__init__() |
52 | + self.wpa_settings = NM.SettingWirelessSecurity.new() |
53 | + self.wpa_settings.set_property(NM.SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk") |
54 | + self.wpa_settings.set_property(NM.SETTING_WIRELESS_SECURITY_PSK, psk) |
55 | + |
56 | + self.setting_name = NM.SETTING_WIRELESS_SECURITY_SETTING_NAME |
57 | + self.needed_secrets = [NM.SETTING_WIRELESS_SECURITY_PSK] |
58 | + |
59 | + |
60 | +class WifiAuthenticationWPAEAP(WifiAuthentication): |
61 | + def __init__(self, modes, phase2, identity, password, client_cert=False): |
62 | + super().__init__() |
63 | + |
64 | + self.make_certs() |
65 | + |
66 | + self.wpa_settings = NM.SettingWirelessSecurity.new() |
67 | + self.wpa_settings.set_property(NM.SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap") |
68 | + |
69 | + self.wpa_eap_settings = NM.Setting8021x.new() |
70 | + self.wpa_eap_settings.set_property(NM.SETTING_802_1X_EAP, modes) |
71 | + |
72 | + self.wpa_eap_settings.set_property(NM.SETTING_802_1X_IDENTITY, identity) |
73 | + |
74 | + if password: |
75 | + self.wpa_eap_settings.set_property(NM.SETTING_802_1X_PASSWORD, password) |
76 | + |
77 | + if phase2 == 'tls': |
78 | + self.wpa_eap_settings.set_property(NM.SETTING_802_1X_PHASE2_AUTHEAP, phase2) |
79 | + else: |
80 | + self.wpa_eap_settings.set_property(NM.SETTING_802_1X_PHASE2_AUTH, phase2) |
81 | + |
82 | + if client_cert: |
83 | + # Certificate paths must start with file:// and be null terminated |
84 | + # See src/libnmc-setting/settings-docs.h |
85 | + self.wpa_eap_settings.set_property( |
86 | + NM.SETTING_802_1X_CA_CERT, |
87 | + GLib.Bytes(f'file://{self.tmpdir}/pki/ca.crt\0'.encode())) |
88 | + self.wpa_eap_settings.set_property( |
89 | + NM.SETTING_802_1X_CLIENT_CERT, |
90 | + GLib.Bytes(f'file://{self.tmpdir}/pki/issued/client.crt\0'.encode())) |
91 | + self.wpa_eap_settings.set_property( |
92 | + NM.SETTING_802_1X_PRIVATE_KEY, |
93 | + GLib.Bytes(f'file://{self.tmpdir}/pki/private/client.key\0'.encode())) |
94 | + self.wpa_eap_settings.set_property( |
95 | + NM.SETTING_802_1X_PRIVATE_KEY_PASSWORD, 'passw0rd') |
96 | + |
97 | + self.setting_name = NM.SETTING_802_1X_SETTING_NAME |
98 | + self.needed_secrets = [NM.SETTING_802_1X_PASSWORD, NM.SETTING_802_1X_PRIVATE_KEY_PASSWORD] |
99 | + |
100 | + def make_certs(self): |
101 | + os.mkdir(self.tmpdir) |
102 | + |
103 | + with open(f'{self.tmpdir}/hostapd.eap_user', 'w') as f: |
104 | + f.write('''* PEAP,TLS |
105 | + |
106 | +"account1" MSCHAPV2 "password1" [2] |
107 | +"account2" MSCHAPV2 "password2" [2] |
108 | +''') |
109 | + |
110 | + # Create a CA, server and client certificates protected by passw0rd and the DH parameters |
111 | + create_ca_script = """/usr/share/easy-rsa/easyrsa init-pki |
112 | +EASYRSA_BATCH=1 /usr/share/easy-rsa/easyrsa build-ca nopass |
113 | +EASYRSA_PASSOUT=pass:passw0rd EASYRSA_BATCH=1 /usr/share/easy-rsa/easyrsa build-server-full server |
114 | +EASYRSA_PASSOUT=pass:passw0rd EASYRSA_BATCH=1 /usr/share/easy-rsa/easyrsa build-client-full client |
115 | +/usr/share/easy-rsa/easyrsa gen-dh |
116 | +""" |
117 | + |
118 | + with open(f'{self.tmpdir}/make_certs.sh', 'w') as f: |
119 | + f.write(create_ca_script) |
120 | + |
121 | + cmd = ['bash', 'make_certs.sh'] |
122 | + subprocess.call(cmd, stdout=subprocess.DEVNULL, |
123 | + stderr=subprocess.DEVNULL, cwd=self.tmpdir) |
124 | + |
125 | + |
126 | class NetworkManagerTest(network_test_base.NetworkTestBase): |
127 | """Provide common functionality for NM tests""" |
128 | |
129 | @@ -260,11 +357,11 @@ class NetworkManagerTest(network_test_base.NetworkTestBase): |
130 | |
131 | return self.nmdev_w.get_access_points()[0] |
132 | |
133 | - def connect_to_ap(self, ap, secret, ipv6_mode, ip6_privacy): |
134 | + def connect_to_ap(self, ap, auth_settings, ipv6_mode, ip6_privacy): |
135 | """Connect to an NMAccessPoint. |
136 | |
137 | - secret should be None for open networks, and a string with the password |
138 | - for WEP/WPA. |
139 | + auth_settings should be None for open networks and an instance of |
140 | + WifiAuthentication for WEP/WPA-PSK/WPA-EAP. |
141 | |
142 | ip6_privacy is a NM.SettingIP6ConfigPrivacy flag. |
143 | |
144 | @@ -284,13 +381,10 @@ class NetworkManagerTest(network_test_base.NetworkTestBase): |
145 | # map of a map of gpointers to gpointers which is too much for PyGI |
146 | partial_conn = NM.SimpleConnection.new() |
147 | partial_conn.add_setting(NM.SettingIP4Config(method=ip4_method)) |
148 | - if secret: |
149 | - partial_conn.add_setting(NM.SettingWirelessSecurity.new()) |
150 | - # FIXME: needs update for other auth types |
151 | - partial_conn.update_secrets( |
152 | - NM.SETTING_WIRELESS_SECURITY_SETTING_NAME, |
153 | - GLib.Variant("a{sv}", {"psk": GLib.Variant("s", secret)}), |
154 | - ) |
155 | + if auth_settings: |
156 | + partial_conn.add_setting(auth_settings.auth_settings) |
157 | + if isinstance(auth_settings, WifiAuthenticationWPAEAP): |
158 | + partial_conn.add_setting(auth_settings.auth_eap_settings) |
159 | if ip6_privacy is not None: |
160 | partial_conn.add_setting( |
161 | NM.SettingIP6Config(ip6_privacy=ip6_privacy, method=ip6_method) |
162 | @@ -344,16 +438,15 @@ class NetworkManagerTest(network_test_base.NetworkTestBase): |
163 | |
164 | # verify need_secrets() |
165 | needed_secrets = conn.need_secrets() |
166 | - if secret is None: |
167 | + if auth_settings is None: |
168 | self.assertEqual(needed_secrets, (None, [])) |
169 | else: |
170 | self.assertEqual( |
171 | - needed_secrets[0], NM.SETTING_WIRELESS_SECURITY_SETTING_NAME |
172 | + needed_secrets[0], auth_settings.setting_name |
173 | ) |
174 | self.assertEqual(type(needed_secrets[1]), list) |
175 | self.assertGreaterEqual(len(needed_secrets[1]), 1) |
176 | - # FIXME: needs update for other auth types |
177 | - self.assertIn(needed_secrets[1][0], [NM.SETTING_WIRELESS_SECURITY_PSK]) |
178 | + self.assertIn(needed_secrets[1][0], auth_settings.needed_secrets) |
179 | |
180 | # we are usually ACTIVATING at this point; wait for completion |
181 | # TODO: 5s is not enough, argh slow DHCP client |
182 | @@ -540,7 +633,7 @@ wpa_passphrase=12345678 |
183 | % SSID, |
184 | None, |
185 | 54000, |
186 | - "12345678", |
187 | + WifiAuthenticationWPAPSK('12345678'), |
188 | ) |
189 | |
190 | def test_wpa2_ip4(self): |
191 | @@ -558,7 +651,7 @@ wpa_passphrase=12345678 |
192 | % SSID, |
193 | None, |
194 | 54000, |
195 | - "12345678", |
196 | + WifiAuthenticationWPAPSK('12345678'), |
197 | ) |
198 | |
199 | def test_wpa2_ip6(self): |
200 | @@ -576,10 +669,70 @@ wpa_passphrase=12345678 |
201 | % SSID, |
202 | "ra-only", |
203 | 54000, |
204 | - "12345678", |
205 | + WifiAuthenticationWPAPSK('12345678'), |
206 | ip6_privacy=NM.SettingIP6ConfigPrivacy.PREFER_TEMP_ADDR, |
207 | ) |
208 | |
209 | + def test_wpa_enterprise_authentication_ip4(self): |
210 | + """WPA2 + 802.1x, 802.11g, IPv4""" |
211 | + |
212 | + self.do_test( |
213 | + """hw_mode=g |
214 | +channel=1 |
215 | +ssid=%s |
216 | +auth_algs=1 |
217 | +eap_server=1 |
218 | +ieee8021x=1 |
219 | +eapol_version=2 |
220 | +wpa=2 |
221 | +wpa_key_mgmt=WPA-EAP |
222 | +wpa_pairwise=TKIP |
223 | +rsn_pairwise=CCMP |
224 | +eap_user_file=/tmp/hostapd/hostapd.eap_user |
225 | +ca_cert=/tmp/hostapd/pki/ca.crt |
226 | +server_cert=/tmp/hostapd/pki/issued/server.crt |
227 | +private_key=/tmp/hostapd/pki/private/server.key |
228 | +private_key_passwd=passw0rd |
229 | +dh_file=/tmp/hostapd/pki/dh.pem |
230 | +ctrl_interface=/var/run/hostapd |
231 | +ctrl_interface_group=0 |
232 | +""" |
233 | + % SSID, |
234 | + None, |
235 | + 54000, |
236 | + WifiAuthenticationWPAEAP(['peap'], 'mschapv2', 'account1', 'password1') |
237 | + ) |
238 | + |
239 | + def test_wpa_enterprise_authentication_with_client_certificate_ip4(self): |
240 | + """WPA2 + 802.1x with client certificate, 802.11g, IPv4""" |
241 | + |
242 | + self.do_test( |
243 | + """hw_mode=g |
244 | +channel=1 |
245 | +ssid=%s |
246 | +auth_algs=1 |
247 | +eap_server=1 |
248 | +ieee8021x=1 |
249 | +eapol_version=2 |
250 | +wpa=2 |
251 | +wpa_key_mgmt=WPA-EAP |
252 | +wpa_pairwise=TKIP CCMP |
253 | +rsn_pairwise=TKIP CCMP |
254 | +eap_user_file=/tmp/hostapd/hostapd.eap_user |
255 | +ca_cert=/tmp/hostapd/pki/ca.crt |
256 | +server_cert=/tmp/hostapd/pki/issued/server.crt |
257 | +private_key=/tmp/hostapd/pki/private/server.key |
258 | +private_key_passwd=passw0rd |
259 | +dh_file=/tmp/hostapd/pki/dh.pem |
260 | +ctrl_interface=/var/run/hostapd |
261 | +ctrl_interface_group=0 |
262 | +""" |
263 | + % SSID, |
264 | + None, |
265 | + 54000, |
266 | + WifiAuthenticationWPAEAP(['tls'], 'tls', 'client', None, client_cert=True) |
267 | + ) |
268 | + |
269 | @network_test_base.run_in_subprocess |
270 | def test_rfkill(self): |
271 | """shut down connection on killswitch, restore it on unblock""" |
272 | @@ -626,7 +779,7 @@ wpa_passphrase=12345678 |
273 | hostapd_conf, |
274 | ipv6_mode, |
275 | expected_max_bitrate, |
276 | - secret=None, |
277 | + auth_settings=None, |
278 | ip6_privacy=None, |
279 | ): |
280 | """Actual test code, parameterized for the particular test case""" |
281 | @@ -645,7 +798,7 @@ wpa_passphrase=12345678 |
282 | self.assertEqual(self.filtered_active_connections(), []) |
283 | |
284 | # connect to that AP |
285 | - (conn, active_conn) = self.connect_to_ap(ap, secret, ipv6_mode, ip6_privacy) |
286 | + (conn, active_conn) = self.connect_to_ap(ap, auth_settings, ipv6_mode, ip6_privacy) |
287 | |
288 | # check NMActiveConnection object |
289 | self.assertIn( |
290 | @@ -660,7 +813,7 @@ wpa_passphrase=12345678 |
291 | wireless_setting = conn.get_setting_wireless() |
292 | self.assertEqual(wireless_setting.get_ssid().get_data(), SSID.encode()) |
293 | self.assertEqual(wireless_setting.get_hidden(), False) |
294 | - if secret: |
295 | + if auth_settings: |
296 | self.assertEqual( |
297 | conn.get_setting_wireless_security().get_name(), |
298 | NM.SETTING_WIRELESS_SECURITY_SETTING_NAME, |
299 | diff --git a/debian/tests/nm_netplan.py b/debian/tests/nm_netplan.py |
300 | index cc86bcf..b396627 100644 |
301 | --- a/debian/tests/nm_netplan.py |
302 | +++ b/debian/tests/nm_netplan.py |
303 | @@ -103,13 +103,29 @@ class TestNetplan(unittest.TestCase): |
304 | subprocess.call(cmd, stdout=subprocess.DEVNULL) |
305 | |
306 | def _add_connection(self, connection): |
307 | - main_loop = GLib.MainLoop() |
308 | + self.main_loop = GLib.MainLoop() |
309 | + self.cancel = Gio.Cancellable() |
310 | + self.timeout_tag = 0 |
311 | + |
312 | def add_cb(client, result, data): |
313 | self.nmclient.add_connection_finish(result) |
314 | - main_loop.quit() |
315 | + self.main_loop.quit() |
316 | + |
317 | + def timeout_cb(): |
318 | + self.timeout_tag = -1 |
319 | + self.cancel.cancel() |
320 | + self.main_loop.quit() |
321 | + return GLib.SOURCE_REMOVE |
322 | + |
323 | + self.timeout_tag = GLib.timeout_add_seconds(120, timeout_cb) |
324 | + |
325 | + self.nmclient.add_connection_async(connection, True, self.cancel, add_cb, None) |
326 | + self.main_loop.run() |
327 | + |
328 | + if self.timeout_tag < 0: |
329 | + self.timeout_tag = 0 |
330 | + self.fail('nm_netplan.py: main loop timed out during connection creation') |
331 | |
332 | - self.nmclient.add_connection_async(connection, True, None, add_cb, None) |
333 | - main_loop.run() |
334 | |
335 | def _delete_connection(self, connection): |
336 | uuid = connection.get_uuid() |
337 | @@ -124,7 +140,7 @@ class TestNetplan(unittest.TestCase): |
338 | |
339 | def _nmcli(self, parameters): |
340 | cmd = ['nmcli'] + parameters |
341 | - subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) |
342 | + return subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) |
343 | |
344 | def _bridge_show(self, bridge): |
345 | cmd = ['bridge', '-j', 'link', 'show', bridge] |
346 | @@ -351,7 +367,6 @@ class TestNetplan(unittest.TestCase): |
347 | # There should be zero netplan NM yaml after deleting the bridge |
348 | self.assertEqual(self._get_number_of_yaml_files(), 0) |
349 | |
350 | - @unittest.skip('XXX: the netplan yaml file will be deleted when we try to change the connetion') |
351 | def test_create_an_interface_and_change_it(self): |
352 | """Add a tap interface and change it after create adding IP addresses to it.""" |
353 | |
354 | @@ -536,7 +551,7 @@ class TestNetplan(unittest.TestCase): |
355 | with open('/etc/netplan/10-test.yaml', 'w') as f: |
356 | f.write(netplan_yaml) |
357 | |
358 | - self._netplan_generate(); |
359 | + self._netplan_generate() |
360 | self._nmcli_con_reload() |
361 | self.nmclient = NM.Client.new() |
362 | |
363 | @@ -565,7 +580,7 @@ class TestNetplan(unittest.TestCase): |
364 | with open('/etc/netplan/10-test.yaml', 'w') as f: |
365 | f.write(netplan_yaml) |
366 | |
367 | - self._netplan_generate(); |
368 | + self._netplan_generate() |
369 | self._nmcli_con_reload() |
370 | self.nmclient = NM.Client.new() |
371 | |
372 | @@ -607,7 +622,7 @@ class TestNetplan(unittest.TestCase): |
373 | with open('/etc/netplan/10-test.yaml', 'w') as f: |
374 | f.write(netplan_yaml) |
375 | |
376 | - self._netplan_generate(); |
377 | + self._netplan_generate() |
378 | self._nmcli_con_reload() |
379 | self._nmcli(['con', 'mod', 'netplan-eth123', 'ipv4.method', 'auto']) |
380 | |
381 | @@ -672,7 +687,7 @@ EASYRSA_BATCH=1 /usr/share/easy-rsa/easyrsa build-client-full client nopass |
382 | f.write(client_config) |
383 | |
384 | cmd = ['bash', 'openvpn_spinup.sh'] |
385 | - subprocess.call(cmd, stdout=subprocess.DEVNULL) |
386 | + subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) |
387 | |
388 | openvpn_server_cmd = ['openvpn', '--config', 'server.conf'] |
389 | p_server = subprocess.Popen(openvpn_server_cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) |
390 | @@ -711,6 +726,124 @@ EASYRSA_BATCH=1 /usr/share/easy-rsa/easyrsa build-client-full client nopass |
391 | self.assertEqual(self._get_number_of_yaml_files(), 2, |
392 | msg='More than expected YAML files were found after stopping the OpenVPN server') |
393 | |
394 | + def test_migrate_existing_tunnel_with_nmcli(self): |
395 | + """ |
396 | + Change an existing tunnel defined in Netplan with nmcli so it will be "imported" |
397 | + to NM. See LP: #2016473. |
398 | + """ |
399 | + |
400 | + netplan_yaml = '''network: |
401 | + renderer: NetworkManager |
402 | + tunnels: |
403 | + wg0: |
404 | + mode: wireguard |
405 | + port: 51821 |
406 | + key: YCNQCAes1OTbD2ynY+aBlaA5x4ZFJhsc4co+XHpZ4FU= |
407 | + addresses: |
408 | + - 172.17.0.1/24 |
409 | + peers: |
410 | + - allowed-ips: [172.17.0.0/24] |
411 | + endpoint: 4.4.4.4:51821 |
412 | + keys: |
413 | + public: cwkb7k0xDgLSnunZpFIjLJw4u+mJDDr+aBR5DqzpmgI=''' |
414 | + |
415 | + with open('/etc/netplan/10-test.yaml', 'w') as f: |
416 | + f.write(netplan_yaml) |
417 | + |
418 | + self._netplan_generate() |
419 | + self._nmcli_con_reload() |
420 | + self._nmcli(['con', 'mod', 'netplan-wg0', 'con-name', 'netplan-wireguard-wg0']) |
421 | + |
422 | + # After being processed by NM (and libnetplan) the new file shouldn't |
423 | + # redefine the interface type. If it does, "netplan get" will fail |
424 | + # when it tries to load the YAML hierarchy. |
425 | + out = subprocess.run(['netplan', 'get'], |
426 | + capture_output=True, text=True) |
427 | + |
428 | + self.assertEqual(out.returncode, 0, f'"netplan get" failed due to issues in the resulting YAML files.') |
429 | + |
430 | + def test_create_wireguard_tunnel_in_multiple_steps_nmcli(self): |
431 | + """ |
432 | + Network Manager should be able to create partial Wireguard connections |
433 | + |
434 | + See: LP: #2016473 |
435 | + """ |
436 | + |
437 | + ret = self._nmcli(['con', 'add', 'type', 'wireguard', 'con-name', 'client-wg0', 'ifname', 'wg0', 'autoconnect', 'no']) |
438 | + self.assertEqual(ret, 0, 'nmcli con add failed.') |
439 | + ret = self._nmcli(['con', 'modify', 'client-wg0', 'ipv4.method', 'manual', 'ipv4.addresses', '10.1.2.3/24']) |
440 | + self.assertEqual(ret, 0, 'nmcli ipv4 modify failed.') |
441 | + ret = self._nmcli(['con', 'modify', 'client-wg0', 'wireguard.private-key', 'aPUcp5vHz8yMLrzk8SsDyYnV33IhE/k20e52iKJFV0A=']) |
442 | + self.assertEqual(ret, 0, 'nmcli add private key failed.') |
443 | + |
444 | + # Use examples/python/gi/nm-wg-set to add one peer, |
445 | + # nmcli doesn't support adding peers yet. |
446 | + wg_peer_cmd = ['python3', 'examples/python/gi/nm-wg-set', 'client-wg0', |
447 | + 'peer', 'cwkb7k0xDgLSnunZpFIjLJw4u+mJDDr+aBR5DqzpmgI=', |
448 | + 'endpoint', '1.2.3.4:12345', 'allowed-ips', '192.168.0.0/24'] |
449 | + out = subprocess.run(wg_peer_cmd, capture_output=True, text=True) |
450 | + self.assertEqual(out.returncode, 0, 'nm-wg-set failed to add the peer') |
451 | + |
452 | + def test_import_wireguard_profile_from_file(self): |
453 | + """ |
454 | + Network Manager should be able to import a Wireguard connection |
455 | + from a configuration file. |
456 | + |
457 | + See: LP: #2016473 |
458 | + """ |
459 | + wg_profile = '''[Interface] |
460 | +PrivateKey = wFemfDk+MQbdHQnpADll/4fN/TaI7OPEMgbALP4BtF0= |
461 | +Address = 10.8.1.2/24 |
462 | +DNS = 10.8.1.1 |
463 | +[Peer] |
464 | +PublicKey = e3Yvr6qmGHGfOcF1wgsIWILT57FzpIgKjjP+AfcMwGI= |
465 | +AllowedIPs = 192.168.0.0/24 |
466 | +Endpoint = 1.2.3.4:12345 |
467 | +PersistentKeepalive = 15''' |
468 | + |
469 | + wg_profile_path = '/etc/netplan/wg-client.conf' |
470 | + |
471 | + with open(wg_profile_path, 'w') as f: |
472 | + f.write(wg_profile) |
473 | + |
474 | + ret = self._nmcli(['con', 'import', 'type', 'wireguard', 'file', wg_profile_path]) |
475 | + self.assertEqual(ret, 0, f'nmcli failed to import the Wireguard profile from {wg_profile_path}') |
476 | + |
477 | + def test_create_wifi_connection_with_8021x(self): |
478 | + """ |
479 | + See LP: #2016625. |
480 | + """ |
481 | + |
482 | + openssl_cmd = [ |
483 | + 'openssl', 'req', '-new', '-newkey', 'rsa:2048', |
484 | + '-nodes', '-x509', '-subj', |
485 | + '/C=US/ST=A/L=B/O=C/CN=www.a.com', '-keyout', |
486 | + '/etc/netplan/a.key', '-out', '/etc/netplan/a.crt'] |
487 | + |
488 | + subprocess.check_output(openssl_cmd, universal_newlines=True, |
489 | + stderr=subprocess.DEVNULL) |
490 | + |
491 | + ret = self._nmcli(['con', 'add', 'con-name', 'eduroam', |
492 | + 'type', 'wifi', 'ssid', 'eduroam', |
493 | + 'wifi-sec.key-mgmt', 'wpa-eap', '802-1x.eap', |
494 | + 'peap', '802-1x.identity', 'user@example.org', |
495 | + '802-1x.password', 'testing123', |
496 | + '802-1x.phase2-auth', 'mschapv2', |
497 | + '802-1x.ca-cert', '/etc/netplan/a.crt']) |
498 | + |
499 | + self.assertEqual(ret, 0, 'nmcli failed to add connection') |
500 | + |
501 | + def test_create_gre_connection(self): |
502 | + """ |
503 | + Test case for LP: #1952967. |
504 | + """ |
505 | + |
506 | + ret = self._nmcli(['con', 'add', 'con-name', '"IP tunnel connection 1"', |
507 | + 'type', 'ip-tunnel', 'ip-tunnel.mode', 'gre', |
508 | + 'ifname', 'gre10', 'remote', |
509 | + '10.20.20.2', 'local', '10.20.20.1']) |
510 | + |
511 | + self.assertEqual(ret, 0, 'nmcli failed to add connection') |
512 | |
513 | if __name__ == '__main__': |
514 | runner = unittest.TextTestRunner(stream=sys.stdout, verbosity=2) |
Thank you! LGTM. I left one little (non-blocking) inline comment, which you could consider fixing.
This MP depends on some fixes in libnetplan, so we should hold it back until those fixed landed in Mantic. Once that's done (and the new autopkgtests PASS), we should rebase this MP against https:/ /code.launchpad .net/~network- manager/ network- manager/ +git/ubuntu/ +ref/ubuntu/ master and get it landed via the offical NetworkManager package.