diff options
author | 2017-03-23 06:19:54 -0700 | |
---|---|---|
committer | 2017-03-23 06:19:54 -0700 | |
commit | 88df88a19674ccc0017836941b8ee32eaadf19fb (patch) | |
tree | f930c90f75846ec8d8e33cf27325ff8fafc85d5c /charms/trusty/ceilometer/charmhelpers/contrib/hardening | |
parent | 9f50a40437477432a21b326b15c343ca6b8fe516 (diff) |
Deleted charms with wrong license. Will source them differently in future.
Change-Id: I0fc99ea03c6b6ca4701e63793cb2be60e56c7588
Signed-off-by: Stuart Mackie <wsmackie@juniper.net>
Diffstat (limited to 'charms/trusty/ceilometer/charmhelpers/contrib/hardening')
55 files changed, 0 insertions, 3850 deletions
diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/README.hardening.md b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/README.hardening.md deleted file mode 100644 index 91280c0..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/README.hardening.md +++ /dev/null @@ -1,38 +0,0 @@ -# Juju charm-helpers hardening library - -## Description - -This library provides multiple implementations of system and application -hardening that conform to the standards of http://hardening.io/. - -Current implementations include: - - * OS - * SSH - * MySQL - * Apache - -## Requirements - -* Juju Charms - -## Usage - -1. Synchronise this library into your charm and add the harden() decorator - (from contrib.hardening.harden) to any functions or methods you want to use - to trigger hardening of your application/system. - -2. Add a config option called 'harden' to your charm config.yaml and set it to - a space-delimited list of hardening modules you want to run e.g. "os ssh" - -3. Override any config defaults (contrib.hardening.defaults) by adding a file - called hardening.yaml to your charm root containing the name(s) of the - modules whose settings you want override at root level and then any settings - with overrides e.g. - - os: - general: - desktop_enable: True - -4. Now just run your charm as usual and hardening will be applied each time the - hook runs. diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/__init__.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/__init__.py deleted file mode 100644 index a133532..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# 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/>. diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/apache/__init__.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/apache/__init__.py deleted file mode 100644 index 277b8c7..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/apache/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# 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/>. - -from os import path - -TEMPLATES_DIR = path.join(path.dirname(__file__), 'templates') diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/apache/checks/__init__.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/apache/checks/__init__.py deleted file mode 100644 index d130479..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/apache/checks/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -# 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/>. - -from charmhelpers.core.hookenv import ( - log, - DEBUG, -) -from charmhelpers.contrib.hardening.apache.checks import config - - -def run_apache_checks(): - log("Starting Apache hardening checks.", level=DEBUG) - checks = config.get_audits() - for check in checks: - log("Running '%s' check" % (check.__class__.__name__), level=DEBUG) - check.ensure_compliance() - - log("Apache hardening checks complete.", level=DEBUG) diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/apache/checks/config.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/apache/checks/config.py deleted file mode 100644 index 8249ca0..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/apache/checks/config.py +++ /dev/null @@ -1,100 +0,0 @@ -# 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 re -import subprocess - - -from charmhelpers.core.hookenv import ( - log, - INFO, -) -from charmhelpers.contrib.hardening.audits.file import ( - FilePermissionAudit, - DirectoryPermissionAudit, - NoReadWriteForOther, - TemplatedFile, -) -from charmhelpers.contrib.hardening.audits.apache import DisabledModuleAudit -from charmhelpers.contrib.hardening.apache import TEMPLATES_DIR -from charmhelpers.contrib.hardening import utils - - -def get_audits(): - """Get Apache hardening config audits. - - :returns: dictionary of audits - """ - if subprocess.call(['which', 'apache2'], stdout=subprocess.PIPE) != 0: - log("Apache server does not appear to be installed on this node - " - "skipping apache hardening", level=INFO) - return [] - - context = ApacheConfContext() - settings = utils.get_settings('apache') - audits = [ - FilePermissionAudit(paths='/etc/apache2/apache2.conf', user='root', - group='root', mode=0o0640), - - TemplatedFile(os.path.join(settings['common']['apache_dir'], - 'mods-available/alias.conf'), - context, - TEMPLATES_DIR, - mode=0o0755, - user='root', - service_actions=[{'service': 'apache2', - 'actions': ['restart']}]), - - TemplatedFile(os.path.join(settings['common']['apache_dir'], - 'conf-enabled/hardening.conf'), - context, - TEMPLATES_DIR, - mode=0o0640, - user='root', - service_actions=[{'service': 'apache2', - 'actions': ['restart']}]), - - DirectoryPermissionAudit(settings['common']['apache_dir'], - user='root', - group='root', - mode=0o640), - - DisabledModuleAudit(settings['hardening']['modules_to_disable']), - - NoReadWriteForOther(settings['common']['apache_dir']), - ] - - return audits - - -class ApacheConfContext(object): - """Defines the set of key/value pairs to set in a apache config file. - - This context, when called, will return a dictionary containing the - key/value pairs of setting to specify in the - /etc/apache/conf-enabled/hardening.conf file. - """ - def __call__(self): - settings = utils.get_settings('apache') - ctxt = settings['hardening'] - - out = subprocess.check_output(['apache2', '-v']) - ctxt['apache_version'] = re.search(r'.+version: Apache/(.+?)\s.+', - out).group(1) - ctxt['apache_icondir'] = '/usr/share/apache2/icons/' - ctxt['traceenable'] = settings['hardening']['traceenable'] - return ctxt diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/apache/templates/__init__.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/apache/templates/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/apache/templates/__init__.py +++ /dev/null diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/apache/templates/alias.conf b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/apache/templates/alias.conf deleted file mode 100644 index e46a58a..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/apache/templates/alias.conf +++ /dev/null @@ -1,31 +0,0 @@ -############################################################################### -# WARNING: This configuration file is maintained by Juju. Local changes may -# be overwritten. -############################################################################### -<IfModule alias_module> - # - # Aliases: Add here as many aliases as you need (with no limit). The format is - # Alias fakename realname - # - # Note that if you include a trailing / on fakename then the server will - # require it to be present in the URL. So "/icons" isn't aliased in this - # example, only "/icons/". If the fakename is slash-terminated, then the - # realname must also be slash terminated, and if the fakename omits the - # trailing slash, the realname must also omit it. - # - # We include the /icons/ alias for FancyIndexed directory listings. If - # you do not use FancyIndexing, you may comment this out. - # - Alias /icons/ "{{ apache_icondir }}/" - - <Directory "{{ apache_icondir }}"> - Options -Indexes -MultiViews -FollowSymLinks - AllowOverride None -{% if apache_version == '2.4' -%} - Require all granted -{% else -%} - Order allow,deny - Allow from all -{% endif %} - </Directory> -</IfModule> diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/apache/templates/hardening.conf b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/apache/templates/hardening.conf deleted file mode 100644 index 0794541..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/apache/templates/hardening.conf +++ /dev/null @@ -1,18 +0,0 @@ -############################################################################### -# WARNING: This configuration file is maintained by Juju. Local changes may -# be overwritten. -############################################################################### - -<Location / > - <LimitExcept {{ allowed_http_methods }} > - # http://httpd.apache.org/docs/2.4/upgrading.html - {% if apache_version > '2.2' -%} - Require all granted - {% else -%} - Order Allow,Deny - Deny from all - {% endif %} - </LimitExcept> -</Location> - -TraceEnable {{ traceenable }} diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/audits/__init__.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/audits/__init__.py deleted file mode 100644 index 6a7057b..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/audits/__init__.py +++ /dev/null @@ -1,63 +0,0 @@ -# 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/>. - - -class BaseAudit(object): # NO-QA - """Base class for hardening checks. - - The lifecycle of a hardening check is to first check to see if the system - is in compliance for the specified check. If it is not in compliance, the - check method will return a value which will be supplied to the. - """ - def __init__(self, *args, **kwargs): - self.unless = kwargs.get('unless', None) - super(BaseAudit, self).__init__() - - def ensure_compliance(self): - """Checks to see if the current hardening check is in compliance or - not. - - If the check that is performed is not in compliance, then an exception - should be raised. - """ - pass - - def _take_action(self): - """Determines whether to perform the action or not. - - Checks whether or not an action should be taken. This is determined by - the truthy value for the unless parameter. If unless is a callback - method, it will be invoked with no parameters in order to determine - whether or not the action should be taken. Otherwise, the truthy value - of the unless attribute will determine if the action should be - performed. - """ - # Do the action if there isn't an unless override. - if self.unless is None: - return True - - # Invoke the callback if there is one. - if hasattr(self.unless, '__call__'): - results = self.unless() - if results: - return False - else: - return True - - if self.unless: - return False - else: - return True diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/audits/apache.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/audits/apache.py deleted file mode 100644 index cf3c987..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/audits/apache.py +++ /dev/null @@ -1,100 +0,0 @@ -# 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 re -import subprocess - -from six import string_types - -from charmhelpers.core.hookenv import ( - log, - INFO, - ERROR, -) - -from charmhelpers.contrib.hardening.audits import BaseAudit - - -class DisabledModuleAudit(BaseAudit): - """Audits Apache2 modules. - - Determines if the apache2 modules are enabled. If the modules are enabled - then they are removed in the ensure_compliance. - """ - def __init__(self, modules): - if modules is None: - self.modules = [] - elif isinstance(modules, string_types): - self.modules = [modules] - else: - self.modules = modules - - def ensure_compliance(self): - """Ensures that the modules are not loaded.""" - if not self.modules: - return - - try: - loaded_modules = self._get_loaded_modules() - non_compliant_modules = [] - for module in self.modules: - if module in loaded_modules: - log("Module '%s' is enabled but should not be." % - (module), level=INFO) - non_compliant_modules.append(module) - - if len(non_compliant_modules) == 0: - return - - for module in non_compliant_modules: - self._disable_module(module) - self._restart_apache() - except subprocess.CalledProcessError as e: - log('Error occurred auditing apache module compliance. ' - 'This may have been already reported. ' - 'Output is: %s' % e.output, level=ERROR) - - @staticmethod - def _get_loaded_modules(): - """Returns the modules which are enabled in Apache.""" - output = subprocess.check_output(['apache2ctl', '-M']) - modules = [] - for line in output.strip().split(): - # Each line of the enabled module output looks like: - # module_name (static|shared) - # Plus a header line at the top of the output which is stripped - # out by the regex. - matcher = re.search(r'^ (\S*)', line) - if matcher: - modules.append(matcher.group(1)) - return modules - - @staticmethod - def _disable_module(module): - """Disables the specified module in Apache.""" - try: - subprocess.check_call(['a2dismod', module]) - except subprocess.CalledProcessError as e: - # Note: catch error here to allow the attempt of disabling - # multiple modules in one go rather than failing after the - # first module fails. - log('Error occurred disabling module %s. ' - 'Output is: %s' % (module, e.output), level=ERROR) - - @staticmethod - def _restart_apache(): - """Restarts the apache process""" - subprocess.check_output(['service', 'apache2', 'restart']) diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/audits/apt.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/audits/apt.py deleted file mode 100644 index e94af03..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/audits/apt.py +++ /dev/null @@ -1,105 +0,0 @@ -# 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/>. - -from __future__ import absolute_import # required for external apt import -from apt import apt_pkg -from six import string_types - -from charmhelpers.fetch import ( - apt_cache, - apt_purge -) -from charmhelpers.core.hookenv import ( - log, - DEBUG, - WARNING, -) -from charmhelpers.contrib.hardening.audits import BaseAudit - - -class AptConfig(BaseAudit): - - def __init__(self, config, **kwargs): - self.config = config - - def verify_config(self): - apt_pkg.init() - for cfg in self.config: - value = apt_pkg.config.get(cfg['key'], cfg.get('default', '')) - if value and value != cfg['expected']: - log("APT config '%s' has unexpected value '%s' " - "(expected='%s')" % - (cfg['key'], value, cfg['expected']), level=WARNING) - - def ensure_compliance(self): - self.verify_config() - - -class RestrictedPackages(BaseAudit): - """Class used to audit restricted packages on the system.""" - - def __init__(self, pkgs, **kwargs): - super(RestrictedPackages, self).__init__(**kwargs) - if isinstance(pkgs, string_types) or not hasattr(pkgs, '__iter__'): - self.pkgs = [pkgs] - else: - self.pkgs = pkgs - - def ensure_compliance(self): - cache = apt_cache() - - for p in self.pkgs: - if p not in cache: - continue - - pkg = cache[p] - if not self.is_virtual_package(pkg): - if not pkg.current_ver: - log("Package '%s' is not installed." % pkg.name, - level=DEBUG) - continue - else: - log("Restricted package '%s' is installed" % pkg.name, - level=WARNING) - self.delete_package(cache, pkg) - else: - log("Checking restricted virtual package '%s' provides" % - pkg.name, level=DEBUG) - self.delete_package(cache, pkg) - - def delete_package(self, cache, pkg): - """Deletes the package from the system. - - Deletes the package form the system, properly handling virtual - packages. - - :param cache: the apt cache - :param pkg: the package to remove - """ - if self.is_virtual_package(pkg): - log("Package '%s' appears to be virtual - purging provides" % - pkg.name, level=DEBUG) - for _p in pkg.provides_list: - self.delete_package(cache, _p[2].parent_pkg) - elif not pkg.current_ver: - log("Package '%s' not installed" % pkg.name, level=DEBUG) - return - else: - log("Purging package '%s'" % pkg.name, level=DEBUG) - apt_purge(pkg.name) - - def is_virtual_package(self, pkg): - return pkg.has_provides and not pkg.has_versions diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/audits/file.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/audits/file.py deleted file mode 100644 index 0fb545a..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/audits/file.py +++ /dev/null @@ -1,552 +0,0 @@ -# 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 grp -import os -import pwd -import re - -from subprocess import ( - CalledProcessError, - check_output, - check_call, -) -from traceback import format_exc -from six import string_types -from stat import ( - S_ISGID, - S_ISUID -) - -from charmhelpers.core.hookenv import ( - log, - DEBUG, - INFO, - WARNING, - ERROR, -) -from charmhelpers.core import unitdata -from charmhelpers.core.host import file_hash -from charmhelpers.contrib.hardening.audits import BaseAudit -from charmhelpers.contrib.hardening.templating import ( - get_template_path, - render_and_write, -) -from charmhelpers.contrib.hardening import utils - - -class BaseFileAudit(BaseAudit): - """Base class for file audits. - - Provides api stubs for compliance check flow that must be used by any class - that implemented this one. - """ - - def __init__(self, paths, always_comply=False, *args, **kwargs): - """ - :param paths: string path of list of paths of files we want to apply - compliance checks are criteria to. - :param always_comply: if true compliance criteria is always applied - else compliance is skipped for non-existent - paths. - """ - super(BaseFileAudit, self).__init__(*args, **kwargs) - self.always_comply = always_comply - if isinstance(paths, string_types) or not hasattr(paths, '__iter__'): - self.paths = [paths] - else: - self.paths = paths - - def ensure_compliance(self): - """Ensure that the all registered files comply to registered criteria. - """ - for p in self.paths: - if os.path.exists(p): - if self.is_compliant(p): - continue - - log('File %s is not in compliance.' % p, level=INFO) - else: - if not self.always_comply: - log("Non-existent path '%s' - skipping compliance check" - % (p), level=INFO) - continue - - if self._take_action(): - log("Applying compliance criteria to '%s'" % (p), level=INFO) - self.comply(p) - - def is_compliant(self, path): - """Audits the path to see if it is compliance. - - :param path: the path to the file that should be checked. - """ - raise NotImplementedError - - def comply(self, path): - """Enforces the compliance of a path. - - :param path: the path to the file that should be enforced. - """ - raise NotImplementedError - - @classmethod - def _get_stat(cls, path): - """Returns the Posix st_stat information for the specified file path. - - :param path: the path to get the st_stat information for. - :returns: an st_stat object for the path or None if the path doesn't - exist. - """ - return os.stat(path) - - -class FilePermissionAudit(BaseFileAudit): - """Implements an audit for file permissions and ownership for a user. - - This class implements functionality that ensures that a specific user/group - will own the file(s) specified and that the permissions specified are - applied properly to the file. - """ - def __init__(self, paths, user, group=None, mode=0o600, **kwargs): - self.user = user - self.group = group - self.mode = mode - super(FilePermissionAudit, self).__init__(paths, user, group, mode, - **kwargs) - - @property - def user(self): - return self._user - - @user.setter - def user(self, name): - try: - user = pwd.getpwnam(name) - except KeyError: - log('Unknown user %s' % name, level=ERROR) - user = None - self._user = user - - @property - def group(self): - return self._group - - @group.setter - def group(self, name): - try: - group = None - if name: - group = grp.getgrnam(name) - else: - group = grp.getgrgid(self.user.pw_gid) - except KeyError: - log('Unknown group %s' % name, level=ERROR) - self._group = group - - def is_compliant(self, path): - """Checks if the path is in compliance. - - Used to determine if the path specified meets the necessary - requirements to be in compliance with the check itself. - - :param path: the file path to check - :returns: True if the path is compliant, False otherwise. - """ - stat = self._get_stat(path) - user = self.user - group = self.group - - compliant = True - if stat.st_uid != user.pw_uid or stat.st_gid != group.gr_gid: - log('File %s is not owned by %s:%s.' % (path, user.pw_name, - group.gr_name), - level=INFO) - compliant = False - - # POSIX refers to the st_mode bits as corresponding to both the - # file type and file permission bits, where the least significant 12 - # bits (o7777) are the suid (11), sgid (10), sticky bits (9), and the - # file permission bits (8-0) - perms = stat.st_mode & 0o7777 - if perms != self.mode: - log('File %s has incorrect permissions, currently set to %s' % - (path, oct(stat.st_mode & 0o7777)), level=INFO) - compliant = False - - return compliant - - def comply(self, path): - """Issues a chown and chmod to the file paths specified.""" - utils.ensure_permissions(path, self.user.pw_name, self.group.gr_name, - self.mode) - - -class DirectoryPermissionAudit(FilePermissionAudit): - """Performs a permission check for the specified directory path.""" - - def __init__(self, paths, user, group=None, mode=0o600, - recursive=True, **kwargs): - super(DirectoryPermissionAudit, self).__init__(paths, user, group, - mode, **kwargs) - self.recursive = recursive - - def is_compliant(self, path): - """Checks if the directory is compliant. - - Used to determine if the path specified and all of its children - directories are in compliance with the check itself. - - :param path: the directory path to check - :returns: True if the directory tree is compliant, otherwise False. - """ - if not os.path.isdir(path): - log('Path specified %s is not a directory.' % path, level=ERROR) - raise ValueError("%s is not a directory." % path) - - if not self.recursive: - return super(DirectoryPermissionAudit, self).is_compliant(path) - - compliant = True - for root, dirs, _ in os.walk(path): - if len(dirs) > 0: - continue - - if not super(DirectoryPermissionAudit, self).is_compliant(root): - compliant = False - continue - - return compliant - - def comply(self, path): - for root, dirs, _ in os.walk(path): - if len(dirs) > 0: - super(DirectoryPermissionAudit, self).comply(root) - - -class ReadOnly(BaseFileAudit): - """Audits that files and folders are read only.""" - def __init__(self, paths, *args, **kwargs): - super(ReadOnly, self).__init__(paths=paths, *args, **kwargs) - - def is_compliant(self, path): - try: - output = check_output(['find', path, '-perm', '-go+w', - '-type', 'f']).strip() - - # The find above will find any files which have permission sets - # which allow too broad of write access. As such, the path is - # compliant if there is no output. - if output: - return False - - return True - except CalledProcessError as e: - log('Error occurred checking finding writable files for %s. ' - 'Error information is: command %s failed with returncode ' - '%d and output %s.\n%s' % (path, e.cmd, e.returncode, e.output, - format_exc(e)), level=ERROR) - return False - - def comply(self, path): - try: - check_output(['chmod', 'go-w', '-R', path]) - except CalledProcessError as e: - log('Error occurred removing writeable permissions for %s. ' - 'Error information is: command %s failed with returncode ' - '%d and output %s.\n%s' % (path, e.cmd, e.returncode, e.output, - format_exc(e)), level=ERROR) - - -class NoReadWriteForOther(BaseFileAudit): - """Ensures that the files found under the base path are readable or - writable by anyone other than the owner or the group. - """ - def __init__(self, paths): - super(NoReadWriteForOther, self).__init__(paths) - - def is_compliant(self, path): - try: - cmd = ['find', path, '-perm', '-o+r', '-type', 'f', '-o', - '-perm', '-o+w', '-type', 'f'] - output = check_output(cmd).strip() - - # The find above here will find any files which have read or - # write permissions for other, meaning there is too broad of access - # to read/write the file. As such, the path is compliant if there's - # no output. - if output: - return False - - return True - except CalledProcessError as e: - log('Error occurred while finding files which are readable or ' - 'writable to the world in %s. ' - 'Command output is: %s.' % (path, e.output), level=ERROR) - - def comply(self, path): - try: - check_output(['chmod', '-R', 'o-rw', path]) - except CalledProcessError as e: - log('Error occurred attempting to change modes of files under ' - 'path %s. Output of command is: %s' % (path, e.output)) - - -class NoSUIDSGIDAudit(BaseFileAudit): - """Audits that specified files do not have SUID/SGID bits set.""" - def __init__(self, paths, *args, **kwargs): - super(NoSUIDSGIDAudit, self).__init__(paths=paths, *args, **kwargs) - - def is_compliant(self, path): - stat = self._get_stat(path) - if (stat.st_mode & (S_ISGID | S_ISUID)) != 0: - return False - - return True - - def comply(self, path): - try: - log('Removing suid/sgid from %s.' % path, level=DEBUG) - check_output(['chmod', '-s', path]) - except CalledProcessError as e: - log('Error occurred removing suid/sgid from %s.' - 'Error information is: command %s failed with returncode ' - '%d and output %s.\n%s' % (path, e.cmd, e.returncode, e.output, - format_exc(e)), level=ERROR) - - -class TemplatedFile(BaseFileAudit): - """The TemplatedFileAudit audits the contents of a templated file. - - This audit renders a file from a template, sets the appropriate file - permissions, then generates a hashsum with which to check the content - changed. - """ - def __init__(self, path, context, template_dir, mode, user='root', - group='root', service_actions=None, **kwargs): - self.context = context - self.user = user - self.group = group - self.mode = mode - self.template_dir = template_dir - self.service_actions = service_actions - super(TemplatedFile, self).__init__(paths=path, always_comply=True, - **kwargs) - - def is_compliant(self, path): - """Determines if the templated file is compliant. - - A templated file is only compliant if it has not changed (as - determined by its sha256 hashsum) AND its file permissions are set - appropriately. - - :param path: the path to check compliance. - """ - same_templates = self.templates_match(path) - same_content = self.contents_match(path) - same_permissions = self.permissions_match(path) - - if same_content and same_permissions and same_templates: - return True - - return False - - def run_service_actions(self): - """Run any actions on services requested.""" - if not self.service_actions: - return - - for svc_action in self.service_actions: - name = svc_action['service'] - actions = svc_action['actions'] - log("Running service '%s' actions '%s'" % (name, actions), - level=DEBUG) - for action in actions: - cmd = ['service', name, action] - try: - check_call(cmd) - except CalledProcessError as exc: - log("Service name='%s' action='%s' failed - %s" % - (name, action, exc), level=WARNING) - - def comply(self, path): - """Ensures the contents and the permissions of the file. - - :param path: the path to correct - """ - dirname = os.path.dirname(path) - if not os.path.exists(dirname): - os.makedirs(dirname) - - self.pre_write() - render_and_write(self.template_dir, path, self.context()) - utils.ensure_permissions(path, self.user, self.group, self.mode) - self.run_service_actions() - self.save_checksum(path) - self.post_write() - - def pre_write(self): - """Invoked prior to writing the template.""" - pass - - def post_write(self): - """Invoked after writing the template.""" - pass - - def templates_match(self, path): - """Determines if the template files are the same. - - The template file equality is determined by the hashsum of the - template files themselves. If there is no hashsum, then the content - cannot be sure to be the same so treat it as if they changed. - Otherwise, return whether or not the hashsums are the same. - - :param path: the path to check - :returns: boolean - """ - template_path = get_template_path(self.template_dir, path) - key = 'hardening:template:%s' % template_path - template_checksum = file_hash(template_path) - kv = unitdata.kv() - stored_tmplt_checksum = kv.get(key) - if not stored_tmplt_checksum: - kv.set(key, template_checksum) - kv.flush() - log('Saved template checksum for %s.' % template_path, - level=DEBUG) - # Since we don't have a template checksum, then assume it doesn't - # match and return that the template is different. - return False - elif stored_tmplt_checksum != template_checksum: - kv.set(key, template_checksum) - kv.flush() - log('Updated template checksum for %s.' % template_path, - level=DEBUG) - return False - - # Here the template hasn't changed based upon the calculated - # checksum of the template and what was previously stored. - return True - - def contents_match(self, path): - """Determines if the file content is the same. - - This is determined by comparing hashsum of the file contents and - the saved hashsum. If there is no hashsum, then the content cannot - be sure to be the same so treat them as if they are not the same. - Otherwise, return True if the hashsums are the same, False if they - are not the same. - - :param path: the file to check. - """ - checksum = file_hash(path) - - kv = unitdata.kv() - stored_checksum = kv.get('hardening:%s' % path) - if not stored_checksum: - # If the checksum hasn't been generated, return False to ensure - # the file is written and the checksum stored. - log('Checksum for %s has not been calculated.' % path, level=DEBUG) - return False - elif stored_checksum != checksum: - log('Checksum mismatch for %s.' % path, level=DEBUG) - return False - - return True - - def permissions_match(self, path): - """Determines if the file owner and permissions match. - - :param path: the path to check. - """ - audit = FilePermissionAudit(path, self.user, self.group, self.mode) - return audit.is_compliant(path) - - def save_checksum(self, path): - """Calculates and saves the checksum for the path specified. - - :param path: the path of the file to save the checksum. - """ - checksum = file_hash(path) - kv = unitdata.kv() - kv.set('hardening:%s' % path, checksum) - kv.flush() - - -class DeletedFile(BaseFileAudit): - """Audit to ensure that a file is deleted.""" - def __init__(self, paths): - super(DeletedFile, self).__init__(paths) - - def is_compliant(self, path): - return not os.path.exists(path) - - def comply(self, path): - os.remove(path) - - -class FileContentAudit(BaseFileAudit): - """Audit the contents of a file.""" - def __init__(self, paths, cases, **kwargs): - # Cases we expect to pass - self.pass_cases = cases.get('pass', []) - # Cases we expect to fail - self.fail_cases = cases.get('fail', []) - super(FileContentAudit, self).__init__(paths, **kwargs) - - def is_compliant(self, path): - """ - Given a set of content matching cases i.e. tuple(regex, bool) where - bool value denotes whether or not regex is expected to match, check that - all cases match as expected with the contents of the file. Cases can be - expected to pass of fail. - - :param path: Path of file to check. - :returns: Boolean value representing whether or not all cases are - found to be compliant. - """ - log("Auditing contents of file '%s'" % (path), level=DEBUG) - with open(path, 'r') as fd: - contents = fd.read() - - matches = 0 - for pattern in self.pass_cases: - key = re.compile(pattern, flags=re.MULTILINE) - results = re.search(key, contents) - if results: - matches += 1 - else: - log("Pattern '%s' was expected to pass but instead it failed" - % (pattern), level=WARNING) - - for pattern in self.fail_cases: - key = re.compile(pattern, flags=re.MULTILINE) - results = re.search(key, contents) - if not results: - matches += 1 - else: - log("Pattern '%s' was expected to fail but instead it passed" - % (pattern), level=WARNING) - - total = len(self.pass_cases) + len(self.fail_cases) - log("Checked %s cases and %s passed" % (total, matches), level=DEBUG) - return matches == total - - def comply(self, *args, **kwargs): - """NOOP since we just issue warnings. This is to avoid the - NotImplememtedError. - """ - log("Not applying any compliance criteria, only checks.", level=INFO) diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/__init__.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/__init__.py +++ /dev/null diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/apache.yaml b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/apache.yaml deleted file mode 100644 index e5ada29..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/apache.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# NOTE: this file contains the default configuration for the 'apache' hardening -# code. If you want to override any settings you must add them to a file -# called hardening.yaml in the root directory of your charm using the -# name 'apache' as the root key followed by any of the following with new -# values. - -common: - apache_dir: '/etc/apache2' - -hardening: - traceenable: 'off' - allowed_http_methods: "GET POST" - modules_to_disable: [ cgi, cgid ]
\ No newline at end of file diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/apache.yaml.schema b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/apache.yaml.schema deleted file mode 100644 index 227589b..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/apache.yaml.schema +++ /dev/null @@ -1,9 +0,0 @@ -# NOTE: this schema must contain all valid keys from it's associated defaults -# file. It is used to validate user-provided overrides. -common: - apache_dir: - traceenable: - -hardening: - allowed_http_methods: - modules_to_disable: diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/mysql.yaml b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/mysql.yaml deleted file mode 100644 index 682d22b..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/mysql.yaml +++ /dev/null @@ -1,38 +0,0 @@ -# NOTE: this file contains the default configuration for the 'mysql' hardening -# code. If you want to override any settings you must add them to a file -# called hardening.yaml in the root directory of your charm using the -# name 'mysql' as the root key followed by any of the following with new -# values. - -hardening: - mysql-conf: /etc/mysql/my.cnf - hardening-conf: /etc/mysql/conf.d/hardening.cnf - -security: - # @see http://www.symantec.com/connect/articles/securing-mysql-step-step - # @see http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_chroot - chroot: None - - # @see http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_safe-user-create - safe-user-create: 1 - - # @see http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_secure-auth - secure-auth: 1 - - # @see http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_symbolic-links - skip-symbolic-links: 1 - - # @see http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_skip-show-database - skip-show-database: True - - # @see http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_local_infile - local-infile: 0 - - # @see https://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_allow-suspicious-udfs - allow-suspicious-udfs: 0 - - # @see https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_automatic_sp_privileges - automatic-sp-privileges: 0 - - # @see https://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_secure-file-priv - secure-file-priv: /tmp diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/mysql.yaml.schema b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/mysql.yaml.schema deleted file mode 100644 index 2edf325..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/mysql.yaml.schema +++ /dev/null @@ -1,15 +0,0 @@ -# NOTE: this schema must contain all valid keys from it's associated defaults -# file. It is used to validate user-provided overrides. -hardening: - mysql-conf: - hardening-conf: -security: - chroot: - safe-user-create: - secure-auth: - skip-symbolic-links: - skip-show-database: - local-infile: - allow-suspicious-udfs: - automatic-sp-privileges: - secure-file-priv: diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/os.yaml b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/os.yaml deleted file mode 100644 index ddd4286..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/os.yaml +++ /dev/null @@ -1,67 +0,0 @@ -# NOTE: this file contains the default configuration for the 'os' hardening -# code. If you want to override any settings you must add them to a file -# called hardening.yaml in the root directory of your charm using the -# name 'os' as the root key followed by any of the following with new -# values. - -general: - desktop_enable: False # (type:boolean) - -environment: - extra_user_paths: [] - umask: 027 - root_path: / - -auth: - pw_max_age: 60 - # discourage password cycling - pw_min_age: 7 - retries: 5 - lockout_time: 600 - timeout: 60 - allow_homeless: False # (type:boolean) - pam_passwdqc_enable: True # (type:boolean) - pam_passwdqc_options: 'min=disabled,disabled,16,12,8' - root_ttys: - console - tty1 - tty2 - tty3 - tty4 - tty5 - tty6 - uid_min: 1000 - gid_min: 1000 - sys_uid_min: 100 - sys_uid_max: 999 - sys_gid_min: 100 - sys_gid_max: 999 - chfn_restrict: - -security: - users_allow: [] - suid_sgid_enforce: True # (type:boolean) - # user-defined blacklist and whitelist - suid_sgid_blacklist: [] - suid_sgid_whitelist: [] - # if this is True, remove any suid/sgid bits from files that were not in the whitelist - suid_sgid_dry_run_on_unknown: False # (type:boolean) - suid_sgid_remove_from_unknown: False # (type:boolean) - # remove packages with known issues - packages_clean: True # (type:boolean) - packages_list: - xinetd - inetd - ypserv - telnet-server - rsh-server - rsync - kernel_enable_module_loading: True # (type:boolean) - kernel_enable_core_dump: False # (type:boolean) - -sysctl: - kernel_secure_sysrq: 244 # 4 + 16 + 32 + 64 + 128 - kernel_enable_sysrq: False # (type:boolean) - forwarding: False # (type:boolean) - ipv6_enable: False # (type:boolean) - arp_restricted: True # (type:boolean) diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/os.yaml.schema b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/os.yaml.schema deleted file mode 100644 index 88b3966..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/os.yaml.schema +++ /dev/null @@ -1,42 +0,0 @@ -# NOTE: this schema must contain all valid keys from it's associated defaults -# file. It is used to validate user-provided overrides. -general: - desktop_enable: -environment: - extra_user_paths: - umask: - root_path: -auth: - pw_max_age: - pw_min_age: - retries: - lockout_time: - timeout: - allow_homeless: - pam_passwdqc_enable: - pam_passwdqc_options: - root_ttys: - uid_min: - gid_min: - sys_uid_min: - sys_uid_max: - sys_gid_min: - sys_gid_max: - chfn_restrict: -security: - users_allow: - suid_sgid_enforce: - suid_sgid_blacklist: - suid_sgid_whitelist: - suid_sgid_dry_run_on_unknown: - suid_sgid_remove_from_unknown: - packages_clean: - packages_list: - kernel_enable_module_loading: - kernel_enable_core_dump: -sysctl: - kernel_secure_sysrq: - kernel_enable_sysrq: - forwarding: - ipv6_enable: - arp_restricted: diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/ssh.yaml b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/ssh.yaml deleted file mode 100644 index cd529bc..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/ssh.yaml +++ /dev/null @@ -1,49 +0,0 @@ -# NOTE: this file contains the default configuration for the 'ssh' hardening -# code. If you want to override any settings you must add them to a file -# called hardening.yaml in the root directory of your charm using the -# name 'ssh' as the root key followed by any of the following with new -# values. - -common: - service_name: 'ssh' - network_ipv6_enable: False # (type:boolean) - ports: [22] - remote_hosts: [] - -client: - package: 'openssh-client' - cbc_required: False # (type:boolean) - weak_hmac: False # (type:boolean) - weak_kex: False # (type:boolean) - roaming: False - password_authentication: 'no' - -server: - host_key_files: ['/etc/ssh/ssh_host_rsa_key', '/etc/ssh/ssh_host_dsa_key', - '/etc/ssh/ssh_host_ecdsa_key'] - cbc_required: False # (type:boolean) - weak_hmac: False # (type:boolean) - weak_kex: False # (type:boolean) - allow_root_with_key: False # (type:boolean) - allow_tcp_forwarding: 'no' - allow_agent_forwarding: 'no' - allow_x11_forwarding: 'no' - use_privilege_separation: 'sandbox' - listen_to: ['0.0.0.0'] - use_pam: 'no' - package: 'openssh-server' - password_authentication: 'no' - alive_interval: '600' - alive_count: '3' - sftp_enable: False # (type:boolean) - sftp_group: 'sftponly' - sftp_chroot: '/home/%u' - deny_users: [] - allow_users: [] - deny_groups: [] - allow_groups: [] - print_motd: 'no' - print_last_log: 'no' - use_dns: 'no' - max_auth_tries: 2 - max_sessions: 10 diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/ssh.yaml.schema b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/ssh.yaml.schema deleted file mode 100644 index d05e054..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/defaults/ssh.yaml.schema +++ /dev/null @@ -1,42 +0,0 @@ -# NOTE: this schema must contain all valid keys from it's associated defaults -# file. It is used to validate user-provided overrides. -common: - service_name: - network_ipv6_enable: - ports: - remote_hosts: -client: - package: - cbc_required: - weak_hmac: - weak_kex: - roaming: - password_authentication: -server: - host_key_files: - cbc_required: - weak_hmac: - weak_kex: - allow_root_with_key: - allow_tcp_forwarding: - allow_agent_forwarding: - allow_x11_forwarding: - use_privilege_separation: - listen_to: - use_pam: - package: - password_authentication: - alive_interval: - alive_count: - sftp_enable: - sftp_group: - sftp_chroot: - deny_users: - allow_users: - deny_groups: - allow_groups: - print_motd: - print_last_log: - use_dns: - max_auth_tries: - max_sessions: diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/harden.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/harden.py deleted file mode 100644 index ac7568d..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/harden.py +++ /dev/null @@ -1,84 +0,0 @@ -# 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 six - -from collections import OrderedDict - -from charmhelpers.core.hookenv import ( - config, - log, - DEBUG, - WARNING, -) -from charmhelpers.contrib.hardening.host.checks import run_os_checks -from charmhelpers.contrib.hardening.ssh.checks import run_ssh_checks -from charmhelpers.contrib.hardening.mysql.checks import run_mysql_checks -from charmhelpers.contrib.hardening.apache.checks import run_apache_checks - - -def harden(overrides=None): - """Hardening decorator. - - This is the main entry point for running the hardening stack. In order to - run modules of the stack you must add this decorator to charm hook(s) and - ensure that your charm config.yaml contains the 'harden' option set to - one or more of the supported modules. Setting these will cause the - corresponding hardening code to be run when the hook fires. - - This decorator can and should be applied to more than one hook or function - such that hardening modules are called multiple times. This is because - subsequent calls will perform auditing checks that will report any changes - to resources hardened by the first run (and possibly perform compliance - actions as a result of any detected infractions). - - :param overrides: Optional list of stack modules used to override those - provided with 'harden' config. - :returns: Returns value returned by decorated function once executed. - """ - def _harden_inner1(f): - log("Hardening function '%s'" % (f.__name__), level=DEBUG) - - def _harden_inner2(*args, **kwargs): - RUN_CATALOG = OrderedDict([('os', run_os_checks), - ('ssh', run_ssh_checks), - ('mysql', run_mysql_checks), - ('apache', run_apache_checks)]) - - enabled = overrides or (config("harden") or "").split() - if enabled: - modules_to_run = [] - # modules will always be performed in the following order - for module, func in six.iteritems(RUN_CATALOG): - if module in enabled: - enabled.remove(module) - modules_to_run.append(func) - - if enabled: - log("Unknown hardening modules '%s' - ignoring" % - (', '.join(enabled)), level=WARNING) - - for hardener in modules_to_run: - log("Executing hardening module '%s'" % - (hardener.__name__), level=DEBUG) - hardener() - else: - log("No hardening applied to '%s'" % (f.__name__), level=DEBUG) - - return f(*args, **kwargs) - return _harden_inner2 - - return _harden_inner1 diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/__init__.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/__init__.py deleted file mode 100644 index 277b8c7..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# 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/>. - -from os import path - -TEMPLATES_DIR = path.join(path.dirname(__file__), 'templates') diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/__init__.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/__init__.py deleted file mode 100644 index c3bd598..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/__init__.py +++ /dev/null @@ -1,50 +0,0 @@ -# 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/>. - -from charmhelpers.core.hookenv import ( - log, - DEBUG, -) -from charmhelpers.contrib.hardening.host.checks import ( - apt, - limits, - login, - minimize_access, - pam, - profile, - securetty, - suid_sgid, - sysctl -) - - -def run_os_checks(): - log("Starting OS hardening checks.", level=DEBUG) - checks = apt.get_audits() - checks.extend(limits.get_audits()) - checks.extend(login.get_audits()) - checks.extend(minimize_access.get_audits()) - checks.extend(pam.get_audits()) - checks.extend(profile.get_audits()) - checks.extend(securetty.get_audits()) - checks.extend(suid_sgid.get_audits()) - checks.extend(sysctl.get_audits()) - - for check in checks: - log("Running '%s' check" % (check.__class__.__name__), level=DEBUG) - check.ensure_compliance() - - log("OS hardening checks complete.", level=DEBUG) diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/apt.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/apt.py deleted file mode 100644 index 2c221cd..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/apt.py +++ /dev/null @@ -1,39 +0,0 @@ -# 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/>. - -from charmhelpers.contrib.hardening.utils import get_settings -from charmhelpers.contrib.hardening.audits.apt import ( - AptConfig, - RestrictedPackages, -) - - -def get_audits(): - """Get OS hardening apt audits. - - :returns: dictionary of audits - """ - audits = [AptConfig([{'key': 'APT::Get::AllowUnauthenticated', - 'expected': 'false'}])] - - settings = get_settings('os') - clean_packages = settings['security']['packages_clean'] - if clean_packages: - security_packages = settings['security']['packages_list'] - if security_packages: - audits.append(RestrictedPackages(security_packages)) - - return audits diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/limits.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/limits.py deleted file mode 100644 index 8ce9dc2..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/limits.py +++ /dev/null @@ -1,55 +0,0 @@ -# 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/>. - -from charmhelpers.contrib.hardening.audits.file import ( - DirectoryPermissionAudit, - TemplatedFile, -) -from charmhelpers.contrib.hardening.host import TEMPLATES_DIR -from charmhelpers.contrib.hardening import utils - - -def get_audits(): - """Get OS hardening security limits audits. - - :returns: dictionary of audits - """ - audits = [] - settings = utils.get_settings('os') - - # Ensure that the /etc/security/limits.d directory is only writable - # by the root user, but others can execute and read. - audits.append(DirectoryPermissionAudit('/etc/security/limits.d', - user='root', group='root', - mode=0o755)) - - # If core dumps are not enabled, then don't allow core dumps to be - # created as they may contain sensitive information. - if not settings['security']['kernel_enable_core_dump']: - audits.append(TemplatedFile('/etc/security/limits.d/10.hardcore.conf', - SecurityLimitsContext(), - template_dir=TEMPLATES_DIR, - user='root', group='root', mode=0o0440)) - return audits - - -class SecurityLimitsContext(object): - - def __call__(self): - settings = utils.get_settings('os') - ctxt = {'disable_core_dump': - not settings['security']['kernel_enable_core_dump']} - return ctxt diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/login.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/login.py deleted file mode 100644 index d32c4f6..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/login.py +++ /dev/null @@ -1,67 +0,0 @@ -# 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/>. - -from six import string_types - -from charmhelpers.contrib.hardening.audits.file import TemplatedFile -from charmhelpers.contrib.hardening.host import TEMPLATES_DIR -from charmhelpers.contrib.hardening import utils - - -def get_audits(): - """Get OS hardening login.defs audits. - - :returns: dictionary of audits - """ - audits = [TemplatedFile('/etc/login.defs', LoginContext(), - template_dir=TEMPLATES_DIR, - user='root', group='root', mode=0o0444)] - return audits - - -class LoginContext(object): - - def __call__(self): - settings = utils.get_settings('os') - - # Octal numbers in yaml end up being turned into decimal, - # so check if the umask is entered as a string (e.g. '027') - # or as an octal umask as we know it (e.g. 002). If its not - # a string assume it to be octal and turn it into an octal - # string. - umask = settings['environment']['umask'] - if not isinstance(umask, string_types): - umask = '%s' % oct(umask) - - ctxt = { - 'additional_user_paths': - settings['environment']['extra_user_paths'], - 'umask': umask, - 'pwd_max_age': settings['auth']['pw_max_age'], - 'pwd_min_age': settings['auth']['pw_min_age'], - 'uid_min': settings['auth']['uid_min'], - 'sys_uid_min': settings['auth']['sys_uid_min'], - 'sys_uid_max': settings['auth']['sys_uid_max'], - 'gid_min': settings['auth']['gid_min'], - 'sys_gid_min': settings['auth']['sys_gid_min'], - 'sys_gid_max': settings['auth']['sys_gid_max'], - 'login_retries': settings['auth']['retries'], - 'login_timeout': settings['auth']['timeout'], - 'chfn_restrict': settings['auth']['chfn_restrict'], - 'allow_login_without_home': settings['auth']['allow_homeless'] - } - - return ctxt diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/minimize_access.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/minimize_access.py deleted file mode 100644 index c471064..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/minimize_access.py +++ /dev/null @@ -1,52 +0,0 @@ -# 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/>. - -from charmhelpers.contrib.hardening.audits.file import ( - FilePermissionAudit, - ReadOnly, -) -from charmhelpers.contrib.hardening import utils - - -def get_audits(): - """Get OS hardening access audits. - - :returns: dictionary of audits - """ - audits = [] - settings = utils.get_settings('os') - - # Remove write permissions from $PATH folders for all regular users. - # This prevents changing system-wide commands from normal users. - path_folders = {'/usr/local/sbin', - '/usr/local/bin', - '/usr/sbin', - '/usr/bin', - '/bin'} - extra_user_paths = settings['environment']['extra_user_paths'] - path_folders.update(extra_user_paths) - audits.append(ReadOnly(path_folders)) - - # Only allow the root user to have access to the shadow file. - audits.append(FilePermissionAudit('/etc/shadow', 'root', 'root', 0o0600)) - - if 'change_user' not in settings['security']['users_allow']: - # su should only be accessible to user and group root, unless it is - # expressly defined to allow users to change to root via the - # security_users_allow config option. - audits.append(FilePermissionAudit('/bin/su', 'root', 'root', 0o750)) - - return audits diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/pam.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/pam.py deleted file mode 100644 index 383fe28..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/pam.py +++ /dev/null @@ -1,134 +0,0 @@ -# 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/>. - -from subprocess import ( - check_output, - CalledProcessError, -) - -from charmhelpers.core.hookenv import ( - log, - DEBUG, - ERROR, -) -from charmhelpers.fetch import ( - apt_install, - apt_purge, - apt_update, -) -from charmhelpers.contrib.hardening.audits.file import ( - TemplatedFile, - DeletedFile, -) -from charmhelpers.contrib.hardening import utils -from charmhelpers.contrib.hardening.host import TEMPLATES_DIR - - -def get_audits(): - """Get OS hardening PAM authentication audits. - - :returns: dictionary of audits - """ - audits = [] - - settings = utils.get_settings('os') - - if settings['auth']['pam_passwdqc_enable']: - audits.append(PasswdqcPAM('/etc/passwdqc.conf')) - - if settings['auth']['retries']: - audits.append(Tally2PAM('/usr/share/pam-configs/tally2')) - else: - audits.append(DeletedFile('/usr/share/pam-configs/tally2')) - - return audits - - -class PasswdqcPAMContext(object): - - def __call__(self): - ctxt = {} - settings = utils.get_settings('os') - - ctxt['auth_pam_passwdqc_options'] = \ - settings['auth']['pam_passwdqc_options'] - - return ctxt - - -class PasswdqcPAM(TemplatedFile): - """The PAM Audit verifies the linux PAM settings.""" - def __init__(self, path): - super(PasswdqcPAM, self).__init__(path=path, - template_dir=TEMPLATES_DIR, - context=PasswdqcPAMContext(), - user='root', - group='root', - mode=0o0640) - - def pre_write(self): - # Always remove? - for pkg in ['libpam-ccreds', 'libpam-cracklib']: - log("Purging package '%s'" % pkg, level=DEBUG), - apt_purge(pkg) - - apt_update(fatal=True) - for pkg in ['libpam-passwdqc']: - log("Installing package '%s'" % pkg, level=DEBUG), - apt_install(pkg) - - def post_write(self): - """Updates the PAM configuration after the file has been written""" - try: - check_output(['pam-auth-update', '--package']) - except CalledProcessError as e: - log('Error calling pam-auth-update: %s' % e, level=ERROR) - - -class Tally2PAMContext(object): - - def __call__(self): - ctxt = {} - settings = utils.get_settings('os') - - ctxt['auth_lockout_time'] = settings['auth']['lockout_time'] - ctxt['auth_retries'] = settings['auth']['retries'] - - return ctxt - - -class Tally2PAM(TemplatedFile): - """The PAM Audit verifies the linux PAM settings.""" - def __init__(self, path): - super(Tally2PAM, self).__init__(path=path, - template_dir=TEMPLATES_DIR, - context=Tally2PAMContext(), - user='root', - group='root', - mode=0o0640) - - def pre_write(self): - # Always remove? - apt_purge('libpam-ccreds') - apt_update(fatal=True) - apt_install('libpam-modules') - - def post_write(self): - """Updates the PAM configuration after the file has been written""" - try: - check_output(['pam-auth-update', '--package']) - except CalledProcessError as e: - log('Error calling pam-auth-update: %s' % e, level=ERROR) diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/profile.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/profile.py deleted file mode 100644 index f744335..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/profile.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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/>. - -from charmhelpers.contrib.hardening.audits.file import TemplatedFile -from charmhelpers.contrib.hardening.host import TEMPLATES_DIR -from charmhelpers.contrib.hardening import utils - - -def get_audits(): - """Get OS hardening profile audits. - - :returns: dictionary of audits - """ - audits = [] - - settings = utils.get_settings('os') - - # If core dumps are not enabled, then don't allow core dumps to be - # created as they may contain sensitive information. - if not settings['security']['kernel_enable_core_dump']: - audits.append(TemplatedFile('/etc/profile.d/pinerolo_profile.sh', - ProfileContext(), - template_dir=TEMPLATES_DIR, - mode=0o0755, user='root', group='root')) - return audits - - -class ProfileContext(object): - - def __call__(self): - ctxt = {} - return ctxt diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/securetty.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/securetty.py deleted file mode 100644 index e33c73c..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/securetty.py +++ /dev/null @@ -1,39 +0,0 @@ -# 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/>. - -from charmhelpers.contrib.hardening.audits.file import TemplatedFile -from charmhelpers.contrib.hardening.host import TEMPLATES_DIR -from charmhelpers.contrib.hardening import utils - - -def get_audits(): - """Get OS hardening Secure TTY audits. - - :returns: dictionary of audits - """ - audits = [] - audits.append(TemplatedFile('/etc/securetty', SecureTTYContext(), - template_dir=TEMPLATES_DIR, - mode=0o0400, user='root', group='root')) - return audits - - -class SecureTTYContext(object): - - def __call__(self): - settings = utils.get_settings('os') - ctxt = {'ttys': settings['auth']['root_ttys']} - return ctxt diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/suid_sgid.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/suid_sgid.py deleted file mode 100644 index 0534689..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/suid_sgid.py +++ /dev/null @@ -1,131 +0,0 @@ -# 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 subprocess - -from charmhelpers.core.hookenv import ( - log, - INFO, -) -from charmhelpers.contrib.hardening.audits.file import NoSUIDSGIDAudit -from charmhelpers.contrib.hardening import utils - - -BLACKLIST = ['/usr/bin/rcp', '/usr/bin/rlogin', '/usr/bin/rsh', - '/usr/libexec/openssh/ssh-keysign', - '/usr/lib/openssh/ssh-keysign', - '/sbin/netreport', - '/usr/sbin/usernetctl', - '/usr/sbin/userisdnctl', - '/usr/sbin/pppd', - '/usr/bin/lockfile', - '/usr/bin/mail-lock', - '/usr/bin/mail-unlock', - '/usr/bin/mail-touchlock', - '/usr/bin/dotlockfile', - '/usr/bin/arping', - '/usr/sbin/uuidd', - '/usr/bin/mtr', - '/usr/lib/evolution/camel-lock-helper-1.2', - '/usr/lib/pt_chown', - '/usr/lib/eject/dmcrypt-get-device', - '/usr/lib/mc/cons.saver'] - -WHITELIST = ['/bin/mount', '/bin/ping', '/bin/su', '/bin/umount', - '/sbin/pam_timestamp_check', '/sbin/unix_chkpwd', '/usr/bin/at', - '/usr/bin/gpasswd', '/usr/bin/locate', '/usr/bin/newgrp', - '/usr/bin/passwd', '/usr/bin/ssh-agent', - '/usr/libexec/utempter/utempter', '/usr/sbin/lockdev', - '/usr/sbin/sendmail.sendmail', '/usr/bin/expiry', - '/bin/ping6', '/usr/bin/traceroute6.iputils', - '/sbin/mount.nfs', '/sbin/umount.nfs', - '/sbin/mount.nfs4', '/sbin/umount.nfs4', - '/usr/bin/crontab', - '/usr/bin/wall', '/usr/bin/write', - '/usr/bin/screen', - '/usr/bin/mlocate', - '/usr/bin/chage', '/usr/bin/chfn', '/usr/bin/chsh', - '/bin/fusermount', - '/usr/bin/pkexec', - '/usr/bin/sudo', '/usr/bin/sudoedit', - '/usr/sbin/postdrop', '/usr/sbin/postqueue', - '/usr/sbin/suexec', - '/usr/lib/squid/ncsa_auth', '/usr/lib/squid/pam_auth', - '/usr/kerberos/bin/ksu', - '/usr/sbin/ccreds_validate', - '/usr/bin/Xorg', - '/usr/bin/X', - '/usr/lib/dbus-1.0/dbus-daemon-launch-helper', - '/usr/lib/vte/gnome-pty-helper', - '/usr/lib/libvte9/gnome-pty-helper', - '/usr/lib/libvte-2.90-9/gnome-pty-helper'] - - -def get_audits(): - """Get OS hardening suid/sgid audits. - - :returns: dictionary of audits - """ - checks = [] - settings = utils.get_settings('os') - if not settings['security']['suid_sgid_enforce']: - log("Skipping suid/sgid hardening", level=INFO) - return checks - - # Build the blacklist and whitelist of files for suid/sgid checks. - # There are a total of 4 lists: - # 1. the system blacklist - # 2. the system whitelist - # 3. the user blacklist - # 4. the user whitelist - # - # The blacklist is the set of paths which should NOT have the suid/sgid bit - # set and the whitelist is the set of paths which MAY have the suid/sgid - # bit setl. The user whitelist/blacklist effectively override the system - # whitelist/blacklist. - u_b = settings['security']['suid_sgid_blacklist'] - u_w = settings['security']['suid_sgid_whitelist'] - - blacklist = set(BLACKLIST) - set(u_w + u_b) - whitelist = set(WHITELIST) - set(u_b + u_w) - - checks.append(NoSUIDSGIDAudit(blacklist)) - - dry_run = settings['security']['suid_sgid_dry_run_on_unknown'] - - if settings['security']['suid_sgid_remove_from_unknown'] or dry_run: - # If the policy is a dry_run (e.g. complain only) or remove unknown - # suid/sgid bits then find all of the paths which have the suid/sgid - # bit set and then remove the whitelisted paths. - root_path = settings['environment']['root_path'] - unknown_paths = find_paths_with_suid_sgid(root_path) - set(whitelist) - checks.append(NoSUIDSGIDAudit(unknown_paths, unless=dry_run)) - - return checks - - -def find_paths_with_suid_sgid(root_path): - """Finds all paths/files which have an suid/sgid bit enabled. - - Starting with the root_path, this will recursively find all paths which - have an suid or sgid bit set. - """ - cmd = ['find', root_path, '-perm', '-4000', '-o', '-perm', '-2000', - '-type', 'f', '!', '-path', '/proc/*', '-print'] - - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, _ = p.communicate() - return set(out.split('\n')) diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/sysctl.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/sysctl.py deleted file mode 100644 index 4a76d74..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/checks/sysctl.py +++ /dev/null @@ -1,211 +0,0 @@ -# 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) diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/10.hardcore.conf b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/10.hardcore.conf deleted file mode 100644 index 0014191..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/10.hardcore.conf +++ /dev/null @@ -1,8 +0,0 @@ -############################################################################### -# WARNING: This configuration file is maintained by Juju. Local changes may -# be overwritten. -############################################################################### -{% if disable_core_dump -%} -# Prevent core dumps for all users. These are usually only needed by developers and may contain sensitive information. -* hard core 0 -{% endif %}
\ No newline at end of file diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/99-juju-hardening.conf b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/99-juju-hardening.conf deleted file mode 100644 index 101f1e1..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/99-juju-hardening.conf +++ /dev/null @@ -1,7 +0,0 @@ -############################################################################### -# WARNING: This configuration file is maintained by Juju. Local changes may -# be overwritten. -############################################################################### -{% for key, value in sysctl_settings -%} -{{ key }}={{ value }} -{% endfor -%} diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/__init__.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/__init__.py +++ /dev/null diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/login.defs b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/login.defs deleted file mode 100644 index db137d6..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/login.defs +++ /dev/null @@ -1,349 +0,0 @@ -############################################################################### -# WARNING: This configuration file is maintained by Juju. Local changes may -# be overwritten. -############################################################################### -# -# /etc/login.defs - Configuration control definitions for the login package. -# -# Three items must be defined: MAIL_DIR, ENV_SUPATH, and ENV_PATH. -# If unspecified, some arbitrary (and possibly incorrect) value will -# be assumed. All other items are optional - if not specified then -# the described action or option will be inhibited. -# -# Comment lines (lines beginning with "#") and blank lines are ignored. -# -# Modified for Linux. --marekm - -# REQUIRED for useradd/userdel/usermod -# Directory where mailboxes reside, _or_ name of file, relative to the -# home directory. If you _do_ define MAIL_DIR and MAIL_FILE, -# MAIL_DIR takes precedence. -# -# Essentially: -# - MAIL_DIR defines the location of users mail spool files -# (for mbox use) by appending the username to MAIL_DIR as defined -# below. -# - MAIL_FILE defines the location of the users mail spool files as the -# fully-qualified filename obtained by prepending the user home -# directory before $MAIL_FILE -# -# NOTE: This is no more used for setting up users MAIL environment variable -# which is, starting from shadow 4.0.12-1 in Debian, entirely the -# job of the pam_mail PAM modules -# See default PAM configuration files provided for -# login, su, etc. -# -# This is a temporary situation: setting these variables will soon -# move to /etc/default/useradd and the variables will then be -# no more supported -MAIL_DIR /var/mail -#MAIL_FILE .mail - -# -# Enable logging and display of /var/log/faillog login failure info. -# This option conflicts with the pam_tally PAM module. -# -FAILLOG_ENAB yes - -# -# Enable display of unknown usernames when login failures are recorded. -# -# WARNING: Unknown usernames may become world readable. -# See #290803 and #298773 for details about how this could become a security -# concern -LOG_UNKFAIL_ENAB no - -# -# Enable logging of successful logins -# -LOG_OK_LOGINS yes - -# -# Enable "syslog" logging of su activity - in addition to sulog file logging. -# SYSLOG_SG_ENAB does the same for newgrp and sg. -# -SYSLOG_SU_ENAB yes -SYSLOG_SG_ENAB yes - -# -# If defined, all su activity is logged to this file. -# -#SULOG_FILE /var/log/sulog - -# -# If defined, file which maps tty line to TERM environment parameter. -# Each line of the file is in a format something like "vt100 tty01". -# -#TTYTYPE_FILE /etc/ttytype - -# -# If defined, login failures will be logged here in a utmp format -# last, when invoked as lastb, will read /var/log/btmp, so... -# -FTMP_FILE /var/log/btmp - -# -# If defined, the command name to display when running "su -". For -# example, if this is defined as "su" then a "ps" will display the -# command is "-su". If not defined, then "ps" would display the -# name of the shell actually being run, e.g. something like "-sh". -# -SU_NAME su - -# -# If defined, file which inhibits all the usual chatter during the login -# sequence. If a full pathname, then hushed mode will be enabled if the -# user's name or shell are found in the file. If not a full pathname, then -# hushed mode will be enabled if the file exists in the user's home directory. -# -HUSHLOGIN_FILE .hushlogin -#HUSHLOGIN_FILE /etc/hushlogins - -# -# *REQUIRED* The default PATH settings, for superuser and normal users. -# -# (they are minimal, add the rest in the shell startup files) -ENV_SUPATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin -ENV_PATH PATH=/usr/local/bin:/usr/bin:/bin{% if additional_user_paths %}{{ additional_user_paths }}{% endif %} - -# -# Terminal permissions -# -# TTYGROUP Login tty will be assigned this group ownership. -# TTYPERM Login tty will be set to this permission. -# -# If you have a "write" program which is "setgid" to a special group -# which owns the terminals, define TTYGROUP to the group number and -# TTYPERM to 0620. Otherwise leave TTYGROUP commented out and assign -# TTYPERM to either 622 or 600. -# -# In Debian /usr/bin/bsd-write or similar programs are setgid tty -# However, the default and recommended value for TTYPERM is still 0600 -# to not allow anyone to write to anyone else console or terminal - -# Users can still allow other people to write them by issuing -# the "mesg y" command. - -TTYGROUP tty -TTYPERM 0600 - -# -# Login configuration initializations: -# -# ERASECHAR Terminal ERASE character ('\010' = backspace). -# KILLCHAR Terminal KILL character ('\025' = CTRL/U). -# UMASK Default "umask" value. -# -# The ERASECHAR and KILLCHAR are used only on System V machines. -# -# UMASK is the default umask value for pam_umask and is used by -# useradd and newusers to set the mode of the new home directories. -# 022 is the "historical" value in Debian for UMASK -# 027, or even 077, could be considered better for privacy -# There is no One True Answer here : each sysadmin must make up his/her -# mind. -# -# If USERGROUPS_ENAB is set to "yes", that will modify this UMASK default value -# for private user groups, i. e. the uid is the same as gid, and username is -# the same as the primary group name: for these, the user permissions will be -# used as group permissions, e. g. 022 will become 002. -# -# Prefix these values with "0" to get octal, "0x" to get hexadecimal. -# -ERASECHAR 0177 -KILLCHAR 025 -UMASK {{ umask }} - -# Enable setting of the umask group bits to be the same as owner bits (examples: `022` -> `002`, `077` -> `007`) for non-root users, if the uid is the same as gid, and username is the same as the primary group name. -# If set to yes, userdel will remove the user´s group if it contains no more members, and useradd will create by default a group with the name of the user. -USERGROUPS_ENAB yes - -# -# Password aging controls: -# -# PASS_MAX_DAYS Maximum number of days a password may be used. -# PASS_MIN_DAYS Minimum number of days allowed between password changes. -# PASS_WARN_AGE Number of days warning given before a password expires. -# -PASS_MAX_DAYS {{ pwd_max_age }} -PASS_MIN_DAYS {{ pwd_min_age }} -PASS_WARN_AGE 7 - -# -# Min/max values for automatic uid selection in useradd -# -UID_MIN {{ uid_min }} -UID_MAX 60000 -# System accounts -SYS_UID_MIN {{ sys_uid_min }} -SYS_UID_MAX {{ sys_uid_max }} - -# Min/max values for automatic gid selection in groupadd -GID_MIN {{ gid_min }} -GID_MAX 60000 -# System accounts -SYS_GID_MIN {{ sys_gid_min }} -SYS_GID_MAX {{ sys_gid_max }} - -# -# Max number of login retries if password is bad. This will most likely be -# overriden by PAM, since the default pam_unix module has it's own built -# in of 3 retries. However, this is a safe fallback in case you are using -# an authentication module that does not enforce PAM_MAXTRIES. -# -LOGIN_RETRIES {{ login_retries }} - -# -# Max time in seconds for login -# -LOGIN_TIMEOUT {{ login_timeout }} - -# -# Which fields may be changed by regular users using chfn - use -# any combination of letters "frwh" (full name, room number, work -# phone, home phone). If not defined, no changes are allowed. -# For backward compatibility, "yes" = "rwh" and "no" = "frwh". -# -{% if chfn_restrict %} -CHFN_RESTRICT {{ chfn_restrict }} -{% endif %} - -# -# Should login be allowed if we can't cd to the home directory? -# Default in no. -# -DEFAULT_HOME {% if allow_login_without_home %} yes {% else %} no {% endif %} - -# -# If defined, this command is run when removing a user. -# It should remove any at/cron/print jobs etc. owned by -# the user to be removed (passed as the first argument). -# -#USERDEL_CMD /usr/sbin/userdel_local - -# -# Enable setting of the umask group bits to be the same as owner bits -# (examples: 022 -> 002, 077 -> 007) for non-root users, if the uid is -# the same as gid, and username is the same as the primary group name. -# -# If set to yes, userdel will remove the user´s group if it contains no -# more members, and useradd will create by default a group with the name -# of the user. -# -USERGROUPS_ENAB yes - -# -# Instead of the real user shell, the program specified by this parameter -# will be launched, although its visible name (argv[0]) will be the shell's. -# The program may do whatever it wants (logging, additional authentification, -# banner, ...) before running the actual shell. -# -# FAKE_SHELL /bin/fakeshell - -# -# If defined, either full pathname of a file containing device names or -# a ":" delimited list of device names. Root logins will be allowed only -# upon these devices. -# -# This variable is used by login and su. -# -#CONSOLE /etc/consoles -#CONSOLE console:tty01:tty02:tty03:tty04 - -# -# List of groups to add to the user's supplementary group set -# when logging in on the console (as determined by the CONSOLE -# setting). Default is none. -# -# Use with caution - it is possible for users to gain permanent -# access to these groups, even when not logged in on the console. -# How to do it is left as an exercise for the reader... -# -# This variable is used by login and su. -# -#CONSOLE_GROUPS floppy:audio:cdrom - -# -# If set to "yes", new passwords will be encrypted using the MD5-based -# algorithm compatible with the one used by recent releases of FreeBSD. -# It supports passwords of unlimited length and longer salt strings. -# Set to "no" if you need to copy encrypted passwords to other systems -# which don't understand the new algorithm. Default is "no". -# -# This variable is deprecated. You should use ENCRYPT_METHOD. -# -MD5_CRYPT_ENAB no - -# -# If set to MD5 , MD5-based algorithm will be used for encrypting password -# If set to SHA256, SHA256-based algorithm will be used for encrypting password -# If set to SHA512, SHA512-based algorithm will be used for encrypting password -# If set to DES, DES-based algorithm will be used for encrypting password (default) -# Overrides the MD5_CRYPT_ENAB option -# -# Note: It is recommended to use a value consistent with -# the PAM modules configuration. -# -ENCRYPT_METHOD SHA512 - -# -# Only used if ENCRYPT_METHOD is set to SHA256 or SHA512. -# -# Define the number of SHA rounds. -# With a lot of rounds, it is more difficult to brute forcing the password. -# But note also that it more CPU resources will be needed to authenticate -# users. -# -# If not specified, the libc will choose the default number of rounds (5000). -# The values must be inside the 1000-999999999 range. -# If only one of the MIN or MAX values is set, then this value will be used. -# If MIN > MAX, the highest value will be used. -# -# SHA_CRYPT_MIN_ROUNDS 5000 -# SHA_CRYPT_MAX_ROUNDS 5000 - -################# OBSOLETED BY PAM ############## -# # -# These options are now handled by PAM. Please # -# edit the appropriate file in /etc/pam.d/ to # -# enable the equivelants of them. -# -############### - -#MOTD_FILE -#DIALUPS_CHECK_ENAB -#LASTLOG_ENAB -#MAIL_CHECK_ENAB -#OBSCURE_CHECKS_ENAB -#PORTTIME_CHECKS_ENAB -#SU_WHEEL_ONLY -#CRACKLIB_DICTPATH -#PASS_CHANGE_TRIES -#PASS_ALWAYS_WARN -#ENVIRON_FILE -#NOLOGINS_FILE -#ISSUE_FILE -#PASS_MIN_LEN -#PASS_MAX_LEN -#ULIMIT -#ENV_HZ -#CHFN_AUTH -#CHSH_AUTH -#FAIL_DELAY - -################# OBSOLETED ####################### -# # -# These options are no more handled by shadow. # -# # -# Shadow utilities will display a warning if they # -# still appear. # -# # -################################################### - -# CLOSE_SESSIONS -# LOGIN_STRING -# NO_PASSWORD_CONSOLE -# QMAIL_DIR - - - diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/modules b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/modules deleted file mode 100644 index ef0354e..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/modules +++ /dev/null @@ -1,117 +0,0 @@ -############################################################################### -# WARNING: This configuration file is maintained by Juju. Local changes may -# be overwritten. -############################################################################### -# /etc/modules: kernel modules to load at boot time. -# -# This file contains the names of kernel modules that should be loaded -# at boot time, one per line. Lines beginning with "#" are ignored. -# Parameters can be specified after the module name. - -# Arch -# ---- -# -# Modules for certains builds, contains support modules and some CPU-specific optimizations. - -{% if arch == "x86_64" -%} -# Optimize for x86_64 cryptographic features -twofish-x86_64-3way -twofish-x86_64 -aes-x86_64 -salsa20-x86_64 -blowfish-x86_64 -{% endif -%} - -{% if cpuVendor == "intel" -%} -# Intel-specific optimizations -ghash-clmulni-intel -aesni-intel -kvm-intel -{% endif -%} - -{% if cpuVendor == "amd" -%} -# AMD-specific optimizations -kvm-amd -{% endif -%} - -kvm - - -# Crypto -# ------ - -# Some core modules which comprise strong cryptography. -blowfish_common -blowfish_generic -ctr -cts -lrw -lzo -rmd160 -rmd256 -rmd320 -serpent -sha512_generic -twofish_common -twofish_generic -xts -zlib - - -# Drivers -# ------- - -# Basics -lp -rtc -loop - -# Filesystems -ext2 -btrfs - -{% if desktop_enable -%} -# Desktop -psmouse -snd -snd_ac97_codec -snd_intel8x0 -snd_page_alloc -snd_pcm -snd_timer -soundcore -usbhid -{% endif -%} - -# Lib -# --- -xz - - -# Net -# --- - -# All packets needed for netfilter rules (ie iptables, ebtables). -ip_tables -x_tables -iptable_filter -iptable_nat - -# Targets -ipt_LOG -ipt_REJECT - -# Modules -xt_connlimit -xt_tcpudp -xt_recent -xt_limit -xt_conntrack -nf_conntrack -nf_conntrack_ipv4 -nf_defrag_ipv4 -xt_state -nf_nat - -# Addons -xt_pknock
\ No newline at end of file diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/passwdqc.conf b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/passwdqc.conf deleted file mode 100644 index f98d14e..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/passwdqc.conf +++ /dev/null @@ -1,11 +0,0 @@ -############################################################################### -# WARNING: This configuration file is maintained by Juju. Local changes may -# be overwritten. -############################################################################### -Name: passwdqc password strength enforcement -Default: yes -Priority: 1024 -Conflicts: cracklib -Password-Type: Primary -Password: - requisite pam_passwdqc.so {{ auth_pam_passwdqc_options }} diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/pinerolo_profile.sh b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/pinerolo_profile.sh deleted file mode 100644 index fd2de79..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/pinerolo_profile.sh +++ /dev/null @@ -1,8 +0,0 @@ -############################################################################### -# WARNING: This configuration file is maintained by Juju. Local changes may -# be overwritten. -############################################################################### -# Disable core dumps via soft limits for all users. Compliance to this setting -# is voluntary and can be modified by users up to a hard limit. This setting is -# a sane default. -ulimit -S -c 0 > /dev/null 2>&1 diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/securetty b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/securetty deleted file mode 100644 index 15b18d4..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/securetty +++ /dev/null @@ -1,11 +0,0 @@ -############################################################################### -# WARNING: This configuration file is maintained by Juju. Local changes may -# be overwritten. -############################################################################### -# A list of TTYs, from which root can log in -# see `man securetty` for reference -{% if ttys -%} -{% for tty in ttys -%} -{{ tty }} -{% endfor -%} -{% endif -%} diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/tally2 b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/tally2 deleted file mode 100644 index d962029..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/host/templates/tally2 +++ /dev/null @@ -1,14 +0,0 @@ -############################################################################### -# WARNING: This configuration file is maintained by Juju. Local changes may -# be overwritten. -############################################################################### -Name: tally2 lockout after failed attempts enforcement -Default: yes -Priority: 1024 -Conflicts: cracklib -Auth-Type: Primary -Auth-Initial: - required pam_tally2.so deny={{ auth_retries }} onerr=fail unlock_time={{ auth_lockout_time }} -Account-Type: Primary -Account-Initial: - required pam_tally2.so diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/mysql/__init__.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/mysql/__init__.py deleted file mode 100644 index 277b8c7..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/mysql/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# 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/>. - -from os import path - -TEMPLATES_DIR = path.join(path.dirname(__file__), 'templates') diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/mysql/checks/__init__.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/mysql/checks/__init__.py deleted file mode 100644 index d4f0ec1..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/mysql/checks/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -# 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/>. - -from charmhelpers.core.hookenv import ( - log, - DEBUG, -) -from charmhelpers.contrib.hardening.mysql.checks import config - - -def run_mysql_checks(): - log("Starting MySQL hardening checks.", level=DEBUG) - checks = config.get_audits() - for check in checks: - log("Running '%s' check" % (check.__class__.__name__), level=DEBUG) - check.ensure_compliance() - - log("MySQL hardening checks complete.", level=DEBUG) diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/mysql/checks/config.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/mysql/checks/config.py deleted file mode 100644 index 3af8b89..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/mysql/checks/config.py +++ /dev/null @@ -1,89 +0,0 @@ -# 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 six -import subprocess - -from charmhelpers.core.hookenv import ( - log, - WARNING, -) -from charmhelpers.contrib.hardening.audits.file import ( - FilePermissionAudit, - DirectoryPermissionAudit, - TemplatedFile, -) -from charmhelpers.contrib.hardening.mysql import TEMPLATES_DIR -from charmhelpers.contrib.hardening import utils - - -def get_audits(): - """Get MySQL hardening config audits. - - :returns: dictionary of audits - """ - if subprocess.call(['which', 'mysql'], stdout=subprocess.PIPE) != 0: - log("MySQL does not appear to be installed on this node - " - "skipping mysql hardening", level=WARNING) - return [] - - settings = utils.get_settings('mysql') - hardening_settings = settings['hardening'] - my_cnf = hardening_settings['mysql-conf'] - - audits = [ - FilePermissionAudit(paths=[my_cnf], user='root', - group='root', mode=0o0600), - - TemplatedFile(hardening_settings['hardening-conf'], - MySQLConfContext(), - TEMPLATES_DIR, - mode=0o0750, - user='mysql', - group='root', - service_actions=[{'service': 'mysql', - 'actions': ['restart']}]), - - # MySQL and Percona charms do not allow configuration of the - # data directory, so use the default. - DirectoryPermissionAudit('/var/lib/mysql', - user='mysql', - group='mysql', - recursive=False, - mode=0o755), - - DirectoryPermissionAudit('/etc/mysql', - user='root', - group='root', - recursive=False, - mode=0o700), - ] - - return audits - - -class MySQLConfContext(object): - """Defines the set of key/value pairs to set in a mysql config file. - - This context, when called, will return a dictionary containing the - key/value pairs of setting to specify in the - /etc/mysql/conf.d/hardening.cnf file. - """ - def __call__(self): - settings = utils.get_settings('mysql') - # Translate for python3 - return {'mysql_settings': - [(k, v) for k, v in six.iteritems(settings['security'])]} diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/mysql/templates/__init__.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/mysql/templates/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/mysql/templates/__init__.py +++ /dev/null diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/mysql/templates/hardening.cnf b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/mysql/templates/hardening.cnf deleted file mode 100644 index 8242586..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/mysql/templates/hardening.cnf +++ /dev/null @@ -1,12 +0,0 @@ -############################################################################### -# WARNING: This configuration file is maintained by Juju. Local changes may -# be overwritten. -############################################################################### -[mysqld] -{% for setting, value in mysql_settings -%} -{% if value == 'True' -%} -{{ setting }} -{% elif value != 'None' and value != None -%} -{{ setting }} = {{ value }} -{% endif -%} -{% endfor -%} diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/ssh/__init__.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/ssh/__init__.py deleted file mode 100644 index 277b8c7..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/ssh/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# 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/>. - -from os import path - -TEMPLATES_DIR = path.join(path.dirname(__file__), 'templates') diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/ssh/checks/__init__.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/ssh/checks/__init__.py deleted file mode 100644 index b85150d..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/ssh/checks/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -# 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/>. - -from charmhelpers.core.hookenv import ( - log, - DEBUG, -) -from charmhelpers.contrib.hardening.ssh.checks import config - - -def run_ssh_checks(): - log("Starting SSH hardening checks.", level=DEBUG) - checks = config.get_audits() - for check in checks: - log("Running '%s' check" % (check.__class__.__name__), level=DEBUG) - check.ensure_compliance() - - log("SSH hardening checks complete.", level=DEBUG) diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/ssh/checks/config.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/ssh/checks/config.py deleted file mode 100644 index 3fb6ae8..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/ssh/checks/config.py +++ /dev/null @@ -1,394 +0,0 @@ -# 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 - -from charmhelpers.core.hookenv import ( - log, - DEBUG, -) -from charmhelpers.fetch import ( - apt_install, - apt_update, -) -from charmhelpers.core.host import lsb_release -from charmhelpers.contrib.hardening.audits.file import ( - TemplatedFile, - FileContentAudit, -) -from charmhelpers.contrib.hardening.ssh import TEMPLATES_DIR -from charmhelpers.contrib.hardening import utils - - -def get_audits(): - """Get SSH hardening config audits. - - :returns: dictionary of audits - """ - audits = [SSHConfig(), SSHDConfig(), SSHConfigFileContentAudit(), - SSHDConfigFileContentAudit()] - return audits - - -class SSHConfigContext(object): - - type = 'client' - - def get_macs(self, allow_weak_mac): - if allow_weak_mac: - weak_macs = 'weak' - else: - weak_macs = 'default' - - default = 'hmac-sha2-512,hmac-sha2-256,hmac-ripemd160' - macs = {'default': default, - 'weak': default + ',hmac-sha1'} - - default = ('hmac-sha2-512-etm@openssh.com,' - 'hmac-sha2-256-etm@openssh.com,' - 'hmac-ripemd160-etm@openssh.com,umac-128-etm@openssh.com,' - 'hmac-sha2-512,hmac-sha2-256,hmac-ripemd160') - macs_66 = {'default': default, - 'weak': default + ',hmac-sha1'} - - # Use newer ciphers on Ubuntu Trusty and above - if lsb_release()['DISTRIB_CODENAME'].lower() >= 'trusty': - log("Detected Ubuntu 14.04 or newer, using new macs", level=DEBUG) - macs = macs_66 - - return macs[weak_macs] - - def get_kexs(self, allow_weak_kex): - if allow_weak_kex: - weak_kex = 'weak' - else: - weak_kex = 'default' - - default = 'diffie-hellman-group-exchange-sha256' - weak = (default + ',diffie-hellman-group14-sha1,' - 'diffie-hellman-group-exchange-sha1,' - 'diffie-hellman-group1-sha1') - kex = {'default': default, - 'weak': weak} - - default = ('curve25519-sha256@libssh.org,' - 'diffie-hellman-group-exchange-sha256') - weak = (default + ',diffie-hellman-group14-sha1,' - 'diffie-hellman-group-exchange-sha1,' - 'diffie-hellman-group1-sha1') - kex_66 = {'default': default, - 'weak': weak} - - # Use newer kex on Ubuntu Trusty and above - if lsb_release()['DISTRIB_CODENAME'].lower() >= 'trusty': - log('Detected Ubuntu 14.04 or newer, using new key exchange ' - 'algorithms', level=DEBUG) - kex = kex_66 - - return kex[weak_kex] - - def get_ciphers(self, cbc_required): - if cbc_required: - weak_ciphers = 'weak' - else: - weak_ciphers = 'default' - - default = 'aes256-ctr,aes192-ctr,aes128-ctr' - cipher = {'default': default, - 'weak': default + 'aes256-cbc,aes192-cbc,aes128-cbc'} - - default = ('chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,' - 'aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr') - ciphers_66 = {'default': default, - 'weak': default + ',aes256-cbc,aes192-cbc,aes128-cbc'} - - # Use newer ciphers on ubuntu Trusty and above - if lsb_release()['DISTRIB_CODENAME'].lower() >= 'trusty': - log('Detected Ubuntu 14.04 or newer, using new ciphers', - level=DEBUG) - cipher = ciphers_66 - - return cipher[weak_ciphers] - - def __call__(self): - settings = utils.get_settings('ssh') - if settings['common']['network_ipv6_enable']: - addr_family = 'any' - else: - addr_family = 'inet' - - ctxt = { - 'addr_family': addr_family, - 'remote_hosts': settings['common']['remote_hosts'], - 'password_auth_allowed': - settings['client']['password_authentication'], - 'ports': settings['common']['ports'], - 'ciphers': self.get_ciphers(settings['client']['cbc_required']), - 'macs': self.get_macs(settings['client']['weak_hmac']), - 'kexs': self.get_kexs(settings['client']['weak_kex']), - 'roaming': settings['client']['roaming'], - } - return ctxt - - -class SSHConfig(TemplatedFile): - def __init__(self): - path = '/etc/ssh/ssh_config' - super(SSHConfig, self).__init__(path=path, - template_dir=TEMPLATES_DIR, - context=SSHConfigContext(), - user='root', - group='root', - mode=0o0644) - - def pre_write(self): - settings = utils.get_settings('ssh') - apt_update(fatal=True) - apt_install(settings['client']['package']) - if not os.path.exists('/etc/ssh'): - os.makedir('/etc/ssh') - # NOTE: don't recurse - utils.ensure_permissions('/etc/ssh', 'root', 'root', 0o0755, - maxdepth=0) - - def post_write(self): - # NOTE: don't recurse - utils.ensure_permissions('/etc/ssh', 'root', 'root', 0o0755, - maxdepth=0) - - -class SSHDConfigContext(SSHConfigContext): - - type = 'server' - - def __call__(self): - settings = utils.get_settings('ssh') - if settings['common']['network_ipv6_enable']: - addr_family = 'any' - else: - addr_family = 'inet' - - ctxt = { - 'ssh_ip': settings['server']['listen_to'], - 'password_auth_allowed': - settings['server']['password_authentication'], - 'ports': settings['common']['ports'], - 'addr_family': addr_family, - 'ciphers': self.get_ciphers(settings['server']['cbc_required']), - 'macs': self.get_macs(settings['server']['weak_hmac']), - 'kexs': self.get_kexs(settings['server']['weak_kex']), - 'host_key_files': settings['server']['host_key_files'], - 'allow_root_with_key': settings['server']['allow_root_with_key'], - 'password_authentication': - settings['server']['password_authentication'], - 'use_priv_sep': settings['server']['use_privilege_separation'], - 'use_pam': settings['server']['use_pam'], - 'allow_x11_forwarding': settings['server']['allow_x11_forwarding'], - 'print_motd': settings['server']['print_motd'], - 'print_last_log': settings['server']['print_last_log'], - 'client_alive_interval': - settings['server']['alive_interval'], - 'client_alive_count': settings['server']['alive_count'], - 'allow_tcp_forwarding': settings['server']['allow_tcp_forwarding'], - 'allow_agent_forwarding': - settings['server']['allow_agent_forwarding'], - 'deny_users': settings['server']['deny_users'], - 'allow_users': settings['server']['allow_users'], - 'deny_groups': settings['server']['deny_groups'], - 'allow_groups': settings['server']['allow_groups'], - 'use_dns': settings['server']['use_dns'], - 'sftp_enable': settings['server']['sftp_enable'], - 'sftp_group': settings['server']['sftp_group'], - 'sftp_chroot': settings['server']['sftp_chroot'], - 'max_auth_tries': settings['server']['max_auth_tries'], - 'max_sessions': settings['server']['max_sessions'], - } - return ctxt - - -class SSHDConfig(TemplatedFile): - def __init__(self): - path = '/etc/ssh/sshd_config' - super(SSHDConfig, self).__init__(path=path, - template_dir=TEMPLATES_DIR, - context=SSHDConfigContext(), - user='root', - group='root', - mode=0o0600, - service_actions=[{'service': 'ssh', - 'actions': - ['restart']}]) - - def pre_write(self): - settings = utils.get_settings('ssh') - apt_update(fatal=True) - apt_install(settings['server']['package']) - if not os.path.exists('/etc/ssh'): - os.makedir('/etc/ssh') - # NOTE: don't recurse - utils.ensure_permissions('/etc/ssh', 'root', 'root', 0o0755, - maxdepth=0) - - def post_write(self): - # NOTE: don't recurse - utils.ensure_permissions('/etc/ssh', 'root', 'root', 0o0755, - maxdepth=0) - - -class SSHConfigFileContentAudit(FileContentAudit): - def __init__(self): - self.path = '/etc/ssh/ssh_config' - super(SSHConfigFileContentAudit, self).__init__(self.path, {}) - - def is_compliant(self, *args, **kwargs): - self.pass_cases = [] - self.fail_cases = [] - settings = utils.get_settings('ssh') - - if lsb_release()['DISTRIB_CODENAME'].lower() >= 'trusty': - if not settings['server']['weak_hmac']: - self.pass_cases.append(r'^MACs.+,hmac-ripemd160$') - else: - self.pass_cases.append(r'^MACs.+,hmac-sha1$') - - if settings['server']['weak_kex']: - self.fail_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha256[,\s]?') # noqa - self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group14-sha1[,\s]?') # noqa - self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha1[,\s]?') # noqa - self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group1-sha1[,\s]?') # noqa - else: - self.pass_cases.append(r'^KexAlgorithms.+,diffie-hellman-group-exchange-sha256$') # noqa - self.fail_cases.append(r'^KexAlgorithms.*diffie-hellman-group14-sha1[,\s]?') # noqa - - if settings['server']['cbc_required']: - self.pass_cases.append(r'^Ciphers\s.*-cbc[,\s]?') - self.fail_cases.append(r'^Ciphers\s.*aes128-ctr[,\s]?') - self.fail_cases.append(r'^Ciphers\s.*aes192-ctr[,\s]?') - self.fail_cases.append(r'^Ciphers\s.*aes256-ctr[,\s]?') - else: - self.fail_cases.append(r'^Ciphers\s.*-cbc[,\s]?') - self.pass_cases.append(r'^Ciphers\schacha20-poly1305@openssh.com,.+') # noqa - self.pass_cases.append(r'^Ciphers\s.*aes128-ctr$') - self.pass_cases.append(r'^Ciphers\s.*aes192-ctr[,\s]?') - self.pass_cases.append(r'^Ciphers\s.*aes256-ctr[,\s]?') - else: - if not settings['client']['weak_hmac']: - self.fail_cases.append(r'^MACs.+,hmac-sha1$') - else: - self.pass_cases.append(r'^MACs.+,hmac-sha1$') - - if settings['client']['weak_kex']: - self.fail_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha256[,\s]?') # noqa - self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group14-sha1[,\s]?') # noqa - self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha1[,\s]?') # noqa - self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group1-sha1[,\s]?') # noqa - else: - self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha256$') # noqa - self.fail_cases.append(r'^KexAlgorithms\sdiffie-hellman-group14-sha1[,\s]?') # noqa - self.fail_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha1[,\s]?') # noqa - self.fail_cases.append(r'^KexAlgorithms\sdiffie-hellman-group1-sha1[,\s]?') # noqa - - if settings['client']['cbc_required']: - self.pass_cases.append(r'^Ciphers\s.*-cbc[,\s]?') - self.fail_cases.append(r'^Ciphers\s.*aes128-ctr[,\s]?') - self.fail_cases.append(r'^Ciphers\s.*aes192-ctr[,\s]?') - self.fail_cases.append(r'^Ciphers\s.*aes256-ctr[,\s]?') - else: - self.fail_cases.append(r'^Ciphers\s.*-cbc[,\s]?') - self.pass_cases.append(r'^Ciphers\s.*aes128-ctr[,\s]?') - self.pass_cases.append(r'^Ciphers\s.*aes192-ctr[,\s]?') - self.pass_cases.append(r'^Ciphers\s.*aes256-ctr[,\s]?') - - if settings['client']['roaming']: - self.pass_cases.append(r'^UseRoaming yes$') - else: - self.fail_cases.append(r'^UseRoaming yes$') - - return super(SSHConfigFileContentAudit, self).is_compliant(*args, - **kwargs) - - -class SSHDConfigFileContentAudit(FileContentAudit): - def __init__(self): - self.path = '/etc/ssh/sshd_config' - super(SSHDConfigFileContentAudit, self).__init__(self.path, {}) - - def is_compliant(self, *args, **kwargs): - self.pass_cases = [] - self.fail_cases = [] - settings = utils.get_settings('ssh') - - if lsb_release()['DISTRIB_CODENAME'].lower() >= 'trusty': - if not settings['server']['weak_hmac']: - self.pass_cases.append(r'^MACs.+,hmac-ripemd160$') - else: - self.pass_cases.append(r'^MACs.+,hmac-sha1$') - - if settings['server']['weak_kex']: - self.fail_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha256[,\s]?') # noqa - self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group14-sha1[,\s]?') # noqa - self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha1[,\s]?') # noqa - self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group1-sha1[,\s]?') # noqa - else: - self.pass_cases.append(r'^KexAlgorithms.+,diffie-hellman-group-exchange-sha256$') # noqa - self.fail_cases.append(r'^KexAlgorithms.*diffie-hellman-group14-sha1[,\s]?') # noqa - - if settings['server']['cbc_required']: - self.pass_cases.append(r'^Ciphers\s.*-cbc[,\s]?') - self.fail_cases.append(r'^Ciphers\s.*aes128-ctr[,\s]?') - self.fail_cases.append(r'^Ciphers\s.*aes192-ctr[,\s]?') - self.fail_cases.append(r'^Ciphers\s.*aes256-ctr[,\s]?') - else: - self.fail_cases.append(r'^Ciphers\s.*-cbc[,\s]?') - self.pass_cases.append(r'^Ciphers\schacha20-poly1305@openssh.com,.+') # noqa - self.pass_cases.append(r'^Ciphers\s.*aes128-ctr$') - self.pass_cases.append(r'^Ciphers\s.*aes192-ctr[,\s]?') - self.pass_cases.append(r'^Ciphers\s.*aes256-ctr[,\s]?') - else: - if not settings['server']['weak_hmac']: - self.pass_cases.append(r'^MACs.+,hmac-ripemd160$') - else: - self.pass_cases.append(r'^MACs.+,hmac-sha1$') - - if settings['server']['weak_kex']: - self.fail_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha256[,\s]?') # noqa - self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group14-sha1[,\s]?') # noqa - self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha1[,\s]?') # noqa - self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group1-sha1[,\s]?') # noqa - else: - self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha256$') # noqa - self.fail_cases.append(r'^KexAlgorithms\sdiffie-hellman-group14-sha1[,\s]?') # noqa - self.fail_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha1[,\s]?') # noqa - self.fail_cases.append(r'^KexAlgorithms\sdiffie-hellman-group1-sha1[,\s]?') # noqa - - if settings['server']['cbc_required']: - self.pass_cases.append(r'^Ciphers\s.*-cbc[,\s]?') - self.fail_cases.append(r'^Ciphers\s.*aes128-ctr[,\s]?') - self.fail_cases.append(r'^Ciphers\s.*aes192-ctr[,\s]?') - self.fail_cases.append(r'^Ciphers\s.*aes256-ctr[,\s]?') - else: - self.fail_cases.append(r'^Ciphers\s.*-cbc[,\s]?') - self.pass_cases.append(r'^Ciphers\s.*aes128-ctr[,\s]?') - self.pass_cases.append(r'^Ciphers\s.*aes192-ctr[,\s]?') - self.pass_cases.append(r'^Ciphers\s.*aes256-ctr[,\s]?') - - if settings['server']['sftp_enable']: - self.pass_cases.append(r'^Subsystem\ssftp') - else: - self.fail_cases.append(r'^Subsystem\ssftp') - - return super(SSHDConfigFileContentAudit, self).is_compliant(*args, - **kwargs) diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/ssh/templates/__init__.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/ssh/templates/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/ssh/templates/__init__.py +++ /dev/null diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/ssh/templates/ssh_config b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/ssh/templates/ssh_config deleted file mode 100644 index 9742d8e..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/ssh/templates/ssh_config +++ /dev/null @@ -1,70 +0,0 @@ -############################################################################### -# WARNING: This configuration file is maintained by Juju. Local changes may -# be overwritten. -############################################################################### -# This is the ssh client system-wide configuration file. See -# ssh_config(5) for more information. This file provides defaults for -# users, and the values can be changed in per-user configuration files -# or on the command line. - -# Configuration data is parsed as follows: -# 1. command line options -# 2. user-specific file -# 3. system-wide file -# Any configuration value is only changed the first time it is set. -# Thus, host-specific definitions should be at the beginning of the -# configuration file, and defaults at the end. - -# Site-wide defaults for some commonly used options. For a comprehensive -# list of available options, their meanings and defaults, please see the -# ssh_config(5) man page. - -# Restrict the following configuration to be limited to this Host. -{% if remote_hosts -%} -Host {{ ' '.join(remote_hosts) }} -{% endif %} -ForwardAgent no -ForwardX11 no -ForwardX11Trusted yes -RhostsRSAAuthentication no -RSAAuthentication yes -PasswordAuthentication {{ password_auth_allowed }} -HostbasedAuthentication no -GSSAPIAuthentication no -GSSAPIDelegateCredentials no -GSSAPIKeyExchange no -GSSAPITrustDNS no -BatchMode no -CheckHostIP yes -AddressFamily {{ addr_family }} -ConnectTimeout 0 -StrictHostKeyChecking ask -IdentityFile ~/.ssh/identity -IdentityFile ~/.ssh/id_rsa -IdentityFile ~/.ssh/id_dsa -# The port at the destination should be defined -{% for port in ports -%} -Port {{ port }} -{% endfor %} -Protocol 2 -Cipher 3des -{% if ciphers -%} -Ciphers {{ ciphers }} -{%- endif %} -{% if macs -%} -MACs {{ macs }} -{%- endif %} -{% if kexs -%} -KexAlgorithms {{ kexs }} -{%- endif %} -EscapeChar ~ -Tunnel no -TunnelDevice any:any -PermitLocalCommand no -VisualHostKey no -RekeyLimit 1G 1h -SendEnv LANG LC_* -HashKnownHosts yes -{% if roaming -%} -UseRoaming {{ roaming }} -{% endif %} diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/ssh/templates/sshd_config b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/ssh/templates/sshd_config deleted file mode 100644 index 5f87298..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/ssh/templates/sshd_config +++ /dev/null @@ -1,159 +0,0 @@ -############################################################################### -# WARNING: This configuration file is maintained by Juju. Local changes may -# be overwritten. -############################################################################### -# Package generated configuration file -# See the sshd_config(5) manpage for details - -# What ports, IPs and protocols we listen for -{% for port in ports -%} -Port {{ port }} -{% endfor -%} -AddressFamily {{ addr_family }} -# Use these options to restrict which interfaces/protocols sshd will bind to -{% if ssh_ip -%} -{% for ip in ssh_ip -%} -ListenAddress {{ ip }} -{% endfor %} -{%- else -%} -ListenAddress :: -ListenAddress 0.0.0.0 -{% endif -%} -Protocol 2 -{% if ciphers -%} -Ciphers {{ ciphers }} -{% endif -%} -{% if macs -%} -MACs {{ macs }} -{% endif -%} -{% if kexs -%} -KexAlgorithms {{ kexs }} -{% endif -%} -# HostKeys for protocol version 2 -{% for keyfile in host_key_files -%} -HostKey {{ keyfile }} -{% endfor -%} - -# Privilege Separation is turned on for security -{% if use_priv_sep -%} -UsePrivilegeSeparation {{ use_priv_sep }} -{% endif -%} - -# Lifetime and size of ephemeral version 1 server key -KeyRegenerationInterval 3600 -ServerKeyBits 1024 - -# Logging -SyslogFacility AUTH -LogLevel VERBOSE - -# Authentication: -LoginGraceTime 30s -{% if allow_root_with_key -%} -PermitRootLogin without-password -{% else -%} -PermitRootLogin no -{% endif %} -PermitTunnel no -PermitUserEnvironment no -StrictModes yes - -RSAAuthentication yes -PubkeyAuthentication yes -AuthorizedKeysFile %h/.ssh/authorized_keys - -# Don't read the user's ~/.rhosts and ~/.shosts files -IgnoreRhosts yes -# For this to work you will also need host keys in /etc/ssh_known_hosts -RhostsRSAAuthentication no -# similar for protocol version 2 -HostbasedAuthentication no -# Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication -IgnoreUserKnownHosts yes - -# To enable empty passwords, change to yes (NOT RECOMMENDED) -PermitEmptyPasswords no - -# Change to yes to enable challenge-response passwords (beware issues with -# some PAM modules and threads) -ChallengeResponseAuthentication no - -# Change to no to disable tunnelled clear text passwords -PasswordAuthentication {{ password_authentication }} - -# Kerberos options -KerberosAuthentication no -KerberosGetAFSToken no -KerberosOrLocalPasswd no -KerberosTicketCleanup yes - -# GSSAPI options -GSSAPIAuthentication no -GSSAPICleanupCredentials yes - -X11Forwarding {{ allow_x11_forwarding }} -X11DisplayOffset 10 -X11UseLocalhost yes -GatewayPorts no -PrintMotd {{ print_motd }} -PrintLastLog {{ print_last_log }} -TCPKeepAlive no -UseLogin no - -ClientAliveInterval {{ client_alive_interval }} -ClientAliveCountMax {{ client_alive_count }} -AllowTcpForwarding {{ allow_tcp_forwarding }} -AllowAgentForwarding {{ allow_agent_forwarding }} - -MaxStartups 10:30:100 -#Banner /etc/issue.net - -# Allow client to pass locale environment variables -AcceptEnv LANG LC_* - -# Set this to 'yes' to enable PAM authentication, account processing, -# and session processing. If this is enabled, PAM authentication will -# be allowed through the ChallengeResponseAuthentication and -# PasswordAuthentication. Depending on your PAM configuration, -# PAM authentication via ChallengeResponseAuthentication may bypass -# the setting of "PermitRootLogin without-password". -# If you just want the PAM account and session checks to run without -# PAM authentication, then enable this but set PasswordAuthentication -# and ChallengeResponseAuthentication to 'no'. -UsePAM {{ use_pam }} - -{% if deny_users -%} -DenyUsers {{ deny_users }} -{% endif -%} -{% if allow_users -%} -AllowUsers {{ allow_users }} -{% endif -%} -{% if deny_groups -%} -DenyGroups {{ deny_groups }} -{% endif -%} -{% if allow_groups -%} -AllowGroups allow_groups -{% endif -%} -UseDNS {{ use_dns }} -MaxAuthTries {{ max_auth_tries }} -MaxSessions {{ max_sessions }} - -{% if sftp_enable -%} -# Configuration, in case SFTP is used -## override default of no subsystems -## Subsystem sftp /opt/app/openssh5/libexec/sftp-server -Subsystem sftp internal-sftp -l VERBOSE - -## These lines must appear at the *end* of sshd_config -Match Group {{ sftp_group }} -ForceCommand internal-sftp -l VERBOSE -ChrootDirectory {{ sftp_chroot }} -{% else -%} -# Configuration, in case SFTP is used -## override default of no subsystems -## Subsystem sftp /opt/app/openssh5/libexec/sftp-server -## These lines must appear at the *end* of sshd_config -Match Group sftponly -ForceCommand internal-sftp -l VERBOSE -ChrootDirectory /sftpchroot/home/%u -{% endif %} diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/templating.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/templating.py deleted file mode 100644 index d2ab7dc..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/templating.py +++ /dev/null @@ -1,71 +0,0 @@ -# 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 - -from charmhelpers.core.hookenv import ( - log, - DEBUG, - WARNING, -) - -try: - from jinja2 import FileSystemLoader, Environment -except ImportError: - from charmhelpers.fetch import apt_install - from charmhelpers.fetch import apt_update - apt_update(fatal=True) - apt_install('python-jinja2', fatal=True) - from jinja2 import FileSystemLoader, Environment - - -# NOTE: function separated from main rendering code to facilitate easier -# mocking in unit tests. -def write(path, data): - with open(path, 'wb') as out: - out.write(data) - - -def get_template_path(template_dir, path): - """Returns the template file which would be used to render the path. - - The path to the template file is returned. - :param template_dir: the directory the templates are located in - :param path: the file path to be written to. - :returns: path to the template file - """ - return os.path.join(template_dir, os.path.basename(path)) - - -def render_and_write(template_dir, path, context): - """Renders the specified template into the file. - - :param template_dir: the directory to load the template from - :param path: the path to write the templated contents to - :param context: the parameters to pass to the rendering engine - """ - env = Environment(loader=FileSystemLoader(template_dir)) - template_file = os.path.basename(path) - template = env.get_template(template_file) - log('Rendering from template: %s' % template.name, level=DEBUG) - rendered_content = template.render(context) - if not rendered_content: - log("Render returned None - skipping '%s'" % path, - level=WARNING) - return - - write(path, rendered_content.encode('utf-8').strip()) - log('Wrote template %s' % path, level=DEBUG) diff --git a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/utils.py b/charms/trusty/ceilometer/charmhelpers/contrib/hardening/utils.py deleted file mode 100644 index a6743a4..0000000 --- a/charms/trusty/ceilometer/charmhelpers/contrib/hardening/utils.py +++ /dev/null @@ -1,157 +0,0 @@ -# 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 glob -import grp -import os -import pwd -import six -import yaml - -from charmhelpers.core.hookenv import ( - log, - DEBUG, - INFO, - WARNING, - ERROR, -) - - -# Global settings cache. Since each hook fire entails a fresh module import it -# is safe to hold this in memory and not risk missing config changes (since -# they will result in a new hook fire and thus re-import). -__SETTINGS__ = {} - - -def _get_defaults(modules): - """Load the default config for the provided modules. - - :param modules: stack modules config defaults to lookup. - :returns: modules default config dictionary. - """ - default = os.path.join(os.path.dirname(__file__), - 'defaults/%s.yaml' % (modules)) - return yaml.safe_load(open(default)) - - -def _get_schema(modules): - """Load the config schema for the provided modules. - - NOTE: this schema is intended to have 1-1 relationship with they keys in - the default config and is used a means to verify valid overrides provided - by the user. - - :param modules: stack modules config schema to lookup. - :returns: modules default schema dictionary. - """ - schema = os.path.join(os.path.dirname(__file__), - 'defaults/%s.yaml.schema' % (modules)) - return yaml.safe_load(open(schema)) - - -def _get_user_provided_overrides(modules): - """Load user-provided config overrides. - - :param modules: stack modules to lookup in user overrides yaml file. - :returns: overrides dictionary. - """ - overrides = os.path.join(os.environ['JUJU_CHARM_DIR'], - 'hardening.yaml') - if os.path.exists(overrides): - log("Found user-provided config overrides file '%s'" % - (overrides), level=DEBUG) - settings = yaml.safe_load(open(overrides)) - if settings and settings.get(modules): - log("Applying '%s' overrides" % (modules), level=DEBUG) - return settings.get(modules) - - log("No overrides found for '%s'" % (modules), level=DEBUG) - else: - log("No hardening config overrides file '%s' found in charm " - "root dir" % (overrides), level=DEBUG) - - return {} - - -def _apply_overrides(settings, overrides, schema): - """Get overrides config overlayed onto modules defaults. - - :param modules: require stack modules config. - :returns: dictionary of modules config with user overrides applied. - """ - if overrides: - for k, v in six.iteritems(overrides): - if k in schema: - if schema[k] is None: - settings[k] = v - elif type(schema[k]) is dict: - settings[k] = _apply_overrides(settings[k], overrides[k], - schema[k]) - else: - raise Exception("Unexpected type found in schema '%s'" % - type(schema[k]), level=ERROR) - else: - log("Unknown override key '%s' - ignoring" % (k), level=INFO) - - return settings - - -def get_settings(modules): - global __SETTINGS__ - if modules in __SETTINGS__: - return __SETTINGS__[modules] - - schema = _get_schema(modules) - settings = _get_defaults(modules) - overrides = _get_user_provided_overrides(modules) - __SETTINGS__[modules] = _apply_overrides(settings, overrides, schema) - return __SETTINGS__[modules] - - -def ensure_permissions(path, user, group, permissions, maxdepth=-1): - """Ensure permissions for path. - - If path is a file, apply to file and return. If path is a directory, - apply recursively (if required) to directory contents and return. - - :param user: user name - :param group: group name - :param permissions: octal permissions - :param maxdepth: maximum recursion depth. A negative maxdepth allows - infinite recursion and maxdepth=0 means no recursion. - :returns: None - """ - if not os.path.exists(path): - log("File '%s' does not exist - cannot set permissions" % (path), - level=WARNING) - return - - _user = pwd.getpwnam(user) - os.chown(path, _user.pw_uid, grp.getgrnam(group).gr_gid) - os.chmod(path, permissions) - - if maxdepth == 0: - log("Max recursion depth reached - skipping further recursion", - level=DEBUG) - return - elif maxdepth > 0: - maxdepth -= 1 - - if os.path.isdir(path): - contents = glob.glob("%s/*" % (path)) - for c in contents: - ensure_permissions(c, user=user, group=group, - permissions=permissions, maxdepth=maxdepth) |