~bbaqar/charms/trusty/neutron-api-plumgrid/temp

« back to all changes in this revision

Viewing changes to hooks/charmhelpers/contrib/hardening/ssh/checks/config.py

  • Committer: bbaqar at plumgrid
  • Date: 2016-05-17 18:03:56 UTC
  • mfrom: (19.1.13 neutron-api-plumgrid)
  • Revision ID: bbaqar@plumgrid.com-20160517180356-vyr2sx8kkq205gyl
changes

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
 
 
19
 
from charmhelpers.core.hookenv import (
20
 
    log,
21
 
    DEBUG,
22
 
)
23
 
from charmhelpers.fetch import (
24
 
    apt_install,
25
 
    apt_update,
26
 
)
27
 
from charmhelpers.core.host import lsb_release
28
 
from charmhelpers.contrib.hardening.audits.file import (
29
 
    TemplatedFile,
30
 
    FileContentAudit,
31
 
)
32
 
from charmhelpers.contrib.hardening.ssh import TEMPLATES_DIR
33
 
from charmhelpers.contrib.hardening import utils
34
 
 
35
 
 
36
 
def get_audits():
37
 
    """Get SSH hardening config audits.
38
 
 
39
 
    :returns:  dictionary of audits
40
 
    """
41
 
    audits = [SSHConfig(), SSHDConfig(), SSHConfigFileContentAudit(),
42
 
              SSHDConfigFileContentAudit()]
43
 
    return audits
44
 
 
45
 
 
46
 
class SSHConfigContext(object):
47
 
 
48
 
    type = 'client'
49
 
 
50
 
    def get_macs(self, allow_weak_mac):
51
 
        if allow_weak_mac:
52
 
            weak_macs = 'weak'
53
 
        else:
54
 
            weak_macs = 'default'
55
 
 
56
 
        default = 'hmac-sha2-512,hmac-sha2-256,hmac-ripemd160'
57
 
        macs = {'default': default,
58
 
                'weak': default + ',hmac-sha1'}
59
 
 
60
 
        default = ('hmac-sha2-512-etm@openssh.com,'
61
 
                   'hmac-sha2-256-etm@openssh.com,'
62
 
                   'hmac-ripemd160-etm@openssh.com,umac-128-etm@openssh.com,'
63
 
                   'hmac-sha2-512,hmac-sha2-256,hmac-ripemd160')
64
 
        macs_66 = {'default': default,
65
 
                   'weak': default + ',hmac-sha1'}
66
 
 
67
 
        # Use newer ciphers on Ubuntu Trusty and above
68
 
        if lsb_release()['DISTRIB_CODENAME'].lower() >= 'trusty':
69
 
            log("Detected Ubuntu 14.04 or newer, using new macs", level=DEBUG)
70
 
            macs = macs_66
71
 
 
72
 
        return macs[weak_macs]
73
 
 
74
 
    def get_kexs(self, allow_weak_kex):
75
 
        if allow_weak_kex:
76
 
            weak_kex = 'weak'
77
 
        else:
78
 
            weak_kex = 'default'
79
 
 
80
 
        default = 'diffie-hellman-group-exchange-sha256'
81
 
        weak = (default + ',diffie-hellman-group14-sha1,'
82
 
                'diffie-hellman-group-exchange-sha1,'
83
 
                'diffie-hellman-group1-sha1')
84
 
        kex = {'default': default,
85
 
               'weak': weak}
86
 
 
87
 
        default = ('curve25519-sha256@libssh.org,'
88
 
                   'diffie-hellman-group-exchange-sha256')
89
 
        weak = (default + ',diffie-hellman-group14-sha1,'
90
 
                'diffie-hellman-group-exchange-sha1,'
91
 
                'diffie-hellman-group1-sha1')
92
 
        kex_66 = {'default': default,
93
 
                  'weak': weak}
94
 
 
95
 
        # Use newer kex on Ubuntu Trusty and above
96
 
        if lsb_release()['DISTRIB_CODENAME'].lower() >= 'trusty':
97
 
            log('Detected Ubuntu 14.04 or newer, using new key exchange '
98
 
                'algorithms', level=DEBUG)
99
 
            kex = kex_66
