Bug 1857309

Summary: [Azure][RHEL 8] cloud-init Permission denied with the use of mount option noexec
Product: Red Hat Enterprise Linux 8 Reporter: schandle
Component: cloud-initAssignee: Eduardo Otubo <eterrell>
Status: CLOSED ERRATA QA Contact: xiachen
Severity: high Docs Contact:
Priority: urgent    
Version: 8.2CC: eterrell, gallwasingeborg, huzhao, jgreguske, mrezanin, mtessun, pdwyer, ribarry, vkuznets, xiachen, xialiu, xiliang, yacao, yuxisun
Target Milestone: rcKeywords: ZStream
Target Release: 8.0Flags: pm-rhel: mirror+
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: cloud-init-20.3-1.el8 Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of:
: 1871914 1871915 1871916 1879989 1879990 (view as bug list) Environment:
Last Closed: 2021-05-18 15:44:14 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On:    
Bug Blocks: 1871914, 1871915, 1871916, 1879989, 1879990    

Description schandle 2020-07-15 16:17:13 UTC
Description of problem:
VM mounts /var/tmp with noexec option.  After, there are additional modifications to the network that are copied to /var/tmp/ to resolve the dhcp issues.  This is causing permission denied. 

Version-Release number of selected component (if applicable):
Red Hat Enterprise Linux 8.2
cloud-init-18.5-12.el8_2.2.noarch

How reproducible:
100% 

Steps to Reproduce:
1. Create VM on prem
2. mount flag 'noexec' for /tmp and /var/tmp 


Actual results:
~~~
2020-07-15 09:39:29,905 - util.py[DEBUG]: Copying /usr/sbin/dhclient to /var/tmp/cloud-init/cloud-init-dhcp-bn4ikkcb/dhclient
2020-07-15 09:39:29,979 - util.py[DEBUG]: Running command ['ip', 'link', 'set', 'dev', 'eth0', 'up'] with allowed return codes [0] (shell=False, capture=True)
2020-07-15 09:39:30,259 - util.py[DEBUG]: Running command ['/var/tmp/cloud-init/cloud-init-dhcp-bn4ikkcb/dhclient', '-1', '-v', '-lf', '/var/tmp/cloud-init/cloud-init-dhcp-bn4ikkcb/dhcp.leases', '-pf', '/var/tmp/cloud-init/cloud-init-dhcp-bn4ikkcb/dhclient.pid', 'eth0', '-sf', '/bin/true'] with allowed return codes [0] (shell=False, capture=True)
2020-07-15 09:39:30,261 - handlers.py[DEBUG]: finish: azure-ds/get_metadata_from_imds: FAIL: get_metadata_from_imds
2020-07-15 09:39:30,262 - handlers.py[DEBUG]: finish: azure-ds/crawl_metadata: FAIL: crawl_metadata
2020-07-15 09:39:30,262 - util.py[DEBUG]: Crawl of metadata service took 0.858 seconds
2020-07-15 09:39:30,262 - handlers.py[DEBUG]: finish: azure-ds/_get_data: FAIL: _get_data
2020-07-15 09:39:30,262 - handlers.py[DEBUG]: finish: init-local/search-Azure: FAIL: no local data found from DataSourceAzure
2020-07-15 09:39:30,262 - util.py[WARNING]: Getting data from <class 'cloudinit.sources.DataSourceAzure.DataSourceAzure'> failed
2020-07-15 09:39:30,272 - util.py[DEBUG]: Getting data from <class 'cloudinit.sources.DataSourceAzure.DataSourceAzure'> failed
Traceback (most recent call last):
  File "/usr/lib/python3.6/site-packages/cloudinit/util.py", line 2056, in subp
    env=env, shell=shell)
  File "/usr/lib64/python3.6/subprocess.py", line 729, in __init__
    restore_signals, start_new_session)
  File "/usr/lib64/python3.6/subprocess.py", line 1364, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
