Modernize mod_nss's cipher suites

The list of supported TLS cipher suites in /etc/httpd/conf.d/nss.conf
has been modernized. Insecure or less secure algorithms such as RC4,
DES and 3DES are removed. Perfect forward secrecy suites with ephemeral
ECDH key exchange have been added. IE 8 on Windows XP is no longer
supported.

The list of enabled cipher suites has been generated with the script
contrib/nssciphersuite/nssciphersuite.py.

TLS_RSA_WITH_AES_128_CBC_SHA256
TLS_RSA_WITH_AES_256_CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
TLS_RSA_WITH_AES_128_GCM_SHA256
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_256_GCM_SHA384
TLS_RSA_WITH_AES_256_CBC_SHA

https://fedorahosted.org/freeipa/ticket/5589

Signed-off-by: Christian Heimes <cheimes@redhat.com>
Reviewed-By: Martin Basti <mbasti@redhat.com>
This commit is contained in:
Christian Heimes 2016-01-21 16:09:10 +01:00 committed by Martin Basti
parent 42d3644276
commit 5ac3a3cee5
4 changed files with 220 additions and 0 deletions

View File

@ -0,0 +1,36 @@
Cipher suite for mod_nss
------------------------
The nssciphersuite.py script parses mod_nss' nss_engine_cipher.c file and
creates a list of secure cipher suites for TLS. The script filters out
insecure, obsolete and slow ciphers according to some rules.
As of January 2016 and mod_nss 1.0.12 the cipher suite list contains 14
cipher suites for TLS 1.0, 1.1 and 1.2 for RSA and ECDSA certificates. The
cipher suite list also supports Perfect Forward Secrecy with ephemeral ECDH
key exchange. https://www.ssllabs.com/ gives a 'A' grade.
Note:
No suite is compatible with IE 8 and earlier on Windows XP. If you need IE 8
support, append "+rsa_3des_sha" to enable TLS_RSA_WITH_3DES_EDE_CBC_SHA.
# disabled cipher attributes: SSL_3DES, SSL_CAMELLIA, SSL_CAMELLIA128, SSL_CAMELLIA256, SSL_DES, SSL_DSS, SSL_MD5, SSL_RC2, SSL_RC4, SSL_aDSS, SSL_aNULL, SSL_eNULL, SSL_kECDHe, SSL_kECDHr, kECDH
# weak strength: SSL_EXPORT40, SSL_EXPORT56, SSL_LOW, SSL_STRONG_NONE
# enabled cipher suites:
# TLS_RSA_WITH_AES_128_CBC_SHA256
# TLS_RSA_WITH_AES_256_CBC_SHA256
# TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
# TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
# TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
# TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
# TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
# TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
# TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
# TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
# TLS_RSA_WITH_AES_128_GCM_SHA256
# TLS_RSA_WITH_AES_128_CBC_SHA
# TLS_RSA_WITH_AES_256_GCM_SHA384
# TLS_RSA_WITH_AES_256_CBC_SHA
#
NSSCipherSuite +aes_128_sha_256,+aes_256_sha_256,+ecdhe_ecdsa_aes_128_gcm_sha_256,+ecdhe_ecdsa_aes_128_sha,+ecdhe_ecdsa_aes_256_gcm_sha_384,+ecdhe_ecdsa_aes_256_sha,+ecdhe_rsa_aes_128_gcm_sha_256,+ecdhe_rsa_aes_128_sha,+ecdhe_rsa_aes_256_gcm_sha_384,+ecdhe_rsa_aes_256_sha,+rsa_aes_128_gcm_sha_256,+rsa_aes_128_sha,+rsa_aes_256_gcm_sha_384,+rsa_aes_256_sha

View File

