Skip to content

Commit adc14ee

Browse files
authored
Drop unused TLSv1.1 support completely in tlsserver module (#519)
* Drop unused TLSv1.1 support completely in tlsserver module. Adjust unit tests accordingly. Should make it clear even for code scans that we always enforce TLSv1.2+ everywhere. * Switch ssl version to use `SSLContext` with `PROTOCOL_TLS_SERVER` argument in symmetry with pyOpenSSL version and to avoid any client/server confusion caused by the use of `create_default_context`. * Rework legacy_tls handling to be consistent across our services. All of them now default to TLSv1.2+ and only adjust ciphers offered if the service-specific `enable_SVC_legacy_tls` conf option is set. * Drop the outdated client comments and disable ftps tls_legacy now that modern pyOpenSSL is available on every supported platform. * Add `enable_openid_legacy_tls` conf option and implement the same cipher handling in the built-in OpenID service. * Drop unused copy of `ssl_ciphers` in `daemon_conf` as hinted in review feedback. * Switch kwargs in tlsserver unit tests to use explicit naming format as suggested in review feedback.
1 parent 15f1cad commit adc14ee

8 files changed

Lines changed: 153 additions & 309 deletions

File tree

mig/install/MiGserver-template.conf

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -586,24 +586,15 @@ enable_sftp_subsys = __ENABLE_SFTP_SUBSYS__
586586
# Pure Python WsgiDAV-based webdav(s) daemon
587587
enable_davs = __ENABLE_DAVS__
588588
# Allow sub-optimal but still relatively strong legacy TLS support in WebDAVS
589-
# NOTE: Python-2.7.x ssl supports TLSv1.2+ with strong ciphers and all popular
589+
# NOTE: Python-2.7+ ssl supports TLSv1.2+ with strong ciphers and all popular
590590
# clients (including Windows 10+ native WebDAVS) also work with those.
591-
# NOTE: Apparently Win 7 (+8.1?) native WebDAVS only works with semi-strong
592-
# legacy ciphers and TLSv1.0+v1.1 unless updated and enabled
593-
# NOTE: Win 7 went EoL in January 2020 and should no longer be needed
594591
#enable_davs_legacy_tls = False
595592
# Pure Python pyftpdlib-based ftp(s) daemon
596593
enable_ftps = __ENABLE_FTPS__
597594
# Allow sub-optimal but still relatively strong legacy TLS supports in FTPS
598-
# NOTE: Recent PyOpenSSL supports TLSv1.2+ with strong ciphers and all popular
595+
# NOTE: Modern PyOpenSSL supports TLSv1.2+ with strong ciphers and all popular
599596
# clients also work with those.
600-
# NOTE: CentOS 7 native pyOpenSSL 0.13 does NOT support elliptic curve ciphers
601-
# and FileZilla fails on listdir with remaining strong DHE ciphers.
602-
# Installing a recent pyopenssl e.g. from the centos-openstack-X repo
603-
# allows disabling legacy tls support without breaking FileZilla support.
604-
# TODO: disable as soon as a recent pyopenssl version is available - the one
605-
# from pip breaks paramiko so do NOT go there.
606-
enable_ftps_legacy_tls = True
597+
#enable_ftps_legacy_tls = False
607598
# Enable WSGI served web pages (faster than CGI) - requires apache wsgi module
608599
enable_wsgi = __ENABLE_WSGI__
609600
# Enable system notify helper used e.g. to warn about failed user logins
@@ -638,6 +629,10 @@ peers_explicit_fields = __PEERS_EXPLICIT_FIELDS__
638629
peers_contact_hint = __PEERS_CONTACT_HINT__
639630
# Enable OpenID daemon for web access with user/pw from local user DB
640631
enable_openid = __ENABLE_OPENID__
632+
# Allow sub-optimal but still relatively strong legacy TLS support in OpenID 2.0
633+
# NOTE: Python-2.7+ ssl supports TLSv1.2+ with strong ciphers and all popular
634+
# clients also work with those.
635+
#enable_openid_legacy_tls = False
641636
# Enable share links for easy external exchange of data with anyone
642637
enable_sharelinks = __ENABLE_SHARELINKS__
643638
# Enable storage quota

mig/server/grid_ftps.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
from mig.shared.accountstate import check_account_accessible
9797
from mig.shared.base import invisible_path, force_utf8, force_native_str
9898
from mig.shared.conf import get_configuration_object
99+
from mig.shared.defaults import STRONG_TLS_CIPHERS, STRONG_TLS_LEGACY_CIPHERS
99100
from mig.shared.fileio import user_chroot_exceptions
100101
from mig.shared.griddaemons.ftps import default_max_user_hits, \
101102
default_user_abuse_hits, default_proto_abuse_hits, \
@@ -493,14 +494,21 @@ def start_service(conf):
493494
handler.tls_data_required = True
494495
keyfile = certfile = conf.user_ftps_key
495496
handler.certfile = certfile
497+
ciphers = None
496498
# Harden TLS/SSL if possible, requires recent pyftpdlib
497499
if hasattr(handler, 'ssl_context'):
498-
dhparamsfile = configuration.user_shared_dhparams
500+
dhparams_path = configuration.user_shared_dhparams
499501
legacy_tls = configuration.site_enable_ftps_legacy_tls
502+
if ciphers is not None:
503+
use_ciphers = ciphers
504+
elif legacy_tls:
505+
use_ciphers = STRONG_TLS_LEGACY_CIPHERS
506+
else:
507+
use_ciphers = STRONG_TLS_CIPHERS
500508
ssl_ctx = hardened_openssl_context(conf, OpenSSL, keyfile,
501509
certfile,
502-
dhparamsfile=dhparamsfile,
503-
allow_pre_tlsv12=legacy_tls)
510+
dhparamsfile=dhparams_path,
511+
ciphers=use_ciphers)
504512
handler.ssl_context = ssl_ctx
505513
else:
506514
logger.warning("Unable to enforce explicit strong TLS connections")

