--- # Disable cloud-init so it never regenerates SSH keys or resets config on reboot - name: Disable cloud-init ansible.builtin.file: path: /etc/cloud/cloud-init.disabled state: touch mode: "0644" - name: Find active cloud-init services ansible.builtin.shell: systemctl list-unit-files 'cloud-*' --no-legend | awk '{print $1}' register: cloud_init_services changed_when: false - name: Stop cloud-init services ansible.builtin.service: name: "{{ item }}" state: stopped enabled: false loop: "{{ cloud_init_services.stdout_lines }}" when: cloud_init_services.stdout_lines | length > 0 # Set the hostname to match our inventory name - name: Set hostname ansible.builtin.hostname: name: "{{ server_hostname }}" # Update /etc/hosts to include the new hostname - name: Update /etc/hosts with hostname ansible.builtin.lineinfile: path: /etc/hosts regexp: '^127\.0\.1\.1' line: "127.0.1.1 {{ server_hostname }}.{{ domain }} {{ server_hostname }}" state: present # Refresh apt cache before installing anything - name: Update apt cache ansible.builtin.apt: update_cache: true cache_valid_time: 3600 # Upgrade all installed packages to latest - name: Upgrade all packages ansible.builtin.apt: upgrade: dist autoremove: true # Install essential tools and security packages - name: Install base packages ansible.builtin.apt: name: - curl - git - htop - rsync - ufw - fail2ban - unattended-upgrades - apt-listchanges state: present # Allow SSH before enabling the firewall (don't lock ourselves out) - name: UFW — allow SSH community.general.ufw: rule: allow port: "22" proto: tcp # Allow HTTP for Caddy - name: UFW — allow HTTP community.general.ufw: rule: allow port: "80" proto: tcp # Allow HTTPS for Caddy - name: UFW — allow HTTPS community.general.ufw: rule: allow port: "443" proto: tcp # Allow Gitea SSH - name: UFW — allow Gitea SSH community.general.ufw: rule: allow port: "2222" proto: tcp # Default deny incoming, allow outgoing, then enable - name: UFW — enable with default deny incoming community.general.ufw: state: enabled default: deny direction: incoming - name: UFW — default allow outgoing community.general.ufw: default: allow direction: outgoing # Enable automatic security updates - name: Enable unattended-upgrades ansible.builtin.copy: dest: /etc/apt/apt.conf.d/20auto-upgrades content: | APT::Periodic::Update-Package-Lists "1"; APT::Periodic::Unattended-Upgrade "1"; APT::Periodic::AutocleanInterval "7"; mode: "0644" # Disable root password login — key-only auth - name: Disable SSH password authentication ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: '^#?PasswordAuthentication' line: 'PasswordAuthentication no' state: present notify: Restart sshd # Disable root login via password (permit key-based only) - name: Set SSH to permit root login with key only ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: '^#?PermitRootLogin' line: 'PermitRootLogin prohibit-password' state: present notify: Restart sshd