Improve security layer (#352)

* Improve security layer

* Update logger

* Fix access

* Validate token

* fix

* fix some bugs

* fix lint
This commit is contained in:
Pascal Vizeli 2018-02-11 00:05:20 +01:00 committed by GitHub
parent 0c67cc13a1
commit 3bf446cbdb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 61 additions and 15 deletions

View File

@ -34,9 +34,16 @@ class AddonManager(CoreSysAttributes):
return list(self.repositories_obj.values())
def get(self, addon_slug):
"""Return a adddon from slug."""
"""Return a add-on from slug."""
return self.addons_obj.get(addon_slug)
def from_uuid(self, uuid):
"""Return a add-on from uuid."""
for addon in self.list_addons:
if addon.is_installed and uuid == addon.uuid:
return addon
return None
async def load(self):
"""Startup addon management."""
self.data.reload()

View File

@ -17,6 +17,16 @@ _LOGGER = logging.getLogger(__name__)
class APIProxy(CoreSysAttributes):
"""API Proxy for Home-Assistant."""
def _check_access(self, request):
"""Check the Hass.io token."""
hassio_token = request.headers.get(HEADER_HA_ACCESS)
addon = self._addons.from_uuid(hassio_token)
if not addon:
_LOGGER.warning("Unknown Home-Assistant API access!")
else:
_LOGGER.info("%s access from %s", request.path, addon.slug)
async def _api_client(self, request, path, timeout=300):
"""Return a client request with proxy origin for Home-Assistant."""
url = f"{self._homeassistant.api_url}/api/{path}"
@ -59,6 +69,8 @@ class APIProxy(CoreSysAttributes):
async def stream(self, request):
"""Proxy HomeAssistant EventStream Requests."""
self._check_access(request)
_LOGGER.info("Home-Assistant EventStream start")
client = await self._api_client(request, 'stream', timeout=None)
@ -83,12 +95,14 @@ class APIProxy(CoreSysAttributes):
client.close()
_LOGGER.info("Home-Assistant EventStream close")
return response
async def api(self, request):
"""Proxy HomeAssistant API Requests."""
path = request.match_info.get('path', '')
self._check_access(request)
# Normal request
_LOGGER.info("Home-Assistant /api/%s request", path)
path = request.match_info.get('path', '')
client = await self._api_client(request, path)
data = await client.read()
@ -138,7 +152,17 @@ class APIProxy(CoreSysAttributes):
'type': 'auth_required',
'ha_version': self._homeassistant.version,
})
await server.receive_json() # get internal token
# Check API access
response = await server.receive_json()
hassio_token = response.get('api_password')
addon = self._addons.from_uuid(hassio_token)
if not addon:
_LOGGER.warning("Unauthorized websocket access!")
else:
_LOGGER.info("Websocket access from %s", addon.slug)
await server.send_json({
'type': 'auth_ok',
'ha_version': self._homeassistant.version,

View File

@ -1,12 +1,19 @@
"""Handle security part of this API."""
import logging
import re
from aiohttp.web import middleware
from aiohttp.web_exceptions import HTTPUnauthorized
from ..const import HEADER_TOKEN, REQUEST_FROM
_LOGGER = logging.getLogger(__name__)
NO_SECURITY_CHECK = set((
re.compile(r"^/homeassistant/api/.*$"),
re.compile(r"^/homeassistant/websocket$")
))
@middleware
async def security_layer(request, handler):
@ -14,21 +21,29 @@ async def security_layer(request, handler):
coresys = request.app['coresys']
hassio_token = request.headers.get(HEADER_TOKEN)
# Ignore security check
for rule in NO_SECURITY_CHECK:
if rule.match(request.path):
_LOGGER.debug("Passthrough %s", request.path)
return await handler(request)
# Need to be removed later
if not hassio_token:
_LOGGER.warning("No valid hassio token for API access!")
_LOGGER.warning("No valid Hass.io token for API access!")
request[REQUEST_FROM] = 'UNKNOWN'
# From Home-Assistant
elif hassio_token == coresys.homeassistant.uuid:
request[REQUEST_FROM] = 'homeassistant'
# From Add-on
else:
for addon in coresys.addons.list_addons:
if hassio_token != addon.uuid:
continue
request[REQUEST_FROM] = addon.slug
break
return await handler(request)
# Home-Assistant
if hassio_token == coresys.homeassistant.uuid:
_LOGGER.debug("%s access from Home-Assistant", request.path)
request[REQUEST_FROM] = 'homeassistant'
return await handler(request)
# Add-on
addon = coresys.addons.from_uuid(hassio_token)
if addon:
_LOGGER.info("%s access from %s", request.path, addon.slug)
request[REQUEST_FROM] = addon.slug
return await handler(request)
raise HTTPUnauthorized()