Repository: Azure/WALinuxAgent
Pull request #1165 (2018-05-09 21:17:02)

enable firewall drop


Patch:
diff --git a/azurelinuxagent/common/osutil/default.py b/azurelinuxagent/common/osutil/default.py
index 0e053a8f..0d637b54 100644
--- a/azurelinuxagent/common/osutil/default.py
+++ b/azurelinuxagent/common/osutil/default.py
@@ -60,15 +60,17 @@ FIREWALL_ACCEPT = "iptables {0} -t security -{1} OUTPUT -d {2} -p tcp -m owner -
 # Note:
 # -- Initially "flight" the change to ACCEPT packets and develop a metric baseline
 #    A subsequent release will convert the ACCEPT to DROP
-FIREWALL_DROP = "iptables {0} -t security -{1} OUTPUT -d {2} -p tcp -m conntrack --ctstate INVALID,NEW -j ACCEPT"
-# FIREWALL_DROP = "iptables {0} -t security -{1} OUTPUT -d {2} -p tcp -m conntrack --ctstate INVALID,NEW -j DROP"
+# FIREWALL_DROP = "iptables {0} -t security -{1} OUTPUT -d {2} -p tcp -m conntrack --ctstate INVALID,NEW -j ACCEPT"
+FIREWALL_DROP = "iptables {0} -t security -{1} OUTPUT -d {2} -p tcp -m conntrack --ctstate INVALID,NEW -j DROP"
 FIREWALL_LIST = "iptables {0} -t security -L -nxv"
 FIREWALL_PACKETS = "iptables {0} -t security -L OUTPUT --zero OUTPUT -nxv"
 FIREWALL_FLUSH = "iptables {0} -t security --flush"
 
 # Precisely delete the rules created by the agent.
-FIREWALL_DELETE_CONNTRACK = "iptables {0} -t security -D OUTPUT -d {1} -p tcp -m conntrack --ctstate INVALID,NEW -j ACCEPT"
-FIREWALL_DELETE_OWNER = "iptables {0} -t security -D OUTPUT -d {1} -p tcp -m owner --uid-owner {2} -j ACCEPT"
+# this rule was used <= 2.2.25.  This rule helped to validate our change, and determine impact.
+FIREWALL_DELETE_CONNTRACK_ACCEPT = "iptables {0} -t security -D OUTPUT -d {1} -p tcp -m conntrack --ctstate INVALID,NEW -j ACCEPT"
+FIREWALL_DELETE_OWNER_ACCEPT = "iptables {0} -t security -D OUTPUT -d {1} -p tcp -m owner --uid-owner {2} -j ACCEPT"
+FIREWALL_DELETE_CONNTRACK_DROP = "iptables {0} -t security -D OUTPUT -d {1} -p tcp -m conntrack --ctstate INVALID,NEW -j DROP"
 
 PACKET_PATTERN = "^\s*(\d+)\s+(\d+)\s+DROP\s+.*{0}[^\d]*$"
 
@@ -168,8 +170,12 @@ class DefaultOSUtil(object):
 
             wait = self.get_firewall_will_wait()
 
-            self._delete_rule(FIREWALL_DELETE_CONNTRACK.format(wait, dst_ip))
-            self._delete_rule(FIREWALL_DELETE_OWNER.format(wait, dst_ip, uid))
+            # This rule was <= 2.2.25 only, and may still exist on some VMs.  Until 2.2.25
+            # has aged out, keep this cleanup in place.
+            self._delete_rule(FIREWALL_DELETE_CONNTRACK_ACCEPT.format(wait, dst_ip))
+
+            self._delete_rule(FIREWALL_DELETE_OWNER_ACCEPT.format(wait, dst_ip, uid))
+            self._delete_rule(FIREWALL_DELETE_CONNTRACK_DROP.format(wait, dst_ip))
 
             return True
 
