#!/bin/sh

umask 022
PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin
export PATH
set -e
trap '[ $? -eq 0 ] || printf "WARNING: %s did not complete successfully.\n" "$0" >&2' EXIT
trap 'exit 1' INT HUP TERM

# Stage-0 bootstrap: NFS is not yet mounted, so /home/admin does not exist.
# On the web tree, aitken.conf and common.sh sit one level up from this script
# (admin/aitken.conf, admin/common.sh).  setup.sh is always fetched and run
# from the web tree for stage-0; it is not sourced directly from the repo.
_dir="$(cd "$(dirname "$0")" && pwd)"
. "${_dir}/../aitken.conf"
. "${_dir}/../common.sh"

# Shorthand for processor scripts; these live under ADMIN_BASE so they are
# only accessible after NFS is mounted (steps below).
_PROC="${ADMIN_BASE}/system/os/freebsd-14.4/processors"


#
# Validate hostname
#
[ -n "$1" ] || die "Usage: $0 <fqdn>"
hostname="$1"
validate_fqdn "$hostname"
# shellcheck disable=SC2086
require_known_domain "$hostname" $VALID_DOMAINS

log ""
log "Setting up a new FreeBSD 14.4 host (${hostname})."
log "Press Enter to continue, or ^C to abort."
read -r _x


#
# Remove local ZFS home dataset
#
# The default FreeBSD ZFS-on-root layout mounts zroot/home at /home.  It must
# be destroyed before NFS can own /home.  This runs pre-NFS (from /tmp) so the
# script itself is never in /home.
#
log "Removing local ZFS home dataset (if present)..."
if zfs list zroot/home > /dev/null 2>&1; then
    if mount -t nfs | grep -q " on /home "; then
        log "  /home already NFS-mounted; skipping zfs unmount/destroy."
    else
        zfs unmount zroot/home
        zfs destroy zroot/home
        syslog "zroot/home removed."
    fi
else
    log "  zroot/home does not exist, skipping."
fi


#
# Update system base
#
# Run freebsd-update non-interactively before enabling NFS so updates apply
# against an unmodified system tree.  Some versions exit 1 when already
# up to date, so we allow failure here.
#
log "Updating system base..."
printf '\n' | PAGER=cat freebsd-update --not-running-from-cron fetch || true
printf '\n' | PAGER=cat freebsd-update install || true
syslog "freebsd-update complete."


#
# Configure DNS resolvers
#
# Write /etc/resolv.conf authoritatively from aitken.conf.  Runs before the
# NFS mount so DNS is correct when the NFS server hostname is resolved.
# On a static-IP host nothing overwrites resolv.conf after boot.
#
log "Configuring DNS resolvers..."
backup_file /etc/resolv.conf
{
    printf 'search %s\n' "$DNS_SEARCH"
    # shellcheck disable=SC2086
    for _ns in $DNS_RESOLVERS; do
        printf 'nameserver %s\n' "$_ns"
    done
} > /etc/resolv.conf
syslog "DNS configured: search=${DNS_SEARCH} resolvers=${DNS_RESOLVERS}"


#
# Mount NFS filesystems
#
# nfsv3 forces NFSv3 to avoid NFSv4 UID/GID mapping issues with TrueNAS Core.
# See README.md for background.
#
log "Installing NFS client and mounting filesystems..."

sysrc rpcbind_enable="YES"
sysrc nfs_client_enable="YES"
sysrc rpc_lockd_enable="YES"
sysrc rpc_statd_enable="YES"

service rpcbind status > /dev/null 2>&1 || service rpcbind start
service nfsclient status > /dev/null 2>&1 || service nfsclient start
service statd status > /dev/null 2>&1 || service statd start
service lockd status > /dev/null 2>&1 || service lockd start

ensure_dir /media/pics   root wheel 755
ensure_dir /media/videos root wheel 755

