Compare commits

...

No commits in common. "c8" and "c9-beta" have entirely different histories.
c8 ... c9-beta

15 changed files with 298 additions and 831 deletions

View File

@ -1 +1 @@
3511edebc20381fc108c92e41218cbfc4ed78fa8 SOURCES/v2.13.1.1.tar.gz e8f30e34eedc9280531af1bb1af9efb53d1a1039 SOURCES/v2.14.0.1.tar.gz

2
.gitignore vendored
View File

@ -1 +1 @@
SOURCES/v2.13.1.1.tar.gz SOURCES/v2.14.0.1.tar.gz

View File

@ -1,8 +1,8 @@
From 8ac14e61d8067bd8b4e60c59b35b4a4227f8a242 Mon Sep 17 00:00:00 2001 From ccbdd1a80e22c708a11cd518de8bad3807ae5ada Mon Sep 17 00:00:00 2001
From: Mohammed Gamal <mgamal@redhat.com> From: Mohammed Gamal <mgamal@redhat.com>
Date: Fri, 29 Jul 2022 13:07:13 +0200 Date: Fri, 29 Jul 2022 13:07:13 +0200
Subject: redhat: Use NetworkManager to set DHCP hostnames on recent RHEL Subject: [PATCH] redhat: Use NetworkManager to set DHCP hostnames on recent
distros RHEL distros
RH-Author: Mohamed Gamal Morsy <mmorsy@redhat.com> RH-Author: Mohamed Gamal Morsy <mmorsy@redhat.com>
RH-MergeRequest: 3: redhat: Use NetworkManager to set DHCP hostnames on recent RHEL distros RH-MergeRequest: 3: redhat: Use NetworkManager to set DHCP hostnames on recent RHEL distros
@ -48,6 +48,3 @@ index a9a10347..b85b2d42 100644
+ +
+ if return_code != 0: + if return_code != 0:
+ logger.error("failed to set DHCP hostname for interface {0}: return code {1}".format(ifname, return_code)) + logger.error("failed to set DHCP hostname for interface {0}: return code {1}".format(ifname, return_code))
--
2.39.3

View File

@ -1,7 +1,7 @@
From 92baa5663a0baa45e37243936221b0c1795ff324 Mon Sep 17 00:00:00 2001 From 97b3d6b1c8051fc24e47b584a4f07a1abce9d232 Mon Sep 17 00:00:00 2001
From: Ani Sinha <anisinha@redhat.com> From: Ani Sinha <anisinha@redhat.com>
Date: Mon, 6 May 2024 11:50:49 +0530 Date: Mon, 6 May 2024 11:50:49 +0530
Subject: Disable automatic log collector Subject: [PATCH] Disable automatic log collector
RH-Author: Ani Sinha <anisinha@redhat.com> RH-Author: Ani Sinha <anisinha@redhat.com>
RH-MergeRequest: 11: Disable automatic log collector RH-MergeRequest: 11: Disable automatic log collector
@ -38,6 +38,3 @@ index 3c9ad5d4..62d8148e 100644
# How frequently to collect logs, default is each hour # How frequently to collect logs, default is each hour
Logs.CollectPeriod=3600 Logs.CollectPeriod=3600
--
2.39.3

View File

@ -1,7 +1,7 @@
From c8df88fd1fedb25727fff64ecc5dde1a59d7a976 Mon Sep 17 00:00:00 2001 From 57d4ae44c3ce666bb1925abe4defc24e400a6454 Mon Sep 17 00:00:00 2001
From: Vitaly Kuznetsov <vkuznets@redhat.com> From: Vitaly Kuznetsov <vkuznets@redhat.com>
Date: Mon, 6 Jan 2025 17:13:11 +0100 Date: Mon, 6 Jan 2025 17:13:11 +0100
Subject: redhat: Add a udev rule to avoid managing slave NICs with Subject: [PATCH] redhat: Add a udev rule to avoid managing slave NICs with
NetworkManager NetworkManager
RH-Author: Vitaly Kuznetsov <vkuznets@redhat.com> RH-Author: Vitaly Kuznetsov <vkuznets@redhat.com>
@ -22,10 +22,9 @@ Patch-name: wla-redhat-Add-a-udev-rule-to-avoid-managing-slave-NICs-.patch
Patch-id: Patch-id:
Patch-present-in-specfile: True Patch-present-in-specfile: True
--- ---
.distro/WALinuxAgent.spec | 1 +
config/10-azure-unmanaged-sriov.rules | 6 ++++++ config/10-azure-unmanaged-sriov.rules | 6 ++++++
setup.py | 3 ++- setup.py | 3 ++-
3 files changed, 9 insertions(+), 1 deletion(-) 2 files changed, 8 insertions(+), 1 deletion(-)
create mode 100644 config/10-azure-unmanaged-sriov.rules create mode 100644 config/10-azure-unmanaged-sriov.rules
diff --git a/config/10-azure-unmanaged-sriov.rules b/config/10-azure-unmanaged-sriov.rules diff --git a/config/10-azure-unmanaged-sriov.rules b/config/10-azure-unmanaged-sriov.rules
@ -41,7 +40,7 @@ index 00000000..7eefcb26
+# NM_UNMANAGED=1 for NetworkManager +# NM_UNMANAGED=1 for NetworkManager
+SUBSYSTEM=="net", ACTION!="remove", DRIVERS=="mana|mlx4_core|mlx5_core", ATTR{flags}=="0x?[89ABCDEF]??", ENV{AZURE_UNMANAGED_SRIOV}="1", ENV{ID_NET_MANAGED_BY}="unmanaged", ENV{NM_UNMANAGED}="1" +SUBSYSTEM=="net", ACTION!="remove", DRIVERS=="mana|mlx4_core|mlx5_core", ATTR{flags}=="0x?[89ABCDEF]??", ENV{AZURE_UNMANAGED_SRIOV}="1", ENV{ID_NET_MANAGED_BY}="unmanaged", ENV{NM_UNMANAGED}="1"
diff --git a/setup.py b/setup.py diff --git a/setup.py b/setup.py
index e83f5989..cf6e90b5 100755 index ca5e2d20..6471dba3 100755
--- a/setup.py --- a/setup.py
+++ b/setup.py +++ b/setup.py
@@ -82,7 +82,8 @@ def set_openbsd_rc_files(data_files, dest="/etc/rc.d/", src=None): @@ -82,7 +82,8 @@ def set_openbsd_rc_files(data_files, dest="/etc/rc.d/", src=None):
@ -54,6 +53,3 @@ index e83f5989..cf6e90b5 100755
"config/99-azure-product-uuid.rules"] "config/99-azure-product-uuid.rules"]
data_files.append((dest, src)) data_files.append((dest, src))
--
2.39.3

View File

