summaryrefslogtreecommitdiffstats
path: root/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/sysctl.py
diff options
context:
space:
mode:
Diffstat (limited to 'charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/sysctl.py')
-rw-r--r--charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/sysctl.py211
1 files changed, 211 insertions, 0 deletions
diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/sysctl.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/sysctl.py
new file mode 100644
index 0000000..4a76d74
--- /dev/null
+++ b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/sysctl.py
@@ -0,0 +1,211 @@
+# Copyright 2016 Canonical Limited.
+#
+# This file is part of charm-helpers.
+#
+# charm-helpers is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3 as
+# published by the Free Software Foundation.
+#
+# charm-helpers is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import platform
+import re
+import six
+import subprocess
+
+from charmhelpers.core.hookenv import (
+ log,
+ INFO,
+ WARNING,
+)
+from charmhelpers.contrib.hardening import utils
+from charmhelpers.contrib.hardening.audits.file import (
+ FilePermissionAudit,
+ TemplatedFile,
+)
+from charmhelpers.contrib.hardening.host import TEMPLATES_DIR
+
+
+SYSCTL_DEFAULTS = """net.ipv4.ip_forward=%(net_ipv4_ip_forward)s
+net.ipv6.conf.all.forwarding=%(net_ipv6_conf_all_forwarding)s
+net.ipv4.conf.all.rp_filter=1
+net.ipv4.conf.default.rp_filter=1
+net.ipv4.icmp_echo_ignore_broadcasts=1
+net.ipv4.icmp_ignore_bogus_error_responses=1
+net.ipv4.icmp_ratelimit=100
+net.ipv4.icmp_ratemask=88089
+net.ipv6.conf.all.disable_ipv6=%(net_ipv6_conf_all_disable_ipv6)s
+net.ipv4.tcp_timestamps=%(net_ipv4_tcp_timestamps)s
+net.ipv4.conf.all.arp_ignore=%(net_ipv4_conf_all_arp_ignore)s
+net.ipv4.conf.all.arp_announce=%(net_ipv4_conf_all_arp_announce)s
+net.ipv4.tcp_rfc1337=1
+net.ipv4.tcp_syncookies=1
+net.ipv4.conf.all.shared_media=1
+net.ipv4.conf.default.shared_media=1
+net.ipv4.conf.all.accept_source_route=0
+net.ipv4.conf.default.accept_source_route=0
+net.ipv4.conf.all.accept_redirects=0
+net.ipv4.conf.default.accept_redirects=0
+net.ipv6.conf.all.accept_redirects=0
+net.ipv6.conf.default.accept_redirects=0
+net.ipv4.conf.all.secure_redirects=0
+net.ipv4.conf.default.secure_redirects=0
+net.ipv4.conf.all.send_redirects=0
+net.ipv4.conf.default.send_redirects=0
+net.ipv4.conf.all.log_martians=0
+net.ipv6.conf.default.router_solicitations=0
+net.ipv6.conf.default.accept_ra_rtr_pref=0
+net.ipv6.conf.default.accept_ra_pinfo=0
+net.ipv6.conf.default.accept_ra_defrtr=0
+net.ipv6.conf.default.autoconf=0
+net.ipv6.conf.default.dad_transmits=0
+net.ipv6.conf.default.max_addresses=1
+net.ipv6.conf.all.accept_ra=0
+net.ipv6.conf.default.accept_ra=0
+kernel.modules_disabled=%(kernel_modules_disabled)s
+kernel.sysrq=%(kernel_sysrq)s
+fs.suid_dumpable=%(fs_suid_dumpable)s
+kernel.randomize_va_space=2
+"""
+
+
+def get_audits():
+ """Get OS hardening sysctl audits.
+
+ :returns: dictionary of audits
+ """
+ audits = []
+ settings = utils.get_settings('os')
+
+ # Apply the sysctl settings which are configured to be applied.
+ audits.append(SysctlConf())
+ # Make sure that only root has access to the sysctl.conf file, and
+ # that it is read-only.
+ audits.append(FilePermissionAudit('/etc/sysctl.conf',
+ user='root',
+ group='root', mode=0o0440))
+ # If module loading is not enabled, then ensure that the modules
+ # file has the appropriate permissions and rebuild the initramfs
+ if not settings['security']['kernel_enable_module_loading']:
+ audits.append(ModulesTemplate())
+
+ return audits
+
+
+class ModulesContext(object):
+
+ def __call__(self):
+ settings = utils.get_settings('os')
+ with open('/proc/cpuinfo', 'r') as fd:
+ cpuinfo = fd.readlines()
+
+ for line in cpuinfo:
+ match = re.search(r"^vendor_id\s+:\s+(.+)", line)
+ if match:
+ vendor = match.group(1)
+
+ if vendor == "GenuineIntel":
+ vendor = "intel"
+ elif vendor == "AuthenticAMD":
+ vendor = "amd"
+
+ ctxt = {'arch': platform.processor(),
+ 'cpuVendor': vendor,
+ 'desktop_enable': settings['general']['desktop_enable']}
+
+ return ctxt
+
+
+class ModulesTemplate(object):
+
+ def __init__(self):
+ super(ModulesTemplate, self).__init__('/etc/initramfs-tools/modules',
+ ModulesContext(),
+ templates_dir=TEMPLATES_DIR,
+ user='root', group='root',
+ mode=0o0440)
+
+ def post_write(self):
+ subprocess.check_call(['update-initramfs', '-u'])
+
+
+class SysCtlHardeningContext(object):
+ def __call__(self):
+ settings = utils.get_settings('os')
+ ctxt = {'sysctl': {}}
+
+ log("Applying sysctl settings", level=INFO)
+ extras = {'net_ipv4_ip_forward': 0,
+ 'net_ipv6_conf_all_forwarding': 0,
+ 'net_ipv6_conf_all_disable_ipv6': 1,
+ 'net_ipv4_tcp_timestamps': 0,
+ 'net_ipv4_conf_all_arp_ignore': 0,
+ 'net_ipv4_conf_all_arp_announce': 0,
+ 'kernel_sysrq': 0,
+ 'fs_suid_dumpable': 0,
+ 'kernel_modules_disabled': 1}
+
+ if settings['sysctl']['ipv6_enable']:
+ extras['net_ipv6_conf_all_disable_ipv6'] = 0
+
+ if settings['sysctl']['forwarding']:
+ extras['net_ipv4_ip_forward'] = 1
+ extras['net_ipv6_conf_all_forwarding'] = 1
+
+ if settings['sysctl']['arp_restricted']:
+ extras['net_ipv4_conf_all_arp_ignore'] = 1
+ extras['net_ipv4_conf_all_arp_announce'] = 2
+
+ if settings['security']['kernel_enable_module_loading']:
+ extras['kernel_modules_disabled'] = 0
+
+ if settings['sysctl']['kernel_enable_sysrq']:
+ sysrq_val = settings['sysctl']['kernel_secure_sysrq']
+ extras['kernel_sysrq'] = sysrq_val
+
+ if settings['security']['kernel_enable_core_dump']:
+ extras['fs_suid_dumpable'] = 1
+
+ settings.update(extras)
+ for d in (SYSCTL_DEFAULTS % settings).split():
+ d = d.strip().partition('=')
+ key = d[0].strip()
+ path = os.path.join('/proc/sys', key.replace('.', '/'))
+ if not os.path.exists(path):
+ log("Skipping '%s' since '%s' does not exist" % (key, path),
+ level=WARNING)
+ continue
+
+ ctxt['sysctl'][key] = d[2] or None
+
+ # Translate for python3
+ return {'sysctl_settings':
+ [(k, v) for k, v in six.iteritems(ctxt['sysctl'])]}
+
+
+class SysctlConf(TemplatedFile):
+ """An audit check for sysctl settings."""
+ def __init__(self):
+ self.conffile = '/etc/sysctl.d/99-juju-hardening.conf'
+ super(SysctlConf, self).__init__(self.conffile,
+ SysCtlHardeningContext(),
+ template_dir=TEMPLATES_DIR,
+ user='root', group='root',
+ mode=0o0440)
+
+ def post_write(self):
+ try:
+ subprocess.check_call(['sysctl', '-p', self.conffile])
+ except subprocess.CalledProcessError as e:
+ # NOTE: on some systems if sysctl cannot apply all settings it
+ # will return non-zero as well.
+ log("sysctl command returned an error (maybe some "
+ "keys could not be set) - %s" % (e),
+ level=WARNING)