leapp-repository/SOURCES/0068-Add-NetworkManagerConnectionScanner-actor.patch
2023-03-29 09:01:41 +00:00

289 lines
10 KiB
Diff

From c4dd229113c70a7c402e4488ab0a30e4605e8d60 Mon Sep 17 00:00:00 2001
From: Lubomir Rintel <lkundrak@v3.sk>
Date: Mon, 26 Sep 2022 10:58:31 +0200
Subject: [PATCH 68/75] Add NetworkManagerConnectionScanner actor
This scans the NetworkManager connection profiles in form of keyfiles at
/etc/NetworkManager/system-connections and produces a
NetworkManagerConnection whenever for each one.
This doesn't need the NetworkManager daemon to be actually running,
but needs GObject introspection to be available. The reason for that is
that libnm is used (via Gir) to strip the secrets.
Add requirement for
NetworkManager-libnm
python3-gobject-base
packages. Both are available for all architectures on RHEL 8 and 9.
Currently require them only on RHEL 8 as they are not used in the
code anywhere for RHEL 9 and they seem to be used only for upgrade
RHEL 8 to RHEL 9.
Bump leapp-repository-dependencies to 9
---
packaging/leapp-repository.spec | 7 +-
.../other_specs/leapp-el7toel8-deps.spec | 3 +-
.../networkmanagerconnectionscanner/actor.py | 18 +++
.../networkmanagerconnectionscanner.py | 65 +++++++++++
...it_test_networkmanagerconnectionscanner.py | 105 ++++++++++++++++++
5 files changed, 196 insertions(+), 2 deletions(-)
create mode 100644 repos/system_upgrade/el8toel9/actors/networkmanagerconnectionscanner/actor.py
create mode 100644 repos/system_upgrade/el8toel9/actors/networkmanagerconnectionscanner/libraries/networkmanagerconnectionscanner.py
create mode 100644 repos/system_upgrade/el8toel9/actors/networkmanagerconnectionscanner/tests/unit_test_networkmanagerconnectionscanner.py
diff --git a/packaging/leapp-repository.spec b/packaging/leapp-repository.spec
index 044e7275..8d6376ea 100644
--- a/packaging/leapp-repository.spec
+++ b/packaging/leapp-repository.spec
@@ -2,7 +2,7 @@
%global repositorydir %{leapp_datadir}/repositories
%global custom_repositorydir %{leapp_datadir}/custom-repositories
-%define leapp_repo_deps 8
+%define leapp_repo_deps 9
%if 0%{?rhel} == 7
%define leapp_python_sitelib %{python2_sitelib}
@@ -176,6 +176,11 @@ Requires: kmod
# and missing dracut could be killing situation for us :)
Requires: dracut
+# Required to scan NetworkManagerConnection (e.g. to recognize secrets)
+# NM is requested to be used on RHEL 8+ systems
+Requires: NetworkManager-libnm
+Requires: python3-gobject-base
+
%endif
##################################################
# end requirement
diff --git a/packaging/other_specs/leapp-el7toel8-deps.spec b/packaging/other_specs/leapp-el7toel8-deps.spec
index 822b6f63..4a181ee1 100644
--- a/packaging/other_specs/leapp-el7toel8-deps.spec
+++ b/packaging/other_specs/leapp-el7toel8-deps.spec
@@ -9,7 +9,7 @@
%endif
-%define leapp_repo_deps 8
+%define leapp_repo_deps 9
%define leapp_framework_deps 5
# NOTE: the Version contains the %{rhel} macro just for the convenience to
@@ -68,6 +68,7 @@ Requires: cpio
# just to be sure that /etc/modprobe.d is present
Requires: kmod
+
%description -n %{lrdname}
%{summary}
diff --git a/repos/system_upgrade/el8toel9/actors/networkmanagerconnectionscanner/actor.py b/repos/system_upgrade/el8toel9/actors/networkmanagerconnectionscanner/actor.py
new file mode 100644
index 00000000..6ee66b52
--- /dev/null
+++ b/repos/system_upgrade/el8toel9/actors/networkmanagerconnectionscanner/actor.py
@@ -0,0 +1,18 @@
+from leapp.actors import Actor
+from leapp.libraries.actor import networkmanagerconnectionscanner
+from leapp.models import NetworkManagerConnection
+from leapp.tags import FactsPhaseTag, IPUWorkflowTag
+
+
+class NetworkManagerConnectionScanner(Actor):
+ """
+ Scan NetworkManager connection keyfiles
+ """
+
+ name = "network_manager_connection_scanner"
+ consumes = ()
+ produces = (NetworkManagerConnection,)
+ tags = (IPUWorkflowTag, FactsPhaseTag,)
+
+ def process(self):
+ networkmanagerconnectionscanner.process()
diff --git a/repos/system_upgrade/el8toel9/actors/networkmanagerconnectionscanner/libraries/networkmanagerconnectionscanner.py b/repos/system_upgrade/el8toel9/actors/networkmanagerconnectionscanner/libraries/networkmanagerconnectionscanner.py
new file mode 100644
index 00000000..b148de6b
--- /dev/null
+++ b/repos/system_upgrade/el8toel9/actors/networkmanagerconnectionscanner/libraries/networkmanagerconnectionscanner.py
@@ -0,0 +1,65 @@
+import errno
+import os
+
+from leapp.exceptions import StopActorExecutionError
+from leapp.libraries.common import utils
+from leapp.libraries.stdlib import api
+from leapp.models import NetworkManagerConnection, NetworkManagerConnectionProperty, NetworkManagerConnectionSetting
+
+libnm_available = False
+err_details = None
+try:
+ import gi
+ try:
+ gi.require_version("NM", "1.0")
+ from gi.repository import GLib, NM
+ libnm_available = True
+ except ValueError:
+ err_details = 'NetworkManager-libnm package is not available'
+except ImportError:
+ err_details = 'python3-gobject-base package is not available'
+
+NM_CONN_DIR = "/etc/NetworkManager/system-connections"
+
+
+def process_file(filename):
+ # We're running this through libnm in order to clear the secrets.
+ # We don't know what keys are secret, but libnm does.
+ keyfile = GLib.KeyFile()
+ keyfile.load_from_file(filename, GLib.KeyFileFlags.NONE)
+ con = NM.keyfile_read(keyfile, NM_CONN_DIR, NM.KeyfileHandlerFlags.NONE)
+ con.clear_secrets()
+ keyfile = NM.keyfile_write(con, NM.KeyfileHandlerFlags.NONE)
+ cp = utils.parse_config(keyfile.to_data()[0])
+
+ settings = []
+ for setting_name in cp.sections():
+ properties = []
+ for name, value in cp.items(setting_name, raw=True):
+ properties.append(NetworkManagerConnectionProperty(name=name, value=value))
+ settings.append(
+ NetworkManagerConnectionSetting(name=setting_name, properties=properties)
+ )
+ api.produce(NetworkManagerConnection(filename=filename, settings=settings))
+
+
+def process_dir(directory):
+ try:
+ keyfiles = os.listdir(directory)
+ except OSError as e:
+ if e.errno == errno.ENOENT:
+ return
+ raise
+
+ for f in keyfiles:
+ process_file(os.path.join(NM_CONN_DIR, f))
+
+
+def process():
+ if libnm_available:
+ process_dir(NM_CONN_DIR)
+ else:
+ raise StopActorExecutionError(
+ message='Failed to read NetworkManager connections',
+ details=err_details
+ )
diff --git a/repos/system_upgrade/el8toel9/actors/networkmanagerconnectionscanner/tests/unit_test_networkmanagerconnectionscanner.py b/repos/system_upgrade/el8toel9/actors/networkmanagerconnectionscanner/tests/unit_test_networkmanagerconnectionscanner.py
new file mode 100644
index 00000000..46af07c1
--- /dev/null
+++ b/repos/system_upgrade/el8toel9/actors/networkmanagerconnectionscanner/tests/unit_test_networkmanagerconnectionscanner.py
@@ -0,0 +1,105 @@
+import errno
+import textwrap
+
+import pytest
+import six
+
+from leapp.libraries.actor import networkmanagerconnectionscanner as nmconnscanner
+from leapp.libraries.common.testutils import make_OSError, produce_mocked
+from leapp.libraries.stdlib import api
+from leapp.models import NetworkManagerConnection
+
+_builtins_open = "builtins.open" if six.PY3 else "__builtin__.open"
+
+
+def _listdir_nm_conn(path):
+ if path == nmconnscanner.NM_CONN_DIR:
+ return ["conn1.nmconnection"]
+ raise make_OSError(errno.ENOENT)
+
+
+def _listdir_nm_conn2(path):
+ if path == nmconnscanner.NM_CONN_DIR:
+ return ["conn1.nmconnection", "conn2.nmconnection"]
+ raise make_OSError(errno.ENOENT)
+
+
+def _load_from_file(keyfile, filename, flags):
+ if filename.endswith(".nmconnection"):
+ return keyfile.load_from_data(textwrap.dedent("""
+ [connection]
+ type=wifi
+ id=conn1
+ uuid=a1bc695d-c548-40e8-9c7f-205a6587135d
+
+ [wifi]
+ mode=infrastructure
+ ssid=wifi
+
+ [wifi-security]
+ auth-alg=open
+ key-mgmt=none
+ wep-key-type=1
+ wep-key0=abcde
+ """), nmconnscanner.GLib.MAXSIZE, flags)
+ raise make_OSError(errno.ENOENT)
+
+
+@pytest.mark.skipif(not nmconnscanner.libnm_available, reason="NetworkManager g-ir not installed")
+def test_no_conf(monkeypatch):
+ """
+ No report if there are no keyfiles
+ """
+
+ monkeypatch.setattr(nmconnscanner.os, "listdir", lambda _: ())
+ monkeypatch.setattr(api, "produce", produce_mocked())
+ nmconnscanner.process()
+ assert not api.produce.called
+
+
+@pytest.mark.skipif(not nmconnscanner.libnm_available, reason="NetworkManager g-ir not installed")
+def test_nm_conn(monkeypatch):
+ """
+ Check a basic keyfile
+ """
+
+ monkeypatch.setattr(nmconnscanner.os, "listdir", _listdir_nm_conn)
+ monkeypatch.setattr(api, "produce", produce_mocked())
+ monkeypatch.setattr(nmconnscanner.GLib.KeyFile, "load_from_file", _load_from_file)
+ nmconnscanner.process()
+
+ assert api.produce.called == 1
+ assert len(api.produce.model_instances) == 1
+ nm_conn = api.produce.model_instances[0]
+ assert isinstance(nm_conn, NetworkManagerConnection)
+ assert nm_conn.filename == "/etc/NetworkManager/system-connections/conn1.nmconnection"
+ assert len(nm_conn.settings) == 3
+ assert nm_conn.settings[0].name == "connection"
+ assert len(nm_conn.settings[0].properties) == 4
+ assert nm_conn.settings[0].properties[0].name == "id"
+ assert nm_conn.settings[0].properties[0].value == "conn1"
+ assert nm_conn.settings[2].name == "wifi-security"
+
+ # It's important that wek-key0 is gone
+ assert len(nm_conn.settings[2].properties) == 3
+ assert nm_conn.settings[2].properties[0].name == "auth-alg"
+ assert nm_conn.settings[2].properties[0].value == "open"
+ assert nm_conn.settings[2].properties[1].name != "wep-key0"
+ assert nm_conn.settings[2].properties[2].name != "wep-key0"
+
+
+@pytest.mark.skipif(not nmconnscanner.libnm_available, reason="NetworkManager g-ir not installed")
+def test_nm_conn2(monkeypatch):
+ """
+ Check a pair of keyfiles
+ """
+
+ monkeypatch.setattr(nmconnscanner.os, "listdir", _listdir_nm_conn2)
+ monkeypatch.setattr(api, "produce", produce_mocked())
+ monkeypatch.setattr(nmconnscanner.GLib.KeyFile, "load_from_file", _load_from_file)
+ nmconnscanner.process()
+
+ assert api.produce.called == 2
+ assert len(api.produce.model_instances) == 2
+ assert api.produce.model_instances[0].filename.endswith("/conn1.nmconnection")
+ assert api.produce.model_instances[1].filename.endswith("/conn2.nmconnection")
--
2.39.0