backup_file /etc/fstab
printf '%s\n' "$NFS_MOUNTS" | while IFS=' 	' read -r _export _mnt; do
    [ -z "$_export" ] && continue
    append_once "${_export}" /etc/fstab \
        "${_export} ${_mnt} nfs rw,nfsv3 0 0"
done
mkdir -p /cdrom
append_once "/dev/cd0" /etc/fstab \
    "/dev/cd0	/cdrom	cd9660	ro,noauto	0	0"

mount -a -t nfs
df -h

require_nfs_mount /home


#
# Disable extra virtual terminals
#
log "Disabling extra virtual terminals..."
backup_file /etc/ttys
"${_PROC}/freebsd-disable-extra-vtys.sh"
kill -1 1


#
# Bootstrap pkg(8) and install base packages
#
log "Setting up pkg(8)..."
pkg bootstrap -y

log "Installing bash and bash-completion..."
pkg install -y --quiet shells/bash bash-completion
[ -L /bin/bash ] || ln -s /usr/local/bin/bash /bin/bash

log "Installing qemu-guest-agent..."
# NOTE: The qemu guest agent will fail until it is enabled in Proxmox.
pkg install -y --quiet qemu-guest-agent
sysrc qemu_guest_agent_enable="YES"
sysrc qemu_guest_agent_flags="-d -v -l /var/log/qemu-ga.log"
service qemu-guest-agent status > /dev/null 2>&1 || service qemu-guest-agent start || warn "qemu-guest-agent failed to start"

# qemu-guest-agent transitively pulls in python3; create a convenience symlink.
# Detect the installed binary rather than hardcoding the minor version.
if [ ! -L /usr/local/bin/python3 ]; then
    _py=$(ls /usr/local/bin/python3.* 2>/dev/null | grep -v config | head -1)
    [ -n "$_py" ] && ln -sf "$_py" /usr/local/bin/python3
fi


#
# Configure Kerberos
#
log "Configuring Kerberos..."
install_file "${ADMIN_BASE}/system/files/etc/krb5.conf" /etc/krb5.conf 0644

if ! kadmin -p "${KERBEROS_ADMIN_PRINCIPAL}" add --random-key --use-defaults \
        "host/${hostname}"; then
    kadmin -p "${KERBEROS_ADMIN_PRINCIPAL}" get "host/${hostname}" > /dev/null \
        || die "kadmin: host/${hostname} add failed and principal does not exist in KDC"
    syslog "Kerberos: host/${hostname} principal already exists, re-extracting keytab"
fi
kadmin -p "${KERBEROS_ADMIN_PRINCIPAL}" ext_keytab "host/${hostname}"
chmod 4755 /usr/bin/ksu


#
# Configure SSH (Kerberos authentication via Include drop-ins)
#
# An Include directive is inserted at the top of both ssh_config and
# sshd_config so they pull in *.conf from their respective .d directories --
# the same drop-ins Ubuntu uses.  The Include directive is supported by
# FreeBSD's ssh_config(5) and sshd_config(5) though not present by default.
#
# Verification: after reboot, confirm with:
#   ssh -G localhost | grep gssapi
#   sshd -T | grep gssapi
#
log "Configuring SSH..."
pkg install -y --quiet pam_krb5-4.11_2

ensure_dir /etc/ssh/ssh_config.d  root wheel 755
ensure_dir /etc/ssh/sshd_config.d root wheel 755

backup_file /etc/ssh/ssh_config
"${_PROC}/freebsd-ssh-include.sh" /etc/ssh/ssh_config

backup_file /etc/ssh/sshd_config
"${_PROC}/freebsd-ssh-include.sh" /etc/ssh/sshd_config

install_file "${ADMIN_BASE}/system/files/etc/ssh/ssh_config.d/10-aitken-ssh.conf" \
    /etc/ssh/ssh_config.d/10-aitken-ssh.conf 0644
install_file "${ADMIN_BASE}/system/files/etc/ssh/sshd_config.d/10-aitken-sshd.conf" \
    /etc/ssh/sshd_config.d/10-aitken-sshd.conf 0644
install_file "${ADMIN_BASE}/system/files/etc/issue" /etc/issue 0644

backup_file /etc/pam.d/sshd
"${_PROC}/freebsd-pam-krb5.sh"

service sshd restart


#
# Configure sendmail
#
# Non-mailserver hosts: disable inbound sendmail, keep outbound MSP queue.
# Root alias forwards system mail to the admin address.
#
log "Configuring sendmail..."
backup_file /etc/mail/aliases
_tab=$(printf '\t')
if ! grep -qF "root:${_tab}${ADMIN_USER}@${PRIMARY_DOMAIN}" /etc/mail/aliases; then
    sed -i '' "s|^# root:.*|root:${_tab}${ADMIN_USER}@${PRIMARY_DOMAIN}|" \
        /etc/mail/aliases
fi
newaliases

sysrc sendmail_enable="NO"
sysrc sendmail_submit_enable="NO"
sysrc sendmail_outbound_enable="YES"
sysrc sendmail_msp_queue_enable="YES"

service sendmail status > /dev/null 2>&1 && service sendmail restart || true


#
# Install additional packages
#
log "Installing additional packages..."
pkg install -y --quiet curl perl5 wget lsof vim bat glow hs-ShellCheck


#
# Configure admin syslog routing
#
# Route ADMIN_SYSLOG_FACILITY to ADMIN_LOG_FILE.  Written to /etc/syslog.d so
# we never touch the freebsd-update-managed /etc/syslog.conf.  Relies on
# stock /etc/syslog.conf including /etc/syslog.d (verify: grep include
# /etc/syslog.conf).  The newsyslog drop-in handles log rotation.
#
log "Configuring admin syslog routing..."
printf '# aitken.com admin logging -- facility defined in aitken.conf\n%s.*\t%s\n' \
    "$ADMIN_SYSLOG_FACILITY" "$ADMIN_LOG_FILE" \
    > /etc/syslog.d/40-aitken-admin.conf
chmod 0644 /etc/syslog.d/40-aitken-admin.conf
touch "$ADMIN_LOG_FILE"
chmod 0640 "$ADMIN_LOG_FILE"
install_file "${ADMIN_BASE}/system/files/etc/newsyslog.conf.d/aitken-admin.conf" \
    /etc/newsyslog.conf.d/aitken-admin.conf 0644
service syslogd restart
syslog "admin syslog routing active: ${ADMIN_SYSLOG_FACILITY}.* -> ${ADMIN_LOG_FILE}"


#
# Update login.conf default PATH
#
# Promotes /usr/local/bin and /usr/local/sbin ahead of the system directories
# so locally-installed binaries take precedence.
#
log "Updating login.conf default PATH..."
backup_file /etc/login.conf
"${_PROC}/freebsd-set-default-path.sh"


#
# Configure user accounts
#
log "Configuring user accounts..."

# Set useradd defaults: home base /home, no password, bash login shell.
pw useradd -D -b /home -w no -s /usr/local/bin/bash

# Remove the unused toor account; ignore error if already gone.
pw userdel toor > /dev/null 2>&1 || true

# Reconfigure root shell and description.
pw usermod 0 -c "System Administrator" -s /usr/local/bin/bash

install_file "${ADMIN_BASE}/system/files/root/.k5login" /root/.k5login 0600

# Create admin user if absent; home directory is on NFS so we create it
# separately without skel files -- pw useradd -m would clobber existing dotfiles.
id "${ADMIN_USER}" > /dev/null 2>&1 || \
    pw useradd "${ADMIN_USER}" -u "${ADMIN_UID}" -c "Jeff Aitken" \
               -G "${ADMIN_GROUP}"


#
# Set MOTD
#
log "Setting MOTD..."
backup_file /etc/motd.template
sed "s/MYHOSTNAME/${hostname}/g" \
    "${ADMIN_BASE}/system/files/etc/motd.template" > /etc/motd.template


log ""
log "Setup complete.  Reboot now."
exit 0
