~plumgrid-team/charms/trusty/plumgrid-director/trunk

« back to all changes in this revision

Viewing changes to hooks/charmhelpers/contrib/hardening/host/checks/sysctl.py

  • Committer: bbaqar at plumgrid
  • Date: 2016-04-25 09:14:38 UTC
  • mfrom: (30.1.3 plumgrid-director)
  • Revision ID: bbaqar@plumgrid.com-20160425091438-4hk5s00dydf00jem
Merge: Liberty/Mitaka support

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2016 Canonical Limited.
 
2
#
 
3
# This file is part of charm-helpers.
 
4
#
 
5
# charm-helpers is free software: you can redistribute it and/or modify
 
6
# it under the terms of the GNU Lesser General Public License version 3 as
 
7
# published by the Free Software Foundation.
 
8
#
 
9
# charm-helpers is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU Lesser General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU Lesser General Public License
 
15
# along with charm-helpers.  If not, see <http://www.gnu.org/licenses/>.
 
16
 
 
17
import os
 
18
import platform
 
19
import re
 
20
import six
 
21
import subprocess
 
22
 
 
23
from charmhelpers.core.hookenv import (
 
24
    log,
 
25
    INFO,
 
26
    WARNING,
 
27
)
 
28
from charmhelpers.contrib.hardening import utils
 
29
from charmhelpers.contrib.hardening.audits.file import (
 
30
    FilePermissionAudit,
 
31
    TemplatedFile,
 
32
)
 
33
from charmhelpers.contrib.hardening.host import TEMPLATES_DIR
 
34
 
 
35
 
 
36
SYSCTL_DEFAULTS = """net.ipv4.ip_forward=%(net_ipv4_ip_forward)s
 
37
net.ipv6.conf.all.forwarding=%(net_ipv6_conf_all_forwarding)s
 
38
net.ipv4.conf.all.rp_filter=1
 
39
net.ipv4.conf.default.rp_filter=1
 
40
net.ipv4.icmp_echo_ignore_broadcasts=1
 
41
net.ipv4.icmp_ignore_bogus_error_responses=1
 
42
net.ipv4.icmp_ratelimit=100
 
43
net.ipv4.icmp_ratemask=88089
 
44
net.ipv6.conf.all.disable_ipv6=%(net_ipv6_conf_all_disable_ipv6)s
 
45
net.ipv4.tcp_timestamps=%(net_ipv4_tcp_timestamps)s
 
46
net.ipv4.conf.all.arp_ignore=%(net_ipv4_conf_all_arp_ignore)s
 
47
net.ipv4.conf.all.arp_announce=%(net_ipv4_conf_all_arp_announce)s
 
48
net.ipv4.tcp_rfc1337=1
 
49
net.ipv4.tcp_syncookies=1
 
50
net.ipv4.conf.all.shared_media=1
 
51
net.ipv4.conf.default.shared_media=1
 
52
net.ipv4.conf.all.accept_source_route=0
 
53
net.ipv4.conf.default.accept_source_route=0
 
54
net.ipv4.conf.all.accept_redirects=0
 
55
net.ipv4.conf.default.accept_redirects=0
 
56
net.ipv6.conf.all.accept_redirects=0
 
57
net.ipv6.conf.default.accept_redirects=0
 
58
net.ipv4.conf.all.secure_redirects=0
 
59
net.ipv4.conf.default.secure_redirects=0
 
60
net.ipv4.conf.all.send_redirects=0
 
61
net.ipv4.conf.default.send_redirects=0
 
62
net.ipv4.conf.all.log_martians=0
 
63
net.ipv6.conf.default.router_solicitations=0
 
64
net.ipv6.conf.default.accept_ra_rtr_pref=0
 
65
net.ipv6.conf.default.accept_ra_pinfo=0
 
66
net.ipv6.conf.default.accept_ra_defrtr=0
 
67
net.ipv6.conf.default.autoconf=0
 
68
net.ipv6.conf.default.dad_transmits=0
 
69
net.ipv6.conf.default.max_addresses=1
 
70
net.ipv6.conf.all.accept_ra=0
 
71
net.ipv6.conf.default.accept_ra=0
 
72
kernel.modules_disabled=%(kernel_modules_disabled)s
 
73
kernel.sysrq=%(kernel_sysrq)s
 
74
fs.suid_dumpable=%(fs_suid_dumpable)s
 
75
kernel.randomize_va_space=2
 
76
"""
 
77
 
 
78
 
 
79
def get_audits():
 
80
    """Get OS hardening sysctl audits.
 
81
 
 
82
    :returns:  dictionary of audits
 
83
    """
 
84
    audits = []
 
85
    settings = utils.get_settings('os')
 
86
 
 
87
    # Apply the sysctl settings which are configured to be applied.
 
88
    audits.append(SysctlConf())
 
89
    # Make sure that only root has access to the sysctl.conf file, and
 
90
    # that it is read-only.
 
91
    audits.append(FilePermissionAudit('/etc/sysctl.conf',
 
92
                                      user='root',
 
93
                                      group='root', mode=0o0440))
 
94
    # If module loading is not enabled, then ensure that the modules
 
95
    # file has the appropriate permissions and rebuild the initramfs
 
96
    if not settings['security']['kernel_enable_module_loading']:
 
