Dotfiles/setup/modules/FreeipaAnsible/freeipa-client.sh

160 lines
6.2 KiB
Bash
Executable File

#!/bin/bash
# freeipa-client.sh — FreeIPA client enrollment
#
# Three modes:
# --answerfile FILE Read settings from a JSON file (jq required)
# --interactive Prompt for every setting
# [flags] Pass any flags directly to freeipa-enroll.sh
#
# All flags accepted by freeipa-enroll.sh work here too and override
# any values loaded from an answerfile or interactive prompts.
#
# JSON answerfile schema:
# {
# "domain": "freeipa.abdelbaki.eu",
# "realm": "FREEIPA.ABDELBAKI.EU",
# "server": "freeipa.abdelbaki.eu",
# "hostname": "", <- leave blank to use current hostname
# "principal": "admin",
# "password": "", <- will prompt if blank
# "mkhomedir": true,
# "sudo": true,
# "dns_update": true,
# "ntp_server": "",
# "fido2": false,
# "fido2_users": []
# }
set -euo pipefail
SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENROLL="$SELF_DIR/freeipa-enroll.sh"
[[ ! -x "$ENROLL" ]] && { echo "Error: freeipa-enroll.sh not found at $ENROLL" >&2; exit 1; }
# Defaults (match freeipa-enroll.sh hardcoded values; freeipa-server.sh will
# sed-replace these when generating customized copies)
IPA_DOMAIN="freeipa.abdelbaki.eu"
IPA_REALM="FREEIPA.ABDELBAKI.EU"
IPA_SERVER="freeipa.abdelbaki.eu"
MODE=""
ANSWERFILE=""
PASSTHROUGH=()
while [[ $# -gt 0 ]]; do
case $1 in
--answerfile) MODE="answerfile"; ANSWERFILE="$2"; shift 2 ;;
--interactive) MODE="interactive"; shift ;;
*) PASSTHROUGH+=("$1"); shift ;;
esac
done
# ─── Answerfile mode ──────────────────────────────────────────────────────────
if [[ "$MODE" == "answerfile" ]]; then
[[ ! -f "$ANSWERFILE" ]] && { echo "Error: answerfile not found: $ANSWERFILE" >&2; exit 1; }
command -v jq &>/dev/null || { echo "Error: jq is required for answerfile mode" >&2; exit 1; }
get() { jq -r "$1 // empty" "$ANSWERFILE"; }
get_bool() { jq -r "$1 // empty" "$ANSWERFILE"; }
AF_DOMAIN=$(get '.domain')
AF_REALM=$(get '.realm')
AF_SERVER=$(get '.server')
AF_HOSTNAME=$(get '.hostname')
AF_PRINCIPAL=$(get '.principal')
AF_PASSWORD=$(get '.password')
AF_NTP=$(get '.ntp_server')
AF_MKHOMEDIR=$(get_bool '.mkhomedir')
AF_SUDO=$(get_bool '.sudo')
AF_DNS_UPDATE=$(get_bool '.dns_update')
AF_FIDO2=$(get_bool '.fido2')
mapfile -t AF_FIDO2_USERS < <(jq -r '.fido2_users // [] | .[]' "$ANSWERFILE")
ARGS=()
[[ -n "$AF_DOMAIN" ]] && ARGS+=(--domain "$AF_DOMAIN")
[[ -n "$AF_REALM" ]] && ARGS+=(--realm "$AF_REALM")
[[ -n "$AF_SERVER" ]] && ARGS+=(--server "$AF_SERVER")
[[ -n "$AF_HOSTNAME" ]] && ARGS+=(--hostname "$AF_HOSTNAME")
[[ -n "$AF_PRINCIPAL" ]] && ARGS+=(--principal "$AF_PRINCIPAL")
[[ -n "$AF_NTP" ]] && ARGS+=(--ntp-server "$AF_NTP")
if [[ -z "$AF_PASSWORD" ]]; then
printf '[?] Password for %s@%s: ' "${AF_PRINCIPAL:-admin}" "${AF_REALM:-REALM}"
read -rs AF_PASSWORD; echo
fi
ARGS+=(--password "$AF_PASSWORD")
[[ "$AF_MKHOMEDIR" == "false" ]] && ARGS+=(--no-mkhomedir)
[[ "$AF_SUDO" == "false" ]] && ARGS+=(--no-sudo)
[[ "$AF_DNS_UPDATE" == "false" ]] && ARGS+=(--no-dns-update)
[[ "$AF_FIDO2" == "true" ]] && ARGS+=(--fido2)
for U in "${AF_FIDO2_USERS[@]}"; do
[[ -n "$U" ]] && ARGS+=(--fido2-user "$U")
done
exec "$ENROLL" "${ARGS[@]}" "${PASSTHROUGH[@]}"
fi
# ─── Interactive mode ─────────────────────────────────────────────────────────
if [[ "$MODE" == "interactive" ]]; then
p() { printf '\033[0;35m[?]\033[0m %s ' "$*"; }
p "IPA Domain [$IPA_DOMAIN]:"; read -r I; IPA_DOMAIN="${I:-$IPA_DOMAIN}"
IPA_REALM="${IPA_DOMAIN^^}"
p "Kerberos Realm [$IPA_REALM]:"; read -r I; IPA_REALM="${I:-$IPA_REALM}"
p "IPA Server [$IPA_SERVER]:"; read -r I; IPA_SERVER="${I:-$IPA_SERVER}"
CURRENT_HOST=$(hostname -f 2>/dev/null || hostname)
p "This host's FQDN [$CURRENT_HOST]:"; read -r I; CLIENT_HOSTNAME="${I:-$CURRENT_HOST}"
p "Admin principal [admin]:"; read -r PRINCIPAL; PRINCIPAL="${PRINCIPAL:-admin}"
p "Admin password (no echo):"; read -rs PASSWORD; echo
[[ -z "$PASSWORD" ]] && { echo "Error: password is required" >&2; exit 1; }
p "Enable home directory creation? [Y/n]:"; read -r I
MKHOMEDIR=true; [[ "${I,,}" == "n"* ]] && MKHOMEDIR=false
p "Configure sudo via SSSD? [Y/n]:"; read -r I
SUDO=true; [[ "${I,,}" == "n"* ]] && SUDO=false
p "Update DNS record? [Y/n]:"; read -r I
DNS_UPDATE=true; [[ "${I,,}" == "n"* ]] && DNS_UPDATE=false
p "NTP server (blank to skip):"; read -r NTP_SERVER
p "Enable FIDO2 authentication? [y/N]:"; read -r I
FIDO2=false; [[ "${I,,}" == "y"* ]] && FIDO2=true
FIDO2_USER_LIST=()
if [[ "$FIDO2" == true ]]; then
p "FIDO2 users (comma-separated, blank to skip):"; read -r I
if [[ -n "$I" ]]; then
IFS=',' read -ra FIDO2_USER_LIST <<< "$I"
fi
fi
ARGS=(
--domain "$IPA_DOMAIN"
--realm "$IPA_REALM"
--server "$IPA_SERVER"
--hostname "$CLIENT_HOSTNAME"
--principal "$PRINCIPAL"
--password "$PASSWORD"
)
[[ "$MKHOMEDIR" == false ]] && ARGS+=(--no-mkhomedir)
[[ "$SUDO" == false ]] && ARGS+=(--no-sudo)
[[ "$DNS_UPDATE" == false ]] && ARGS+=(--no-dns-update)
[[ "$FIDO2" == true ]] && ARGS+=(--fido2)
[[ -n "$NTP_SERVER" ]] && ARGS+=(--ntp-server "$NTP_SERVER")
for U in "${FIDO2_USER_LIST[@]}"; do
[[ -n "${U// /}" ]] && ARGS+=(--fido2-user "${U// /}")
done
exec "$ENROLL" "${ARGS[@]}" "${PASSTHROUGH[@]}"
fi
# ─── Direct passthrough ───────────────────────────────────────────────────────
# No --answerfile or --interactive: forward all args directly to freeipa-enroll.sh
exec "$ENROLL" "${PASSTHROUGH[@]}"