Passwortrichtlinie des UniFi OS Server

Immer wie­der gibt es Soft­ware, wel­che vor­schreibt, wie genau ein Pass­wort aus­zu­se­hen hat. Der Uni­Fi OS Ser­ver ist eine davon und erlaubt es nicht, die Richt­li­nie anzu­pas­sen. Mit fol­gen­dem Bash-Skript kann das Pass­wort über die undo­ku­men­tier­te API belie­big geän­dert werden.

#!/usr/bin/env bash
set -u

if [[ $# -ne 3 ]]; then
	echo "Verwendung: $0 <Server-IP> '<altes-passwort>' '<neues-passwort>'"
	exit 1
fi

USER="admin"
BASE_URL="https://$1:11443"
OLD_PASSWORD="$2"
NEW_PASSWORD="$3"

LOGIN_BODY="$(mktemp)"
LOGIN_HEADERS="$(mktemp)"
PASSWORD_BODY="$(mktemp)"
PASSWORD_HEADERS="$(mktemp)"

trap 'rm -f "$LOGIN_BODY" "$LOGIN_HEADERS" "$PASSWORD_BODY" "$PASSWORD_HEADERS"' EXIT

# Login
LOGIN_JSON="$(
	jq -n \
		--arg username "$USER" \
		--arg password "$OLD_PASSWORD" \
		'{
			username: $username,
			password: $password
		}'
)"

wget \
	--no-check-certificate \
	--quiet \
	--server-response \
	--header="Content-Type: application/json" \
	--post-data="$LOGIN_JSON" \
	-O "$LOGIN_BODY" \
	"$BASE_URL/api/auth/login" \
	2>"$LOGIN_HEADERS"

HTTP_STATUS="$(
	sed -n 's/^  HTTP\/[^ ]* \([0-9][0-9][0-9]\).*/\1/p' "$LOGIN_HEADERS" | tail -n 1
)"

if [[ "$HTTP_STATUS" != "200" ]]; then
	echo "Login fehlgeschlagen (HTTP $HTTP_STATUS)."
	if [[ -s "$LOGIN_BODY" ]]; then
		cat "$LOGIN_BODY"
	else
		cat "$LOGIN_HEADERS"
	fi
	exit 1
fi

USER_UUID="$(jq -r '.id // empty' "$LOGIN_BODY")"
if [[ -z "$USER_UUID" ]]; then
	echo "Fehler: Keine User-UUID in der Login-Antwort gefunden."
	exit 1
fi

TOKEN="$(sed -n 's/.*Set-Cookie: TOKEN=\([^;]*\).*/\1/p' "$LOGIN_HEADERS" | tail -n 1)"
if [[ -z "$TOKEN" ]]; then
	echo "Fehler: Kein TOKEN-Cookie gefunden."
	exit 1
fi

JWT_PAYLOAD="$(
	printf '%s' "$TOKEN" |
	cut -d. -f2 |
	tr '_-' '/+' |
	awk '{l=length($0)%4; if(l==2) printf "%s==",$0; else if(l==3) printf "%s=",$0; else printf "%s",$0}' |
	base64 -d 2>/dev/null
)"

CSRF_TOKEN="$(
	printf '%s' "$JWT_PAYLOAD" |
	jq -r '.csrfToken // empty'
)"

if [[ -z "$CSRF_TOKEN" ]]; then
	echo "Kein csrfToken im JWT gefunden."
	exit 1
fi

echo "Login erfolgreich."
echo "  User-UUID: $USER_UUID"
echo "  JWT: $TOKEN"
echo "  csrfToken: $CSRF_TOKEN"

# Passwort ändern
PASSWORD_JSON="$(
	jq -n \
		--arg password "$NEW_PASSWORD" \
		'{
			password: $password
		}'
)"

wget \
	--no-check-certificate \
	--quiet \
	--server-response \
	--header="Content-Type: application/json" \
	--header="Cookie: TOKEN=$TOKEN" \
	--header="X-CSRF-Token: $CSRF_TOKEN" \
	--body-data="$PASSWORD_JSON" \
	--method=PUT \
	-O "$PASSWORD_BODY" \
	"$BASE_URL/proxy/users/api/v2/user/$USER_UUID" \
	2>"$PASSWORD_HEADERS"

HTTP_STATUS="$(sed -n 's/.*HTTP\/[^ ]* \([0-9][0-9][0-9]\).*/\1/p' "$PASSWORD_HEADERS" | tail -n 1)"
if [[ "$HTTP_STATUS" != "200" ]]; then
	echo "Passwortänderung fehlgeschlagen (HTTP $HTTP_STATUS)."
	if [[ -s "$PASSWORD_BODY" ]]; then
		cat "$PASSWORD_BODY"
	else
		cat "$PASSWORD_HEADERS"
	fi
	exit 1
fi

echo "Passwort erfolgreich geändert."