@ -1,4 +1,4 @@
From 756fe22f41c0607394a9b9ba20c15677b3389a21 Mon Sep 17 00:00:00 2001 From 5fb82a76eb6c14b2802e073c497a8c3de143375f Mon Sep 17 00:00:00 2001
From: Li Tian <94442129+litian1992@users.noreply.github.com> From: Li Tian <94442129+litian1992@users.noreply.github.com>
Date: Tue, 5 Aug 2025 03:18:10 +0800 Date: Tue, 5 Aug 2025 03:18:10 +0800
Subject: [PATCH] docs: add waagent manpage (#3401) Subject: [PATCH] docs: add waagent manpage (#3401)
@ -20,11 +20,14 @@ Signed-off-by: Li Tian <litian@redhat.com>
Co-authored-by: maddieford <93676569+maddieford@users.noreply.github.com> Co-authored-by: maddieford <93676569+maddieford@users.noreply.github.com>
(cherry picked from commit 7f9b5568b6572745b7d0776de0ff6fb70a28dba0) (cherry picked from commit 7f9b5568b6572745b7d0776de0ff6fb70a28dba0)
Signed-off-by: Li Tian <litian@redhat.com> Signed-off-by: Li Tian <litian@redhat.com>
Patch-name: wla-docs-add-waagent-manpage-3401.patch
Patch-id:
Patch-present-in-specfile: True
--- ---
.distro/WALinuxAgent.spec | 1 + doc/man/waagent.1 | 117 ++++++++++++++++++++++++++++++++++++++++++++++
doc/man/waagent.1 | 117 ++++++++++++++++++++++++++++++++++++++ setup.py | 14 ++++++
setup.py | 14 +++++ 2 files changed, 131 insertions(+)
3 files changed, 132 insertions(+)
create mode 100644 doc/man/waagent.1 create mode 100644 doc/man/waagent.1
diff --git a/doc/man/waagent.1 b/doc/man/waagent.1 diff --git a/doc/man/waagent.1 b/doc/man/waagent.1
@ -151,7 +154,7 @@ index 00000000..b1d8e9eb
+.SH AUTHORS +.SH AUTHORS
+Microsoft Azure Linux Team +Microsoft Azure Linux Team
diff --git a/setup.py b/setup.py diff --git a/setup.py b/setup.py
index cf6e90b5..9ed135fb 100755 index 6471dba3..30784ca6 100755
--- a/setup.py --- a/setup.py
+++ b/setup.py +++ b/setup.py
@@ -17,7 +17,9 @@ @@ -17,7 +17,9 @@
@ -194,9 +197,6 @@ index cf6e90b5..9ed135fb 100755
set_udev_files(data_files) set_udev_files(data_files)
set_systemd_files(data_files, dest=systemd_dir_path) set_systemd_files(data_files, dest=systemd_dir_path)
+ set_man_files(data_files) + set_man_files(data_files)
else: elif name == 'chainguard':
# Use default setting set_bin_files(data_files, dest=agent_bin_path, src=["bin/py3/waagent"])
set_bin_files(data_files, dest=agent_bin_path) set_conf_files(data_files, src=["config/chainguard/waagent.conf"])
--
2.50.1

View File

@ -1,4 +1,4 @@
From 846d9f18e2ee331e35a7243f73de3bb3c18875df Mon Sep 17 00:00:00 2001 From f08fe8708565901e1cfe453d5847095aaefb9355 Mon Sep 17 00:00:00 2001
From: Yuxin Sun <yuxisun@redhat.com> From: Yuxin Sun <yuxisun@redhat.com>
Date: Fri, 27 Jun 2025 01:34:20 +0800 Date: Fri, 27 Jun 2025 01:34:20 +0800
Subject: [PATCH] Use systemctl instead of service to manager services in new Subject: [PATCH] Use systemctl instead of service to manager services in new
@ -19,6 +19,10 @@ Upstream PR: https://github.com/Azure/WALinuxAgent/pull/3403
(cherry picked from commit a6cfdfdc3e04884a08cd6dd20fa035b687943fe9) (cherry picked from commit a6cfdfdc3e04884a08cd6dd20fa035b687943fe9)
Signed-off-by: Yuxin Sun <yuxisun@redhat.com> Signed-off-by: Yuxin Sun <yuxisun@redhat.com>
Patch-name: wla-Use-systemctl-instead-of-service-to-manager-services.patch
Patch-id:
Patch-present-in-specfile: True
--- ---
azurelinuxagent/common/osutil/redhat.py | 14 +++++++++++++- azurelinuxagent/common/osutil/redhat.py | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-) 1 file changed, 13 insertions(+), 1 deletion(-)
@ -55,6 +59,3 @@ index b85b2d42..cf2d2f78 100644
DefaultOSUtil.publish_hostname(self, hostname) DefaultOSUtil.publish_hostname(self, hostname)
def set_dhcp_hostname(self, hostname): def set_dhcp_hostname(self, hostname):
--
2.50.1

View File