@ -0,0 +1,147 @@
#!/usr/bin/python3
#
# Authors:
# Christian Heimes <cheimes@redhat.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Copyright (C) 2016 Red Hat, Inc.
# All rights reserved.
#
"""Generate safe NSSCipherSuite stanza for mod_nss
"""
from __future__ import print_function
import operator
import re
from urllib.request import urlopen # pylint: disable=no-name-in-module
SOURCE = "https://git.fedorahosted.org/cgit/mod_nss.git/plain/nss_engine_cipher.c"
CIPHER_RE = re.compile(
r'\s*\{'
r'\"(?P<name>\w+)\",\s*'
r'(?P<num>(TLS|SSL)_\w+),\s*'
r'\"(?P<openssl_name>[\w-]+)\",\s*'
r'(?P<attr>[\w|]+),\s*'
r'(?P<version>\w+),\s*'
r'(?P<strength>\w+),\s*'
r'(?P<bits>\d+),\s*'
r'(?P<alg_bits>\d+)'
)
DISABLED_CIPHERS = {
# ciphers without encryption or authentication
'SSL_eNULL', 'SSL_aNULL',
# MD5 is broken
# SHA-1 is still required as PRF algorithm for TLSv1.0
'SSL_MD5',
# RC2 and RC4 stream ciphers are broken.
'SSL_RC2', 'SSL_RC4',
# DES is broken and Triple DES is too weak.
'SSL_DES', 'SSL_3DES',
# DSA is problematic.
'SSL_DSS', 'SSL_aDSS',
# prefer AES over Camellia.
'SSL_CAMELLIA128', 'SSL_CAMELLIA256', 'SSL_CAMELLIA',
# non-ephemeral EC Diffie-Hellmann with fixed parameters are not
# used by common browser and are therefore irrelevant for HTTPS.
'kECDH', 'SSL_kECDHr', 'SSL_kECDHe'
}
WEAK_STRENGTH = {
'SSL_STRONG_NONE',
'SSL_EXPORT40',
'SSL_EXPORT56',
'SSL_LOW'
}
def parse_nss_engine_cipher(lines, encoding='utf-8'):
"""Parse nss_engine_cipher.c and get list of ciphers
:param lines: iterable or list of lines
:param encoding: default encoding
:return: list of cipher dicts
"""
ciphers = []
start = False
for line in lines:
if not isinstance(line, str):
line = line.decode(encoding)
if line.startswith('cipher_properties'):
start = True
elif not start:
continue
elif line.startswith('};'):
break
mo = CIPHER_RE.match(line)
if not mo:
continue
match = mo.groupdict()
match['attr'] = set(match['attr'].split('|'))
match['bits'] = int(match['bits'])
match['alg_bits'] = int(match['alg_bits'])
# some cipher elemets aren't flagged
for algo in ['SHA256', 'SHA384']:
if match['num'].endswith(algo):
match['attr'].add('SSL_{}'.format(algo))
# cipher block chaining isn't tracked
if '_CBC' in match['num']:
match['attr'].add('SSL_CBC')
if match['attr'].intersection(DISABLED_CIPHERS):
match['enabled'] = False
elif match['strength'] in WEAK_STRENGTH:
match['enabled'] = False
else:
match['enabled'] = True
# EECDH + AES-CBC and large hash functions is slow and not more secure
if (match['attr'].issuperset({'SSL_CBC', 'SSL_kEECDH'}) and
match['attr'].intersection({'SSL_SHA256', 'SSL_SHA384'})):
match['enabled'] = False
ciphers.append(match)
ciphers.sort(key=operator.itemgetter('name'))
return ciphers
def main():
with urlopen(SOURCE) as r:
ciphers = parse_nss_engine_cipher(r)
# with open('nss_engine_cipher.c') as f:
# ciphers = parse_nss_engine_cipher(f)
print("# disabled cipher attributes: {}".format(
', '.join(sorted(DISABLED_CIPHERS))))
print("# weak strength: {}".format(', '.join(sorted(WEAK_STRENGTH))))
print("# enabled cipher suites:")
suite = []
for cipher in ciphers:
if cipher['enabled']:
print("# {:36}".format(cipher['num']))
suite.append('+{}'.format(cipher['name']))
print()
print("NSSCipherSuite {}".format(','.join(suite)))
if __name__ == '__main__':
main()

View File

@ -57,6 +57,19 @@ SELINUX_BOOLEAN_SETTINGS = dict(
KDCPROXY_USER = 'kdcproxy'
HTTPD_USER = constants.HTTPD_USER
# See contrib/nsscipersuite/nssciphersuite.py
NSS_CIPHER_SUITE = [
'+aes_128_sha_256', '+aes_256_sha_256',
'+ecdhe_ecdsa_aes_128_gcm_sha_256', '+ecdhe_ecdsa_aes_128_sha',
'+ecdhe_ecdsa_aes_256_gcm_sha_384', '+ecdhe_ecdsa_aes_256_sha',
'+ecdhe_rsa_aes_128_gcm_sha_256', '+ecdhe_rsa_aes_128_sha',
'+ecdhe_rsa_aes_256_gcm_sha_384', '+ecdhe_rsa_aes_256_sha',
'+rsa_aes_128_gcm_sha_256', '+rsa_aes_128_sha',
'+rsa_aes_256_gcm_sha_384', '+rsa_aes_256_sha'
]
NSS_CIPHER_REVISION = '20160129'
def httpd_443_configured():
"""
We now allow mod_ssl to be installed so don't automatically disable it.
@ -146,6 +159,8 @@ class HTTPInstance(service.Service):
self.step("setting mod_nss port to 443", self.__set_mod_nss_port)
self.step("setting mod_nss cipher suite",
self.set_mod_nss_cipher_suite)
self.step("setting mod_nss protocol list to TLSv1.0 - TLSv1.2",
self.set_mod_nss_protocol)
self.step("setting mod_nss password file", self.__set_mod_nss_passwordfile)
@ -255,6 +270,10 @@ class HTTPInstance(service.Service):
installutils.set_directive(paths.HTTPD_NSS_CONF, 'NSSRenegotiation', 'on', False)
installutils.set_directive(paths.HTTPD_NSS_CONF, 'NSSRequireSafeNegotiation', 'on', False)
def set_mod_nss_cipher_suite(self):
ciphers = ','.join(NSS_CIPHER_SUITE)
installutils.set_directive(paths.HTTPD_NSS_CONF, 'NSSCipherSuite', ciphers, False)
def __set_mod_nss_passwordfile(self):
installutils.set_directive(paths.HTTPD_NSS_CONF, 'NSSPassPhraseDialog', 'file:' + paths.HTTPD_PASSWORD_CONF)

View File

@ -1343,6 +1343,23 @@ def update_mod_nss_protocol(http):
sysupgrade.set_upgrade_state('nss.conf', 'protocol_updated_tls12', True)
def update_mod_nss_cipher_suite(http):
root_logger.info('[Updating mod_nss cipher suite]')
revision = sysupgrade.get_upgrade_state('nss.conf', 'cipher_suite_updated')
if revision >= httpinstance.NSS_CIPHER_REVISION:
root_logger.debug("Cipher suite already updated")
return
http.set_mod_nss_cipher_suite()
sysupgrade.set_upgrade_state(
'nss.conf',
'cipher_suite_updated',
httpinstance.NSS_CIPHER_REVISION)
def ds_enable_sidgen_extdom_plugins(ds):
"""For AD trust agents, make sure we enable sidgen and extdom plugins
"""
@ -1526,6 +1543,7 @@ def upgrade_configuration():
http.stop()
update_mod_nss_protocol(http)
update_mod_nss_cipher_suite(http)
fix_trust_flags()
export_kra_agent_pem()
http.start()