#cloud-config # 1. Hostname configuration hostname: ${hostname} fqdn: ${hostname} preserve_hostname: false # 2. User creation # On Alpine, the privilege escalation group is "wheel" (not "sudo"). # Keep the default shell (ash/busybox) to stay lightweight. # # IMPORTANT: lock_passwd MUST be false on Alpine. # cloud-init's default (lock_passwd: true) sets shadow to "!", which Alpine's # OpenSSH (compiled without PAM) interprets as "account locked" and refuses # pubkey auth with "User ... not allowed because account is locked". # Setting lock_passwd: false lets cloud-init use "*" instead: no password # login possible, but pubkey works. users: - name: ${username} sudo: ALL=(ALL) NOPASSWD:ALL groups: wheel shell: /bin/ash lock_passwd: false ssh_authorized_keys: - ${ssh_key} # 3. Base system files (DNS + apk repos, written before package_update) # Alpine has no netplan/systemd-resolved, so we write resolv.conf ourselves. write_files: - path: /etc/resolv.conf owner: root:root permissions: '0644' content: | nameserver ${dns_server} - path: /etc/apk/repositories owner: root:root permissions: '0644' content: | http://dl-cdn.alpinelinux.org/alpine/edge/main http://dl-cdn.alpinelinux.org/alpine/edge/community # 4. System update (apk) package_update: true package_upgrade: true # 5. Package installation # - qemu-guest-agent: main repo (required so Proxmox/Terraform can read the VM IP) # - tailscale: community repo packages: - qemu-guest-agent - curl - sudo - tailscale # 6. Services & configuration (OpenRC, not systemd) # Intentional order: qemu-guest-agent first to unblock Terraform as soon as possible. runcmd: # Enable and start qemu-guest-agent via OpenRC - [ rc-update, add, qemu-guest-agent, default ] - [ rc-service, qemu-guest-agent, start ] # Unlock the user account: cloud-init's lock_passwd: false is unreliable on # Alpine (passwd -u refuses on passwordless accounts), so we force the shadow # entry to "*" ourselves. "*" means: no password login possible, but pubkey # auth works (unlike "!" which sshd treats as locked). - [ sh, -c, "sed -i 's/^${username}:!:/${username}:*:/' /etc/shadow" ] # Enable and start Tailscale via OpenRC - [ rc-update, add, tailscale, default ] - [ rc-service, tailscale, start ] # Register the node with the tailnet - [ tailscale, up, --authkey=${tailscale_auth_key}, --hostname=${vm_name} ]