diff --git a/homeassistant/components/http/__init__.py b/homeassistant/components/http/__init__.py index 37a6805dfb5..c8eba41e66b 100644 --- a/homeassistant/components/http/__init__.py +++ b/homeassistant/components/http/__init__.py @@ -19,6 +19,7 @@ import homeassistant.helpers.config_validation as cv import homeassistant.remote as rem import homeassistant.util as hass_util from homeassistant.util.logging import HideSensitiveDataFilter +from homeassistant.util import ssl as ssl_util from .auth import setup_auth from .ban import setup_bans @@ -49,21 +50,6 @@ CONF_TRUSTED_NETWORKS = 'trusted_networks' CONF_LOGIN_ATTEMPTS_THRESHOLD = 'login_attempts_threshold' CONF_IP_BAN_ENABLED = 'ip_ban_enabled' -# TLS configuration follows the best-practice guidelines specified here: -# https://wiki.mozilla.org/Security/Server_Side_TLS -# Modern guidelines are followed. -SSL_VERSION = ssl.PROTOCOL_TLS # pylint: disable=no-member -SSL_OPTS = ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | \ - ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | \ - ssl.OP_CIPHER_SERVER_PREFERENCE -if hasattr(ssl, 'OP_NO_COMPRESSION'): - SSL_OPTS |= ssl.OP_NO_COMPRESSION -CIPHERS = "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:" \ - "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:" \ - "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:" \ - "ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:" \ - "ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256" - _LOGGER = logging.getLogger(__name__) DEFAULT_SERVER_HOST = '0.0.0.0' @@ -300,9 +286,7 @@ class HomeAssistantHTTP(object): if self.ssl_certificate: try: - context = ssl.SSLContext(SSL_VERSION) - context.options |= SSL_OPTS - context.set_ciphers(CIPHERS) + context = ssl_util.server_context() context.load_cert_chain(self.ssl_certificate, self.ssl_key) except OSError as error: _LOGGER.error("Could not read SSL certificate from %s: %s", diff --git a/homeassistant/helpers/aiohttp_client.py b/homeassistant/helpers/aiohttp_client.py index 5ee2cd56081..71f3374f0c0 100644 --- a/homeassistant/helpers/aiohttp_client.py +++ b/homeassistant/helpers/aiohttp_client.py @@ -1,6 +1,5 @@ """Helper for aiohttp webclient stuff.""" import asyncio -import ssl import sys import aiohttp @@ -8,11 +7,11 @@ from aiohttp.hdrs import USER_AGENT, CONTENT_TYPE from aiohttp import web from aiohttp.web_exceptions import HTTPGatewayTimeout, HTTPBadGateway import async_timeout -import certifi from homeassistant.core import callback from homeassistant.const import EVENT_HOMEASSISTANT_CLOSE, __version__ from homeassistant.loader import bind_hass +from homeassistant.util import ssl as ssl_util DATA_CONNECTOR = 'aiohttp_connector' DATA_CONNECTOR_NOTVERIFY = 'aiohttp_connector_notverify' @@ -154,9 +153,7 @@ def _async_get_connector(hass, verify_ssl=True): return hass.data[key] if verify_ssl: - ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - ssl_context.load_verify_locations(cafile=certifi.where(), - capath=None) + ssl_context = ssl_util.client_context() else: ssl_context = False diff --git a/homeassistant/util/ssl.py b/homeassistant/util/ssl.py new file mode 100644 index 00000000000..fc02009b7af --- /dev/null +++ b/homeassistant/util/ssl.py @@ -0,0 +1,46 @@ +"""Helper to create SSL contexts.""" +import ssl + +import certifi + + +def client_context(): + """Return an SSL context for making requests.""" + context = _get_context() + context.verify_mode = ssl.CERT_REQUIRED + context.check_hostname = True + context.load_verify_locations(cafile=certifi.where(), capath=None) + return context + + +def server_context(): + """Return an SSL context for being a server.""" + context = _get_context() + context.options |= ssl.OP_CIPHER_SERVER_PREFERENCE + return context + + +def _get_context(): + """Return an SSL context following the Mozilla recommendations. + + TLS configuration follows the best-practice guidelines specified here: + https://wiki.mozilla.org/Security/Server_Side_TLS + Modern guidelines are followed. + """ + context = ssl.SSLContext(ssl.PROTOCOL_TLS) # pylint: disable=no-member + + context.options |= ( + ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | + ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 + ) + if hasattr(ssl, 'OP_NO_COMPRESSION'): + context.options |= ssl.OP_NO_COMPRESSION + + context.set_ciphers( + "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:" + "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:" + "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:" + "ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:" + "ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256" + ) + return context