From 8eb27c52679ac601060363e04d8039bcf4dd52ee Mon Sep 17 00:00:00 2001 From: Austin Alvarado Date: Fri, 7 Apr 2023 14:59:21 -0600 Subject: [PATCH] docs: Create Home Assistant config (#535) --- example_configs/home-assistant.md | 23 ++++++++++ example_configs/lldap-ha-auth.sh | 70 +++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 example_configs/home-assistant.md create mode 100644 example_configs/lldap-ha-auth.sh diff --git a/example_configs/home-assistant.md b/example_configs/home-assistant.md new file mode 100644 index 0000000..c9a0182 --- /dev/null +++ b/example_configs/home-assistant.md @@ -0,0 +1,23 @@ +# Home Assistant Configuration + +Home Assistant configures ldap auth via the [Command Line Auth Provider](https://www.home-assistant.io/docs/authentication/providers/#command-line). The wiki mentions a script that can be used for LDAP authentication, but it doesn't work in the container version (it is lacking both `ldapsearch` and `curl` ldap protocol support). Thankfully LLDAP has a graphql API to save the day! + +## Graphql-based Auth Script + +The [auth script](lldap-ha-auth.sh) attempts to authenticate a user against an LLDAP server, using credentials provided via `username` and `password` environment variables. The first argument must be the URL of your LLDAP server, accessible from Home Assistant. You can provide an additional optional argument to confine allowed logins to a single group. The script will output the user's display name as the `name` variable, if not empty. + +1. Copy the [auth script](lldap-ha-auth.sh) to your home assistant instance. In this example, we use `/config/lldap-auth.sh`. +2. Add the following to your configuration.yaml in Home assistant: +```yaml +homeassistant: + auth_providers: + # Ensure you have the homeassistant provider enabled if you want to continue using your existing accounts + - type: homeassistant + - type: command_line + command: /config/lldap-auth.sh + # Only allow users in the 'homeassistant_user' group to login. + # Change to ["https://lldap.example.com"] to allow all users + args: ["https://lldap.example.com", "homeassistant_user"] + meta: true +``` +3. Reload your config or restart Home Assistant \ No newline at end of file diff --git a/example_configs/lldap-ha-auth.sh b/example_configs/lldap-ha-auth.sh new file mode 100644 index 0000000..06f93b7 --- /dev/null +++ b/example_configs/lldap-ha-auth.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +# Usernames should be validated using a regular expression to be of +# a known format. Special characters will be escaped anyway, but it is +# generally not recommended to allow more than necessary. +# This pattern is set by default. In your config file, you can either +# overwrite it with a different one or use "unset USERNAME_PATTERN" to +# disable validation completely. +USERNAME_PATTERN='^[a-z|A-Z|0-9|_|-|.]+$' + +# When the timeout (in seconds) is exceeded (e.g. due to slow networking), +# authentication fails. +TIMEOUT=3 + +# Log messages to stderr. +log() { + echo "$1" >&2 +} + +# Get server address +if [ -z "$1" ]; then + log "Usage: lldap-auth.sh " + exit 2 +fi +SERVER_URL="${1%/}" + +# Check username and password are present and not malformed. +if [ -z "$username" ] || [ -z "$password" ]; then + log "Need username and password environment variables." + exit 2 +elif [ ! -z "$USERNAME_PATTERN" ]; then + username_match=$(echo "$username" | sed -r "s/$USERNAME_PATTERN/x/") + if [ "$username_match" != "x" ]; then + log "Username '$username' has an invalid format." + exit 2 + fi +fi + +RESPONSE=$(curl -f -s -X POST -m "$TIMEOUT" -H "Content-type: application/json" -d '{"username":"'"$username"'","password":"'"$password"'"}' "$SERVER_URL/auth/simple/login") +if [[ $? -ne 0 ]]; then + log "Auth failed" + exit 1 +fi +TOKEN=$(jq -e -r .token <<< $RESPONSE) +if [[ $? -ne 0 ]]; then + log "Failed to parse token" + exit 1 +fi + +RESPONSE=$(curl -f -s -m "$TIMEOUT" -H "Content-type: application/json" -H "Authorization: Bearer ${TOKEN}" -d '{"variables":{"id":"'"$username"'"},"query":"query($id:String!){user(userId:$id){displayName groups{displayName}}}"}' "$SERVER_URL/api/graphql") +if [[ $? -ne 0 ]]; then + log "Failed to get user" + exit 1 +fi + +USER_JSON=$(jq -e .data.user <<< $RESPONSE) +if [[ $? -ne 0 ]]; then + log "Failed to parse user json" + exit 1 +fi + +if [[ ! -z "$2" ]] && ! jq -e '.groups|map(.displayName)|index("'"$2"'")' <<< $USER_JSON > /dev/null 2>&1; then + log "User is not in group '$2'" + exit 1 +fi + +DISPLAY_NAME=$(jq -r .displayName <<< $USER_JSON) + +[[ ! -z "$DISPLAY_NAME" ]] && echo "name = $DISPLAY_NAME" +