100
 
 
101
 
        return kex[weak_kex]
102
 
 
103
 
    def get_ciphers(self, cbc_required):
104
 
        if cbc_required:
105
 
            weak_ciphers = 'weak'
106
 
        else:
107
 
            weak_ciphers = 'default'
108
 
 
109
 
        default = 'aes256-ctr,aes192-ctr,aes128-ctr'
110
 
        cipher = {'default': default,
111
 
                  'weak': default + 'aes256-cbc,aes192-cbc,aes128-cbc'}
112
 
 
113
 
        default = ('chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,'
114
 
                   'aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr')
115
 
        ciphers_66 = {'default': default,
116
 
                      'weak': default + ',aes256-cbc,aes192-cbc,aes128-cbc'}
117
 
 
118
 
        # Use newer ciphers on ubuntu Trusty and above
119
 
        if lsb_release()['DISTRIB_CODENAME'].lower() >= 'trusty':
120
 
            log('Detected Ubuntu 14.04 or newer, using new ciphers',
121
 
                level=DEBUG)
122
 
            cipher = ciphers_66
123
 
 
124
 
        return cipher[weak_ciphers]
125
 
 
126
 
    def __call__(self):
127
 
        settings = utils.get_settings('ssh')
128
 
        if settings['common']['network_ipv6_enable']:
129
 
            addr_family = 'any'
130
 
        else:
131
 
            addr_family = 'inet'
132
 
 
133
 
        ctxt = {
134
 
            'addr_family': addr_family,
135
 
            'remote_hosts': settings['common']['remote_hosts'],
136
 
            'password_auth_allowed':
137
 
            settings['client']['password_authentication'],
138
 
            'ports': settings['common']['ports'],
139
 
            'ciphers': self.get_ciphers(settings['client']['cbc_required']),
140
 
            'macs': self.get_macs(settings['client']['weak_hmac']),
141
 
            'kexs': self.get_kexs(settings['client']['weak_kex']),
142
 
            'roaming': settings['client']['roaming'],
143
 
        }
144
 
        return ctxt
145
 
 
146
 
 
147
 
class SSHConfig(TemplatedFile):
148
 
    def __init__(self):
149
 
        path = '/etc/ssh/ssh_config'
150
 
        super(SSHConfig, self).__init__(path=path,
151
 
                                        template_dir=TEMPLATES_DIR,
152
 
                                        context=SSHConfigContext(),
153
 
                                        user='root',
154
 
                                        group='root',
155
 
                                        mode=0o0644)
156
 
 
157
 
    def pre_write(self):
158
 
        settings = utils.get_settings('ssh')
159
 
        apt_update(fatal=True)
160
 
        apt_install(settings['client']['package'])
161
 
        if not os.path.exists('/etc/ssh'):
162
 
            os.makedir('/etc/ssh')
163
 
            # NOTE: don't recurse
164
 
            utils.ensure_permissions('/etc/ssh', 'root', 'root', 0o0755,
165
 
                                     maxdepth=0)
166
 
 
167
 
    def post_write(self):
168
 
        # NOTE: don't recurse
169
 
        utils.ensure_permissions('/etc/ssh', 'root', 'root', 0o0755,
170
 
                                 maxdepth=0)
171
 
 
172
 
 
173
 
class SSHDConfigContext(SSHConfigContext):
174
 
 
175
 
    type = 'server'
176
 
 
177
 
    def __call__(self):
178
 
        settings = utils.get_settings('ssh')
179
 
        if settings['common']['network_ipv6_enable']:
180
 
            addr_family = 'any'
181
 
        else:
182
 
            addr_family = 'inet'
