319 lines
8.3 KiB
YAML
319 lines
8.3 KiB
YAML
---
|
|
- name: Assert Ubuntu 22.04
|
|
ansible.builtin.assert:
|
|
that:
|
|
- ansible_distribution == "Ubuntu"
|
|
- ansible_distribution_version is version("22.04", "==")
|
|
fail_msg: "Ce playbook cible Ubuntu 22.04 LTS uniquement."
|
|
|
|
- name: Set hostname to FQDN
|
|
ansible.builtin.hostname:
|
|
name: "{{ fqdn }}"
|
|
|
|
- name: Install base packages
|
|
ansible.builtin.apt:
|
|
update_cache: true
|
|
name:
|
|
- ca-certificates
|
|
- curl
|
|
- gnupg
|
|
- sudo
|
|
- vim
|
|
- jq
|
|
- unzip
|
|
- chrony
|
|
- rsyslog
|
|
- openssh-server
|
|
- fail2ban
|
|
- unattended-upgrades
|
|
- apt-listchanges
|
|
- logrotate
|
|
- apparmor
|
|
- apparmor-utils
|
|
- nftables
|
|
- locales
|
|
- lsb-release
|
|
state: present
|
|
|
|
# BBB 3.x install guide expects en_US.UTF-8
|
|
- name: Ensure en_US.UTF-8 locale exists
|
|
ansible.builtin.command: locale-gen en_US.UTF-8
|
|
changed_when: false
|
|
|
|
- name: Set default locale
|
|
ansible.builtin.copy:
|
|
dest: /etc/default/locale
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
content: |
|
|
LANG="en_US.UTF-8"
|
|
|
|
- name: Ensure admin user exists
|
|
ansible.builtin.user:
|
|
name: "{{ admin_user }}"
|
|
groups: sudo
|
|
append: true
|
|
shell: /bin/bash
|
|
create_home: true
|
|
state: present
|
|
|
|
- name: Install admin authorized keys
|
|
ansible.builtin.authorized_key:
|
|
user: "{{ admin_user }}"
|
|
key: "{{ item }}"
|
|
state: present
|
|
loop: "{{ admin_pubkeys }}"
|
|
|
|
- name: Harden sudoers for admin (NOPASSWD)
|
|
ansible.builtin.copy:
|
|
dest: "/etc/sudoers.d/90-{{ admin_user }}"
|
|
content: "{{ admin_user }} ALL=(ALL) NOPASSWD:ALL\n"
|
|
owner: root
|
|
group: root
|
|
mode: "0440"
|
|
|
|
# --- SSH hardening ---
|
|
- name: Template sshd_config
|
|
ansible.builtin.template:
|
|
src: sshd_config.j2
|
|
dest: /etc/ssh/sshd_config
|
|
owner: root
|
|
group: root
|
|
mode: "0600"
|
|
notify: reload sshd
|
|
|
|
- name: Validate sshd configuration
|
|
ansible.builtin.command: sshd -t
|
|
check_mode: no
|
|
changed_when: false
|
|
|
|
# --- journald persistence ---
|
|
- name: Configure journald
|
|
ansible.builtin.template:
|
|
src: journald.conf.j2
|
|
dest: /etc/systemd/journald.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: restart journald
|
|
|
|
- name: Ensure journald persistent directory exists
|
|
ansible.builtin.file:
|
|
path: /var/log/journal
|
|
state: directory
|
|
owner: root
|
|
group: systemd-journal
|
|
mode: "2755"
|
|
when: journald_persistent | bool
|
|
|
|
# --- sysctl hardening (incl. ip_forward for Docker) ---
|
|
- name: Apply sysctl hardening
|
|
ansible.builtin.template:
|
|
src: sysctl-hardening.conf.j2
|
|
dest: /etc/sysctl.d/99-hardening.conf
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: reload sysctl
|
|
|
|
# --- Unattended upgrades ---
|
|
- name: Configure 20auto-upgrades
|
|
ansible.builtin.template:
|
|
src: 20auto-upgrades.j2
|
|
dest: /etc/apt/apt.conf.d/20auto-upgrades
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
when: enable_unattended_upgrades | bool
|
|
|
|
- name: Configure 50unattended-upgrades
|
|
ansible.builtin.template:
|
|
src: 50unattended-upgrades.j2
|
|
dest: /etc/apt/apt.conf.d/50unattended-upgrades
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
when: enable_unattended_upgrades | bool
|
|
|
|
# --- fail2ban (nftables action) ---
|
|
- name: Configure fail2ban jail.local
|
|
ansible.builtin.template:
|
|
src: jail.local.j2
|
|
dest: /etc/fail2ban/jail.local
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
when: enable_fail2ban | bool
|
|
notify: restart fail2ban
|
|
|
|
- name: Ensure fail2ban enabled
|
|
ansible.builtin.service:
|
|
name: fail2ban
|
|
enabled: true
|
|
state: started
|
|
when: enable_fail2ban | bool and not ansible_check_mode
|
|
|
|
# --- Docker CE (repo officiel) ---
|
|
- name: Map architecture for Docker repo
|
|
ansible.builtin.set_fact:
|
|
docker_arch: >-
|
|
{{
|
|
{'x86_64':'amd64','aarch64':'arm64','armv7l':'armhf'}.get(ansible_architecture, 'amd64')
|
|
}}
|
|
when: docker_install | bool
|
|
|
|
- name: Add Docker GPG key (keyring)
|
|
ansible.builtin.shell: |
|
|
install -m 0755 -d /etc/apt/keyrings
|
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
|
chmod a+r /etc/apt/keyrings/docker.gpg
|
|
args:
|
|
creates: /etc/apt/keyrings/docker.gpg
|
|
when: docker_install | bool
|
|
|
|
- name: Add Docker apt repo
|
|
ansible.builtin.apt_repository:
|
|
repo: "deb [arch={{ docker_arch }} signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
|
|
filename: docker
|
|
state: present
|
|
when: docker_install | bool
|
|
|
|
- name: Install Docker CE packages
|
|
ansible.builtin.apt:
|
|
update_cache: true
|
|
name:
|
|
- docker-ce
|
|
- docker-ce-cli
|
|
- containerd.io
|
|
- docker-buildx-plugin
|
|
- docker-compose-plugin
|
|
state: present
|
|
when: docker_install | bool
|
|
|
|
- name: Configure Docker daemon.json
|
|
ansible.builtin.template:
|
|
src: docker-daemon.json.j2
|
|
dest: /etc/docker/daemon.json
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
when: docker_install | bool
|
|
notify: restart docker
|
|
|
|
- name: Enable and start Docker
|
|
ansible.builtin.service:
|
|
name: docker
|
|
enabled: true
|
|
state: started
|
|
when: docker_install | bool and not ansible_check_mode
|
|
|
|
- name: Optionally add admin user to docker group (NOT recommended)
|
|
ansible.builtin.user:
|
|
name: "{{ admin_user }}"
|
|
groups: docker
|
|
append: true
|
|
when: docker_install | bool and docker_add_admin_to_group | bool
|
|
|
|
# --- Host firewall (nftables policy layer; Docker keeps its own rules) ---
|
|
- name: Install apply script for nftables hostfilter
|
|
ansible.builtin.copy:
|
|
src: apply-hostfilter-nft.sh
|
|
dest: /usr/local/sbin/apply-hostfilter-nft
|
|
owner: root
|
|
group: root
|
|
mode: "0755"
|
|
|
|
- name: Ensure /etc/nftables directory exists
|
|
ansible.builtin.file:
|
|
path: /etc/nftables
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: "0755"
|
|
|
|
- name: Deploy nftables rules (hostfilter table)
|
|
ansible.builtin.template:
|
|
src: hostfilter.nft.j2
|
|
dest: /etc/nftables/hostfilter.nft
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: restart hostfilter firewall
|
|
|
|
- name: Deploy systemd unit for hostfilter firewall
|
|
ansible.builtin.template:
|
|
src: hostfilter-nft.service.j2
|
|
dest: /etc/systemd/system/hostfilter-nft.service
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: restart hostfilter firewall
|
|
|
|
- name: Enable and start hostfilter firewall
|
|
ansible.builtin.systemd:
|
|
name: hostfilter-nft
|
|
enabled: true
|
|
state: started
|
|
daemon_reload: true
|
|
when: not ansible_check_mode
|
|
|
|
# Ensure fqdn -> public IPv4 mapping
|
|
- name: Ensure /etc/hosts has fqdn -> public IPv4 mapping
|
|
ansible.builtin.lineinfile:
|
|
path: /etc/hosts
|
|
regexp: '^\s*{{ ansible_default_ipv4.address | regex_escape() }}\s+{{ fqdn | regex_escape() }}\s*$'
|
|
line: "{{ ansible_default_ipv4.address }} {{ fqdn }}"
|
|
state: present
|
|
|
|
# --- Swap (useful with 12G RAM) ---
|
|
- name: Create swapfile
|
|
ansible.builtin.command: "fallocate -l {{ swapfile_size_mb }}M {{ swapfile_path }}"
|
|
args:
|
|
creates: "{{ swapfile_path }}"
|
|
when: manage_swapfile | bool
|
|
|
|
- name: Set swapfile permissions
|
|
ansible.builtin.file:
|
|
path: "{{ swapfile_path }}"
|
|
owner: root
|
|
group: root
|
|
mode: "0600"
|
|
when: manage_swapfile | bool
|
|
|
|
- name: Check if swap is already active
|
|
ansible.builtin.command: "swapon --noheadings --show=NAME"
|
|
register: swapon_show
|
|
changed_when: false
|
|
failed_when: false
|
|
when: manage_swapfile | bool
|
|
|
|
- name: Check if swapfile already has swap signature
|
|
ansible.builtin.command: "blkid -o value -s TYPE {{ swapfile_path }}"
|
|
register: swapfile_type
|
|
changed_when: false
|
|
failed_when: false
|
|
when: manage_swapfile | bool
|
|
|
|
- name: Make swap (only if not already swap and not active)
|
|
ansible.builtin.command: "mkswap {{ swapfile_path }}"
|
|
when:
|
|
- manage_swapfile | bool
|
|
- (swapfile_type.stdout | default('')) != 'swap'
|
|
- (swapfile_path not in (swapon_show.stdout_lines | default([])))
|
|
# - (swapfile_path not in (swapon_show.stdout | default('')))
|
|
|
|
- name: Enable swap (only if not active)
|
|
ansible.builtin.command: "swapon {{ swapfile_path }}"
|
|
when:
|
|
- manage_swapfile | bool
|
|
- (swapfile_path not in (swapon_show.stdout_lines | default([])))
|
|
# - (swapfile_path not in (swapon_show.stdout | default('')))
|
|
failed_when: false
|
|
|
|
- name: Persist swap in fstab
|
|
ansible.builtin.lineinfile:
|
|
path: /etc/fstab
|
|
line: "{{ swapfile_path }} none swap sw 0 0"
|
|
state: present
|
|
when: manage_swapfile | bool
|