#!/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[@]}"