#cloud-config # ============================================================================== # Cloud-Init User-Data Template for K3s Single-Node Cluster (com-ermnvldmr) # # Usage: # 1. Copy this template to user-data.yaml: # cp ci/cloud-init/user-data.template.yaml ci/cloud-init/user-data.yaml # 2. Replace the stubs below (SSH authorized key & Age private key). # 3. Pass user-data.yaml during VM creation in Yandex Cloud / Cloud Provider. # Note: user-data.yaml is ignored by git to protect private keys. # ============================================================================== preserve_hostname: false hostname: com-ermnvldmr ssh_pwauth: false # ------------------------------------------------------------------------------ # 1. User & SSH Key Configuration (Replace stub with your public SSH key) # ------------------------------------------------------------------------------ users: - name: adam sudo: ALL=(ALL) NOPASSWD:ALL shell: /bin/bash ssh_authorized_keys: - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPLACEHOLDERFORYOURPUBLICSSHKEY user@hostname" write_files: # ---------------------------------------------------------------------------- # 2. SOPS / Age Decryption Key (Replace stub with your private AGE key) # ---------------------------------------------------------------------------- - path: /root/.config/sops/age/keys.txt owner: root:root permissions: '0600' content: | # public key: age1jpa7hqyzzdfq7lvnw3l8qy6648mtjgpcqnljl5jz92dcqjrnsqfsn7m0fl AGE-SECRET-KEY-1PLACEHOLDERFORYOURPRIVATEAGEKEY # ---------------------------------------------------------------------------- # 3. Security Hardening Configuration # ---------------------------------------------------------------------------- - path: /etc/ssh/sshd_config.d/99-security-hardening.conf owner: root:root permissions: '0600' content: | PasswordAuthentication no PermitRootLogin prohibit-password KbdInteractiveAuthentication no X11Forwarding no MaxAuthTries 3 ClientAliveInterval 300 ClientAliveCountMax 2 - path: /etc/sysctl.d/99-k3s-hardening.conf owner: root:root permissions: '0644' content: | # Network SYN Flood Protection net.ipv4.tcp_syncookies = 1 # Disable ICMP redirects net.ipv4.conf.all.accept_redirects = 0 net.ipv4.conf.default.accept_redirects = 0 net.ipv4.conf.all.send_redirects = 0 net.ipv4.conf.default.send_redirects = 0 # Disable Source Routing net.ipv4.conf.all.accept_source_route = 0 net.ipv4.conf.default.accept_source_route = 0 # Anti-spoofing (Reverse Path Filtering) net.ipv4.conf.all.rp_filter = 1 net.ipv4.conf.default.rp_filter = 1 # Required for K8s pod networking net.ipv4.ip_forward = 1 # System Hardening kernel.kptr_restrict = 2 fs.protected_hardlinks = 1 fs.protected_symlinks = 1 fs.inotify.max_user_watches = 524288 fs.inotify.max_user_instances = 512 - path: /etc/rancher/k3s/config.yaml owner: root:root permissions: '0600' content: | write-kubeconfig-mode: "0644" # ---------------------------------------------------------------------------- # 4. Bootstrap Cluster Script # ---------------------------------------------------------------------------- - path: /usr/local/bin/bootstrap-cluster.sh owner: root:root permissions: '0755' content: | #!/usr/bin/env bash set -euo pipefail echo "=== [Bootstrap] Ensuring Reliable DNS Resolution ===" if ! grep -q "nameserver" /etc/resolv.conf 2>/dev/null; then cat << 'EOF_DNS' > /etc/resolv.conf nameserver 1.1.1.1 nameserver 8.8.8.8 nameserver 77.88.8.8 EOF_DNS fi echo "=== [Bootstrap] Applying Sysctl Hardening ===" sysctl --system echo "=== [Bootstrap] Ensuring SSH Host Keys & Starting SSH ===" ssh-keygen -A systemctl restart ssh || systemctl restart sshd || true echo "=== [Bootstrap] Configuring UFW Firewall ===" ufw default deny incoming ufw default allow outgoing ufw default allow routed ufw allow 22/tcp comment 'SSH' ufw allow 80/tcp comment 'HTTP Traefik' ufw allow 443/tcp comment 'HTTPS Traefik' # Allow Kubernetes Pod & Service traffic ufw allow in on cni0 || true ufw allow in on flannel.1 || true ufw allow from 10.42.0.0/16 comment 'K3s Pod CIDR' ufw allow from 10.43.0.0/16 comment 'K3s Service CIDR' ufw --force enable echo "=== [Bootstrap] Enabling Fail2ban ===" systemctl enable --now fail2ban || true echo "=== [Bootstrap] Checking for Secondary Persistent Data Disk ===" if [ -b /dev/vdb ]; then echo "Secondary disk /dev/vdb detected." if ! blkid /dev/vdb >/dev/null 2>&1; then echo "Formatting /dev/vdb with ext4 filesystem..." mkfs.ext4 -F -L k3s-storage /dev/vdb fi mkdir -p /var/lib/rancher/k3s/storage if ! grep -qs '/var/lib/rancher/k3s/storage' /proc/mounts; then mount /dev/vdb /var/lib/rancher/k3s/storage echo "LABEL=k3s-storage /var/lib/rancher/k3s/storage ext4 defaults,noatime 0 2" >> /etc/fstab echo "Mounted /dev/vdb to /var/lib/rancher/k3s/storage." fi fi echo "=== [Bootstrap] Starting K3s Service ===" systemctl enable --now k3s.service echo "=== [Bootstrap] Waiting for K3s API server ready ===" export KUBECONFIG=/etc/rancher/k3s/k3s.yaml until kubectl get nodes >/dev/null 2>&1; do echo "Waiting for Kubernetes API..." sleep 3 done echo "=== [Bootstrap] Cluster node is online ===" kubectl get nodes echo "=== [Bootstrap] Creating flux-system namespace ===" kubectl create namespace flux-system --dry-run=client -o yaml | kubectl apply -f - echo "=== [Bootstrap] Injecting SOPS age decryption key ===" if [ -f /root/.config/sops/age/keys.txt ]; then kubectl create secret generic sops-age \ --namespace=flux-system \ --from-file=age.agekey=/root/.config/sops/age/keys.txt \ --dry-run=client -o yaml | kubectl apply -f - echo "sops-age secret created." else echo "WARNING: /root/.config/sops/age/keys.txt not found. Inject key manually via kubectl." fi echo "=== [Bootstrap] Installing Flux Operator / Custom Resources ===" kubectl apply -f https://github.com/fluxcd/flux2/releases/download/v2.4.0/install.yaml echo "=== [Bootstrap] Setting up Kubectl access for non-root users ===" for home_dir in /home/*; do if [ -d "$home_dir" ]; then user_name=$(basename "$home_dir") mkdir -p "$home_dir/.kube" cp /etc/rancher/k3s/k3s.yaml "$home_dir/.kube/config" chown -R "$user_name:$user_name" "$home_dir/.kube" chmod 600 "$home_dir/.kube/config" fi done echo "=== [Bootstrap] Applying initial GitOps root sync ===" kubectl apply -f - << 'EOF_GITOPS' apiVersion: source.toolkit.fluxcd.io/v1 kind: GitRepository metadata: name: ermnvldmr-repo namespace: flux-system spec: interval: 1m url: https://github.com/deytenit/ermnvldmr.com.git ref: branch: next --- apiVersion: kustomize.toolkit.fluxcd.io/v1 kind: Kustomization metadata: name: cluster-sync namespace: flux-system spec: interval: 5m sourceRef: kind: GitRepository name: ermnvldmr-repo path: ./ci/k8s/clusters/production prune: true wait: true timeout: 2m decryption: provider: sops secretRef: name: sops-age EOF_GITOPS echo "=== [Bootstrap] First-Boot Bootstrap Sequence Complete ===" runcmd: - /usr/local/bin/bootstrap-cluster.sh