Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
- Remove middleware reference from Gitea Traefik labels (caused routing issues) - Optimize Gitea connection pool settings (MAX_IDLE_CONNS=30, authentication_timeout=180s) - Add explicit service reference in Traefik labels - Fix intermittent 504 timeouts by improving PostgreSQL connection handling Fixes Gitea unreachability via git.michaelschiemer.de
351 lines
12 KiB
YAML
351 lines
12 KiB
YAML
---
|
|
- name: Initial Server Setup - Debian 13 (Trixie)
|
|
hosts: production
|
|
become: yes
|
|
gather_facts: yes
|
|
|
|
vars:
|
|
# User configuration
|
|
deploy_user: "{{ ansible_user | default('deploy') }}"
|
|
deploy_user_groups: ['sudo'] # docker group added after Docker installation
|
|
|
|
# SSH configuration
|
|
ssh_key_only_auth: false # Set to true AFTER SSH keys are properly configured
|
|
ssh_disable_root_login: false # Set to true after deploy user is configured
|
|
|
|
# Firewall configuration
|
|
firewall_enable: false # Set to true after initial setup is complete
|
|
firewall_ports:
|
|
- { port: 22, proto: 'tcp', comment: 'SSH' }
|
|
- { port: 80, proto: 'tcp', comment: 'HTTP' }
|
|
- { port: 443, proto: 'tcp', comment: 'HTTPS' }
|
|
- { port: 51820, proto: 'udp', comment: 'WireGuard' }
|
|
|
|
# System packages
|
|
system_base_packages:
|
|
- curl
|
|
- wget
|
|
- git
|
|
- vim
|
|
- sudo
|
|
- ufw
|
|
- fail2ban
|
|
- rsync
|
|
|
|
tasks:
|
|
- name: Display system information
|
|
ansible.builtin.debug:
|
|
msg:
|
|
- "Distribution: {{ ansible_distribution }} {{ ansible_distribution_version }}"
|
|
- "Hostname: {{ ansible_hostname }}"
|
|
- "Deploy User: {{ deploy_user }}"
|
|
|
|
# ========================================
|
|
# 1. System Updates
|
|
# ========================================
|
|
|
|
- name: Check and wait for apt locks to be released
|
|
ansible.builtin.shell:
|
|
cmd: |
|
|
for lock in /var/lib/dpkg/lock /var/lib/apt/lists/lock /var/cache/apt/archives/lock; do
|
|
if [ -f "$lock" ]; then
|
|
echo "Waiting for lock: $lock"
|
|
count=0
|
|
while [ -f "$lock" ] && [ $count -lt 60 ]; do
|
|
sleep 1
|
|
count=$((count + 1))
|
|
done
|
|
if [ -f "$lock" ]; then
|
|
echo "Warning: Lock still exists after 60s: $lock"
|
|
else
|
|
echo "Lock released: $lock"
|
|
fi
|
|
fi
|
|
done
|
|
changed_when: false
|
|
failed_when: false
|
|
timeout: 70
|
|
|
|
- name: Update apt cache
|
|
ansible.builtin.shell:
|
|
cmd: timeout 300 apt-get update -qq
|
|
environment:
|
|
DEBIAN_FRONTEND: noninteractive
|
|
APT_LISTCHANGES_FRONTEND: none
|
|
register: apt_update_result
|
|
changed_when: apt_update_result.rc == 0
|
|
failed_when: apt_update_result.rc != 0
|
|
timeout: 300
|
|
|
|
- name: Display apt update result
|
|
ansible.builtin.debug:
|
|
msg: "apt update completed successfully"
|
|
when: apt_update_result.rc == 0
|
|
|
|
- name: Show packages to be upgraded
|
|
ansible.builtin.command:
|
|
cmd: apt list --upgradable 2>/dev/null | tail -n +2 | wc -l
|
|
register: packages_to_upgrade
|
|
changed_when: false
|
|
failed_when: false
|
|
|
|
- name: Display upgrade information
|
|
ansible.builtin.debug:
|
|
msg: "Packages to upgrade: {{ packages_to_upgrade.stdout | default('0') | trim }}"
|
|
|
|
- name: Upgrade system packages
|
|
ansible.builtin.shell:
|
|
cmd: timeout 600 apt-get upgrade -y -qq && apt-get autoremove -y -qq
|
|
environment:
|
|
DEBIAN_FRONTEND: noninteractive
|
|
APT_LISTCHANGES_FRONTEND: none
|
|
register: apt_upgrade_result
|
|
changed_when: apt_upgrade_result.rc == 0
|
|
failed_when: apt_upgrade_result.rc != 0
|
|
timeout: 600
|
|
|
|
- name: Display apt upgrade result
|
|
ansible.builtin.debug:
|
|
msg: "apt upgrade completed: {{ 'Packages upgraded' if apt_upgrade_result.rc == 0 else 'Failed' }}"
|
|
when: apt_upgrade_result.rc is defined
|
|
|
|
# ========================================
|
|
# 2. Install Base Packages
|
|
# ========================================
|
|
|
|
- name: Install base packages
|
|
ansible.builtin.shell:
|
|
cmd: timeout 300 apt-get install -y -qq {{ system_base_packages | join(' ') }}
|
|
environment:
|
|
DEBIAN_FRONTEND: noninteractive
|
|
APT_LISTCHANGES_FRONTEND: none
|
|
register: apt_install_result
|
|
changed_when: apt_install_result.rc == 0
|
|
failed_when: apt_install_result.rc != 0
|
|
timeout: 300
|
|
|
|
- name: Display apt install result
|
|
ansible.builtin.debug:
|
|
msg: "apt install completed: {{ 'Packages installed/updated' if apt_install_result.rc == 0 else 'Failed' }}"
|
|
when: apt_install_result.rc is defined
|
|
|
|
# ========================================
|
|
# 3. Create Deploy User
|
|
# ========================================
|
|
|
|
- name: Check if deploy user exists
|
|
ansible.builtin.shell:
|
|
cmd: timeout 5 getent passwd {{ deploy_user }} >/dev/null 2>&1 && echo "exists" || echo "not_found"
|
|
register: deploy_user_check
|
|
changed_when: false
|
|
failed_when: false
|
|
timeout: 10
|
|
|
|
- name: Create deploy user
|
|
ansible.builtin.user:
|
|
name: "{{ deploy_user }}"
|
|
groups: "{{ deploy_user_groups }}"
|
|
append: yes
|
|
shell: /bin/bash
|
|
create_home: yes
|
|
when:
|
|
- "'not_found' in deploy_user_check.stdout"
|
|
- deploy_user != 'root'
|
|
|
|
- name: Ensure deploy user has sudo access
|
|
ansible.builtin.lineinfile:
|
|
path: /etc/sudoers.d/deploy
|
|
line: "{{ deploy_user }} ALL=(ALL) NOPASSWD: ALL"
|
|
create: yes
|
|
validate: 'visudo -cf %s'
|
|
mode: '0440'
|
|
when: deploy_user != 'root'
|
|
|
|
# ========================================
|
|
# 4. SSH Configuration
|
|
# ========================================
|
|
|
|
- name: Get deploy user home directory
|
|
ansible.builtin.getent:
|
|
database: passwd
|
|
key: "{{ deploy_user }}"
|
|
register: deploy_user_info
|
|
when: deploy_user != 'root'
|
|
ignore_errors: yes
|
|
|
|
- name: Set deploy user home directory (root)
|
|
ansible.builtin.set_fact:
|
|
deploy_user_home: "/root"
|
|
when: deploy_user == 'root'
|
|
|
|
- name: Set deploy user home directory (from getent)
|
|
ansible.builtin.set_fact:
|
|
deploy_user_home: "{{ deploy_user_info.ansible_facts.getent_passwd[deploy_user][4] }}"
|
|
when:
|
|
- deploy_user != 'root'
|
|
- deploy_user_info.ansible_facts.getent_passwd[deploy_user] is defined
|
|
|
|
- name: Set deploy user home directory (fallback)
|
|
ansible.builtin.set_fact:
|
|
deploy_user_home: "/home/{{ deploy_user }}"
|
|
when: deploy_user_home is not defined
|
|
|
|
- name: Ensure .ssh directory exists
|
|
ansible.builtin.file:
|
|
path: "{{ deploy_user_home }}/.ssh"
|
|
state: directory
|
|
owner: "{{ deploy_user }}"
|
|
group: "{{ deploy_user }}"
|
|
mode: '0700'
|
|
|
|
- name: Add SSH public key from control node
|
|
ansible.builtin.authorized_key:
|
|
user: "{{ deploy_user }}"
|
|
state: present
|
|
key: "{{ lookup('file', ansible_ssh_private_key_file | default('~/.ssh/production') + '.pub') }}"
|
|
when: ansible_ssh_private_key_file is defined
|
|
|
|
- name: Verify SSH key is configured before disabling password auth
|
|
ansible.builtin.stat:
|
|
path: "{{ deploy_user_home }}/.ssh/authorized_keys"
|
|
register: ssh_key_file
|
|
when: ssh_key_only_auth | bool
|
|
|
|
- name: Configure SSH key-only authentication
|
|
ansible.builtin.lineinfile:
|
|
path: /etc/ssh/sshd_config
|
|
regexp: "{{ item.regexp }}"
|
|
line: "{{ item.line }}"
|
|
backup: yes
|
|
loop:
|
|
- { regexp: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' }
|
|
- { regexp: '^#?PubkeyAuthentication', line: 'PubkeyAuthentication yes' }
|
|
- { regexp: '^#?AuthorizedKeysFile', line: 'AuthorizedKeysFile .ssh/authorized_keys' }
|
|
when:
|
|
- ssh_key_only_auth | bool
|
|
- ssh_key_file.stat.exists | default(false)
|
|
notify: restart sshd
|
|
|
|
- name: Disable root login (optional)
|
|
ansible.builtin.lineinfile:
|
|
path: /etc/ssh/sshd_config
|
|
regexp: '^#?PermitRootLogin'
|
|
line: 'PermitRootLogin no'
|
|
backup: yes
|
|
when: ssh_disable_root_login | bool
|
|
notify: restart sshd
|
|
|
|
# ========================================
|
|
# 5. Firewall Configuration
|
|
# ========================================
|
|
# WICHTIG: Firewall wird erst am Ende konfiguriert, um SSH-Verbindung nicht zu unterbrechen
|
|
|
|
- name: Check current UFW status
|
|
ansible.builtin.command:
|
|
cmd: ufw status | head -1
|
|
register: ufw_current_status
|
|
changed_when: false
|
|
failed_when: false
|
|
when: firewall_enable | bool
|
|
|
|
- name: Display current firewall status
|
|
ansible.builtin.debug:
|
|
msg: "Current firewall status: {{ ufw_current_status.stdout | default('Unknown') }}"
|
|
when: firewall_enable | bool and ufw_current_status is defined
|
|
|
|
- name: Ensure SSH port is allowed before configuring firewall
|
|
ansible.builtin.command:
|
|
cmd: ufw allow 22/tcp comment 'SSH - Allow before enabling firewall'
|
|
when:
|
|
- firewall_enable | bool
|
|
- "'inactive' in (ufw_current_status.stdout | default(''))"
|
|
ignore_errors: yes
|
|
|
|
- name: Reset UFW to defaults (only if inactive)
|
|
ansible.builtin.command:
|
|
cmd: ufw --force reset
|
|
when:
|
|
- firewall_enable | bool
|
|
- "'inactive' in (ufw_current_status.stdout | default(''))"
|
|
changed_when: false
|
|
|
|
- name: Set UFW default policies
|
|
ansible.builtin.command:
|
|
cmd: "ufw default {{ item.policy }} {{ item.direction }}"
|
|
loop:
|
|
- { policy: 'deny', direction: 'incoming' }
|
|
- { policy: 'allow', direction: 'outgoing' }
|
|
when:
|
|
- firewall_enable | bool
|
|
- "'inactive' in (ufw_current_status.stdout | default(''))"
|
|
|
|
- name: Allow firewall ports (ensure SSH is first)
|
|
ansible.builtin.command:
|
|
cmd: "ufw allow {{ item.port }}/{{ item.proto }} comment '{{ item.comment }}'"
|
|
loop: "{{ firewall_ports }}"
|
|
when:
|
|
- firewall_enable | bool
|
|
- "'inactive' in (ufw_current_status.stdout | default(''))"
|
|
register: ufw_rules
|
|
changed_when: ufw_rules.rc == 0
|
|
|
|
- name: Enable UFW (only if inactive)
|
|
ansible.builtin.command:
|
|
cmd: ufw --force enable
|
|
when:
|
|
- firewall_enable | bool
|
|
- "'inactive' in (ufw_current_status.stdout | default(''))"
|
|
|
|
- name: Display UFW status
|
|
ansible.builtin.command:
|
|
cmd: ufw status verbose
|
|
register: ufw_status
|
|
changed_when: false
|
|
|
|
- name: Show UFW status
|
|
ansible.builtin.debug:
|
|
msg: "{{ ufw_status.stdout_lines }}"
|
|
|
|
# ========================================
|
|
# 6. Fail2ban Configuration
|
|
# ========================================
|
|
|
|
- name: Ensure fail2ban is enabled and started
|
|
ansible.builtin.systemd:
|
|
name: fail2ban
|
|
enabled: yes
|
|
state: started
|
|
when: "'fail2ban' in system_base_packages"
|
|
|
|
# ========================================
|
|
# 7. System Configuration
|
|
# ========================================
|
|
|
|
- name: Configure timezone
|
|
ansible.builtin.timezone:
|
|
name: Europe/Berlin
|
|
|
|
- name: Display setup summary
|
|
ansible.builtin.debug:
|
|
msg:
|
|
- "=========================================="
|
|
- "Initial Server Setup Complete"
|
|
- "=========================================="
|
|
- "Deploy User: {{ deploy_user }}"
|
|
- "SSH Key-only Auth: {{ ssh_key_only_auth }}"
|
|
- "Firewall: {{ 'Enabled' if firewall_enable else 'Disabled' }}"
|
|
- "Fail2ban: {{ 'Enabled' if 'fail2ban' in system_base_packages else 'Disabled' }}"
|
|
- "=========================================="
|
|
- "Next Steps:"
|
|
- "1. Test SSH connection: ssh {{ deploy_user }}@{{ ansible_host }}"
|
|
- "2. Install Docker: ansible-playbook playbooks/install-docker.yml"
|
|
- "3. Deploy Infrastructure: ansible-playbook playbooks/setup-infrastructure.yml"
|
|
- "=========================================="
|
|
|
|
handlers:
|
|
- name: restart sshd
|
|
ansible.builtin.systemd:
|
|
name: sshd
|
|
state: restarted
|
|
|