183
 
 
184
 
        ctxt = {
185
 
            'ssh_ip': settings['server']['listen_to'],
186
 
            'password_auth_allowed':
187
 
            settings['server']['password_authentication'],
188
 
            'ports': settings['common']['ports'],
189
 
            'addr_family': addr_family,
190
 
            'ciphers': self.get_ciphers(settings['server']['cbc_required']),
191
 
            'macs': self.get_macs(settings['server']['weak_hmac']),
192
 
            'kexs': self.get_kexs(settings['server']['weak_kex']),
193
 
            'host_key_files': settings['server']['host_key_files'],
194
 
            'allow_root_with_key': settings['server']['allow_root_with_key'],
195
 
            'password_authentication':
196
 
            settings['server']['password_authentication'],
197
 
            'use_priv_sep': settings['server']['use_privilege_separation'],
198
 
            'use_pam': settings['server']['use_pam'],
199
 
            'allow_x11_forwarding': settings['server']['allow_x11_forwarding'],
200
 
            'print_motd': settings['server']['print_motd'],
201
 
            'print_last_log': settings['server']['print_last_log'],
202
 
            'client_alive_interval':
203
 
            settings['server']['alive_interval'],
204
 
            'client_alive_count': settings['server']['alive_count'],
205
 
            'allow_tcp_forwarding': settings['server']['allow_tcp_forwarding'],
206
 
            'allow_agent_forwarding':
207
 
            settings['server']['allow_agent_forwarding'],
208
 
            'deny_users': settings['server']['deny_users'],
209
 
            'allow_users': settings['server']['allow_users'],
210
 
            'deny_groups': settings['server']['deny_groups'],
211
 
            'allow_groups': settings['server']['allow_groups'],
212
 
            'use_dns': settings['server']['use_dns'],
213
 
            'sftp_enable': settings['server']['sftp_enable'],
214
 
            'sftp_group': settings['server']['sftp_group'],
215
 
            'sftp_chroot': settings['server']['sftp_chroot'],
216
 
            'max_auth_tries': settings['server']['max_auth_tries'],
217
 
            'max_sessions': settings['server']['max_sessions'],
218
 
        }
219
 
        return ctxt
220
 
 
221
 
 
222
 
class SSHDConfig(TemplatedFile):
223
 
    def __init__(self):
224
 
        path = '/etc/ssh/sshd_config'
225
 
        super(SSHDConfig, self).__init__(path=path,
226
 
                                         template_dir=TEMPLATES_DIR,
227
 
                                         context=SSHDConfigContext(),
228
 
                                         user='root',
229
 
                                         group='root',
230
 
                                         mode=0o0600,
231
 
                                         service_actions=[{'service': 'ssh',
232
 
                                                           'actions':
233
 
                                                           ['restart']}])
234
 
 
235
 
    def pre_write(self):
236
 
        settings = utils.get_settings('ssh')
237
 
        apt_update(fatal=True)
238
 
        apt_install(settings['server']['package'])
239
 
        if not os.path.exists('/etc/ssh'):
240
 
            os.makedir('/etc/ssh')
241
 
            # NOTE: don't recurse
242
 
            utils.ensure_permissions('/etc/ssh', 'root', 'root', 0o0755,
243
 
                                     maxdepth=0)
244
 
 
245
 
    def post_write(self):
246
 
        # NOTE: don't recurse
247
 
        utils.ensure_permissions('/etc/ssh', 'root', 'root', 0o0755,
248
 
                                 maxdepth=0)
249
 
 
250
 
 
251
 
class SSHConfigFileContentAudit(FileContentAudit):
252
 
    def __init__(self):
253
 
        self.path = '/etc/ssh/ssh_config'
254
 
        super(SSHConfigFileContentAudit, self).__init__(self.path, {})
255
 
 
256
 
    def is_compliant(self, *args, **kwargs):
257
 
        self.pass_cases = []
258
 
        self.fail_cases = []
259
 
        settings = utils.get_settings('ssh')
260
 
 
261
 
        if lsb_release()['DISTRIB_CODENAME'].lower() >= 'trusty':
262
 
            if not settings['server']['weak_hmac']:
263
 
                self.pass_cases.append(r'^MACs.+,hmac-ripemd160$')
264
 
            else:
265
 
                self.pass_cases.append(r'^MACs.+,hmac-sha1$')
266
 
 
267
 
            if settings['server']['weak_kex']:
268
 
                self.fail_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha256[,\s]?')  # noqa
269
 
                self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group14-sha1[,\s]?')  # noqa
270
 
                self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha1[,\s]?')  # noqa
271
 
                self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group1-sha1[,\s]?')  # noqa
272
 
            else:
273
 
                self.pass_cases.append(r'^KexAlgorithms.+,diffie-hellman-group-exchange-sha256$')  # noqa