mig/server/grid_openid.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@
102102
from mig.shared.base import client_id_dir, cert_field_map, force_utf8, \
103103
force_native_str
104104
from mig.shared.conf import get_configuration_object
105+
from mig.shared.defaults import STRONG_TLS_CIPHERS, \
106+
STRONG_TLS_LEGACY_CIPHERS
105107
from mig.shared.griddaemons.openid import default_max_user_hits, \
106108
default_user_abuse_hits, default_proto_abuse_hits, \
107109
default_username_validator, refresh_user_creds, update_login_map, \
@@ -1654,6 +1656,7 @@ def start_service(configuration):
16541656
data_path = configuration.openid_store
16551657
daemon_conf = configuration.daemon_conf
16561658
nossl = daemon_conf['nossl']
1659+
ciphers = None
16571660
addr = (host, port)
16581661
# TODO: is this threaded version robust enough (thread safety)?
16591662
# OpenIDServer = OpenIDHTTPServer
@@ -1675,12 +1678,20 @@ def start_service(configuration):
16751678
# Use best possible SSL/TLS args for this python version
16761679
key_path = cert_path = configuration.user_openid_key
16771680
dhparams_path = configuration.user_shared_dhparams
1681+
legacy_tls = configuration.site_enable_openid_legacy_tls
1682+
if ciphers is not None:
1683+
use_ciphers = ciphers
1684+
elif legacy_tls:
1685+
use_ciphers = STRONG_TLS_LEGACY_CIPHERS
1686+
else:
1687+
use_ciphers = STRONG_TLS_CIPHERS
16781688
if not os.path.isfile(cert_path):
16791689
logger.error('No such server key: %s' % cert_path)
16801690
sys.exit(1)
16811691
logger.info('Wrapping connections in SSL')
16821692
ssl_ctx = hardened_ssl_context(configuration, key_path, cert_path,
1683-
dhparams_path)
1693+
dhparamsfile=dhparams_path,
1694+
ciphers=use_ciphers)
16841695
httpserver.socket = ssl_ctx.wrap_socket(httpserver.socket,
16851696
server_side=True)
16861697
# Override default SSLSocket accept function to inject timeout support