97
        audits.append(ModulesTemplate())
 
98
 
 
99
    return audits
 
100
 
 
101
 
 
102
class ModulesContext(object):
 
103
 
 
104
    def __call__(self):
 
105
        settings = utils.get_settings('os')
 
106
        with open('/proc/cpuinfo', 'r') as fd:
 
107
            cpuinfo = fd.readlines()
 
108
 
 
109
        for line in cpuinfo:
 
110
            match = re.search(r"^vendor_id\s+:\s+(.+)", line)
 
111
            if match:
 
112
                vendor = match.group(1)
 
113
 
 
114
        if vendor == "GenuineIntel":
 
115
            vendor = "intel"
 
116
        elif vendor == "AuthenticAMD":
 
117
            vendor = "amd"
 
118
 
 
119
        ctxt = {'arch': platform.processor(),
 
120
                'cpuVendor': vendor,
 
121
                'desktop_enable': settings['general']['desktop_enable']}
 
122
 
 
123
        return ctxt
 
124
 
 
125
 
 
126
class ModulesTemplate(object):
 
127
 
 
128
    def __init__(self):
 
129
        super(ModulesTemplate, self).__init__('/etc/initramfs-tools/modules',
 
130
                                              ModulesContext(),
 
131
                                              templates_dir=TEMPLATES_DIR,
 
132
                                              user='root', group='root',
 
133
                                              mode=0o0440)
 
134
 
 
135
    def post_write(self):
 
136
        subprocess.check_call(['update-initramfs', '-u'])
 
137
 
 
138
 
 
139
class SysCtlHardeningContext(object):
 
140
    def __call__(self):
 
141
        settings = utils.get_settings('os')
 
142
        ctxt = {'sysctl': {}}
 
143
 
 
144
        log("Applying sysctl settings", level=INFO)
 
145
        extras = {'net_ipv4_ip_forward': 0,
 
146
                  'net_ipv6_conf_all_forwarding': 0,
 
147
                  'net_ipv6_conf_all_disable_ipv6': 1,
 
148
                  'net_ipv4_tcp_timestamps': 0,
 
149
                  'net_ipv4_conf_all_arp_ignore': 0,
 
150
                  'net_ipv4_conf_all_arp_announce': 0,
 
151
                  'kernel_sysrq': 0,
 
152
                  'fs_suid_dumpable': 0,
 
153
                  'kernel_modules_disabled': 1}
 
154
 
 
155
        if settings['sysctl']['ipv6_enable']:
 
156
            extras['net_ipv6_conf_all_disable_ipv6'] = 0
 
157
 
 
158
        if settings['sysctl']['forwarding']:
 
159
            extras['net_ipv4_ip_forward'] = 1
 
160
            extras['net_ipv6_conf_all_forwarding'] = 1
 
161
 
 
162
        if settings['sysctl']['arp_restricted']:
 
163
            extras['net_ipv4_conf_all_arp_ignore'] = 1
 
164
            extras['net_ipv4_conf_all_arp_announce'] = 2
 
165
 
 
166
        if settings['security']['kernel_enable_module_loading']:
 
167
            extras['kernel_modules_disabled'] = 0
 
168
 
 
169
        if settings['sysctl']['kernel_enable_sysrq']:
 
170
            sysrq_val = settings['sysctl']['kernel_secure_sysrq']
 
171
            extras['kernel_sysrq'] = sysrq_val
 
172
 
 
173
        if settings['security']['kernel_enable_core_dump']:
 
174
            extras['fs_suid_dumpable'] = 1
 
175
 
 
176
        settings.update(extras)
 
177
        for d in (SYSCTL_DEFAULTS % settings).split():
 
178
            d = d.strip().partition('=')
 
179
            key = d[0].strip()
 
180
            path = os.path.join('/proc/sys', key.replace('.', '/'))
 
181
            if not os.path.exists(path):
 
182
                log("Skipping '%s' since '%s' does not exist" % (key, path),
 
183
                    level=WARNING)
 
184
                continue
 
185
 
 
186
            ctxt['sysctl'][key] = d[2] or None
 
187
 
 
188
        # Translate for python3
 
189
        return {'sysctl_settings':
 
190
                [(k, v) for k, v in six.iteritems(ctxt['sysctl'])]}
 
191
 
 
192
 
 
193
class SysctlConf(TemplatedFile):
 
194
    """An audit check for sysctl settings."""
 
195
    def __init__(self):
 
196
        self.conffile = '/etc/sysctl.d/99-juju-hardening.conf'
 
197
        super(SysctlConf, self).__init__(self.conffile,
 
198
                                         SysCtlHardeningContext(),
 
199
                                         template_dir=TEMPLATES_DIR,
 
200
                                         user='root', group='root',
 
201
                                         mode=0o0440)
 
202
 
 
203
    def post_write(self):
 
204
        try:
 
205
            subprocess.check_call(['sysctl', '-p', self.conffile])
 
206
        except subprocess.CalledProcessError as e:
 
207
            # NOTE: on some systems if sysctl cannot apply all settings it
 
208
            #       will return non-zero as well.
 
209
            log("sysctl command returned an error (maybe some "
 
210
                "keys could not be set) - %s" % (e),
 
211
                level=WARNING)