PermissionError: [Errno 13] Permission denied: b'/var/tmp/cloud-init/cloud-init-dhcp-bn4ikkcb/dhclient'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.6/site-packages/cloudinit/sources/__init__.py", line 733, in find_source
    if s.update_metadata([EventType.BOOT_NEW_INSTANCE]):
  File "/usr/lib/python3.6/site-packages/cloudinit/sources/__init__.py", line 622, in update_metadata
    result = self.get_data()
  File "/usr/lib/python3.6/site-packages/cloudinit/sources/__init__.py", line 256, in get_data
    return_value = self._get_data()
  File "/usr/lib/python3.6/site-packages/cloudinit/sources/helpers/azure.py", line 40, in impl
    return func(*args, **kwargs)
  File "/usr/lib/python3.6/site-packages/cloudinit/sources/DataSourceAzure.py", line 498, in _get_data
    func=self.crawl_metadata)
  File "/usr/lib/python3.6/site-packages/cloudinit/util.py", line 2543, in log_time
    ret = func(*args, **kwargs)
  File "/usr/lib/python3.6/site-packages/cloudinit/sources/helpers/azure.py", line 40, in impl
    return func(*args, **kwargs)
  File "/usr/lib/python3.6/site-packages/cloudinit/sources/DataSourceAzure.py", line 439, in crawl_metadata
    self.fallback_interface, retries=3)
  File "/usr/lib/python3.6/site-packages/cloudinit/sources/helpers/azure.py", line 40, in impl
    return func(*args, **kwargs)
  File "/usr/lib/python3.6/site-packages/cloudinit/sources/DataSourceAzure.py", line 1307, in get_metadata_from_imds
    with EphemeralDHCPv4(fallback_nic):
  File "/usr/lib/python3.6/site-packages/cloudinit/net/dhcp.py", line 57, in __enter__
    return self.obtain_lease()
  File "/usr/lib/python3.6/site-packages/cloudinit/net/dhcp.py", line 83, in obtain_lease
    leases = maybe_perform_dhcp_discovery(self.iface)
  File "/usr/lib/python3.6/site-packages/cloudinit/net/dhcp.py", line 135, in maybe_perform_dhcp_discovery
    return dhcp_discovery(dhclient_path, nic, tdir)
  File "/usr/lib/python3.6/site-packages/cloudinit/net/dhcp.py", line 199, in dhcp_discovery
    util.subp(cmd, capture=True)
  File "/usr/lib/python3.6/site-packages/cloudinit/util.py", line 2064, in subp
    stderr="-" if decode else b"-")
cloudinit.util.ProcessExecutionError: Unexpected error while running command.
Command: ['/var/tmp/cloud-init/cloud-init-dhcp-bn4ikkcb/dhclient', '-1', '-v', '-lf', '/var/tmp/cloud-init/cloud-init-dhcp-bn4ikkcb/dhcp.leases', '-pf', '/var/tmp/cloud-init/cloud-init-dhcp-bn4ikkcb/dhclient.pid', 'eth0', '-sf', '/bin/true']
Exit code: -
Reason: [Errno 13] Permission denied: b'/var/tmp/cloud-init/cloud-init-dhcp-bn4ikkcb/dhclient'
~~~

Expected results:
To be able to adhering to CIS standards, with the mount option noexec 

Additional info:

Comment 2 Vitaly Kuznetsov 2020-07-16 17:04:17 UTC
The feature is called 'dhclient sandboxing' and, among other things,
cloud-init copies /usr/sbin/dhclient to /var/tmp/ and tries to execute
it from there. With /var/tmp mounted read-only this is a no-go.

The first question which comes to mind is why do we need to copy
/usr/sbin/dhclient in the first place. Changelog tells us the following:

+    # XXX We copy dhclient out of /sbin/dhclient to avoid dealing with strict
+    # app armor profiles which disallow running dhclient -sf <our-script-file>.

and this is definitely not needed for RHEL.

How do we fix this? I see two options:
1) Check that what we've copied to /var/tmp is executable. Completely untested
patch:

diff --git a/cloudinit/net/dhcp.py b/cloudinit/net/dhcp.py
index c033cc8e0a03..539b1f842120 100644
--- a/cloudinit/net/dhcp.py
+++ b/cloudinit/net/dhcp.py
@@ -215,6 +215,12 @@ def dhcp_discovery(dhclient_cmd_path, interface, cleandir):
     pid_file = os.path.join(cleandir, 'dhclient.pid')
     lease_file = os.path.join(cleandir, 'dhcp.leases')
 
+    # In some cases files in /var/tmp may not be executable, launching dhclient
+    # from there will certainly raise 'Permission denied' error. Try launching
+    # the original dhclient instead.
+    if not util.is_exe(sandbox_dhclient_cmd):
+        sandbox_dhclient_cmd = dhclient_cmd_path
+
     # ISC dhclient needs the interface up to send initial discovery packets.
     # Generally dhclient relies on dhclient-script PREINIT action to bring the
     # link up before attempting discovery. Since we are using -sf /bin/true,

2) Make 'sandboxing' (the dhclient copying part to be precise) a configuration
setting and disable it for RHEL.

Personally, I think the right solution would be to kill the copying completely
but I'm leaving this up to upstream.

Here is a completely untested brew build with 1) implemented:
https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=30155195

It would be great if someone could give it a try.

Comment 10 Eduardo Otubo 2020-08-06 07:30:08 UTC
Upstream pull-request:
https://github.com/canonical/cloud-init/pull/521

Comment 31 xiachen 2020-12-07 08:40:12 UTC
Verified the fix with cloud-init-20.3-5.el8 on Azure, PASS.
Move to VERIFIED.

Comment 32 xiachen 2020-12-07 08:41:56 UTC
Tested with RHEL8.4 nightly

Comment 34 errata-xmlrpc 2021-05-18 15:44:14 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory (cloud-init bug fix and enhancement update), and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

https://access.redhat.com/errata/RHEA-2021:1827

Comment 35 Jorge J. Brown 2022-12-31 06:23:17 UTC Comment hidden (spam)