mig/server/grid_webdavs.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -335,8 +335,7 @@ def __init__(self, certificate, private_key, certificate_chain=None,
335335
context to use in all future connections in the wrap method.
336336
337337
If the optional legacy_tls arg is set the STRONG_TLS_LEGACY_CIPHERS
338-
are used instead of the STRONG_TLS_CIPHERS, and the limitation to
339-
TLSv1.2+ is left out to allow legacy TLSv1.0 and TLSv1.1 connections.
338+
are used instead of the STRONG_TLS_CIPHERS.
340339
This is required to support e.g. native Windows 7 WebDAVS access with
341340
the weak ECDHE-RSA-AES128-SHA cipher.
342341
"""
@@ -345,17 +344,17 @@ def __init__(self, certificate, private_key, certificate_chain=None,
345344
certificate_chain, ciphers)
346345
# logger.debug("proceed with hardening of ssl contetx")
347346
# Set up hardened SSL context once and for all
348-
dhparams = configuration.user_shared_dhparams
347+
dhparams_path = configuration.user_shared_dhparams
349348
if ciphers is not None:
350349
use_ciphers = ciphers
351350
elif legacy_tls:
352351
use_ciphers = STRONG_TLS_LEGACY_CIPHERS
353352
else:
354353
use_ciphers = STRONG_TLS_CIPHERS
355354
self.context = hardened_ssl_context(configuration, self.private_key,
356-
self.certificate, dhparams,
357-
ciphers=use_ciphers,
358-
allow_pre_tlsv12=legacy_tls)
355+
self.certificate,
356+
dhparamsfile=dhparams_path,
357+
ciphers=use_ciphers)
359358
# logger.debug("established hardened ssl contetx")
360359

361360
def __force_close(self, socket_list):
@@ -1951,7 +1950,7 @@ def run(configuration):
19511950
cert = config['ssl_certificate'] = configuration.user_davs_key
19521951
key = config['ssl_private_key'] = configuration.user_davs_key
19531952
chain = config['ssl_certificate_chain'] = ''
1954-
ciphers = config['ssl_ciphers'] = None
1953+
ciphers = None
19551954

19561955
# NOTE: Briefly insert dummy user to avoid bogus warning about anon access
19571956
# We dynamically add users as they connect so it isn't empty.

mig/shared/configuration.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1603,6 +1603,11 @@ def reload_config(self, verbose, skip_log=False, disable_auth_log=False,
16031603
'SITE', 'enable_openid')
16041604
else:
16051605
self.site_enable_openid = False
1606+
if config.has_option('SITE', 'enable_openid_legacy_tls'):
1607+
self.site_enable_openid_legacy_tls = config.getboolean(
1608+
'SITE', 'enable_openid_legacy_tls')
1609+
else:
1610+
self.site_enable_openid_legacy_tls = False
16061611
if config.has_option('GLOBAL', 'user_openid_address'):
16071612
self.user_openid_address = config.get('GLOBAL',
16081613
'user_openid_address')

mig/shared/tlsserver.py

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
#
2121
# You should have received a copy of the GNU General Public License
2222
# along with this program; if not, write to the Free Software
23-
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
24+
# USA.
2425
#
2526
# -- END_HEADER ---
2627
#
@@ -40,16 +41,15 @@
4041
def hardened_ssl_context(configuration, keyfile, certfile, dhparamsfile=None,
4142
ciphers=STRONG_TLS_CIPHERS,
4243
curve_priority=STRONG_TLS_CURVES,
43-
allow_pre_tlsv12=False,
4444
allow_pre_tlsv13=True,
4545
allow_renegotiation=False,
4646
):
4747
"""Build and return a hardened native SSL context to apply to a socket"""
4848
_logger = configuration.logger
4949
_logger.info("enforcing strong SSL/TLS connections")
5050
_logger.debug("using SSL/TLS ciphers: %s" % ciphers)
51-
ssl_protocol = ssl.PROTOCOL_SSLv23
52-
ssl_ctx = ssl.SSLContext(ssl_protocol)
51+
ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
52+
ssl_ctx.minimum_version = ssl.TLSVersion.TLSv1_2
5353
ssl_ctx.load_cert_chain(certfile, keyfile)
5454
ssl_options = 0
5555
# NOTE: Override a number of weak and insecure legacy configurations
@@ -58,11 +58,7 @@ def hardened_ssl_context(configuration, keyfile, certfile, dhparamsfile=None,
5858
ssl_options |= getattr(ssl, 'OP_NO_SSLv2', 0x1000000)
5959
ssl_options |= getattr(ssl, 'OP_NO_SSLv3', 0x2000000)
6060
ssl_options |= getattr(ssl, 'OP_NO_TLSv1', 0x4000000)
61-
ssl_ctx.minimum_version = ssl.TLSVersion.TLSv1_1
62-
# NOTE: refuse weak TLS protocols unless allow_pre_tlsv12
63-
if not allow_pre_tlsv12:
64-
ssl_options |= getattr(ssl, 'OP_NO_TLSv1_1', 0x10000000)
65-
ssl_ctx.minimum_version = ssl.TLSVersion.TLSv1_2
61+
ssl_options |= getattr(ssl, 'OP_NO_TLSv1_1', 0x10000000)
6662
# NOTE: refuse slightly dated TLS 1.2 protocol unless allow_pre_tlsv13
6763
if not allow_pre_tlsv13:
6864
if getattr(ssl, 'HAS_TLSv1_3', False):
@@ -78,17 +74,13 @@ def hardened_ssl_context(configuration, keyfile, certfile, dhparamsfile=None,
7874
ssl_options |= getattr(ssl, 'OP_CIPHER_SERVER_PREFERENCE', 0x400000)
7975
ssl_options |= getattr(ssl, 'OP_SINGLE_ECDH_USE', 0x80000)
8076
ssl_options |= getattr(ssl, 'OP_SINGLE_DH_USE', 0x100000)
81-
# Useful for debugging
82-
# ssl_options |= getattr(ssl, 'OP_NO_TICKET', 0x0004000)
83-
# ssl_options |= getattr(ssl, 'OP_NO_TLSv1_1', 0x10000000)
84-
# ssl_options |= getattr(ssl, 'OP_NO_TLSv1_2', 0x8000000)
85-
if sys.version_info[:2] >= (2, 7) and ssl_ctx:
77+
if sys.version_info[:2] >= (3, 7) and ssl_ctx:
8678
_logger.info("enforcing strong SSL/TLS options")
8779
_logger.debug("SSL/TLS options: %s" % ssl_options)
8880
ssl_ctx.options |= ssl_options
8981
else:
9082
_logger.info("can't enforce strong SSL/TLS options")
91-
_logger.warning("Upgrade to python 2.7.9+ for maximum security")
83+
_logger.warning("Upgrade to python 3.7+ for maximum security")
9284

9385
pfs_available = False
9486
if dhparamsfile:
@@ -134,7 +126,6 @@ def hardened_openssl_context(configuration, OpenSSL, keyfile, certfile,
134126
cacertfile=None, dhparamsfile=None,
135127
ciphers=STRONG_TLS_CIPHERS,
136128
curve_priority=STRONG_TLS_CURVES,
137-
allow_pre_tlsv12=False,
138129
allow_pre_tlsv13=True,
139130
allow_renegotiation=False,
140131
):
@@ -143,8 +134,10 @@ def hardened_openssl_context(configuration, OpenSSL, keyfile, certfile,
143134
SSL, crypto = OpenSSL.SSL, OpenSSL.crypto
144135
_logger.info("enforcing strong SSL/TLS connections")
145136
_logger.debug("using SSL/TLS ciphers: %s" % ciphers)
146-
ssl_protocol = SSL.SSLv23_METHOD
147-
ssl_ctx = SSL.Context(ssl_protocol)
137+
ssl_ctx = SSL.Context(SSL.TLS_SERVER_METHOD)
138+
ssl_ctx.set_min_proto_version(SSL.TLS1_2_VERSION)
139+
# Mimic native ssl exposure of options
140+
ssl_ctx._minimum_version = SSL.TLS1_2_VERSION
148141
ssl_ctx.use_certificate_chain_file(certfile)
149142
ssl_ctx.use_privatekey_file(keyfile)
150143
if cacertfile:
@@ -157,15 +150,7 @@ def hardened_openssl_context(configuration, OpenSSL, keyfile, certfile,
157150
ssl_options |= getattr(SSL, 'OP_NO_SSLv2', 0x1000000)
158151
ssl_options |= getattr(SSL, 'OP_NO_SSLv3', 0x2000000)
159152
ssl_options |= getattr(SSL, 'OP_NO_TLSv1', 0x4000000)
160-
ssl_ctx.set_min_proto_version(SSL.TLS1_1_VERSION)
161-
# Mimic native ssl exposure of options
162-
ssl_ctx._minimum_version = SSL.TLS1_1_VERSION
163-
# NOTE: refuse weak TLS protocols unless allow_pre_tlsv12
164-
if not allow_pre_tlsv12:
165-
ssl_options |= getattr(SSL, 'OP_NO_TLSv1_1', 0x10000000)
166-
ssl_ctx.set_min_proto_version(SSL.TLS1_2_VERSION)
167-
# Mimic native ssl exposure of options
168-
ssl_ctx._minimum_version = SSL.TLS1_2_VERSION
153+
ssl_options |= getattr(SSL, 'OP_NO_TLSv1_1', 0x10000000)
169154
# NOTE: refuse slightly dated TLS 1.2 protocol unless allow_pre_tlsv13
170155
if not allow_pre_tlsv13:
171156
# IMPORTANT: OpenSSL doesn't have TLSv1.3 support marker at the moment,
@@ -186,15 +171,15 @@ def hardened_openssl_context(configuration, OpenSSL, keyfile, certfile,
186171
ssl_options |= getattr(SSL, 'OP_CIPHER_SERVER_PREFERENCE', 0x400000)
187172
ssl_options |= getattr(SSL, 'OP_SINGLE_ECDH_USE', 0x80000)
188173
ssl_options |= getattr(SSL, 'OP_SINGLE_DH_USE', 0x100000)
189-
if sys.version_info[:2] >= (2, 7) and ssl_ctx:
174+
if sys.version_info[:2] >= (3, 7) and ssl_ctx:
190175
_logger.info("enforcing strong SSL/TLS options")
191176
_logger.debug("SSL/TLS options: %s" % ssl_options)
192177
ssl_ctx.set_options(ssl_options)
193178
# Mimic native ssl exposure of options
194179
ssl_ctx._options = ssl_options
195180
else:
196181
_logger.info("can't enforce strong SSL/TLS options")
197-
_logger.warning("Upgrade to python 2.7.9+ for maximum security")
182+
_logger.warning("Upgrade to python 3.7+ for maximum security")
198183
# Mimic native ssl exposure of options
199184
ssl_ctx._options = None
200185

tests/fixture/confs-stdlocal/MiGserver.conf

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -586,24 +586,15 @@ enable_sftp_subsys = False
586586
# Pure Python WsgiDAV-based webdav(s) daemon
587587
enable_davs = False
588588
# Allow sub-optimal but still relatively strong legacy TLS support in WebDAVS
589-
# NOTE: Python-2.7.x ssl supports TLSv1.2+ with strong ciphers and all popular
589+
# NOTE: Python-2.7+ ssl supports TLSv1.2+ with strong ciphers and all popular
590590
# clients (including Windows 10+ native WebDAVS) also work with those.
591-
# NOTE: Apparently Win 7 (+8.1?) native WebDAVS only works with semi-strong
592-
# legacy ciphers and TLSv1.0+v1.1 unless updated and enabled
593-
# NOTE: Win 7 went EoL in January 2020 and should no longer be needed
594591
#enable_davs_legacy_tls = False
595592
# Pure Python pyftpdlib-based ftp(s) daemon
596593
enable_ftps = False
597594
# Allow sub-optimal but still relatively strong legacy TLS supports in FTPS
598-
# NOTE: Recent PyOpenSSL supports TLSv1.2+ with strong ciphers and all popular
595+
# NOTE: Modern PyOpenSSL supports TLSv1.2+ with strong ciphers and all popular
599596
# clients also work with those.
600-
# NOTE: CentOS 7 native pyOpenSSL 0.13 does NOT support elliptic curve ciphers
601-
# and FileZilla fails on listdir with remaining strong DHE ciphers.
602-
# Installing a recent pyopenssl e.g. from the centos-openstack-X repo
603-
# allows disabling legacy tls support without breaking FileZilla support.
604-
# TODO: disable as soon as a recent pyopenssl version is available - the one
605-
# from pip breaks paramiko so do NOT go there.
606-
enable_ftps_legacy_tls = True
597+
#enable_ftps_legacy_tls = False
607598
# Enable WSGI served web pages (faster than CGI) - requires apache wsgi module
608599
enable_wsgi = True
609600
# Enable system notify helper used e.g. to warn about failed user logins
@@ -638,6 +629,10 @@ peers_explicit_fields =
638629
peers_contact_hint = employed here and authorized to invite external users
639630
# Enable OpenID daemon for web access with user/pw from local user DB
640631
enable_openid = False
632+
# Allow sub-optimal but still relatively strong legacy TLS support in OpenID 2.0
633+
# NOTE: Python-2.7+ ssl supports TLSv1.2+ with strong ciphers and all popular
634+
# clients also work with those.
635+
#enable_openid_legacy_tls = False
641636
# Enable share links for easy external exchange of data with anyone
642637
enable_sharelinks = True
643638
# Enable storage quota

0 commit comments

Comments
 (0)