--- # System preparation tasks - name: Update apt cache ansible.builtin.apt: update_cache: yes cache_valid_time: 3600 become: yes - name: Upgrade all packages ansible.builtin.apt: upgrade: safe become: yes tags: - upgrade - name: Install system packages ansible.builtin.apt: name: "{{ system_packages }}" state: present become: yes - name: Check if Forgejo group exists ansible.builtin.getent: database: group key: "{{ forgejo_group }}" register: forgejo_group_check ignore_errors: yes become: yes - name: Create Forgejo system group ansible.builtin.group: name: "{{ forgejo_group }}" gid: "{{ forgejo_gid }}" system: yes state: present become: yes when: forgejo_group_check.failed | default(false) - name: Ensure Forgejo group exists (if already created with different GID) ansible.builtin.group: name: "{{ forgejo_group }}" system: yes state: present become: yes when: not (forgejo_group_check.failed | default(false)) - name: Check if Forgejo user exists ansible.builtin.getent: database: passwd key: "{{ forgejo_user }}" register: forgejo_user_check ignore_errors: yes become: yes - name: Create Forgejo system user ansible.builtin.user: name: "{{ forgejo_user }}" uid: "{{ forgejo_uid }}" group: "{{ forgejo_group }}" system: yes shell: /bin/bash home: "{{ forgejo_base_path }}" create_home: no state: present become: yes when: forgejo_user_check.failed | default(false) - name: Ensure Forgejo user exists (if already created with different UID) ansible.builtin.user: name: "{{ forgejo_user }}" group: "{{ forgejo_group }}" system: yes shell: /bin/bash home: "{{ forgejo_base_path }}" create_home: no state: present become: yes when: not (forgejo_user_check.failed | default(false)) - name: Create Forgejo directory structure ansible.builtin.file: path: "{{ item }}" state: directory owner: "{{ forgejo_user }}" group: "{{ forgejo_group }}" mode: '0755' become: yes loop: - "{{ forgejo_base_path }}" - "{{ forgejo_data_path }}" - "{{ forgejo_config_path }}" - "{{ forgejo_custom_path }}" - "{{ forgejo_backup_path }}" - "{{ forgejo_data_path }}/git" - "{{ forgejo_data_path }}/attachments" - "{{ forgejo_data_path }}/lfs" - "{{ forgejo_data_path }}/avatars" - name: Configure system limits for Forgejo ansible.builtin.pam_limits: domain: "{{ forgejo_user }}" limit_type: "{{ item.limit_type }}" limit_item: "{{ item.limit_item }}" value: "{{ item.value }}" become: yes loop: - { limit_type: 'soft', limit_item: 'nofile', value: '65535' } - { limit_type: 'hard', limit_item: 'nofile', value: '65535' } - { limit_type: 'soft', limit_item: 'nproc', value: '65535' } - { limit_type: 'hard', limit_item: 'nproc', value: '65535' } - name: Configure kernel parameters ansible.builtin.sysctl: name: "{{ item.name }}" value: "{{ item.value }}" state: present reload: yes become: yes loop: - { name: 'net.core.somaxconn', value: '1024' } - { name: 'net.ipv4.tcp_max_syn_backlog', value: '2048' } - { name: 'net.ipv4.ip_forward', value: '1' } - { name: 'vm.swappiness', value: '10' } - { name: 'fs.file-max', value: '65535' } # NOTE: UFW firewall configuration is handled by ufw.yml # We only set up minimal rules here for Docker access during deployment # The full secure configuration (Tailscale-only SSH) is applied in ufw.yml - name: Install UFW ansible.builtin.apt: name: ufw state: present become: yes when: ansible_os_family == "Debian" - name: Allow Docker network to access host services community.general.ufw: rule: allow from_ip: 172.16.0.0/12 comment: "Allow Docker containers to access host services (PostgreSQL, etc.)" become: yes when: ansible_os_family == "Debian" - name: Set timezone to UTC community.general.timezone: name: UTC become: yes - name: Enable automatic security updates ansible.builtin.apt: name: unattended-upgrades state: present become: yes - name: Configure unattended upgrades ansible.builtin.copy: dest: /etc/apt/apt.conf.d/50unattended-upgrades content: | Unattended-Upgrade::Allowed-Origins { "${distro_id}:${distro_codename}-security"; "${distro_id}ESMApps:${distro_codename}-apps-security"; "${distro_id}ESM:${distro_codename}-infra-security"; }; Unattended-Upgrade::AutoFixInterruptedDpkg "true"; Unattended-Upgrade::MinimalSteps "true"; Unattended-Upgrade::Remove-Unused-Dependencies "true"; Unattended-Upgrade::Automatic-Reboot "false"; mode: '0644' become: yes - name: Ensure SSH is properly configured ansible.builtin.lineinfile: path: /etc/ssh/sshd_config regexp: "{{ item.regexp }}" line: "{{ item.line }}" state: present validate: '/usr/sbin/sshd -t -f %s' become: yes loop: - { regexp: '^PermitRootLogin', line: 'PermitRootLogin prohibit-password' } - { regexp: '^PasswordAuthentication', line: 'PasswordAuthentication no' } - { regexp: '^PubkeyAuthentication', line: 'PubkeyAuthentication yes' } notify: Restart sshd when: ansible_connection != 'local' - name: Create systemd service for sshd ansible.builtin.systemd: name: sshd enabled: yes state: started become: yes when: ansible_connection != 'local'