@ -1,31 +0,0 @@
From 246586f31209b70667b494db30c847c5b87fe7a3 Mon Sep 17 00:00:00 2001
From: Darren Archibald <darren.archibald@oracle.com>
Date: Wed, 21 Sep 2022 05:40:34 -0700
Subject: [PATCH] Add Oracle support
Add oracle support to fix waagent.service build issue
Signed-off-by: Darren Archibald <darren.archibald@oracle.com>
Updated for OL9.7
Signed-off-by: Mark Will <mark.will@oracle.com>
---
setup.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/setup.py b/setup.py
index cf6e90b..f2a49f1 100755
--- a/setup.py
+++ b/setup.py
@@ -97,7 +97,7 @@ def get_data_files(name, version, fullname): # pylint: disable=R0912
systemd_dir_path = osutil.get_systemd_unit_file_install_path()
agent_bin_path = osutil.get_agent_bin_path()
- if name in ('redhat', 'rhel', 'centos', 'almalinux', 'cloudlinux', 'rocky'):
+ if name in ('redhat', 'rhel', 'centos', 'almalinux', 'cloudlinux', 'rocky', 'oracle'):
if version.startswith("8") or version.startswith("9"):
# redhat8+ default to py3
set_bin_files(data_files, dest=agent_bin_path,
--
2.27.0

View File

@ -13,5 +13,5 @@ depends() {
# called by dracut # called by dracut
install() { install() {
inst_multiple cut readlink chmod inst_multiple cut readlink chmod
inst_rules 10-azure-unmanaged-sriov.rules 66-azure-storage.rules 99-azure-product-uuid.rules inst_rules 66-azure-storage.rules 99-azure-product-uuid.rules
} }

0
SOURCES/tpm2-luks-import.sh Normal file → Executable file
View File

View File

@ -0,0 +1,153 @@
From d316428f569ddc35561c86e29e1d4b14423b239d Mon Sep 17 00:00:00 2001
From: Yuxin Sun <yuxisun@redhat.com>
Date: Fri, 8 Aug 2025 04:11:48 +0800
Subject: [PATCH] Change redhat waagent-network-setup.service path to /etc
(#3392)
RH-Author: yuxisun <None>
RH-MergeRequest: 27: [c9s]Change redhat waagent-network-setup.service path to /etc (#3392)
RH-Jira: RHEL-82232
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [1/1] a975e122bba51e666a762c4967fa4db559b7e027 (yuxisun/centos_src_WALinuxAgent)
JIRA: https://issues.redhat.com/browse/RHEL-82232
In image mode, the /usr path is readonly. WALA intends to write a /usr/lib/systemd/system/waagent-network-setup.service file to setup the persistent firewall rules, which is not permitted in image mode. So we change the service file path to /etc/systemd/system/waagent-network-setup.service to resolve this issue.
Upstream PR: https://github.com/Azure/WALinuxAgent/pull/3392
Signed-off-by: Yuxin Sun <yuxisun@redhat.com>
Co-authored-by: Norberto Arrieta <narrieta@users.noreply.github.com>
Co-authored-by: maddieford <93676569+maddieford@users.noreply.github.com>
(cherry picked from commit 1d8e1c915b9551d8cd0587f32a80e66fe153c9da)
Signed-off-by: Yuxin Sun <yuxisun@redhat.com>
---
azurelinuxagent/common/osutil/default.py | 4 ++++
azurelinuxagent/common/osutil/redhat.py | 12 +++++++++++
azurelinuxagent/ga/persist_firewall_rules.py | 22 ++++++++++++++++----
tests/ga/test_persist_firewall_rules.py | 2 +-
4 files changed, 35 insertions(+), 5 deletions(-)
diff --git a/azurelinuxagent/common/osutil/default.py b/azurelinuxagent/common/osutil/default.py
index 9eeb0da8..a8830ff3 100644
--- a/azurelinuxagent/common/osutil/default.py
+++ b/azurelinuxagent/common/osutil/default.py
@@ -123,6 +123,10 @@ class DefaultOSUtil(object):
def get_systemd_unit_file_install_path():
return "/lib/systemd/system"
+ @classmethod
+ def get_network_setup_service_install_path(cls):
+ return cls.get_systemd_unit_file_install_path()
+
@staticmethod
def get_agent_bin_path():
return "/usr/sbin"
diff --git a/azurelinuxagent/common/osutil/redhat.py b/azurelinuxagent/common/osutil/redhat.py
index cf2d2f78..a80fc889 100644
--- a/azurelinuxagent/common/osutil/redhat.py
+++ b/azurelinuxagent/common/osutil/redhat.py
@@ -103,6 +103,18 @@ class RedhatOSUtil(Redhat6xOSUtil):
def get_systemd_unit_file_install_path():
return "/usr/lib/systemd/system"
+ @classmethod
+ def get_network_setup_service_install_path(cls):
+ """
+ In image mode, /usr is readonly, so the
+ waagent-network-setup.service is written in /etc/systemd/system.
+ In non-image mode, the default location is /usr/lib/systemd/system.
+ """
+ if os.path.exists('/run/ostree-booted'):
+ return "/etc/systemd/system"
+ else:
+ return cls.get_systemd_unit_file_install_path()
+
def set_hostname(self, hostname):
"""
Unlike redhat 6.x, redhat 7.x will set hostname via hostnamectl
diff --git a/azurelinuxagent/ga/persist_firewall_rules.py b/azurelinuxagent/ga/persist_firewall_rules.py
index 1f80d272..a876de2c 100644
--- a/azurelinuxagent/ga/persist_firewall_rules.py
+++ b/azurelinuxagent/ga/persist_firewall_rules.py
@@ -27,6 +27,7 @@ from azurelinuxagent.common.future import ustr
from azurelinuxagent.common.osutil import get_osutil, systemd
from azurelinuxagent.common.utils import shellutil, fileutil, textutil
from azurelinuxagent.common.utils.shellutil import CommandError
+from azurelinuxagent.common.version import get_distro
class PersistFirewallRulesHandler(object):
@@ -71,23 +72,25 @@ if __name__ == '__main__':
# modify the unit file on VM too
_UNIT_VERSION = "1.4"
+ _DISTRO = get_distro()[0]
+
@staticmethod
def get_service_file_path():
osutil = get_osutil()
service_name = PersistFirewallRulesHandler._AGENT_NETWORK_SETUP_NAME_FORMAT.format(osutil.get_service_name())
- return os.path.join(osutil.get_systemd_unit_file_install_path(), service_name)
+ return os.path.join(osutil.get_network_setup_service_install_path(), service_name)
def __init__(self, dst_ip):
"""
This class deals with ensuring that Firewall rules are persisted over system reboots.
It tries to employ using Firewalld.service if present first as it already has provisions for persistent rules.
- If not, it then creates a new agent-network-setup.service file and copy it over to the osutil.get_systemd_unit_file_install_path() dynamically
+ If not, it then creates a new agent-network-setup.service file and copy it over to the osutil.get_network_setup_service_install_path() dynamically
On top of it, on every service restart it ensures that the WireIP is overwritten and the new IP is blocked as well.
"""
osutil = get_osutil()
self._network_setup_service_name = self._AGENT_NETWORK_SETUP_NAME_FORMAT.format(osutil.get_service_name())
self._is_systemd = systemd.is_systemd()
- self._systemd_file_path = osutil.get_systemd_unit_file_install_path()
+ self._systemd_file_path = osutil.get_network_setup_service_install_path()
self._dst_ip = dst_ip
# The custom service will try to call the current agent executable to setup the firewall rules
self._current_agent_executable_path = os.path.join(os.getcwd(), sys.argv[0])
@@ -118,7 +121,7 @@ if __name__ == '__main__':
def setup(self):
if not self._is_firewall_service_running():
- logger.info("Firewalld service not running/unavailable, trying to set up {0}".format(self._network_setup_service_name))
+ logger.info("Firewalld service not running/unavailable, trying to set up {0}".format(self.get_service_file_path()))
if systemd.is_systemd():
self._setup_network_setup_service()
else:
@@ -176,6 +179,17 @@ if __name__ == '__main__':
# This is to handle the case where WireIP can change midway on service restarts.
# Additionally, incase of auto-update this would also update the location of the new EGG file ensuring that
# the service is always run from the most latest agent.
+
+ # If RHEL and in image mode, we need to clean up the service file in old path
+ if self._DISTRO == 'rhel' and os.path.exists('/run/ostree-booted'):
+ old_service_file_path = os.path.join('/usr/lib/systemd/system/', self._network_setup_service_name)
+ if os.path.exists(old_service_file_path):
+ logger.info("Cleaning up old service file in image mode: {0}".format(old_service_file_path))
+ try:
+ fileutil.rm_files(old_service_file_path)
+ except Exception as error:
+ logger.warn("Unable to delete old service in image mode {0}: {1}".format(self._network_setup_service_name, ustr(error)))
+
self.__setup_binary_file()
network_service_enabled = self.__verify_network_setup_service_enabled()
diff --git a/tests/ga/test_persist_firewall_rules.py b/tests/ga/test_persist_firewall_rules.py
index 18b302f8..1f3f7e56 100644
--- a/tests/ga/test_persist_firewall_rules.py
+++ b/tests/ga/test_persist_firewall_rules.py
@@ -76,7 +76,7 @@ class TestPersistFirewallRulesHandler(AgentTestCase):
osutil = DefaultOSUtil()
osutil.get_agent_bin_path = MagicMock(return_value=self.__agent_bin_dir)
- osutil.get_systemd_unit_file_install_path = MagicMock(return_value=self.__systemd_dir)
+ osutil.get_network_setup_service_install_path = MagicMock(return_value=self.__systemd_dir)
self._expected_service_name = PersistFirewallRulesHandler._AGENT_NETWORK_SETUP_NAME_FORMAT.format(
osutil.get_service_name())
--
2.47.3

View File

@ -1,14 +1,14 @@
From cbf30e0eebbedc5242d03f53d355113a53209635 Mon Sep 17 00:00:00 2001 From 950482d0e2147546dbb00d064937e6375bf860d4 Mon Sep 17 00:00:00 2001
From: Chris Patterson <cpatterson@microsoft.com> From: Chris Patterson <cpatterson@microsoft.com>
Date: Thu, 1 Sep 2022 10:45:47 -0400 Date: Thu, 1 Sep 2022 10:45:47 -0400
Subject: [PATCH] Jira: https://issues.redhat.com/browse/RHEL-134939 Subject: [PATCH] Jira: https://issues.redhat.com/browse/RHEL-133507
RH-Author: yuxisun <None> RH-Author: yuxisun <None>
RH-MergeRequest: 25: waagent.service: set ConditionVirtualization=|microsoft RH-MergeRequest: 25: waagent.service: set ConditionVirtualization=|microsoft
RH-Jira: RHEL-134939 RH-Jira: RHEL-133507
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com> RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com> RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [1/1] 95d939cb838949a4509bd9415873fbdc7e7191f3 RH-Commit: [1/1] 667b85d61146b144ba24baf41156ae32e301b194 (yuxisun/centos_src_WALinuxAgent)
waagent.service: set ConditionVirtualization=|microsoft waagent.service: set ConditionVirtualization=|microsoft
@ -18,7 +18,7 @@ Set it as a triggering condition to make it easier for downstreams or
test setups to add another condition (i.e. run outside of hyperv). test setups to add another condition (i.e. run outside of hyperv).
Signed-off-by: Chris Patterson <cpatterson@microsoft.com> Signed-off-by: Chris Patterson <cpatterson@microsoft.com>
(cherry picked from commit 2d78c9ebaba4742390e92dc5994391949b90ec4c) (cherry picked from commit d1fad5317e2dac9fc82d9c56d2d016e20db5f7a2)
Downstream only Downstream only

View File

@ -0,0 +1,50 @@
From bd24da8a9ec066f354a10581eecbc8b85617adf8 Mon Sep 17 00:00:00 2001
From: Yuxin Sun <yuxisun@redhat.com>
Date: Fri, 31 Oct 2025 17:49:02 +0800
Subject: [PATCH] Remove 10-azure-unmanaged-sriov.rules
RH-Author: yuxisun <None>
RH-MergeRequest: 24: Remove 10-azure-unmanaged-sriov.rules
RH-Jira: RHEL-124218
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [1/1] 4223f72f9f68ba9182c0b05ed76209930a8071de (yuxisun/centos_src_WALinuxAgent)
Signed-off-by: Yuxin Sun <yuxisun@redhat.com>
---
.distro/WALinuxAgent.spec | 2 +-
.distro/sources/module-setup-udev.sh | 2 +-
config/10-azure-unmanaged-sriov.rules | 6 ------
setup.py | 3 +--
4 files changed, 3 insertions(+), 10 deletions(-)
delete mode 100644 config/10-azure-unmanaged-sriov.rules
diff --git a/config/10-azure-unmanaged-sriov.rules b/config/10-azure-unmanaged-sriov.rules
deleted file mode 100644
index 7eefcb26..00000000
--- a/config/10-azure-unmanaged-sriov.rules
+++ /dev/null
@@ -1,6 +0,0 @@
-# Azure VMs with accelerated networking may have MANA, mlx4, or mlx5 SR-IOV devices which are transparently bonded to a synthetic
-# hv_netvsc device. Mark devices with the IFF_SLAVE bit set as unmanaged devices:
-# AZURE_UNMANAGED_SRIOV=1 for 10-azure-unmanaged-sriov.network
-# ID_NET_MANAGED_BY=unmanaged for systemd-networkd >= 255
-# NM_UNMANAGED=1 for NetworkManager
-SUBSYSTEM=="net", ACTION!="remove", DRIVERS=="mana|mlx4_core|mlx5_core", ATTR{flags}=="0x?[89ABCDEF]??", ENV{AZURE_UNMANAGED_SRIOV}="1", ENV{ID_NET_MANAGED_BY}="unmanaged", ENV{NM_UNMANAGED}="1"
diff --git a/setup.py b/setup.py
index 30784ca6..c8e77988 100755
--- a/setup.py
+++ b/setup.py
@@ -84,8 +84,7 @@ def set_openbsd_rc_files(data_files, dest="/etc/rc.d/", src=None):
def set_udev_files(data_files, dest="/etc/udev/rules.d/", src=None):
if src is None:
- src = ["config/10-azure-unmanaged-sriov.rules",
- "config/66-azure-storage.rules",
+ src = ["config/66-azure-storage.rules",
"config/99-azure-product-uuid.rules"]
data_files.append((dest, src))
--
2.47.3

View File

@ -1,704 +0,0 @@
From c446f444a897fc7094a5ce00bd77bb430c79d8ea Mon Sep 17 00:00:00 2001
From: Norberto Arrieta <narrieta@users.noreply.github.com>
Date: Tue, 4 Mar 2025 12:55:27 -0800
Subject: [PATCH] Support for FIPS 140-3 (#3324)
RH-Author: yuxisun <None>
RH-MergeRequest: 24: Support for FIPS 140-3 (#3324)
RH-Jira: RHEL-124949
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
RH-Acked-by: Jon Maloy <jmaloy@redhat.com>
RH-Commit: [1/1] 3aadd91d56764017d13d8dc2cdada02551a7deff
Jira: https://issues.redhat.com/browse/RHEL-124949
When fetching certificates from WireServer, the Agent uses DES_EDE3_CBC. The PFX it receives has a MAC computed using PKCS12KDF. Both are deprecated on FIPS 140-3. This PR switches to AES128_CBC for communication with the WireServer (a subsequent PR will change it to AES256_CBC) and skips MAC verification when it is not needed. The changes also include some minor cleanup to remove data structures that are not used.
Upstream PR: https://github.com/Azure/WALinuxAgent/pull/3324
Signed-off-by: Yuxin Sun yuxisun@redhat.com
---
azurelinuxagent/common/event.py | 20 ++
azurelinuxagent/common/protocol/goal_state.py | 216 +++++++++++-------
azurelinuxagent/common/protocol/restapi.py | 24 --
azurelinuxagent/common/protocol/wire.py | 18 +-
azurelinuxagent/common/utils/cryptutil.py | 46 ++--
azurelinuxagent/ga/update.py | 1 +
tests/common/protocol/test_goal_state.py | 81 ++++++-
tests/common/protocol/test_hostplugin.py | 35 ++-
tests/common/protocol/test_wire.py | 8 +-
tests/ga/test_update.py | 2 +-
10 files changed, 275 insertions(+), 176 deletions(-)
diff --git a/azurelinuxagent/common/event.py b/azurelinuxagent/common/event.py
index 6b9521ca..9b8a926e 100644
--- a/azurelinuxagent/common/event.py
+++ b/azurelinuxagent/common/event.py
@@ -93,6 +93,7 @@ class WALAEventOperation:
FetchGoalState = "FetchGoalState"
Firewall = "Firewall"
GoalState = "GoalState"
+ GoalStateCertificates = "GoalStateCertificates"
GoalStateUnsupportedFeatures = "GoalStateUnsupportedFeatures"
HealthCheck = "HealthCheck"
HealthObservation = "HealthObservation"
@@ -733,6 +734,25 @@ def error(op, fmt, *args):
add_event(op=op, message=fmt.format(*args), is_success=False, log_event=False)
+class LogEvent(object):
+ """
+ Helper class that allows the use of info()/warn()/error() using a specific instance of a logger.
+ """
+ def __init__(self, logger_):
+ self._logger = logger_
+
+ def info(self, op, fmt, *args):
+ self._logger.info(fmt, *args)
+ add_event(op=op, message=fmt.format(*args), is_success=True)
+
+ def warn(self, op, fmt, *args):
+ self._logger.warn(fmt, *args)
+ add_event(op=op, message="[WARNING] " + fmt.format(*args), is_success=False, log_event=False)
+
+ def error(self, op, fmt, *args):
+ self._logger.error(fmt, *args)
+ add_event(op=op, message=fmt.format(*args), is_success=False, log_event=False)
+
def add_log_event(level, message, forced=False, reporter=__event_logger__):
"""
:param level: LoggerLevel of the log event
diff --git a/azurelinuxagent/common/protocol/goal_state.py b/azurelinuxagent/common/protocol/goal_state.py
index f94f3ae5..2556cc73 100644
--- a/azurelinuxagent/common/protocol/goal_state.py
+++ b/azurelinuxagent/common/protocol/goal_state.py
@@ -24,15 +24,14 @@ import json
from azurelinuxagent.common import conf
from azurelinuxagent.common import logger
from azurelinuxagent.common.AgentGlobals import AgentGlobals
-from azurelinuxagent.common.datacontract import set_properties
-from azurelinuxagent.common.event import add_event, WALAEventOperation
+from azurelinuxagent.common.event import add_event, WALAEventOperation, LogEvent
from azurelinuxagent.common.exception import ProtocolError, ResourceGoneError
from azurelinuxagent.common.future import ustr
from azurelinuxagent.common.protocol.extensions_goal_state_factory import ExtensionsGoalStateFactory
from azurelinuxagent.common.protocol.extensions_goal_state import VmSettingsParseError, GoalStateSource
from azurelinuxagent.common.protocol.hostplugin import VmSettingsNotSupported, VmSettingsSupportStopped
-from azurelinuxagent.common.protocol.restapi import Cert, CertList, RemoteAccessUser, RemoteAccessUsersList, ExtHandlerPackage, ExtHandlerPackageList
-from azurelinuxagent.common.utils import fileutil
+from azurelinuxagent.common.protocol.restapi import RemoteAccessUser, RemoteAccessUsersList, ExtHandlerPackage, ExtHandlerPackageList
+from azurelinuxagent.common.utils import fileutil, shellutil
from azurelinuxagent.common.utils.archive import GoalStateHistory, SHARED_CONF_FILE_NAME
from azurelinuxagent.common.utils.cryptutil import CryptUtil
from azurelinuxagent.common.utils.textutil import parse_doc, findall, find, findtext, getattrib, gettext
@@ -41,6 +40,7 @@ from azurelinuxagent.common.utils.textutil import parse_doc, findall, find, find
GOAL_STATE_URI = "http://{0}/machine/?comp=goalstate"
CERTS_FILE_NAME = "Certificates.xml"
P7M_FILE_NAME = "Certificates.p7m"
+PFX_FILE_NAME = "Certificates.pfx"
PEM_FILE_NAME = "Certificates.pem"
TRANSPORT_CERT_FILE_NAME = "TransportCert.pem"
TRANSPORT_PRV_FILE_NAME = "TransportPrivate.pem"
@@ -282,16 +282,8 @@ class GoalState(object):
self._check_and_download_missing_certs_on_disk()
def _download_certificates(self, certs_uri):
- xml_text = self._wire_client.fetch_config(certs_uri, self._wire_client.get_header_for_cert())
- certs = Certificates(xml_text, self.logger)
- # Log and save the certificates summary (i.e. the thumbprint but not the certificate itself) to the goal state history
- for c in certs.summary:
- message = "Downloaded certificate {0}".format(c)
- self.logger.info(message)
- add_event(op=WALAEventOperation.GoalState, message=message)
- if len(certs.warnings) > 0:
- self.logger.warn(certs.warnings)
- add_event(op=WALAEventOperation.GoalState, message=certs.warnings)
+ certs = Certificates(self._wire_client, certs_uri, self.logger)
+ # Save the certificates summary (i.e. the thumbprints but not the certificates themselves) to the goal state history
if self._save_to_history:
self._history.save_certificates(json.dumps(certs.summary))
return certs
@@ -511,31 +503,83 @@ class SharedConfig(object):
self.xml_text = xml_text
-class Certificates(object):
- def __init__(self, xml_text, my_logger):
- self.cert_list = CertList()
- self.summary = [] # debugging info
- self.warnings = []
+class Certificates(LogEvent):
+ def __init__(self, wire_client, uri, logger_):
+ super(Certificates, self).__init__(logger_)
+ self.summary = []
+ self._crypt_util = CryptUtil(conf.get_openssl_cmd())
- # Save the certificates
- local_file = os.path.join(conf.get_lib_dir(), CERTS_FILE_NAME)
- fileutil.write_file(local_file, xml_text)
+ try:
+ pfx_file = self._download_certificates_pfx(wire_client, uri)
+ if pfx_file is None: # The response from the WireServer may not have any certificates
+ return
- # Separate the certificates into individual files.
- xml_doc = parse_doc(xml_text)
- data = findtext(xml_doc, "Data")
- if data is None:
- return
+ try:
+ pem_file = self._convert_certificates_pfx_to_pem(pfx_file)
+ finally:
+ self._remove_file(pfx_file)
- # if the certificates format is not Pkcs7BlobWithPfxContents do not parse it
- certificate_format = findtext(xml_doc, "Format")
- if certificate_format and certificate_format != "Pkcs7BlobWithPfxContents":
- message = "The Format is not Pkcs7BlobWithPfxContents. Format is {0}".format(certificate_format)
- my_logger.warn(message)
- add_event(op=WALAEventOperation.GoalState, message=message)
- return
+ self.summary = self._extract_certificate(pem_file)
+
+ for c in self.summary:
+ self.info(WALAEventOperation.GoalStateCertificates, "Downloaded certificate {0}", c)
+
+ except Exception as e:
+ self.error(WALAEventOperation.GoalStateCertificates, "Error fetching the goal state certificates: {0}", ustr(e))
+
+ def _remove_file(self, file):
+ if os.path.exists(file):
+ try:
+ os.remove(file)
+ except Exception as e:
+ self.warn(WALAEventOperation.GoalStateCertificates, "Failed to remove {0}: {1}", file, ustr(e))
+
+ def _download_certificates_pfx(self, wire_client, uri):
+ """
+ Downloads the certificates from the WireServer and saves them to a pfx file.
+ Returns the full path of the pfx file, or None, if the WireServer response does not have a "Data" element
+ """
+ trans_prv_file = os.path.join(conf.get_lib_dir(), TRANSPORT_PRV_FILE_NAME)
+ trans_cert_file = os.path.join(conf.get_lib_dir(), TRANSPORT_CERT_FILE_NAME)
+ xml_file = os.path.join(conf.get_lib_dir(), CERTS_FILE_NAME)
+ pfx_file = os.path.join(conf.get_lib_dir(), PFX_FILE_NAME)
+
+ for cypher in ["AES128_CBC", "DES_EDE3_CBC"]:
+ headers = wire_client.get_headers_for_encrypted_request(cypher)
+
+ try:
+ xml_text = wire_client.fetch_config(uri, headers)
+ except Exception as e:
+ self.warn(WALAEventOperation.GoalStateCertificates, "Error in Certificates request [cypher: {0}]: {1}", cypher, ustr(e))
+ continue
- cryptutil = CryptUtil(conf.get_openssl_cmd())
+ fileutil.write_file(xml_file, xml_text)
+
+ xml_doc = parse_doc(xml_text)
+ data = findtext(xml_doc, "Data")
+ if data is None:
+ self.info(WALAEventOperation.GoalStateCertificates, "The Data element of the Certificates response is empty")
+ return None
+ certificate_format = findtext(xml_doc, "Format")
+ if certificate_format and certificate_format != "Pkcs7BlobWithPfxContents":
+ self.warn(WALAEventOperation.GoalStateCertificates, "The Certificates format is not Pkcs7BlobWithPfxContents; skipping. Format is {0}", certificate_format)
+ return None
+
+ p7m_file = Certificates._create_p7m_file(data)
+
+ try:
+ self._crypt_util.decrypt_certificates_p7m(p7m_file, trans_prv_file, trans_cert_file, pfx_file)
+ except shellutil.CommandError as e:
+ self.warn(WALAEventOperation.GoalState, "Error in transport decryption [cypher: {0}]: {1}", cypher, ustr(e))
+ self._remove_file(pfx_file)
+ continue
+
+ return pfx_file
+
+ raise Exception("Cannot download certificates using any of the supported cyphers")
+
+ @staticmethod
+ def _create_p7m_file(data):
p7m_file = os.path.join(conf.get_lib_dir(), P7M_FILE_NAME)
p7m = ("MIME-Version:1.0\n" # pylint: disable=W1308
"Content-Disposition: attachment; filename=\"{0}\"\n"
@@ -543,68 +587,72 @@ class Certificates(object):
"Content-Transfer-Encoding: base64\n"
"\n"
"{2}").format(p7m_file, p7m_file, data)
-
fileutil.write_file(p7m_file, p7m)
+ return p7m_file
- trans_prv_file = os.path.join(conf.get_lib_dir(), TRANSPORT_PRV_FILE_NAME)
- trans_cert_file = os.path.join(conf.get_lib_dir(), TRANSPORT_CERT_FILE_NAME)
+ def _convert_certificates_pfx_to_pem(self, pfx_file):
+ """
+ Convert the pfx file to pem file.
+ """
pem_file = os.path.join(conf.get_lib_dir(), PEM_FILE_NAME)
- # decrypt certificates
- cryptutil.decrypt_p7m(p7m_file, trans_prv_file, trans_cert_file, pem_file)
+ for nomacver in [True, False]:
+ try:
+ self._crypt_util.convert_pfx_to_pem(pfx_file, nomacver, pem_file)
+ return pem_file
+ except shellutil.CommandError as e:
+ self._remove_file(pem_file) # An error may leave an empty pem file, which can produce a failure on some versions of open SSL (e.g. 3.2.2) on the next invocation
+ self.warn(WALAEventOperation.GoalState, "Error converting PFX to PEM [-nomacver: {0}]: {1}", nomacver, ustr(e))
+ continue
+
+ raise Exception("Cannot convert PFX to PEM")
+
+ def _extract_certificate(self, pem_file):
+ """
+ Parse the certificates and private keys from the pem file and store them in the certificates directory.
+ """
# The parsing process use public key to match prv and crt.
- buf = []
- prvs = {}
- thumbprints = {}
+ private_keys = {} # map of private keys indexed by public key
+ thumbprints = {} # map of thumbprints indexed by public key
+ buffer = [] # buffer for reading lines belonging to a certificate or private key
index = 0
- v1_cert_list = []
-
- # Ensure pem_file exists before read the certs data since decrypt_p7m may clear the pem_file wen decryption fails
- if os.path.exists(pem_file):
- with open(pem_file) as pem:
- for line in pem.readlines():
- buf.append(line)
- if re.match(r'[-]+END.*KEY[-]+', line):
- tmp_file = Certificates._write_to_tmp_file(index, 'prv', buf)
- pub = cryptutil.get_pubkey_from_prv(tmp_file)
- prvs[pub] = tmp_file
- buf = []
- index += 1
- elif re.match(r'[-]+END.*CERTIFICATE[-]+', line):
- tmp_file = Certificates._write_to_tmp_file(index, 'crt', buf)
- pub = cryptutil.get_pubkey_from_crt(tmp_file)
- thumbprint = cryptutil.get_thumbprint_from_crt(tmp_file)
- thumbprints[pub] = thumbprint
- # Rename crt with thumbprint as the file name
- crt = "{0}.crt".format(thumbprint)
- v1_cert_list.append({
- "name": None,
- "thumbprint": thumbprint
- })
- os.rename(tmp_file, os.path.join(conf.get_lib_dir(), crt))
- buf = []
- index += 1
+
+ with open(pem_file) as pem:
+ for line in pem.readlines():
+ buffer.append(line)
+ if re.match(r'[-]+END.*KEY[-]+', line):
+ tmp_file = Certificates._write_to_tmp_file(index, 'prv', buffer)
+ pub = self._crypt_util.get_pubkey_from_prv(tmp_file)
+ private_keys[pub] = tmp_file
+ buffer = []
+ index += 1
+ elif re.match(r'[-]+END.*CERTIFICATE[-]+', line):
+ tmp_file = Certificates._write_to_tmp_file(index, 'crt', buffer)
+ pub = self._crypt_util.get_pubkey_from_crt(tmp_file)
+ thumbprint = self._crypt_util.get_thumbprint_from_crt(tmp_file)
+ thumbprints[pub] = thumbprint
+ # Rename crt with thumbprint as the file name
+ crt = "{0}.crt".format(thumbprint)
+ os.rename(tmp_file, os.path.join(conf.get_lib_dir(), crt))
+ buffer = []
+ index += 1
# Rename prv key with thumbprint as the file name
- for pubkey in prvs:
+ for pubkey in private_keys:
thumbprint = thumbprints[pubkey]
if thumbprint:
- tmp_file = prvs[pubkey]
+ tmp_file = private_keys[pubkey]
prv = "{0}.prv".format(thumbprint)
os.rename(tmp_file, os.path.join(conf.get_lib_dir(), prv))
else:
- # Since private key has *no* matching certificate,
- # it will not be named correctly
- self.warnings.append("Found NO matching cert/thumbprint for private key!")
+ # Since private key has *no* matching certificate, it will not be named correctly
+ self.warn(WALAEventOperation.GoalState, "Found a private key with no matching cert/thumbprint!")
+ certificates = []
for pubkey, thumbprint in thumbprints.items():
- has_private_key = pubkey in prvs
- self.summary.append({"thumbprint": thumbprint, "hasPrivateKey": has_private_key})
-
- for v1_cert in v1_cert_list:
- cert = Cert()
- set_properties("certs", cert, v1_cert)
- self.cert_list.certificates.append(cert)
+ has_private_key = pubkey in private_keys
+ certificates.append({"thumbprint": thumbprint, "hasPrivateKey": has_private_key})
+ return certificates
@staticmethod
def _write_to_tmp_file(index, suffix, buf):
@@ -614,9 +662,7 @@ class Certificates(object):
class EmptyCertificates:
def __init__(self):
- self.cert_list = CertList()
- self.summary = [] # debugging info
- self.warnings = []
+ self.summary = []
class RemoteAccess(object):
"""
diff --git a/azurelinuxagent/common/protocol/restapi.py b/azurelinuxagent/common/protocol/restapi.py
index 54e020c1..7e563b4a 100644
--- a/azurelinuxagent/common/protocol/restapi.py
+++ b/azurelinuxagent/common/protocol/restapi.py
@@ -43,30 +43,6 @@ class VMInfo(DataContract):
self.tenantName = tenantName
-class CertificateData(DataContract):
- def __init__(self, certificateData=None):
- self.certificateData = certificateData
-
-
-class Cert(DataContract):
- def __init__(self,
- name=None,
- thumbprint=None,
- certificateDataUri=None,
- storeName=None,
- storeLocation=None):
- self.name = name
- self.thumbprint = thumbprint
- self.certificateDataUri = certificateDataUri
- self.storeLocation = storeLocation
- self.storeName = storeName
-
-
-class CertList(DataContract):
- def __init__(self):
- self.certificates = DataContractList(Cert)
-
-
class VMAgentFamily(object):
def __init__(self, name):
self.name = name
diff --git a/azurelinuxagent/common/protocol/wire.py b/azurelinuxagent/common/protocol/wire.py
index 00a01f09..0277b7f0 100644
--- a/azurelinuxagent/common/protocol/wire.py
+++ b/azurelinuxagent/common/protocol/wire.py
@@ -115,8 +115,7 @@ class WireProtocol(DataContract):
return vminfo
def get_certs(self):
- certificates = self.client.get_certs()
- return certificates.cert_list
+ return self.client.get_certs()
def get_goal_state(self):
return self.client.get_goal_state()
@@ -1140,13 +1139,11 @@ class WireClient(object):
"Content-Type": "text/xml;charset=utf-8"
}
- def get_header_for_cert(self):
- return self._get_header_for_encrypted_request("DES_EDE3_CBC")
-
def get_header_for_remote_access(self):
- return self._get_header_for_encrypted_request("AES128_CBC")
+ return self.get_headers_for_encrypted_request("AES128_CBC")
- def _get_header_for_encrypted_request(self, cypher):
+ @staticmethod
+ def get_headers_for_encrypted_request(cypher):
trans_cert_file = os.path.join(conf.get_lib_dir(), TRANSPORT_CERT_FILE_NAME)
try:
content = fileutil.read_file(trans_cert_file)
@@ -1154,12 +1151,15 @@ class WireClient(object):
raise ProtocolError("Failed to read {0}: {1}".format(trans_cert_file, e))
cert = get_bytes_from_pem(content)
- return {
+ headers = {
"x-ms-agent-name": "WALinuxAgent",
"x-ms-version": PROTOCOL_VERSION,
- "x-ms-cipher-name": cypher,
"x-ms-guest-agent-public-x509-cert": cert
}
+ if cypher is not None: # the cypher header is optional, currently defaults to AES128_CBC
+ headers["x-ms-cipher-name"] = cypher
+
+ return headers
def get_host_plugin(self):
if self._host_plugin is None:
diff --git a/azurelinuxagent/common/utils/cryptutil.py b/azurelinuxagent/common/utils/cryptutil.py
index 00126e25..789a9486 100644
--- a/azurelinuxagent/common/utils/cryptutil.py
+++ b/azurelinuxagent/common/utils/cryptutil.py
@@ -86,36 +86,22 @@ class CryptUtil(object):
thumbprint = thumbprint.rstrip().split('=')[1].replace(':', '').upper()
return thumbprint
- def decrypt_p7m(self, p7m_file, trans_prv_file, trans_cert_file, pem_file):
-
- def _cleanup_files(files_to_cleanup):
- for file_path in files_to_cleanup:
- if os.path.exists(file_path):
- try:
- os.remove(file_path)
- logger.info("Removed file {0}", file_path)
- except Exception as e:
- logger.error("Failed to remove file {0}: {1}", file_path, ustr(e))
-
- if not os.path.exists(p7m_file):
- raise IOError(errno.ENOENT, "File not found", p7m_file)
- elif not os.path.exists(trans_prv_file):
- raise IOError(errno.ENOENT, "File not found", trans_prv_file)
- else:
- try:
- shellutil.run_pipe([
- [self.openssl_cmd, "cms", "-decrypt", "-in", p7m_file, "-inkey", trans_prv_file, "-recip", trans_cert_file],
- [self.openssl_cmd, "pkcs12", "-nodes", "-password", "pass:", "-out", pem_file]])
- except shellutil.CommandError as command_error:
- logger.error("Failed to decrypt {0} (return code: {1})\n[stdout]\n{2}\n[stderr]\n{3}",
- p7m_file, command_error.returncode, command_error.stdout, command_error.stderr)
- # If the decryption fails, old version of openssl overwrite the output file(if exist) with empty data while
- # new version of openssl(3.2.2) does not overwrite the output file, So output file may contain old certs data.
- # Correcting the behavior by removing the temporary output files since having empty/no data is makes sense when decryption fails
- # otherwise we end up processing old certs again.
- files_to_remove = [p7m_file, pem_file]
- logger.info("Removing temporary state certificate files {0}", files_to_remove)
- _cleanup_files(files_to_remove)
+ def decrypt_certificates_p7m(self, p7m_file, trans_prv_file, trans_cert_file, pfx_file):
+ umask = None
+ try:
+ umask = os.umask(0o077)
+ with open(pfx_file, "wb") as pfx_file_:
+ shellutil.run_command([self.openssl_cmd, "cms", "-decrypt", "-in", p7m_file, "-inkey", trans_prv_file, "-recip", trans_cert_file], stdout=pfx_file_)
+ finally:
+ if umask is not None:
+ os.umask(umask)
+
+ def convert_pfx_to_pem(self, pfx_file, nomacver, pem_file):
+ command = [self.openssl_cmd, "pkcs12", "-nodes", "-password", "pass:", "-in", pfx_file, "-out", pem_file]
+ if nomacver:
+ command.append("-nomacver")
+
+ shellutil.run_command(command)
def crt_to_ssh(self, input_file, output_file):
with open(output_file, "ab") as file_out:
diff --git a/azurelinuxagent/ga/update.py b/azurelinuxagent/ga/update.py
index 7ab19101..f806ff26 100644
--- a/azurelinuxagent/ga/update.py
+++ b/azurelinuxagent/ga/update.py
@@ -85,6 +85,7 @@ READONLY_FILE_GLOBS = [
"*.p7m",
"*.pem",
"*.prv",
+ "Certificates.xml",
"ovf-env.xml"
]
diff --git a/tests/common/protocol/test_goal_state.py b/tests/common/protocol/test_goal_state.py
index a5f89587..9b70ce05 100644
--- a/tests/common/protocol/test_goal_state.py
+++ b/tests/common/protocol/test_goal_state.py
@@ -6,6 +6,7 @@ import datetime
import glob
import os
import re
+import subprocess
import shutil
import time
@@ -492,9 +493,85 @@ class GoalStateTestCase(AgentTestCase, HttpRequestPredicates):
goal_state = GoalState(protocol.client)
- self.assertEqual(0, len(goal_state.certs.summary), "Cert list should be empty")
- self.assertEqual(1, http_get_handler.certificate_requests, "There should have been exactly 1 requests for the goal state certificates")
+ self.assertEqual(0, len(goal_state.certs.summary), "Certificates should be empty")
+ self.assertEqual(2, http_get_handler.certificate_requests, "There should have been exactly 2 requests for the goal state certificates") # 1 for the initial request, 1 for the retry with an older cypher
+ def test_goal_state_should_try_legacy_cypher_and_then_fail_when_no_cyphers_are_supported_by_the_wireserver(self):
+ cyphers = []
+ def http_get_handler(url, *_, **kwargs):
+ if HttpRequestPredicates.is_certificates_request(url):
+ cypher = kwargs["headers"].get("x-ms-cipher-name")
+ if cypher is None:
+ raise Exception("x-ms-cipher-name header is missing from the Certificates request")
+ cyphers.append(cypher)
+ return MockHttpResponse(status=400, body="unsupported cypher: {0}".format(cypher).encode('utf-8'))
+ return None
+
+ with mock_wire_protocol(wire_protocol_data.DATA_FILE) as protocol:
+ with patch("azurelinuxagent.common.event.LogEvent.error") as log_error_patch:
+ protocol.set_http_handlers(http_get_handler=http_get_handler)
+ goal_state = GoalState(protocol.client)
+
+ log_error_args, _ = log_error_patch.call_args
+
+ self.assertEqual(cyphers, ["AES128_CBC", "DES_EDE3_CBC"], "There should have been 2 requests for the goal state certificates (AES128_CBC and DES_EDE3_CBC)")
+ self.assertEqual(log_error_args[0], "GoalStateCertificates", "An error fetching the goal state Certificates should have been reported")
+ self.assertEqual(0, len(goal_state.certs.summary), "Certificates should be empty")
+ self.assertFalse(os.path.exists(os.path.join(conf.get_lib_dir(), "Certificates.pfx")), "The Certificates.pfx file should not have been created")
+
+ def test_goal_state_should_try_legacy_cypher_and_then_fail_when_no_cyphers_are_supported_by_openssl(self):
+ cyphers = []
+ def http_get_handler(url, *_, **kwargs):
+ if HttpRequestPredicates.is_certificates_request(url):
+ cyphers.append(kwargs["headers"].get("x-ms-cipher-name"))
+ return None
+
+ original_popen = subprocess.Popen
+ openssl = conf.get_openssl_cmd()
+ decrypt_calls = []
+ def mock_fail_popen(command, *args, **kwargs):
+ if len(command) > 3 and command[0:3] == [openssl, "cms", "-decrypt"]:
+ decrypt_calls.append(command)
+ command[1] = "fake_openssl_command" # force an error on the openssl to simulate a decryption failure
+ return original_popen(command, *args, **kwargs)
+
+ with mock_wire_protocol(wire_protocol_data.DATA_FILE) as protocol:
+ protocol.set_http_handlers(http_get_handler=http_get_handler)
+ with patch("azurelinuxagent.common.event.LogEvent.error") as log_error_patch:
+ with patch("azurelinuxagent.ga.cgroupapi.subprocess.Popen", mock_fail_popen):
+ goal_state = GoalState(protocol.client)
+
+ log_error_args, _ = log_error_patch.call_args
+
+ self.assertEqual(cyphers, ["AES128_CBC", "DES_EDE3_CBC"], "There should have been 2 requests for the goal state certificates (AES128_CBC and DES_EDE3_CBC)")
+ self.assertEqual(2, len(decrypt_calls), "There should have been 2 calls to 'openssl cms -decrypt'")
+ self.assertEqual(log_error_args[0], "GoalStateCertificates", "An error fetching the goal state Certificates should have been reported")
+ self.assertEqual(0, len(goal_state.certs.summary), "Certificates should be empty")
+ self.assertFalse(os.path.exists(os.path.join(conf.get_lib_dir(), "Certificates.pfx")), "The Certificates.pfx file should not have been created")
+
+ def test_goal_state_should_try_without_and_with_mac_verification_then_fail_when_the_pfx_cannot_be_converted(self):
+ original_popen = subprocess.Popen
+ openssl = conf.get_openssl_cmd()
+ nomacver = []
+
+ def mock_fail_popen(command, *args, **kwargs):
+ if len(command) > 2 and command[0] == openssl and command[1] == "pkcs12":
+ nomacver.append("-nomacver" in command)
+ # force an error on the openssl to simulate the conversion failure
+ command[1] = "fake_openssl_command"
+ return original_popen(command, *args, **kwargs)
+
+
+ with mock_wire_protocol(wire_protocol_data.DATA_FILE) as protocol:
+ with patch("azurelinuxagent.common.event.LogEvent.error") as log_error_patch:
+ with patch("azurelinuxagent.ga.cgroupapi.subprocess.Popen", mock_fail_popen):
+ goal_state = GoalState(protocol.client)
+
+ log_error_args, _ = log_error_patch.call_args
+
+ self.assertEqual(nomacver, [True, False], "There should have been 2 attempts to parse the PFX (with and without -nomacver)")
+ self.assertEqual(log_error_args[0], "GoalStateCertificates", "An error fetching the goal state Certificates should have been reported")
+ self.assertEqual(0, len(goal_state.certs.summary), "Certificates should be empty")
def test_it_should_raise_when_goal_state_properties_not_initialized(self):
with GoalStateTestCase._create_protocol_ws_and_hgap_in_sync() as protocol:
diff --git a/tests/common/protocol/test_hostplugin.py b/tests/common/protocol/test_hostplugin.py
index 4c97c73f..7d94139b 100644
--- a/tests/common/protocol/test_hostplugin.py
+++ b/tests/common/protocol/test_hostplugin.py
@@ -365,8 +365,7 @@ class TestHostPlugin(HttpRequestPredicates, AgentTestCase):
# ensure host plugin is not set as default
self.assertFalse(wire.HostPluginProtocol.is_default_channel)
- @patch("azurelinuxagent.common.event.add_event")
- def test_put_status_error_reporting(self, patch_add_event):
+ def test_put_status_error_reporting(self):
"""
Validate the telemetry when uploading status fails
"""
@@ -377,22 +376,22 @@ class TestHostPlugin(HttpRequestPredicates, AgentTestCase):
put_error = wire.HttpError("put status http error")
with patch.object(restutil, "http_put", side_effect=put_error):
- with patch.object(wire.HostPluginProtocol,
- "ensure_initialized", return_value=True):
- self.assertRaises(wire.ProtocolError, wire_protocol_client.upload_status_blob)
-
- # The agent tries to upload via HostPlugin and that fails due to
- # http_put having a side effect of "put_error"
- #
- # The agent tries to upload using a direct connection, and that succeeds.
- self.assertEqual(1, wire_protocol_client.status_blob.upload.call_count) # pylint: disable=no-member
- # The agent never touches the default protocol is this code path, so no change.
- self.assertFalse(wire.HostPluginProtocol.is_default_channel)
- # The agent never logs telemetry event for direct fallback
- self.assertEqual(1, patch_add_event.call_count)
- self.assertEqual('ReportStatus', patch_add_event.call_args[1]['op'])
- self.assertTrue('Falling back to direct' in patch_add_event.call_args[1]['message'])
- self.assertEqual(True, patch_add_event.call_args[1]['is_success'])
+ with patch.object(wire.HostPluginProtocol, "ensure_initialized", return_value=True):
+ with patch("azurelinuxagent.common.event.add_event") as patch_add_event:
+ self.assertRaises(wire.ProtocolError, wire_protocol_client.upload_status_blob)
+
+ # The agent tries to upload via HostPlugin and that fails due to
+ # http_put having a side effect of "put_error"
+ #
+ # The agent tries to upload using a direct connection, and that succeeds.
+ self.assertEqual(1, wire_protocol_client.status_blob.upload.call_count) # pylint: disable=no-member
+ # The agent never touches the default protocol is this code path, so no change.
+ self.assertFalse(wire.HostPluginProtocol.is_default_channel)
+ # The agent never logs telemetry event for direct fallback
+ self.assertEqual(1, patch_add_event.call_count)
+ self.assertEqual('ReportStatus', patch_add_event.call_args[1]['op'])
+ self.assertTrue('Falling back to direct' in patch_add_event.call_args[1]['message'])
+ self.assertEqual(True, patch_add_event.call_args[1]['is_success'])
def test_validate_http_request_when_uploading_status(self):
"""Validate correct set of data is sent to HostGAPlugin when reporting VM status"""
diff --git a/tests/common/protocol/test_wire.py b/tests/common/protocol/test_wire.py
index c3dc9461..bec4634f 100644
--- a/tests/common/protocol/test_wire.py
+++ b/tests/common/protocol/test_wire.py
@@ -497,12 +497,6 @@ class TestWireProtocol(AgentTestCase, HttpRequestPredicates):
client.report_event(self._get_telemetry_events_generator(event_list), flush=True)
self.assertEqual(mock_http_request.call_count, 3)
- def test_get_header_for_cert_should_use_triple_des(self, *_):
- with mock_wire_protocol(wire_protocol_data.DATA_FILE) as protocol:
- headers = protocol.client.get_header_for_cert()
- self.assertIn("x-ms-cipher-name", headers)
- self.assertEqual(headers["x-ms-cipher-name"], "DES_EDE3_CBC", "Unexpected x-ms-cipher-name")
-
def test_get_header_for_remote_access_should_use_aes128(self, *_):
with mock_wire_protocol(wire_protocol_data.DATA_FILE) as protocol:
headers = protocol.client.get_header_for_remote_access()
@@ -1096,7 +1090,7 @@ class UpdateGoalStateTestCase(HttpRequestPredicates, AgentTestCase):
self.assertEqual(protocol.client.get_hosting_env().deployment_name, new_hosting_env_deployment_name)
self.assertEqual(protocol.client.get_shared_conf().xml_text, new_shared_conf)
self.assertEqual(sequence_number, new_sequence_number)
- self.assertEqual(len(protocol.client.get_certs().cert_list.certificates), 0)
+ self.assertEqual(len(protocol.client.get_certs().summary), 0)
self.assertEqual(protocol.client.get_host_plugin().container_id, new_container_id)
self.assertEqual(protocol.client.get_host_plugin().role_config_name, new_role_config_name)
diff --git a/tests/ga/test_update.py b/tests/ga/test_update.py
index 167e69dc..376e9fc0 100644
--- a/tests/ga/test_update.py
+++ b/tests/ga/test_update.py
@@ -2059,7 +2059,7 @@ class TryUpdateGoalStateTestCase(HttpRequestPredicates, AgentTestCase):
# Double check the certificates are correct
goal_state = protocol.get_goal_state()
- thumbprints = [c.thumbprint for c in goal_state.certs.cert_list.certificates]
+ thumbprints = [c["thumbprint"] for c in goal_state.certs.summary]
for extension in goal_state.extensions_goal_state.extensions:
for settings in extension.settings:
--
2.51.1

View File

@ -2,52 +2,54 @@
%global dracut_modname_udev 97walinuxagent %global dracut_modname_udev 97walinuxagent
%global dracut_modname_cvm 97walinuxagentcvm %global dracut_modname_cvm 97walinuxagentcvm
Name: WALinuxAgent Name: WALinuxAgent
Version: 2.13.1.1 Version: 2.14.0.1
Release: 3.0.1%{?dist}.2 Release: 4%{?dist}
Summary: The Microsoft Azure Linux Agent Summary: The Microsoft Azure Linux Agent
License: ASL 2.0 License: ASL 2.0
URL: https://github.com/Azure/%{name} URL: https://github.com/Azure/%{name}
Source0: https://github.com/Azure/%{name}/archive/v%{version}.tar.gz Source0: https://github.com/Azure/%{name}/archive/v%{version}.tar.gz
Source1: module-setup-udev.sh Source1: module-setup-udev.sh
Source2: module-setup-cvm.sh Source2: module-setup-cvm.sh
Source3: 90-tpm2-import.rules Source3: 90-tpm2-import.rules
Source4: tpm2-luks-import.sh Source4: tpm2-luks-import.sh
Patch1: 0001-redhat-Use-NetworkManager-to-set-DHCP-hostnames-on-r.patch
Patch2: 0002-Disable-automatic-log-collector.patch
Patch3: 0003-redhat-Add-a-udev-rule-to-avoid-managing-slave-NICs-.patch
Patch4: 0004-docs-add-waagent-manpage-3401.patch
Patch5: 0005-Use-systemctl-instead-of-service-to-manager-services.patch
# For RHEL-124218 - [Azure][WALA][RHEL-9] Remove 10-azure-unmanaged-sriov.rules
Patch6: wla-Remove-10-azure-unmanaged-sriov.rules.patch
# For RHEL-133507 - Backport ConditionVirtualization=|microsoft for waagent in RHEL 9.x
Patch7: wla-Jira-https-issues.redhat.com-browse-RHEL-133507.patch
# For RHEL-82232 - [Azure][image mode][WALA][RHEL-9] Unable to setup the persistent firewall rules
Patch8: wla-Change-redhat-waagent-network-setup.service-path-to-.patch
# For bz#2114830 - [Azure][WALA][RHEL-9.1] Provisioning failed if no ifcfg-eth0 # For bz#2114830 - [Azure][WALA][RHEL-9.1] Provisioning failed if no ifcfg-eth0
Patch0001: wla-redhat-Use-NetworkManager-to-set-DHCP-hostnames-on-r.patch
# For RHEL-7273 - [Azure][WALA] Consider to disable Log collector # For RHEL-7273 - [Azure][WALA] Consider to disable Log collector
Patch0002: wla-Disable-automatic-log-collector.patch
# For RHEL-5880 - [Azure][RHEL-9]68-azure-sriov-nm-unmanaged.rules cannot stop NetworkManager-wait-online.service checking SRIOV interface # For RHEL-5880 - [Azure][RHEL-9]68-azure-sriov-nm-unmanaged.rules cannot stop NetworkManager-wait-online.service checking SRIOV interface
Patch0003: wla-redhat-Add-a-udev-rule-to-avoid-managing-slave-NICs-.patch
# For RHEL-109496 - [Azure][WALA][RHEL-9] Missing man page # For RHEL-109496 - [Azure][WALA][RHEL-9] Missing man page
Patch4: wla-docs-add-waagent-manpage-3401.patch
# For RHEL-97572 - [Azure][RHEL-9][WALA][Image mode] Cannot find 'service' command # For RHEL-97572 - [Azure][RHEL-9][WALA][Image mode] Cannot find 'service' command
Patch5: wla-Use-systemctl-instead-of-service-to-manager-services.patch
# For RHEL-124949 - Update walagent to 2.14 to support FIPS 140-3 on Azure [rhel-9.7.z]
Patch6: wla-Support-for-FIPS-140-3-3324.patch
# For RHEL-134939 - Backport ConditionVirtualization=|microsoft for waagent in RHEL 9.x [rhel-9.7.z]
Patch7: wla-Jira-https-issues.redhat.com-browse-RHEL-134939.patch
Patch1000: 0100-add-oracle-support.patch
BuildArch: noarch BuildArch: noarch
BuildRequires: python3-devel BuildRequires: python3-devel
BuildRequires: python3-setuptools BuildRequires: python3-setuptools
BuildRequires: python3-distro BuildRequires: python3-distro
Requires: %name-udev = %version-%release Requires: %name-udev = %version-%release
%if 0%{?fedora} %if 0%{?fedora}
Requires: ntfsprogs Requires: ntfsprogs
%endif %endif
Requires: openssh Requires: openssh
Requires: openssh-server Requires: openssh-server
Requires: openssl Requires: openssl
Requires: parted Requires: parted
Requires: python3-pyasn1 Requires: python3-pyasn1
Requires: iptables Requires: iptables
Requires: azure-vm-utils >= 0.7.0-1
BuildRequires: systemd BuildRequires: systemd
Requires(post): systemd Requires(post): systemd
Requires(preun): systemd Requires(preun): systemd
Requires(postun): systemd Requires(postun): systemd
@ -59,25 +61,25 @@ images that are built to run in the Microsoft Azure environment.
%if 0%{?with_legacy} %if 0%{?with_legacy}
%package legacy %package legacy
Summary: The Microsoft Azure Linux Agent (legacy) Summary: The Microsoft Azure Linux Agent (legacy)
Requires: %name = %version-%release Requires: %name = %version-%release
Requires: python2 Requires: python2
Requires: net-tools Requires: net-tools
%description legacy %description legacy
The Microsoft Azure Linux Agent supporting old version of extensions. The Microsoft Azure Linux Agent supporting old version of extensions.
%endif %endif
%package udev %package udev
Summary: Udev rules for Microsoft Azure Summary: Udev rules for Microsoft Azure
%description udev %description udev
Udev rules specific to Microsoft Azure Virtual Machines. Udev rules specific to Microsoft Azure Virtual Machines.
%package cvm %package cvm
Summary: Microsoft Azure CVM specific tools Summary: Microsoft Azure CVM specific tools
Requires: tpm2-tools Requires: tpm2-tools
Requires: cryptsetup Requires: cryptsetup
%description cvm %description cvm
Scripts and udev rules specific to Microsoft Azure Confidential Virtual Machines. Scripts and udev rules specific to Microsoft Azure Confidential Virtual Machines.
@ -147,7 +149,6 @@ rm -rf %{_unitdir}/waagent.service.d/
%{python3_sitelib}/*.egg-info %{python3_sitelib}/*.egg-info
%files udev %files udev
%{_udevrulesdir}/10-azure-unmanaged-sriov.rules
%{_udevrulesdir}/66-azure-storage.rules %{_udevrulesdir}/66-azure-storage.rules
%{_udevrulesdir}/99-azure-product-uuid.rules %{_udevrulesdir}/99-azure-product-uuid.rules
%dir %{_prefix}/lib/dracut/modules.d/%{dracut_modname_udev} %dir %{_prefix}/lib/dracut/modules.d/%{dracut_modname_udev}
@ -165,18 +166,25 @@ rm -rf %{_unitdir}/waagent.service.d/
%endif %endif
%changelog %changelog
* Tue Feb 17 2026 Darren Archibald <darren.archibald@oracle.com> - 2.13.1.1-3.0.1.el9_7.2 * Wed Jan 21 2026 Miroslav Rezanina <mrezanin@redhat.com> - 2.14.0.1-4
- Add oracle support to fix waagent.service build issue - wla-Change-redhat-waagent-network-setup.service-path-to-.patch [RHEL-82232]
- Resolves: RHEL-82232
([Azure][image mode][WALA][RHEL-9] Unable to setup the persistent firewall rules)
* Tue Jan 06 2026 Jon Maloy <jmaloy@redhat.com> - 2.13.1.1-3.el9_7.2 * Tue Jan 06 2026 Jon Maloy <jmaloy@redhat.com> - 2.14.0.1-3
- wla-Jira-https-issues.redhat.com-browse-RHEL-134939.patch [RHEL-134939] - wla-Jira-https-issues.redhat.com-browse-RHEL-133507.patch [RHEL-133507]
- Resolves: RHEL-134939 - Resolves: RHEL-133507
(Backport ConditionVirtualization=|microsoft for waagent in RHEL 9.x [rhel-9.7.z]) (Backport ConditionVirtualization=|microsoft for waagent in RHEL 9.x)
* Tue Dec 02 2025 Jon Maloy <jmaloy@redhat.com> - 2.13.1.1-3.el9_7.1 * Wed Nov 12 2025 Miroslav Rezanina <mrezanin@redhat.com> - 2.14.0.1-2
- wla-Support-for-FIPS-140-3-3324.patch [RHEL-124949] - wla-Remove-10-azure-unmanaged-sriov.rules.patch [RHEL-124218]
- Resolves: RHEL-124949 - Resolves: RHEL-124218
(Update walagent to 2.14 to support FIPS 140-3 on Azure [rhel-9.7.z]) ([Azure][WALA][RHEL-9] Remove 10-azure-unmanaged-sriov.rules)
* Thu Oct 30 2025 Yuxin Sun <yuxisun@redhat.com> - 2.14.0.1-1
- Rebase to 2.14.0.1 [RHEL-116436]
- Resolves: RHEL-116436
(Rebase to v2.14.0.1)
* Thu Aug 21 2025 Jon Maloy <jmaloy@redhat.com> - 2.13.1.1-3 * Thu Aug 21 2025 Jon Maloy <jmaloy@redhat.com> - 2.13.1.1-3
- wla-Use-systemctl-instead-of-service-to-manager-services.patch [RHEL-97572] - wla-Use-systemctl-instead-of-service-to-manager-services.patch [RHEL-97572]