Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions docker/home-assistant/.env
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,23 @@ HOST_PORT_PREFIX=
# HA
TZ=Europe/Warsaw
USERDIR=../../dist

ENV_NAME_PREFIX=dev

# OAUTH2-PORXY
## CORE
HA_URL=http://home-assistant:8123

REDIRECT_URL=https://my-proxy.mydomain.mycompany.com/oauth2/callback
DOMAIN=my-proxy.mydomain.mycompany.com
COOKIE_DOMAIN=.mydomain.mycompany.com
COOKIE_DOMAIN=my-proxy.mydomain.mycompany.com
SERVER_ADDRESS=192.168.1.1
COOKIE_SECRET=1234567890123456

##AZURE_AD_SANDBOX_NOT_TESTED_YET!
AZURE_AD_HA_SECRET=SECRET
AZURE_AD_HA_APP=APPLICATION_ID_GUID
AZURE_AD_DISCOVERY=https://login.microsoftonline.com/TENANT_ID/v2.0
AZURE_AD_ISSUER=https://login.microsoftonline.com/TENANT_ID/v2.0
TENANT=TENANT_ID
AZURE_AD_SCOPE=openid email TENANT_ID/.default
##FACEBOOK
FB_HA_SECRET=secret
FB_HA_APP=FB_APP_ID
Expand Down
7 changes: 0 additions & 7 deletions docker/home-assistant/docker-compose.dev.override.yaml

This file was deleted.

33 changes: 17 additions & 16 deletions docker/home-assistant/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ x-logging: &loki-logging

services:
homeassistant:
container_name: home-assistant
container_name: ${ENV_NAME_PREFIX}-home-assistant
restart: always
image: homeassistant/home-assistant:2022.10.5

Expand All @@ -24,8 +24,8 @@ services:
# can base on https://github.com/grafana/grafana/issues/52681
# https://developer.okta.com/blog/2022/07/14/add-auth-to-any-app-with-oauth2-proxy
#config link https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/overview
image: quay.io/oauth2-proxy/oauth2-proxy:latest
container_name: auth-proxy
image: quay.io/oauth2-proxy/oauth2-proxy:v7.4.0
container_name: ${ENV_NAME_PREFIX}-auth-proxy
restart: always
depends_on:
- proxy-server
Expand All @@ -36,12 +36,12 @@ services:
environment:
# DOC https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/oauth_provider/#azure-auth-provider
- OAUTH2_PROXY_SHOW_DEBUG_ON_ERROR=true

- OAUTH2_PROXY_PROVIDER=facebook
- OAUTH2_PROXY_CLIENT_ID=${FB_HA_APP}
- OAUTH2_PROXY_CLIENT_SECRET=${FB_HA_SECRET}
- OAUTH2_PROXY_SCOPE=openid email public_profile

- OAUTH2_PROXY_PROVIDER=azure
- OAUTH2_PROXY_AZURE_TENANT=${TENANT}
- OAUTH2_PROXY_CLIENT_ID=${AZURE_AD_HA_APP}
- OAUTH2_PROXY_CLIENT_SECRET=${AZURE_AD_HA_SECRET}
- OAUTH2_PROXY_OIDC_ISSUER_URL=${AZURE_AD_ISSUER}
- OAUTH2_PROXY_OIDC_EMAIL_CLAIM=upn
- OAUTH2_PROXY_UPSTREAMS=${HA_URL}

- OAUTH2_PROXY_REVERSE_PROXY=true
Expand All @@ -52,18 +52,21 @@ services:

- OAUTH2_PROXY_HTTP_ADDRESS=http://:4180
- OAUTH2_PROXY_REDIRECT_URL=${REDIRECT_URL}
- OAUTH2_PROXY_cookie_domains=${COOKIE_DOMAIN}
- OAUTH2_PROXY_whitelist_domains=${COOKIE_DOMAIN}
#- OAUTH2_PROXY_COOKIE_DOMAINS=${COOKIE_DOMAIN}
#- OAUTH2_PROXY_WHITELIST_DOMAINS=${COOKIE_DOMAIN}
- OAUTH2_PROXY_SSL_UPSTREAM_INSECURE_SKIP_VERIFY=true
- OAUTH2_PROXY_SSL_INSECURE_SKIP_VERIFY=true
- OAUTH2_PROXY_STANDARD_LOGGING=true
- OAUTH2_PROXY_AUTH_LOGGING=true
- OAUTH2_PROXY_REQUEST_LOGGING=true
- OAUTH2_PROXY_ERRORS_TO_INFO_LOG=true
logging: *loki-logging
- OAUTH2_PROXY_PASS_USER_HEADERS=true
- OAUTH2_PROXY_set_xauthrequest=true
- OAUTH2_PROXY_pass_access_token=true
#logging: *loki-logging
proxy-server:
image: nginx:1.19
container_name: proxy-server
container_name: ${ENV_NAME_PREFIX}-proxy-server
ports:
- 443:443
restart: always
Expand All @@ -73,15 +76,13 @@ services:
- ./nginx-config/key.pem.secret:/etc/nginx/ssl/key.pem:ro
- ./nginx-config/passw.secret:/etc/nginx/ssl/passw:ro
- ./nginx-config/nginx.conf:/etc/nginx/conf.d/default.conf
logging: *loki-logging
#logging: *loki-logging
# certbot:
# image: certbot/certbot:latest
# volumes:
# - ${USERDIR}/certbot/www/:/var/www/certbot/:rw
# - ${USERDIR}/certbot/conf/:/etc/letsencrypt/:rw



