#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # shellcheck source=_config.sh source "${SCRIPT_DIR}/_config.sh" log() { printf '[setup] %s\n' "$*"; } die() { printf '[setup][error] %s\n' "$*" >&2; exit 1; } # --- Local prerequisites --- command -v kubectl >/dev/null 2>&1 || die "Missing locally: kubectl (https://kubernetes.io/docs/tasks/tools/)" # --- Server connection --- SERVER_HOST="$(cfg_require server.host "Server IP or SSH alias")" SERVER_USER="$(cfg server.user)" SERVER_USER="${SERVER_USER:-root}" SSH_KEY="$(cfg server.ssh_key | sed "s|~|${HOME}|g")" SSH_OPTS=() if [[ -n "${SSH_KEY}" && -f "${SSH_KEY}" ]]; then SSH_OPTS+=(-i "${SSH_KEY}") fi SSH_OPTS+=(-o StrictHostKeyChecking=accept-new) ssh_cmd() { ssh "${SSH_OPTS[@]}" "${SERVER_USER}@${SERVER_HOST}" "$@" } log "Testing SSH connection to ${SERVER_USER}@${SERVER_HOST}..." ssh_cmd "echo 'SSH connection OK'" || die "Cannot SSH into ${SERVER_HOST}" # --- Server prerequisites --- log "Setting up server prerequisites..." ssh_cmd 'bash -s' <<'REMOTE_SETUP' set -euo pipefail log() { printf '[setup][remote] %s\n' "$*"; } # --- System updates --- log "Updating system packages..." export DEBIAN_FRONTEND=noninteractive apt-get update -qq apt-get upgrade -y -qq # --- SSH hardening --- log "Hardening SSH..." sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config systemctl reload sshd 2>/dev/null || systemctl reload ssh 2>/dev/null || true # --- fail2ban --- if ! command -v fail2ban-client >/dev/null 2>&1; then log "Installing fail2ban..." apt-get install -y -qq fail2ban systemctl enable --now fail2ban else log "fail2ban already installed" fi # --- Unattended security upgrades --- if ! dpkg -l | grep -q unattended-upgrades; then log "Installing unattended-upgrades..." apt-get install -y -qq unattended-upgrades dpkg-reconfigure -plow unattended-upgrades else log "unattended-upgrades already installed" fi # --- Firewall (ufw) --- if command -v ufw >/dev/null 2>&1; then log "Configuring firewall..." ufw default deny incoming ufw default allow outgoing ufw allow 22/tcp # SSH ufw allow 443/tcp # HTTPS (Traefik) ufw allow 6443/tcp # K3s API ufw allow 80/tcp # HTTP (Let's Encrypt ACME challenge) ufw --force enable else log "Installing ufw..." apt-get install -y -qq ufw ufw default deny incoming ufw default allow outgoing ufw allow 22/tcp ufw allow 443/tcp ufw allow 6443/tcp ufw allow 80/tcp ufw --force enable fi log "Server prerequisites complete." REMOTE_SETUP # --- Install K3s --- log "Installing K3s on ${SERVER_HOST}..." log " This takes about 1-2 minutes." ssh_cmd "curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC='server --secrets-encryption' sh -" # --- Wait for K3s to be ready --- log "Waiting for K3s to be ready..." ssh_cmd "until kubectl get nodes >/dev/null 2>&1; do sleep 2; done" # --- Copy kubeconfig --- KUBECONFIG_PATH="${DEPLOY_DIR}/kubeconfig" log "Copying kubeconfig..." ssh_cmd "sudo cat /etc/rancher/k3s/k3s.yaml" > "${KUBECONFIG_PATH}" # Replace 127.0.0.1 with the server's actual IP/hostname # If SERVER_HOST is an SSH alias, resolve the actual IP ACTUAL_HOST="${SERVER_HOST}" if ! echo "${SERVER_HOST}" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'; then # Try to resolve from SSH config RESOLVED="$(ssh -G "${SERVER_HOST}" 2>/dev/null | awk '/^hostname / {print $2}')" if [[ -n "${RESOLVED}" && "${RESOLVED}" != "${SERVER_HOST}" ]]; then ACTUAL_HOST="${RESOLVED}" fi fi sed -i.bak "s|https://127.0.0.1:6443|https://${ACTUAL_HOST}:6443|g" "${KUBECONFIG_PATH}" rm -f "${KUBECONFIG_PATH}.bak" chmod 600 "${KUBECONFIG_PATH}" # --- Verify --- export KUBECONFIG="${KUBECONFIG_PATH}" log "Verifying cluster..." kubectl get nodes log "" log "K3s installed successfully on ${SERVER_HOST}." log "Server hardened: SSH key-only, fail2ban, ufw firewall, unattended-upgrades." log "" log "Next steps:" log " export KUBECONFIG=${KUBECONFIG_PATH}" log " kubectl get nodes" log " ./scripts/02-setup-secrets.sh"