Merge pull request #705 from balloob/ssl-for-ha

SSL support for ha
This commit is contained in:
Paulus Schoutsen 2015-12-06 21:05:25 -08:00
commit fd9da7f9de
2 changed files with 32 additions and 13 deletions

View File

@ -6,16 +6,17 @@ This module provides an API and a HTTP interface for debug purposes.
For more details about the RESTful API, please refer to the documentation at For more details about the RESTful API, please refer to the documentation at
https://home-assistant.io/developers/api/ https://home-assistant.io/developers/api/
""" """
import json
import threading
import logging
import time
import gzip
import os
from datetime import timedelta from datetime import timedelta
from http.server import SimpleHTTPRequestHandler, HTTPServer import gzip
from http import cookies from http import cookies
from http.server import SimpleHTTPRequestHandler, HTTPServer
import json
import logging
import os
from socketserver import ThreadingMixIn from socketserver import ThreadingMixIn
import ssl
import threading
import time
from urllib.parse import urlparse, parse_qs from urllib.parse import urlparse, parse_qs
import homeassistant.core as ha import homeassistant.core as ha
@ -36,6 +37,8 @@ CONF_API_PASSWORD = "api_password"
CONF_SERVER_HOST = "server_host" CONF_SERVER_HOST = "server_host"
CONF_SERVER_PORT = "server_port" CONF_SERVER_PORT = "server_port"
CONF_DEVELOPMENT = "development" CONF_DEVELOPMENT = "development"
CONF_SSL_CERTIFICATE = 'ssl_certificate'
CONF_SSL_KEY = 'ssl_key'
DATA_API_PASSWORD = 'api_password' DATA_API_PASSWORD = 'api_password'
@ -57,11 +60,13 @@ def setup(hass, config):
server_host = conf.get(CONF_SERVER_HOST, '0.0.0.0') server_host = conf.get(CONF_SERVER_HOST, '0.0.0.0')
server_port = conf.get(CONF_SERVER_PORT, SERVER_PORT) server_port = conf.get(CONF_SERVER_PORT, SERVER_PORT)
development = str(conf.get(CONF_DEVELOPMENT, "")) == "1" development = str(conf.get(CONF_DEVELOPMENT, "")) == "1"
ssl_certificate = conf.get(CONF_SSL_CERTIFICATE)
ssl_key = conf.get(CONF_SSL_KEY)
try: try:
server = HomeAssistantHTTPServer( server = HomeAssistantHTTPServer(
(server_host, server_port), RequestHandler, hass, api_password, (server_host, server_port), RequestHandler, hass, api_password,
development) development, ssl_certificate, ssl_key)
except OSError: except OSError:
# If address already in use # If address already in use
_LOGGER.exception("Error setting up HTTP server") _LOGGER.exception("Error setting up HTTP server")
@ -73,7 +78,8 @@ def setup(hass, config):
threading.Thread(target=server.start, daemon=True).start()) threading.Thread(target=server.start, daemon=True).start())
hass.http = server hass.http = server
hass.config.api = rem.API(util.get_local_ip(), api_password, server_port) hass.config.api = rem.API(util.get_local_ip(), api_password, server_port,
ssl_certificate is not None)
return True return True
@ -88,7 +94,7 @@ class HomeAssistantHTTPServer(ThreadingMixIn, HTTPServer):
# pylint: disable=too-many-arguments # pylint: disable=too-many-arguments
def __init__(self, server_address, request_handler_class, def __init__(self, server_address, request_handler_class,
hass, api_password, development): hass, api_password, development, ssl_certificate, ssl_key):
super().__init__(server_address, request_handler_class) super().__init__(server_address, request_handler_class)
self.server_address = server_address self.server_address = server_address
@ -97,6 +103,7 @@ class HomeAssistantHTTPServer(ThreadingMixIn, HTTPServer):
self.development = development self.development = development
self.paths = [] self.paths = []
self.sessions = SessionStore() self.sessions = SessionStore()
self.use_ssl = ssl_certificate is not None
# We will lazy init this one if needed # We will lazy init this one if needed
self.event_forwarder = None self.event_forwarder = None
@ -104,6 +111,12 @@ class HomeAssistantHTTPServer(ThreadingMixIn, HTTPServer):
if development: if development:
_LOGGER.info("running http in development mode") _LOGGER.info("running http in development mode")
if ssl_certificate is not None:
wrap_kwargs = {'certfile': ssl_certificate}
if ssl_key is not None:
wrap_kwargs['keyfile'] = ssl_key
self.socket = ssl.wrap_socket(self.socket, **wrap_kwargs)
def start(self): def start(self):
""" Starts the HTTP server. """ """ Starts the HTTP server. """
def stop_http(event): def stop_http(event):
@ -112,8 +125,11 @@ class HomeAssistantHTTPServer(ThreadingMixIn, HTTPServer):
self.hass.bus.listen_once(ha.EVENT_HOMEASSISTANT_STOP, stop_http) self.hass.bus.listen_once(ha.EVENT_HOMEASSISTANT_STOP, stop_http)
protocol = 'https' if self.use_ssl else 'http'
_LOGGER.info( _LOGGER.info(
"Starting web interface at http://%s:%d", *self.server_address) "Starting web interface at %s://%s:%d",
protocol, self.server_address[0], self.server_address[1])
# 31-1-2015: Refactored frontend/api components out of this component # 31-1-2015: Refactored frontend/api components out of this component
# To prevent stuff from breaking, load the two extracted components # To prevent stuff from breaking, load the two extracted components

View File

@ -51,10 +51,13 @@ class API(object):
""" Object to pass around Home Assistant API location and credentials. """ """ Object to pass around Home Assistant API location and credentials. """
# pylint: disable=too-few-public-methods # pylint: disable=too-few-public-methods
def __init__(self, host, api_password=None, port=None): def __init__(self, host, api_password=None, port=None, use_ssl=False):
self.host = host self.host = host
self.port = port or SERVER_PORT self.port = port or SERVER_PORT
self.api_password = api_password self.api_password = api_password
if use_ssl:
self.base_url = "https://{}:{}".format(host, self.port)
else:
self.base_url = "http://{}:{}".format(host, self.port) self.base_url = "http://{}:{}".format(host, self.port)
self.status = None self.status = None
self._headers = {} self._headers = {}