sailesh1993 / rpms / cloud-init

Forked from rpms/cloud-init a year ago
Clone
c3f9da
From 94753da021d0849f4858e2c2cb98b3276842b665 Mon Sep 17 00:00:00 2001
c3f9da
From: Eduardo Otubo <otubo@redhat.com>
c3f9da
Date: Mon, 24 Aug 2020 15:34:24 +0200
c3f9da
Subject: [PATCH 1/5] DHCP sandboxing failing on noexec mounted /var/tmp (#521)
c3f9da
c3f9da
RH-Author: Eduardo Terrell Ferrari Otubo (eterrell)
c3f9da
RH-MergeRequest: 1: DHCP sandboxing failing on noexec mounted /var/tmp (#521)
c3f9da
RH-Commit: [1/1] 4971d742aa1de27dff61b07ef9d6d478c0889ded (eterrell/cloud-init)
c3f9da
RH-Bugzilla: 1879989
c3f9da
c3f9da
commit db86753f81af73826158c9522f2521f210300e2b
c3f9da
Author: Eduardo Otubo <otubo@redhat.com>
c3f9da
Date:   Mon Aug 24 15:34:24 2020 +0200
c3f9da
c3f9da
    DHCP sandboxing failing on noexec mounted /var/tmp (#521)
c3f9da
c3f9da
    * DHCP sandboxing failing on noexec mounted /var/tmp
c3f9da
c3f9da
    If /var/tmp is mounted with noexec option the DHCP sandboxing will fail
c3f9da
    with Permission Denied. This patch simply avoids this error by checking
c3f9da
    the exec permission updating the dhcp path in negative case.
c3f9da
c3f9da
    rhbz: https://bugzilla.redhat.com/show_bug.cgi?id=1879989
c3f9da
c3f9da
    Signed-off-by: Eduardo Otubo <otubo@redhat.com>
c3f9da
c3f9da
    * Replacing with os.* calls
c3f9da
c3f9da
    * Adding test and removing isfile() useless call.
c3f9da
c3f9da
    Co-authored-by: Rick Harding <rharding@mitechie.com>
c3f9da
c3f9da
Signed-off-by: Eduardo Otubo <otubo@redhat.com>
c3f9da
---
c3f9da
 cloudinit/net/dhcp.py            |  6 ++++++
c3f9da
 cloudinit/net/tests/test_dhcp.py | 46 ++++++++++++++++++++++++++++++++++++++++
c3f9da
 2 files changed, 52 insertions(+)
c3f9da
c3f9da
diff --git a/cloudinit/net/dhcp.py b/cloudinit/net/dhcp.py
c3f9da
index c033cc8..841e72e 100644
c3f9da
--- a/cloudinit/net/dhcp.py
c3f9da
+++ b/cloudinit/net/dhcp.py
c3f9da
@@ -215,6 +215,12 @@ def dhcp_discovery(dhclient_cmd_path, interface, cleandir):
c3f9da
     pid_file = os.path.join(cleandir, 'dhclient.pid')
c3f9da
     lease_file = os.path.join(cleandir, 'dhcp.leases')
c3f9da
 
c3f9da
+    # In some cases files in /var/tmp may not be executable, launching dhclient
c3f9da
+    # from there will certainly raise 'Permission denied' error. Try launching
c3f9da
+    # the original dhclient instead.
c3f9da
+    if not os.access(sandbox_dhclient_cmd, os.X_OK):
c3f9da
+        sandbox_dhclient_cmd = dhclient_cmd_path
c3f9da
+
c3f9da
     # ISC dhclient needs the interface up to send initial discovery packets.
c3f9da
     # Generally dhclient relies on dhclient-script PREINIT action to bring the
c3f9da
     # link up before attempting discovery. Since we are using -sf /bin/true,
c3f9da
diff --git a/cloudinit/net/tests/test_dhcp.py b/cloudinit/net/tests/test_dhcp.py
c3f9da
index c3fa1e0..08e2cfb 100644
c3f9da
--- a/cloudinit/net/tests/test_dhcp.py
c3f9da
+++ b/cloudinit/net/tests/test_dhcp.py
c3f9da
@@ -406,6 +406,52 @@ class TestDHCPDiscoveryClean(CiTestCase):
c3f9da
                  'eth9', '-sf', '/bin/true'], capture=True)])