274
 
                self.fail_cases.append(r'^KexAlgorithms.*diffie-hellman-group14-sha1[,\s]?')  # noqa
275
 
 
276
 
            if settings['server']['cbc_required']:
277
 
                self.pass_cases.append(r'^Ciphers\s.*-cbc[,\s]?')
278
 
                self.fail_cases.append(r'^Ciphers\s.*aes128-ctr[,\s]?')
279
 
                self.fail_cases.append(r'^Ciphers\s.*aes192-ctr[,\s]?')
280
 
                self.fail_cases.append(r'^Ciphers\s.*aes256-ctr[,\s]?')
281
 
            else:
282
 
                self.fail_cases.append(r'^Ciphers\s.*-cbc[,\s]?')
283
 
                self.pass_cases.append(r'^Ciphers\schacha20-poly1305@openssh.com,.+')  # noqa
284
 
                self.pass_cases.append(r'^Ciphers\s.*aes128-ctr$')
285
 
                self.pass_cases.append(r'^Ciphers\s.*aes192-ctr[,\s]?')
286
 
                self.pass_cases.append(r'^Ciphers\s.*aes256-ctr[,\s]?')
287
 
        else:
288
 
            if not settings['client']['weak_hmac']:
289
 
                self.fail_cases.append(r'^MACs.+,hmac-sha1$')
290
 
            else:
291
 
                self.pass_cases.append(r'^MACs.+,hmac-sha1$')
292
 
 
293
 
            if settings['client']['weak_kex']:
294
 
                self.fail_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha256[,\s]?')  # noqa
295
 
                self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group14-sha1[,\s]?')  # noqa
296
 
                self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha1[,\s]?')  # noqa
297
 
                self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group1-sha1[,\s]?')  # noqa
298
 
            else:
299
 
                self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha256$')  # noqa
300
 
                self.fail_cases.append(r'^KexAlgorithms\sdiffie-hellman-group14-sha1[,\s]?')  # noqa
301
 
                self.fail_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha1[,\s]?')  # noqa
302
 
                self.fail_cases.append(r'^KexAlgorithms\sdiffie-hellman-group1-sha1[,\s]?')  # noqa
303
 
 
304
 
            if settings['client']['cbc_required']:
305
 
                self.pass_cases.append(r'^Ciphers\s.*-cbc[,\s]?')
306
 
                self.fail_cases.append(r'^Ciphers\s.*aes128-ctr[,\s]?')
307
 
                self.fail_cases.append(r'^Ciphers\s.*aes192-ctr[,\s]?')
308
 
                self.fail_cases.append(r'^Ciphers\s.*aes256-ctr[,\s]?')
309
 
            else:
310
 
                self.fail_cases.append(r'^Ciphers\s.*-cbc[,\s]?')
311
 
                self.pass_cases.append(r'^Ciphers\s.*aes128-ctr[,\s]?')
312
 
                self.pass_cases.append(r'^Ciphers\s.*aes192-ctr[,\s]?')
313
 
                self.pass_cases.append(r'^Ciphers\s.*aes256-ctr[,\s]?')
314
 
 
315
 
        if settings['client']['roaming']:
316
 
            self.pass_cases.append(r'^UseRoaming yes$')
317
 
        else:
318
 
            self.fail_cases.append(r'^UseRoaming yes$')
319
 
 
320
 
        return super(SSHConfigFileContentAudit, self).is_compliant(*args,
321
 
                                                                   **kwargs)
322
 
 
323
 
 
324
 
class SSHDConfigFileContentAudit(FileContentAudit):
325
 
    def __init__(self):
326
 
        self.path = '/etc/ssh/sshd_config'
327
 
        super(SSHDConfigFileContentAudit, self).__init__(self.path, {})
328
 
 
329
 
    def is_compliant(self, *args, **kwargs):
330
 
        self.pass_cases = []
331
 
        self.fail_cases = []
332
 
        settings = utils.get_settings('ssh')
333
 
 
334
 
        if lsb_release()['DISTRIB_CODENAME'].lower() >= 'trusty':
335
 
            if not settings['server']['weak_hmac']:
