diff --git a/azurelinuxagent/common/osutil/default.py b/azurelinuxagent/common/osutil/default.py index a8830ff3f..d9f945694 100644 --- a/azurelinuxagent/common/osutil/default.py +++ b/azurelinuxagent/common/osutil/default.py @@ -549,6 +549,13 @@ def is_atapiix_mod_loaded(self, max_retry=1): time.sleep(1) return False + def is_mod_available(self, mod_name): + """ + Checks if the given module is available. + """ + ret = shellutil.run("modinfo {0}".format(mod_name), chk_err=False) + return ret == 0 + def mount(self, device, mount_point, option=None, chk_err=True): if not option: option = [] diff --git a/azurelinuxagent/ga/firewall_manager.py b/azurelinuxagent/ga/firewall_manager.py index a75cf4a8e..7250c8874 100644 --- a/azurelinuxagent/ga/firewall_manager.py +++ b/azurelinuxagent/ga/firewall_manager.py @@ -24,6 +24,7 @@ from azurelinuxagent.common import event from azurelinuxagent.common.event import WALAEventOperation from azurelinuxagent.common.utils import shellutil +from azurelinuxagent.common.osutil import get_osutil from azurelinuxagent.common.future import ustr from azurelinuxagent.common.utils.flexible_version import FlexibleVersion @@ -77,6 +78,7 @@ class FirewallManager(object): def __init__(self, wire_server_address): self._wire_server_address = wire_server_address self._verbose = False + self._osutil = get_osutil() # Friendly names for the firewall rules ACCEPT_DNS = "ACCEPT DNS" @@ -308,6 +310,8 @@ def __init__(self, wire_server_address): # # Get the version of iptables and check whether we can use the wait option ("-w"), which was introduced in iptables 1.4.21. # + if not self._osutil.is_mod_available("xt_owner") or not self._osutil.is_mod_available("xt_conntrack"): + raise FirewallManagerNotAvailableError("iptables is not available") try: output = shellutil.run_command(["iptables", "--version"]) # @@ -446,6 +450,14 @@ class FirewallCmd(_FirewallManagerIndividualRules): def __init__(self, wire_server_address): super(FirewallCmd, self).__init__(wire_server_address) + # + # Check for required kernel modules. FirewallCmd uses iptables passthrough rules that require + # xt_owner (for the ACCEPT rule with -m owner) and xt_conntrack (for the DROP rule with -m conntrack). + # If these modules are not available, the firewall-cmd rules will fail. + # + if not self._osutil.is_mod_available("xt_owner") or not self._osutil.is_mod_available("xt_conntrack"): + raise FirewallManagerNotAvailableError("Required kernel modules (xt_owner, xt_conntrack) are not available for firewall-cmd") + try: self._version = shellutil.run_command(["firewall-cmd", "--version"]).strip() except Exception as exception: diff --git a/azurelinuxagent/ga/persist_firewall_rules.py b/azurelinuxagent/ga/persist_firewall_rules.py index 2fd35e665..50397e671 100644 --- a/azurelinuxagent/ga/persist_firewall_rules.py +++ b/azurelinuxagent/ga/persist_firewall_rules.py @@ -22,7 +22,7 @@ from azurelinuxagent.common import logger from azurelinuxagent.common import event from azurelinuxagent.common.event import add_event, WALAEventOperation -from azurelinuxagent.ga.firewall_manager import FirewallCmd, FirewallStateError +from azurelinuxagent.ga.firewall_manager import FirewallCmd, FirewallStateError, FirewallManagerNotAvailableError from azurelinuxagent.common.future import ustr from azurelinuxagent.common.osutil import get_osutil, systemd from azurelinuxagent.common.utils import shellutil, fileutil, textutil @@ -132,7 +132,15 @@ def setup(self): # In case of a failure, this would throw. In such a case, we don't need to try to setup our custom service # because on system reboot, all iptables rules are reset by firewalld.service, so it would be a no-op. # setup permanent firewalld rules - firewall_manager = FirewallCmd(self._dst_ip) + try: + firewall_manager = FirewallCmd(self._dst_ip) + except FirewallManagerNotAvailableError as e: + # If required kernel modules (xt_owner, xt_conntrack) are not available, firewall-cmd cannot set up + # the required rules. Fall back to the custom network-setup service which may handle this better. + event.warn(WALAEventOperation.PersistFirewallRules, "Cannot use firewall-cmd for persistent rules: {0}. Falling back to custom network-setup service.", ustr(e)) + self._setup_network_setup_service() + return + event.info(WALAEventOperation.Firewall, "Using firewall-cmd [version {0}] to manage the persistent firewall rules", firewall_manager.version) try: