2026-03-31 14:52:50 -03:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
marker="/var/lib/metis/rpi4-longhorn-firstboot.done"
|
|
|
|
|
env_file="/etc/metis/firstboot.env"
|
|
|
|
|
key_file="/etc/metis/authorized_keys"
|
|
|
|
|
fstab_append="/etc/metis/fstab.append"
|
2026-04-05 02:42:04 -03:00
|
|
|
sudoers_file="/etc/metis/sudoers-hecate"
|
2026-03-31 14:52:50 -03:00
|
|
|
default_groups=(tty disk dialout sudo audio video plugdev games users systemd-journal input render netdev)
|
|
|
|
|
|
|
|
|
|
exec > >(tee -a /var/log/metis-rpi4-longhorn-firstboot.log) 2>&1
|
|
|
|
|
|
|
|
|
|
retry_cmd() {
|
|
|
|
|
local attempts="$1"
|
|
|
|
|
shift
|
|
|
|
|
local try=1
|
|
|
|
|
until "$@"; do
|
|
|
|
|
if [ "${try}" -ge "${attempts}" ]; then
|
|
|
|
|
return 1
|
|
|
|
|
fi
|
|
|
|
|
try=$((try + 1))
|
|
|
|
|
sleep 5
|
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ensure_network_access() {
|
|
|
|
|
retry_cmd 12 sh -c 'ip route get 1.1.1.1 >/dev/null 2>&1'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if [ -f "${marker}" ]; then
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
mkdir -p /var/lib/metis /mnt/astreae /mnt/asteria
|
|
|
|
|
|
|
|
|
|
if [ -f "${env_file}" ]; then
|
|
|
|
|
# shellcheck disable=SC1090
|
|
|
|
|
. "${env_file}"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
metis_hostname="${METIS_HOSTNAME:-}"
|
|
|
|
|
metis_ssh_user="${METIS_SSH_USER:-atlas}"
|
|
|
|
|
metis_k3s_version="${METIS_K3S_VERSION:-}"
|
|
|
|
|
|
|
|
|
|
if [ -n "${metis_hostname}" ]; then
|
|
|
|
|
hostnamectl set-hostname "${metis_hostname}" || true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if command -v nmcli >/dev/null 2>&1; then
|
|
|
|
|
retry_cmd 10 sh -c 'nmcli general status >/dev/null 2>&1'
|
|
|
|
|
nmcli connection reload || true
|
|
|
|
|
while IFS=: read -r name type device; do
|
2026-04-05 02:42:04 -03:00
|
|
|
[ "${device}" = "end0" ] || [ "${device}" = "eth0" ] || continue
|
2026-03-31 14:52:50 -03:00
|
|
|
[ "${name}" = "end0-static" ] && continue
|
2026-04-05 02:42:04 -03:00
|
|
|
[ "${name}" = "eth0-static" ] && continue
|
2026-03-31 14:52:50 -03:00
|
|
|
case "${type}" in
|
|
|
|
|
ethernet|802-3-ethernet)
|
|
|
|
|
nmcli connection modify "${name}" connection.autoconnect no || true
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
done < <(nmcli -t -f NAME,TYPE,DEVICE connection show 2>/dev/null || true)
|
2026-04-05 02:42:04 -03:00
|
|
|
nmcli connection up end0-static || nmcli connection up eth0-static || true
|
2026-03-31 14:52:50 -03:00
|
|
|
elif [ -f /etc/systemd/network/10-end0-static.network ]; then
|
|
|
|
|
systemctl enable systemd-networkd.service || true
|
|
|
|
|
systemctl restart systemd-networkd.service || true
|
|
|
|
|
systemctl restart systemd-networkd-wait-online.service || true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [ -f "${fstab_append}" ]; then
|
|
|
|
|
while IFS= read -r line; do
|
|
|
|
|
[ -z "${line}" ] && continue
|
|
|
|
|
grep -Fqx "${line}" /etc/fstab || printf '%s\n' "${line}" >> /etc/fstab
|
|
|
|
|
done < "${fstab_append}"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
mount -a || true
|
|
|
|
|
|
|
|
|
|
packages=()
|
|
|
|
|
if ! command -v sshd >/dev/null 2>&1; then
|
|
|
|
|
packages+=("openssh-server")
|
|
|
|
|
fi
|
|
|
|
|
if ! command -v mount.nfs >/dev/null 2>&1; then
|
|
|
|
|
packages+=("nfs-common")
|
|
|
|
|
fi
|
|
|
|
|
if ! command -v iscsiadm >/dev/null 2>&1; then
|
|
|
|
|
packages+=("open-iscsi")
|
|
|
|
|
fi
|
2026-04-05 02:42:04 -03:00
|
|
|
if ! command -v iptables >/dev/null 2>&1; then
|
|
|
|
|
packages+=("iptables")
|
|
|
|
|
fi
|
2026-03-31 14:52:50 -03:00
|
|
|
if [ "${#packages[@]}" -gt 0 ]; then
|
|
|
|
|
export DEBIAN_FRONTEND=noninteractive
|
2026-04-05 02:42:04 -03:00
|
|
|
if ensure_network_access; then
|
|
|
|
|
if ! retry_cmd 5 apt-get update; then
|
|
|
|
|
echo "WARN: apt-get update failed; continuing without package install."
|
|
|
|
|
elif ! retry_cmd 5 apt-get install -y --no-install-recommends "${packages[@]}"; then
|
|
|
|
|
echo "WARN: apt-get install failed for: ${packages[*]}; continuing."
|
|
|
|
|
fi
|
|
|
|
|
else
|
|
|
|
|
echo "WARN: outbound network check failed; skipping package install for: ${packages[*]}"
|
|
|
|
|
fi
|
2026-03-31 14:52:50 -03:00
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
systemctl daemon-reload
|
|
|
|
|
systemctl enable ssh.socket || systemctl enable ssh.service || true
|
|
|
|
|
systemctl restart ssh.socket || systemctl restart ssh.service || systemctl start ssh.socket || systemctl start ssh.service || true
|
|
|
|
|
mkdir -p /etc/iscsi /etc/iscsi/nodes /etc/iscsi/send_targets
|
|
|
|
|
if [ ! -s /etc/iscsi/initiatorname.iscsi ] && command -v iscsi-iname >/dev/null 2>&1; then
|
|
|
|
|
printf 'InitiatorName=%s\n' "$(iscsi-iname)" > /etc/iscsi/initiatorname.iscsi
|
|
|
|
|
fi
|
|
|
|
|
systemctl enable --now iscsid.socket || true
|
|
|
|
|
systemctl enable --now open-iscsi.service || true
|
|
|
|
|
|
|
|
|
|
if [ -s "${key_file}" ]; then
|
|
|
|
|
install -d -m 700 /root/.ssh
|
|
|
|
|
install -m 600 "${key_file}" /root/.ssh/authorized_keys
|
|
|
|
|
|
|
|
|
|
if [ -n "${metis_ssh_user}" ]; then
|
|
|
|
|
group_list=()
|
|
|
|
|
for group_name in "${default_groups[@]}"; do
|
|
|
|
|
if getent group "${group_name}" >/dev/null 2>&1; then
|
|
|
|
|
group_list+=("${group_name}")
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
if [ "${#group_list[@]}" -gt 0 ]; then
|
|
|
|
|
group_csv="$(IFS=,; printf '%s' "${group_list[*]}")"
|
|
|
|
|
else
|
|
|
|
|
group_csv=""
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if ! id "${metis_ssh_user}" >/dev/null 2>&1; then
|
|
|
|
|
if [ -n "${group_csv}" ]; then
|
|
|
|
|
useradd -m -s /bin/bash -G "${group_csv}" "${metis_ssh_user}"
|
|
|
|
|
else
|
|
|
|
|
useradd -m -s /bin/bash "${metis_ssh_user}"
|
|
|
|
|
fi
|
|
|
|
|
elif [ -n "${group_csv}" ]; then
|
|
|
|
|
usermod -a -G "${group_csv}" "${metis_ssh_user}" || true
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
install -d -m 700 -o "${metis_ssh_user}" -g "${metis_ssh_user}" "/home/${metis_ssh_user}/.ssh"
|
|
|
|
|
install -m 600 -o "${metis_ssh_user}" -g "${metis_ssh_user}" "${key_file}" "/home/${metis_ssh_user}/.ssh/authorized_keys"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
2026-04-05 02:42:04 -03:00
|
|
|
if [ -s "${sudoers_file}" ]; then
|
|
|
|
|
install -d -m 755 /etc/sudoers.d
|
|
|
|
|
install -m 440 "${sudoers_file}" /etc/sudoers.d/90-hecate-atlas
|
|
|
|
|
if command -v visudo >/dev/null 2>&1; then
|
|
|
|
|
if ! visudo -cf /etc/sudoers.d/90-hecate-atlas >/dev/null 2>&1; then
|
|
|
|
|
echo "WARN: invalid /etc/sudoers.d/90-hecate-atlas generated by metis; removing it."
|
|
|
|
|
rm -f /etc/sudoers.d/90-hecate-atlas
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
2026-03-31 14:52:50 -03:00
|
|
|
rm -f /root/.not_logged_in_yet
|
|
|
|
|
|
|
|
|
|
if ! command -v k3s >/dev/null 2>&1; then
|
|
|
|
|
installer_env=("INSTALL_K3S_EXEC=agent")
|
|
|
|
|
if [ -n "${metis_k3s_version}" ]; then
|
|
|
|
|
installer_env+=("INSTALL_K3S_VERSION=${metis_k3s_version}")
|
|
|
|
|
fi
|
|
|
|
|
ensure_network_access
|
|
|
|
|
retry_cmd 5 env "${installer_env[@]}" sh -c 'curl -sfL https://get.k3s.io | sh -'
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
systemctl enable k3s-agent
|
|
|
|
|
systemctl restart k3s-agent || systemctl start k3s-agent
|
|
|
|
|
|
|
|
|
|
touch "${marker}"
|