Add selinux context manager for writing files

Glean can be responsible for creation of a few sensitive files, so
make sure we restore the selinux context on any written files.

I have tested this with selinux enabled and disabled; the restorecon
should be a safe operation even if selinux is disabled and will ensure
the right context if you ever re-enable selinux.

Tested with centos-minimal builds and the kolla job that was
previously hitting problems with the /etc/hostname file context.

Change-Id: Ia2eba157e35b1e1b0bcdb830f98061692a198d42
This commit is contained in:
Ian Wienand 2016-04-12 11:22:45 +10:00
parent 64deca3c5b
commit 6bbb3073ae
2 changed files with 27 additions and 9 deletions

View File

@ -16,6 +16,7 @@
# limitations under the License.
import argparse
import contextlib
import errno
import json
import logging
@ -39,6 +40,24 @@ slaves_del = " pre-down ifenslave -d {0} {1}\n"
# Type value for permanent mac addrs as defined by the linux kernel.
PERMANENT_ADDR_TYPE = '0'
# Global flag for selinux restore.
SELINUX_RESTORECON = '/usr/sbin/restorecon'
HAVE_SELINUX = os.path.exists(SELINUX_RESTORECON)
# Wrap open calls in this to make sure that any created or modified
# files retain their selinux context.
@contextlib.contextmanager
def safe_open(*args, **kwargs):
f = open(*args, **kwargs)
try:
yield f
finally:
path = os.path.abspath(f.name)
if HAVE_SELINUX:
logging.debug("Restoring selinux context for %s" % path)
subprocess.call([SELINUX_RESTORECON, path])
def _exists_rh_interface(name):
file_to_check = '/etc/sysconfig/network-scripts/ifcfg-{name}'.format(
@ -628,7 +647,7 @@ def finish_files(files_to_write, args):
while True:
try:
log.debug("Writing output file : %s" % k)
with open(k, 'w') as outfile:
with safe_open(k, 'w') as outfile:
outfile.write(files_to_write[k])
log.debug(" ... done")
break
@ -880,7 +899,7 @@ def set_hostname_from_config_drive(args):
with open('/etc/conf.d/hostname', 'w') as fh:
fh.write("hostname=\"{host}\"\n".format(host=hostname))
else:
with open('/etc/hostname', 'w') as fh:
with safe_open('/etc/hostname', 'w') as fh:
fh.write(hostname)
fh.write('\n')
@ -917,7 +936,7 @@ def set_hostname_from_config_drive(args):
# Write out a hosts entry for hostname
if match is None:
with open('/etc/hosts', 'a+') as fh:
with safe_open('/etc/hosts', 'a+') as fh:
fh.write(u'%s %s\n' % (host_value, host))

View File

@ -73,6 +73,7 @@ class TestGlean(base.BaseTestCase):
mock_handle = mock.Mock()
mock_handle.__enter__ = mock.Mock()
mock_handle.__exit__ = mock.Mock()
mock_handle.name = args[0]
# This is a trick to handle open used as a context
# manager (i.e. with open('foo') as f). It's the
# returned object that gets called, so we point it
@ -86,12 +87,10 @@ class TestGlean(base.BaseTestCase):
# second call opens as usual. We check that there
# was an os.unlink() performed
if args[0].startswith('/etc/resolv.conf'):
self._resolv_unlinked = True
mock_handle.__enter__ = mock.Mock(
side_effect=[IOError(errno.ELOOP,
os.strerror(errno.ELOOP),
args[0]),
mock_handle])
if not self._resolv_unlinked:
self._resolv_unlinked = True
raise IOError(errno.ELOOP,
os.strerror(errno.ELOOP), args[0])
self.file_handle_mocks[args[0]] = mock_handle
return mock_handle
elif args[0].startswith('/sys/class/net'):