c3f9da
         m_kill.assert_has_calls([mock.call(my_pid, signal.SIGKILL)])
c3f9da
 
c3f9da
+    @mock.patch('cloudinit.net.dhcp.util.get_proc_ppid')
c3f9da
+    @mock.patch('cloudinit.net.dhcp.os.kill')
c3f9da
+    @mock.patch('cloudinit.net.dhcp.subp.subp')
c3f9da
+    def test_dhcp_discovery_outside_sandbox(self, m_subp, m_kill, m_getppid):
c3f9da
+        """dhcp_discovery brings up the interface and runs dhclient.
c3f9da
+
c3f9da
+        It also returns the parsed dhcp.leases file generated in the sandbox.
c3f9da
+        """
c3f9da
+        m_subp.return_value = ('', '')
c3f9da
+        tmpdir = self.tmp_dir()
c3f9da
+        dhclient_script = os.path.join(tmpdir, 'dhclient.orig')
c3f9da
+        script_content = '#!/bin/bash\necho fake-dhclient'
c3f9da
+        write_file(dhclient_script, script_content, mode=0o755)
c3f9da
+        lease_content = dedent("""
c3f9da
+            lease {
c3f9da
+              interface "eth9";
c3f9da
+              fixed-address 192.168.2.74;
c3f9da
+              option subnet-mask 255.255.255.0;
c3f9da
+              option routers 192.168.2.1;
c3f9da
+            }
c3f9da
+        """)
c3f9da
+        lease_file = os.path.join(tmpdir, 'dhcp.leases')
c3f9da
+        write_file(lease_file, lease_content)
c3f9da
+        pid_file = os.path.join(tmpdir, 'dhclient.pid')
c3f9da
+        my_pid = 1
c3f9da
+        write_file(pid_file, "%d\n" % my_pid)
c3f9da
+        m_getppid.return_value = 1  # Indicate that dhclient has daemonized
c3f9da
+
c3f9da
+        with mock.patch('os.access', return_value=False):
c3f9da
+            self.assertCountEqual(
c3f9da
+                [{'interface': 'eth9', 'fixed-address': '192.168.2.74',
c3f9da
+                  'subnet-mask': '255.255.255.0', 'routers': '192.168.2.1'}],
c3f9da
+                dhcp_discovery(dhclient_script, 'eth9', tmpdir))
c3f9da
+        # dhclient script got copied
c3f9da
+        with open(os.path.join(tmpdir, 'dhclient.orig')) as stream:
c3f9da
+            self.assertEqual(script_content, stream.read())
c3f9da
+        # Interface was brought up before dhclient called from sandbox
c3f9da
+        m_subp.assert_has_calls([
c3f9da
+            mock.call(
c3f9da
+                ['ip', 'link', 'set', 'dev', 'eth9', 'up'], capture=True),
c3f9da
+            mock.call(
c3f9da
+                [os.path.join(tmpdir, 'dhclient.orig'), '-1', '-v', '-lf',
c3f9da
+                 lease_file, '-pf', os.path.join(tmpdir, 'dhclient.pid'),
c3f9da
+                 'eth9', '-sf', '/bin/true'], capture=True)])
c3f9da
+        m_kill.assert_has_calls([mock.call(my_pid, signal.SIGKILL)])
c3f9da
+
c3f9da
 
c3f9da
 class TestSystemdParseLeases(CiTestCase):
c3f9da
 
c3f9da
-- 
c3f9da
1.8.3.1
c3f9da