diff --git a/azurelinuxagent/ga/env.py b/azurelinuxagent/ga/env.py
index bd1f6d48..98f3db98 100644
--- a/azurelinuxagent/ga/env.py
+++ b/azurelinuxagent/ga/env.py
@@ -102,10 +102,21 @@ class EnvHandler(object):
         Purge unnecessary files from disk cache.
         """
         protocol = self.protocol_util.get_protocol()
+        reset_firewall_fules = False
         while not self.stopped:
             self.osutil.remove_rules_files()
 
             if conf.enable_firewall():
+
+                # If the rules ever change we must reset all rules and start over again.
+                #
+                # There was a rule change at 2.2.26, which started dropping non-root traffic
+                # to WireServer.  The previous rules allowed traffic.  Having both rules in
+                # place negated the fix in 2.2.26.
+                if not reset_firewall_fules:
+                    self.osutil.remove_firewall(dst_ip=protocol.endpoint, uid=os.getuid())
+                    reset_firewall_fules = True
+
                 success = self.osutil.enable_firewall(
                                 dst_ip=protocol.endpoint,
                                 uid=os.getuid())


Test patch:
diff --git a/tests/common/osutil/test_default.py b/tests/common/osutil/test_default.py
index e33f4d88..cbd01ee0 100644
--- a/tests/common/osutil/test_default.py
+++ b/tests/common/osutil/test_default.py
@@ -738,15 +738,20 @@ Chain OUTPUT (policy ACCEPT 104 packets, 43628 bytes)
         version = "iptables v{0}".format(osutil.IPTABLES_LOCKING_VERSION)
         wait = "-w"
 
-        mock_run.side_effect = [0, 1, 0, 1]
+        mock_run.side_effect = [0, 1, 0, 1, 0, 1]
         mock_output.side_effect = [(0, version), (0, "Output")]
         self.assertTrue(util.remove_firewall(dst, uid))
 
         mock_run.assert_has_calls([
-            call(osutil.FIREWALL_DELETE_CONNTRACK.format(wait, dst), chk_err=False),
-            call(osutil.FIREWALL_DELETE_CONNTRACK.format(wait, dst), chk_err=False),
-            call(osutil.FIREWALL_DELETE_OWNER.format(wait, dst, uid), chk_err=False),
-            call(osutil.FIREWALL_DELETE_OWNER.format(wait, dst, uid), chk_err=False),
+            # delete rules < 2.2.26
+            call(osutil.FIREWALL_DELETE_CONNTRACK_ACCEPT.format(wait, dst), chk_err=False),
+            call(osutil.FIREWALL_DELETE_CONNTRACK_ACCEPT.format(wait, dst), chk_err=False),
+            call(osutil.FIREWALL_DELETE_OWNER_ACCEPT.format(wait, dst, uid), chk_err=False),
+            call(osutil.FIREWALL_DELETE_OWNER_ACCEPT.format(wait, dst, uid), chk_err=False),
+
+            # delete rules >= 2.2.26
+            call(osutil.FIREWALL_DELETE_CONNTRACK_DROP.format(wait, dst), chk_err=False),
+            call(osutil.FIREWALL_DELETE_CONNTRACK_DROP.format(wait, dst), chk_err=False),
         ])
         mock_output.assert_has_calls([
             call(osutil.IPTABLES_VERSION)
@@ -770,7 +775,7 @@ Chain OUTPUT (policy ACCEPT 104 packets, 43628 bytes)
         self.assertFalse(util.remove_firewall(dst_ip, uid))
 
         mock_run.assert_has_calls([
-            call(osutil.FIREWALL_DELETE_CONNTRACK.format(wait, dst_ip), chk_err=False),
+            call(osutil.FIREWALL_DELETE_CONNTRACK_ACCEPT.format(wait, dst_ip), chk_err=False),
         ])
         mock_output.assert_has_calls([
             call(osutil.IPTABLES_VERSION)