networks:
default:
external:
Expand Down
2 changes: 1 addition & 1 deletion docker/home-assistant/home-assistant-run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ mkdir -p ../../dist/ha/config

cp -a ../../home-assistant-configuration/. ../../dist/ha/config/

docker-compose -f docker-compose.yaml -f docker-compose.dev.override.yaml --env-file .env.dev up --detach --force-recreate
docker-compose -f docker-compose.yaml --env-file .env.dev up --detach --force-recreate
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Reverse proxy to oauth2-proxy
server {
listen 443 ssl;

proxy_busy_buffers_size 512k;
proxy_buffers 4 512k;
proxy_buffer_size 256k;

server_name ${SERVER_NAME_OAUTH_PROXY};
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
Expand Down
133 changes: 133 additions & 0 deletions home-assistant-configuration/custom_components/auth_header/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import logging
from http import HTTPStatus
from ipaddress import ip_address
from typing import OrderedDict
from aiohttp.web import Request, Response
from typing import Any

import homeassistant.helpers.config_validation as cv
import voluptuous as vol
from homeassistant import data_entry_flow
from homeassistant.components.auth import DOMAIN as AUTH_DOMAIN
from homeassistant.components.auth import indieauth
from homeassistant.components.auth.login_flow import (
LoginFlowIndexView,
_prepare_result_json,
)
from homeassistant.components.http.ban import log_invalid_auth, process_success_login
from homeassistant.components.http.data_validator import RequestDataValidator
from homeassistant.core import HomeAssistant

from . import headers

DOMAIN = "auth_header"
_LOGGER = logging.getLogger(__name__)
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Optional(
"username_header", default="X-Forwarded-Preferred-Username"
): cv.string,
vol.Optional("debug", default=False): cv.boolean,
}
)
},
extra=vol.ALLOW_EXTRA,
)


async def async_setup(hass: HomeAssistant, config):
"""Register custom view which includes request in context"""
# Because we start after auth, we have access to store_result
store_result = hass.data[AUTH_DOMAIN]
# Remove old LoginFlowIndexView
for route in hass.http.app.router._resources:
if route.canonical == "/auth/login_flow":
_LOGGER.debug("Removed original login_flow route")
hass.http.app.router._resources.remove(route)
_LOGGER.debug("Add new login_flow route")
hass.http.register_view(
RequestLoginFlowIndexView(
hass.auth.login_flow, store_result, config[DOMAIN]["debug"]
)
)

# Inject Auth-Header provider.
providers = OrderedDict()
provider = headers.HeaderAuthProvider(
hass,
hass.auth._store,
config[DOMAIN],
)
providers[(provider.type, provider.id)] = provider
providers.update(hass.auth._providers)
hass.auth._providers = providers
_LOGGER.debug("Injected auth_header provider")
return True


def get_actual_ip(request: Request) -> str:
"""Get remote from `request` without considering overrides. This is because
when behind a reverse proxy, hass overrides the .remote attributes with the X-Forwarded-For
value. We still need to check the actual remote though, to verify its from a valid proxy."""
if isinstance(request._transport_peername, (list, tuple)):
return request._transport_peername[0]
return request._transport_peername


class RequestLoginFlowIndexView(LoginFlowIndexView):

debug: bool

def __init__(self, flow_mgr, store_result, debug=False) -> None:
super().__init__(flow_mgr, store_result)
self.debug = debug

@RequestDataValidator(
vol.Schema(
{
vol.Required("client_id"): str,
vol.Required("handler"): vol.Any(str, list),
vol.Required("redirect_uri"): str,
vol.Optional("type", default="authorize"): str,
}
)
)
@log_invalid_auth
async def post(self, request: Request, data: dict[str, Any]) -> Response:
"""Create a new login flow."""
client_id: str = data["client_id"]
redirect_uri: str = data["redirect_uri"]

if not indieauth.verify_client_id(client_id):
return self.json_message("Invalid client id", HTTPStatus.BAD_REQUEST)

handler: tuple[str, ...] | str
if isinstance(data["handler"], list):
handler = tuple(data["handler"])
else:
handler = data["handler"]

try:
_LOGGER.debug(request.headers)
actual_ip = get_actual_ip(request)
_LOGGER.debug("Got actual IP %s", actual_ip)
result = await self._flow_mgr.async_init(
handler, # type: ignore[arg-type]
context={
"request": request,
"ip_address": ip_address(request.remote), # type: ignore[arg-type]
"conn_ip_address": ip_address(actual_ip), # type: ignore[arg-type]
"credential_only": data.get("type") == "link_user",
"redirect_uri": redirect_uri,
},
)
except data_entry_flow.UnknownHandler:
return self.json_message("Invalid handler specified", HTTPStatus.NOT_FOUND)
except data_entry_flow.UnknownStep:
return self.json_message(
"Handler does not support init", HTTPStatus.BAD_REQUEST
)

return await self._async_flow_result_to_response(request, client_id, result)
Loading