#!/bin/sh set -eu install_ssh_key() { if [ -z "${ssh_authorized_keys_base64}" ]; then return 0 fi install -d -m 0700 -o ${template_user} -g ${template_user} /home/${template_user}/.ssh printf '%s' '${ssh_authorized_keys_base64}' | base64 -d >/home/${template_user}/.ssh/authorized_keys chown ${template_user}:${template_user} /home/${template_user}/.ssh/authorized_keys chmod 0600 /home/${template_user}/.ssh/authorized_keys } configure_sudo() { usermod -p '*' '${template_user}' >/dev/null 2>&1 || true printf '%s ALL=(ALL) NOPASSWD:ALL\n' '${template_user}' >/etc/sudoers.d/90-homelab-${template_user} chmod 0440 /etc/sudoers.d/90-homelab-${template_user} } configure_dns() { dns_servers="${node_dns_servers}" if [ -z "$dns_servers" ]; then return 0 fi if systemctl list-unit-files systemd-resolved.service >/dev/null 2>&1; then mkdir -p /etc/systemd/resolved.conf.d { echo "[Resolve]" printf 'DNS=%s\n' "$dns_servers" printf 'FallbackDNS=%s\n' "$dns_servers" echo "DNSSEC=no" } >/etc/systemd/resolved.conf.d/homelab-k8s.conf fi } configure_kubernetes_prereqs() { swapoff -a || true systemctl mask swap.target >/dev/null 2>&1 || true awk ' /^[[:space:]]*#/ { print; next } $3 == "swap" { next } { print } ' /etc/fstab >/etc/fstab.homelab mv /etc/fstab.homelab /etc/fstab printf 'overlay\nbr_netfilter\nip_vs\nip_vs_rr\nip_vs_wrr\nip_vs_sh\nnf_conntrack\n' >/etc/modules-load.d/k8s.conf modprobe overlay || true modprobe br_netfilter || true modprobe ip_vs || true modprobe ip_vs_rr || true modprobe ip_vs_wrr || true modprobe ip_vs_sh || true modprobe nf_conntrack || true cat >/etc/sysctl.d/99-kubernetes-cri.conf <<'SYSCTL' net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 SYSCTL sysctl --system >/dev/null || true } configure_kernel_boot_options() { boot_options="${kernel_cgroup_boot_options}" if [ -z "$boot_options" ] || [ ! -f /etc/default/grub ]; then return 0 fi current_options="$(sed -n 's/^GRUB_CMDLINE_LINUX_DEFAULT=//p' /etc/default/grub | tail -n 1 | sed 's/^"//; s/"$//')" for option in $boot_options; do case " $current_options " in *" $option "*) ;; *) current_options="$current_options $option" ;; esac done current_options="$(printf '%s' "$current_options" | awk '{$1=$1; print}')" escaped_options="$(printf '%s' "$current_options" | sed 's/[\/&]/\\&/g')" if grep -q '^GRUB_CMDLINE_LINUX_DEFAULT=' /etc/default/grub; then sed -i "s/^GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT=\"$escaped_options\"/" /etc/default/grub else printf 'GRUB_CMDLINE_LINUX_DEFAULT="%s"\n' "$current_options" >>/etc/default/grub fi if command -v update-grub >/dev/null 2>&1; then update-grub elif command -v grub-mkconfig >/dev/null 2>&1 && [ -d /boot/grub ]; then grub-mkconfig -o /boot/grub/grub.cfg fi } install_kubernetes_tools() { install -d -m 0755 /etc/apt/keyrings curl -fsSL "https://pkgs.k8s.io/core:/stable:/${kubernetes_minor_version}/deb/Release.key" | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg printf 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/${kubernetes_minor_version}/deb/ /\n' >/etc/apt/sources.list.d/kubernetes.list apt-get update apt-get install -y --no-install-recommends kubelet kubeadm kubectl apt-mark hold kubelet kubeadm kubectl } install_containerd_runtime() { codename="$(. /etc/os-release && printf '%s' "$VERSION_CODENAME")" arch="$(dpkg --print-architecture)" install -d -m 0755 /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc chmod a+r /etc/apt/keyrings/docker.asc cat >/etc/apt/sources.list.d/docker.sources </dev/null 2>&1 && ! dpkg -s containerd.io >/dev/null 2>&1; then apt-get remove -y containerd runc || true fi apt-get update apt-get install -y --no-install-recommends containerd.io apt-mark hold containerd.io } configure_containerd() { mkdir -p /etc/containerd /etc/containerd/certs.d/${registry_endpoint} containerd config default >/etc/containerd/config.toml sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml reset_containerd_registry_tables config_version="$(containerd_config_version)" if [ "$config_version" = "3" ]; then ensure_containerd_registry_config_path "io.containerd.cri.v1.images" '[plugins."io.containerd.cri.v1.images".registry]' else ensure_containerd_registry_config_path "io.containerd.grpc.v1.cri" '[plugins."io.containerd.grpc.v1.cri".registry]' fi } containerd_config_version() { awk -F= ' /^[[:space:]]*version[[:space:]]*=/ { gsub(/[[:space:]]/, "", $2) print $2 exit } ' /etc/containerd/config.toml } reset_containerd_registry_tables() { awk ' function is_registry_table(line) { return line ~ /^[[:space:]]*\[plugins\./ && line ~ /[.]registry([.\]]|$)/ && (line ~ /io[.]containerd[.]grpc[.]v1[.]cri/ || line ~ /io[.]containerd[.]cri[.]v1[.]images/) } is_registry_table($0) { skip = 1; next } skip && /^[[:space:]]*\[/ { skip = 0 } !skip { print } ' /etc/containerd/config.toml >/etc/containerd/config.toml.homelab mv /etc/containerd/config.toml.homelab /etc/containerd/config.toml } ensure_containerd_registry_config_path() { plugin="$1" append_section="$2" awk -v plugin="$plugin" -v append_section="$append_section" ' function is_table(line) { return line ~ /^[[:space:]]*\[/ } function is_target_registry(line) { return is_table(line) && index(line, plugin) > 0 && line ~ /[.]registry[[:space:]]*\]/ } BEGIN { in_target = 0 found = 0 wrote = 0 } is_target_registry($0) { if (in_target && !wrote) { print " config_path = \"/etc/containerd/certs.d\"" } in_target = 1 wrote = 0 found = 1 print next } in_target && is_table($0) { if (!wrote) { print " config_path = \"/etc/containerd/certs.d\"" } in_target = 0 wrote = 0 } in_target && $0 ~ /^[[:space:]]*config_path[[:space:]]*=/ { print " config_path = \"/etc/containerd/certs.d\"" wrote = 1 next } { print } END { if (in_target && !wrote) { print " config_path = \"/etc/containerd/certs.d\"" } if (!found) { print "" print append_section print " config_path = \"/etc/containerd/certs.d\"" } } ' /etc/containerd/config.toml >/etc/containerd/config.toml.homelab mv /etc/containerd/config.toml.homelab /etc/containerd/config.toml } write_containerd_registry_hosts() { cat >/etc/containerd/certs.d/${registry_endpoint}/hosts.toml <<'HOSTS' server = "http://${registry_endpoint}" [host."http://${registry_endpoint}"] capabilities = ["pull", "resolve", "push"] skip_verify = true HOSTS cat >/etc/crictl.yaml <<'CRICTL' runtime-endpoint: unix:///run/containerd/containerd.sock image-endpoint: unix:///run/containerd/containerd.sock timeout: 10 debug: false CRICTL } enable_services() { systemctl enable qemu-guest-agent >/dev/null 2>&1 || true systemctl enable containerd >/dev/null 2>&1 || true systemctl enable kubelet >/dev/null 2>&1 || true systemctl enable iscsid >/dev/null 2>&1 || true systemctl enable ssh >/dev/null 2>&1 || true } install_ssh_key configure_sudo configure_dns configure_kubernetes_prereqs configure_kernel_boot_options install_kubernetes_tools install_containerd_runtime configure_containerd write_containerd_registry_hosts enable_services