--- # Initial server setup playbook for fresh Netcup VPS # Configures security, creates deploy user, and prepares server # Run once on fresh server installation - name: Initial Server Setup for Custom PHP Framework hosts: web_servers become: true gather_facts: true vars: deploy_user: "{{ deploy_user_name | default('deploy') }}" deploy_user_shell: /bin/bash pre_tasks: - name: Verify this is a fresh server setup assert: that: - fresh_server_setup is defined and fresh_server_setup == true - create_deploy_user is defined and create_deploy_user == true fail_msg: "This playbook is only for fresh server setup. Set fresh_server_setup=true to continue." tags: always - name: Update apt cache apt: update_cache: true cache_valid_time: 3600 tags: system tasks: # System Updates and Basic Packages - name: Upgrade all packages apt: upgrade: full autoremove: true autoclean: true tags: system - name: Install essential packages apt: name: - curl - wget - git - unzip - zip - vim - htop - tree - rsync - ca-certificates - gnupg - lsb-release - software-properties-common - apt-transport-https - ufw - fail2ban state: present tags: system # User Management - name: Create deploy user user: name: "{{ deploy_user }}" comment: "Deployment user for Custom PHP Framework" shell: "{{ deploy_user_shell }}" home: "/home/{{ deploy_user }}" create_home: true groups: "{{ deploy_user_groups | default(['sudo']) }}" append: true tags: users - name: Set up authorized_keys for deploy user authorized_key: user: "{{ deploy_user }}" state: present key: "{{ lookup('file', ansible_ssh_private_key_file + '.pub') }}" comment: "Deploy key for {{ deploy_user }}@{{ inventory_hostname }}" tags: users - name: Allow deploy user sudo without password lineinfile: dest: /etc/sudoers.d/{{ deploy_user }} line: "{{ deploy_user }} ALL=(ALL) NOPASSWD:ALL" state: present mode: '0440' create: true validate: 'visudo -cf %s' tags: users # SSH Security Hardening - name: Configure SSH security lineinfile: dest: /etc/ssh/sshd_config regexp: "{{ item.regexp }}" line: "{{ item.line }}" backup: true loop: - { regexp: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' } - { regexp: '^#?PubkeyAuthentication', line: 'PubkeyAuthentication yes' } - { regexp: '^#?PermitRootLogin', line: 'PermitRootLogin prohibit-password' } - { regexp: '^#?PermitEmptyPasswords', line: 'PermitEmptyPasswords no' } - { regexp: '^#?MaxAuthTries', line: 'MaxAuthTries 3' } - { regexp: '^#?ClientAliveInterval', line: 'ClientAliveInterval 300' } - { regexp: '^#?ClientAliveCountMax', line: 'ClientAliveCountMax 2' } notify: restart sshd tags: security - name: Restrict SSH to specific users lineinfile: dest: /etc/ssh/sshd_config line: "AllowUsers root {{ deploy_user }}" state: present notify: restart sshd tags: security # Firewall Configuration - name: Configure UFW default policies ufw: policy: "{{ item.policy }}" direction: "{{ item.direction }}" loop: - { policy: 'deny', direction: 'incoming' } - { policy: 'allow', direction: 'outgoing' } tags: firewall - name: Allow SSH through firewall ufw: rule: allow name: OpenSSH tags: firewall - name: Allow HTTP and HTTPS through firewall ufw: rule: allow port: "{{ item }}" proto: tcp loop: - 80 - 443 tags: firewall - name: Enable UFW ufw: state: enabled tags: firewall # Fail2ban Configuration - name: Configure fail2ban for SSH copy: dest: /etc/fail2ban/jail.local content: | [DEFAULT] bantime = 3600 findtime = 600 maxretry = 3 [sshd] enabled = true port = ssh filter = sshd logpath = /var/log/auth.log maxretry = 3 bantime = 3600 backup: true notify: restart fail2ban tags: security # System Optimization - name: Configure swappiness sysctl: name: vm.swappiness value: '10' state: present tags: performance - name: Configure filesystem parameters sysctl: name: "{{ item.name }}" value: "{{ item.value }}" state: present loop: - { name: 'fs.file-max', value: '2097152' } - { name: 'net.core.somaxconn', value: '65535' } - { name: 'net.ipv4.tcp_max_syn_backlog', value: '65535' } tags: performance # Time Synchronization - name: Install and configure NTP apt: name: ntp state: present tags: system - name: Ensure NTP is running and enabled systemd: name: ntp state: started enabled: true tags: system # Directory Structure - name: Create application directories file: path: "{{ item }}" state: directory owner: "{{ deploy_user }}" group: "{{ deploy_user }}" mode: '0755' loop: - /var/www - /var/www/html - /var/www/backups - /var/www/logs - /var/log/custom-php-framework tags: directories # Log Rotation - name: Configure log rotation for application copy: dest: /etc/logrotate.d/custom-php-framework content: | /var/log/custom-php-framework/*.log { daily missingok rotate 30 compress notifempty create 644 www-data www-data postrotate /bin/systemctl reload-or-restart docker || true endscript } tags: logs handlers: - name: restart sshd systemd: name: sshd state: restarted - name: restart fail2ban systemd: name: fail2ban state: restarted post_tasks: - name: Display setup completion info debug: msg: - "Initial server setup completed successfully!" - "Deploy user '{{ deploy_user }}' created with sudo privileges" - "SSH key authentication configured" - "Firewall enabled (SSH, HTTP, HTTPS allowed)" - "Fail2ban configured for SSH protection" - "Next: Update inventory to use deploy user and run infrastructure setup" tags: always