#!/bin/sh set -eu SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) PROJECT_DIR=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd) FORCE_USER=no if [ "${1:-}" = "--force-user" ]; then FORCE_USER=yes elif [ "$#" -gt 0 ]; then printf 'Usage: %s [--force-user]\n' "$0" >&2 exit 1 fi if [ ! -f "$PROJECT_DIR/.env" ]; then cp "$PROJECT_DIR/.env.example" "$PROJECT_DIR/.env" printf 'Created .env from .env.example\n' fi set -a . "$PROJECT_DIR/.env" set +a BASE_DOMAIN=${BASE_DOMAIN:-tdwiki.home.arpa} case "$BASE_DOMAIN" in *[!a-zA-Z0-9.-]*|.*|*.) printf 'Invalid BASE_DOMAIN: %s\n' "$BASE_DOMAIN" >&2 exit 1 ;; esac mkdir -p \ "$PROJECT_DIR/data/authelia" \ "$PROJECT_DIR/data/caddy/data" \ "$PROJECT_DIR/data/caddy/config" \ "$PROJECT_DIR/secrets" chmod 700 "$PROJECT_DIR/secrets" generate_secret() { file=$1 bytes=$2 if [ ! -s "$file" ]; then umask 077 openssl rand -hex "$bytes" > "$file" printf 'Generated %s\n' "${file#"$PROJECT_DIR/"}" fi } generate_secret "$PROJECT_DIR/secrets/authelia_jwt_secret" 32 generate_secret "$PROJECT_DIR/secrets/authelia_session_secret" 32 generate_secret "$PROJECT_DIR/secrets/authelia_storage_encryption_key" 32 sed "s/__BASE_DOMAIN__/$BASE_DOMAIN/g" \ "$PROJECT_DIR/docker/authelia/configuration.template.yml" \ > "$PROJECT_DIR/docker/authelia/configuration.yml" USERS_FILE="$PROJECT_DIR/docker/authelia/users_database.yml" if [ ! -f "$USERS_FILE" ] || [ "$FORCE_USER" = yes ]; then USERNAME=${AUTHELIA_USERNAME:-} DISPLAY_NAME=${AUTHELIA_DISPLAY_NAME:-} EMAIL=${AUTHELIA_EMAIL:-} PASSWORD=${AUTHELIA_PASSWORD:-} if [ -z "$USERNAME" ]; then printf 'Authelia username [admin]: ' read -r USERNAME USERNAME=${USERNAME:-admin} fi case "$USERNAME" in *[!a-zA-Z0-9_.-]*) printf 'Username may only contain letters, digits, dot, underscore, and hyphen.\n' >&2 exit 1 ;; esac if [ -z "$DISPLAY_NAME" ]; then printf 'Display name [%s]: ' "$USERNAME" read -r DISPLAY_NAME DISPLAY_NAME=${DISPLAY_NAME:-$USERNAME} fi if [ -z "$EMAIL" ]; then printf 'Email [%s@%s]: ' "$USERNAME" "$BASE_DOMAIN" read -r EMAIL EMAIL=${EMAIL:-$USERNAME@$BASE_DOMAIN} fi case "$DISPLAY_NAME$EMAIL" in *'"'*|*'\'*) printf 'Display name and email cannot contain quote or backslash characters.\n' >&2 exit 1 ;; esac if [ -z "$PASSWORD" ]; then if [ ! -t 0 ]; then printf 'Set AUTHELIA_PASSWORD for non-interactive initialization.\n' >&2 exit 1 fi printf 'Password: ' stty -echo read -r PASSWORD stty echo printf '\nConfirm password: ' stty -echo read -r PASSWORD_CONFIRM stty echo printf '\n' if [ "$PASSWORD" != "$PASSWORD_CONFIRM" ]; then printf 'Passwords do not match.\n' >&2 exit 1 fi fi if [ "${#PASSWORD}" -lt 12 ]; then printf 'Password must contain at least 12 characters.\n' >&2 exit 1 fi HASH_OUTPUT=$(docker run --rm authelia/authelia:4.39.20 \ authelia crypto hash generate argon2 \ --password "$PASSWORD" \ --no-confirm) PASSWORD_HASH=${HASH_OUTPUT#Digest: } unset PASSWORD PASSWORD_CONFIRM HASH_OUTPUT escape_sed() { printf '%s' "$1" | sed 's/[|&]/\\&/g' } USERNAME_ESCAPED=$(escape_sed "$USERNAME") DISPLAY_NAME_ESCAPED=$(escape_sed "$DISPLAY_NAME") EMAIL_ESCAPED=$(escape_sed "$EMAIL") PASSWORD_HASH_ESCAPED=$(escape_sed "$PASSWORD_HASH") sed \ -e "s|__USERNAME__|$USERNAME_ESCAPED|g" \ -e "s|__DISPLAY_NAME__|$DISPLAY_NAME_ESCAPED|g" \ -e "s|__EMAIL__|$EMAIL_ESCAPED|g" \ -e "s|__PASSWORD_HASH__|$PASSWORD_HASH_ESCAPED|g" \ "$PROJECT_DIR/docker/authelia/users_database.template.yml" \ > "$USERS_FILE" chmod 600 "$USERS_FILE" printf 'Created Authelia user %s\n' "$USERNAME" fi docker compose --project-directory "$PROJECT_DIR" config --quiet printf '\nInitialization complete.\n' printf 'Add wiki.%s, dav.%s, and auth.%s to local DNS, then run:\n' \ "$BASE_DOMAIN" "$BASE_DOMAIN" "$BASE_DOMAIN" printf ' docker compose up -d --build\n'