BBB-via-ansible/roles/bbb_vps_prep/tasks/main.yml
2026-02-08 18:21:55 -05:00

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