336
 
                self.pass_cases.append(r'^MACs.+,hmac-ripemd160$')
337
 
            else:
338
 
                self.pass_cases.append(r'^MACs.+,hmac-sha1$')
339
 
 
340
 
            if settings['server']['weak_kex']:
341
 
                self.fail_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha256[,\s]?')  # noqa
342
 
                self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group14-sha1[,\s]?')  # noqa
343
 
                self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha1[,\s]?')  # noqa
344
 
                self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group1-sha1[,\s]?')  # noqa
345
 
            else:
346
 
                self.pass_cases.append(r'^KexAlgorithms.+,diffie-hellman-group-exchange-sha256$')  # noqa
347
 
                self.fail_cases.append(r'^KexAlgorithms.*diffie-hellman-group14-sha1[,\s]?')  # noqa
348
 
 
349
 
            if settings['server']['cbc_required']:
350
 
                self.pass_cases.append(r'^Ciphers\s.*-cbc[,\s]?')
351
 
                self.fail_cases.append(r'^Ciphers\s.*aes128-ctr[,\s]?')
352
 
                self.fail_cases.append(r'^Ciphers\s.*aes192-ctr[,\s]?')
353
 
                self.fail_cases.append(r'^Ciphers\s.*aes256-ctr[,\s]?')
354
 
            else:
355
 
                self.fail_cases.append(r'^Ciphers\s.*-cbc[,\s]?')
356
 
                self.pass_cases.append(r'^Ciphers\schacha20-poly1305@openssh.com,.+')  # noqa
357
 
                self.pass_cases.append(r'^Ciphers\s.*aes128-ctr$')
358
 
                self.pass_cases.append(r'^Ciphers\s.*aes192-ctr[,\s]?')
359
 
                self.pass_cases.append(r'^Ciphers\s.*aes256-ctr[,\s]?')
360
 
        else:
361
 
            if not settings['server']['weak_hmac']:
362
 
                self.pass_cases.append(r'^MACs.+,hmac-ripemd160$')
363
 
            else:
364
 
                self.pass_cases.append(r'^MACs.+,hmac-sha1$')
365
 
 
366
 
            if settings['server']['weak_kex']:
367
 
                self.fail_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha256[,\s]?')  # noqa
368
 
                self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group14-sha1[,\s]?')  # noqa
369
 
                self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha1[,\s]?')  # noqa
370
 
                self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group1-sha1[,\s]?')  # noqa
371
 
            else:
372
 
                self.pass_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha256$')  # noqa
373
 
                self.fail_cases.append(r'^KexAlgorithms\sdiffie-hellman-group14-sha1[,\s]?')  # noqa
374
 
                self.fail_cases.append(r'^KexAlgorithms\sdiffie-hellman-group-exchange-sha1[,\s]?')  # noqa
375
 
                self.fail_cases.append(r'^KexAlgorithms\sdiffie-hellman-group1-sha1[,\s]?')  # noqa
376
 
 
377
 
            if settings['server']['cbc_required']:
378
 
                self.pass_cases.append(r'^Ciphers\s.*-cbc[,\s]?')
379
 
                self.fail_cases.append(r'^Ciphers\s.*aes128-ctr[,\s]?')
380
 
                self.fail_cases.append(r'^Ciphers\s.*aes192-ctr[,\s]?')
381
 
                self.fail_cases.append(r'^Ciphers\s.*aes256-ctr[,\s]?')
382
 
            else:
383
 
                self.fail_cases.append(r'^Ciphers\s.*-cbc[,\s]?')
384
 
                self.pass_cases.append(r'^Ciphers\s.*aes128-ctr[,\s]?')
385
 
                self.pass_cases.append(r'^Ciphers\s.*aes192-ctr[,\s]?')
386
 
                self.pass_cases.append(r'^Ciphers\s.*aes256-ctr[,\s]?')
387
 
 
388
 
        if settings['server']['sftp_enable']:
389
 
            self.pass_cases.append(r'^Subsystem\ssftp')
390
 
        else:
391
 
            self.fail_cases.append(r'^Subsystem\ssftp')
392
 
 
393
 
        return super(SSHDConfigFileContentAudit, self).is_compliant(*args,
394
 
                                                                    **kwargs)