From c59c5e9ef163c66438125c82fd752920e084cd0d Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Sun, 8 Feb 2026 15:50:57 -0500 Subject: [PATCH] Initial commit: BBB VPS prep (Ubuntu 22.04, nftables, Docker) --- .gitignore | 12 + Makefile | 57 ++++ README.md | 36 +++ group_vars/all.yml | 41 +++ inventory.ini | 2 + .../files/apply-hostfilter-nft.sh | 9 + roles/bbb_vps_prep/handlers/main.yml | 31 ++ roles/bbb_vps_prep/tasks/main.yml | 289 ++++++++++++++++++ .../bbb_vps_prep/templates/20auto-upgrades.j2 | 5 + .../templates/50unattended-upgrades.j2 | 10 + .../templates/docker-daemon.json.j2 | 7 + .../templates/hostfilter-nft.service.j2 | 12 + .../bbb_vps_prep/templates/hostfilter.nft.j2 | 81 +++++ roles/bbb_vps_prep/templates/jail.local.j2 | 12 + roles/bbb_vps_prep/templates/journald.conf.j2 | 9 + roles/bbb_vps_prep/templates/sshd_config.j2 | 30 ++ .../templates/sysctl-hardening.conf.j2 | 30 ++ site.yml | 7 + 18 files changed, 680 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 group_vars/all.yml create mode 100644 inventory.ini create mode 100755 roles/bbb_vps_prep/files/apply-hostfilter-nft.sh create mode 100644 roles/bbb_vps_prep/handlers/main.yml create mode 100644 roles/bbb_vps_prep/tasks/main.yml create mode 100644 roles/bbb_vps_prep/templates/20auto-upgrades.j2 create mode 100644 roles/bbb_vps_prep/templates/50unattended-upgrades.j2 create mode 100644 roles/bbb_vps_prep/templates/docker-daemon.json.j2 create mode 100644 roles/bbb_vps_prep/templates/hostfilter-nft.service.j2 create mode 100644 roles/bbb_vps_prep/templates/hostfilter.nft.j2 create mode 100644 roles/bbb_vps_prep/templates/jail.local.j2 create mode 100644 roles/bbb_vps_prep/templates/journald.conf.j2 create mode 100644 roles/bbb_vps_prep/templates/sshd_config.j2 create mode 100644 roles/bbb_vps_prep/templates/sysctl-hardening.conf.j2 create mode 100644 site.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6ba0cfa --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +*.retry +*.log +.DS_Store +.vscode/ +.idea/ +.ansible/ +.cache/ +.venv/ +__pycache__/ +group_vars/*secret*.yml +**/vault.yml +.env diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e10ca70 --- /dev/null +++ b/Makefile @@ -0,0 +1,57 @@ +SHELL := /bin/bash + +INVENTORY ?= inventory.ini +PLAYBOOK ?= site.yml +EXTRA_VARS ?= +ANSIBLE ?= ansible-playbook + +.PHONY: help ping check run dry-run diff vars tree clean + +help: + @echo "Targets:" + @echo " make ping - ping hosts (Ansible)" + @echo " make check - syntax-check playbook" + @echo " make run - apply playbook" + @echo " make dry-run - check mode with diff (no changes)" + @echo " make diff - show diff of changes (implies --check)" + @echo " make vars - show vars for a host (requires host=...)" + @echo " make tree - show repository tree" + @echo "" + @echo "Examples:" + @echo " make run EXTRA_VARS='-e ssh_allow_cidrs_v4=[\"1.2.3.4/32\"]'" + @echo " make vars host=bbb" + +ping: + ansible -i $(INVENTORY) vps -m ping + +check: + $(ANSIBLE) -i $(INVENTORY) $(PLAYBOOK) --syntax-check + +run: + $(ANSIBLE) -i $(INVENTORY) $(PLAYBOOK) --become $(EXTRA_VARS) + +dry-run: + $(ANSIBLE) -i $(INVENTORY) $(PLAYBOOK) --become --check --diff $(EXTRA_VARS) + +diff: + $(ANSIBLE) -i $(INVENTORY) $(PLAYBOOK) --become --check --diff $(EXTRA_VARS) + +vars: + @if [[ -z "$(host)" ]]; then echo "ERROR: host is required, e.g. make vars host=bbb"; exit 2; fi + ansible -i $(INVENTORY) $(host) -m debug -a "var=hostvars[inventory_hostname]" | sed -n '1,200p' + +tree: + @python3 - <<'PY' +import os +for root, dirs, files in os.walk('.'): + dirs[:] = [d for d in dirs if d not in {'.git','__pycache__','.venv','.cache'}] + level = root.count(os.sep) + indent = ' ' * level + print(f"{indent}{os.path.basename(root)}/") + subindent = ' ' * (level + 1) + for f in sorted(files): + print(f"{subindent}{f}") +PY + +clean: + @find . -name "*.retry" -delete diff --git a/README.md b/README.md new file mode 100644 index 0000000..48f9e78 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# bbb-vps-prep (Ubuntu 22.04) — nftables + hardening + Docker (BBB 3.x ready) + +Ce dépôt prépare un VPS **Ubuntu 22.04** pour BigBlueButton (BBB 3.x) avec : + +- SSH durci (clés seulement, root interdit, allowlist utilisateurs) +- **nftables** comme firewall (pas UFW, pas iptables en gestion humaine) +- Docker CE (repo officiel) requis par BBB 3.x +- compatibilité Docker : on **n’efface jamais** le ruleset global, on applique une **table nftables dédiée** (`inet hostfilter`) +- fail2ban avec action nftables +- journald persistant + rotation +- sysctl hardening (inclut ip_forward pour Docker) +- swapfile (utile avec 12 Go RAM) + +## Mise en route +1) Éditer `inventory.ini` (IP du VPS) +2) Éditer `group_vars/all.yml` (clé publique, CIDR SSH, FQDN) +3) Exécuter : + +```bash +make check +make run +``` + +Mode dry-run : +```bash +make dry-run +``` + +## Ports ouverts (par défaut) +- SSH: 22/TCP (restreignable par CIDR) +- BBB: 80/TCP, 443/TCP, 16384–32768/UDP +- TURN (cohab): 3478/TCP+UDP, 5349/TCP, 32769–65535/UDP + +## Notes +- Docker gardera ses règles nécessaires au bridge/NAT; ce dépôt ajoute une couche “policy” via nftables. +- TURN sur 443 “exclusif” n’est pas compatible cohabitation 1 IP avec nginx/BBB (on utilise 5349/TLS). diff --git a/group_vars/all.yml b/group_vars/all.yml new file mode 100644 index 0000000..ebb7fe5 --- /dev/null +++ b/group_vars/all.yml @@ -0,0 +1,41 @@ +# --- Identity --- +fqdn: "bbb.chezlepro.ca" + +# --- Admin user + keys --- +admin_user: "ansible" +admin_pubkeys: + - "ssh-ed25519 AAAA...REMPLACE... ton_key_ed25519 ..." + +# --- SSH hardening --- +ssh_port: 22 +ssh_allow_cidrs_v4: ["0.0.0.0/0"] # RECO: remplace par ton IP fixe (ex: "1.2.3.4/32") +ssh_allow_cidrs_v6: ["::/0"] # RECO: remplace par ton IP /128 si applicable +ssh_allow_users: ["ansible"] + +disable_password_auth: true +permit_root_login: "no" + +# --- BBB ports --- +bbb_udp_min: 16384 +bbb_udp_max: 32768 + +# --- TURN cohabitation (1 IP) --- +turn_enabled: true +turn_listen_port: 3478 # TCP/UDP +turn_tls_port: 5349 # TURN over TLS (standard). 443 est conflictuel sur 1 IP avec nginx/BBB +turn_relay_udp_min: 32769 +turn_relay_udp_max: 65535 + +# --- Hardening toggles --- +enable_unattended_upgrades: true +enable_fail2ban: true +journald_persistent: true + +# --- Swap (utile avec 12G RAM) --- +manage_swapfile: true +swapfile_path: /swapfile +swapfile_size_mb: 8192 + +# --- Docker (BBB 3.x requires latest docker) --- +docker_install: true +docker_add_admin_to_group: false # IMPORTANT: groupe docker = root-equivalent diff --git a/inventory.ini b/inventory.ini new file mode 100644 index 0000000..32b3458 --- /dev/null +++ b/inventory.ini @@ -0,0 +1,2 @@ +[vps] +bbb ansible_host=YOUR.VPS.IP ansible_user=ansible diff --git a/roles/bbb_vps_prep/files/apply-hostfilter-nft.sh b/roles/bbb_vps_prep/files/apply-hostfilter-nft.sh new file mode 100755 index 0000000..bc3ea06 --- /dev/null +++ b/roles/bbb_vps_prep/files/apply-hostfilter-nft.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +NFT=/usr/sbin/nft +RULES=/etc/nftables/hostfilter.nft + +# Delete only our table if present; do NOT flush whole ruleset (Docker owns its rules) +$NFT delete table inet hostfilter 2>/dev/null || true +$NFT -f "$RULES" diff --git a/roles/bbb_vps_prep/handlers/main.yml b/roles/bbb_vps_prep/handlers/main.yml new file mode 100644 index 0000000..225dfff --- /dev/null +++ b/roles/bbb_vps_prep/handlers/main.yml @@ -0,0 +1,31 @@ +--- +- name: reload sshd + ansible.builtin.service: + name: ssh + state: reloaded + +- name: restart fail2ban + ansible.builtin.service: + name: fail2ban + state: restarted + +- name: restart journald + ansible.builtin.service: + name: systemd-journald + state: restarted + +- name: reload sysctl + ansible.builtin.command: sysctl --system + changed_when: false + +- name: restart docker + ansible.builtin.systemd: + name: docker + state: restarted + daemon_reload: true + +- name: restart hostfilter firewall + ansible.builtin.systemd: + name: hostfilter-nft + state: restarted + daemon_reload: true diff --git a/roles/bbb_vps_prep/tasks/main.yml b/roles/bbb_vps_prep/tasks/main.yml new file mode 100644 index 0000000..bcb56c3 --- /dev/null +++ b/roles/bbb_vps_prep/tasks/main.yml @@ -0,0 +1,289 @@ +--- +- 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 + 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 + +# --- 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 + +- 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: 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 + +# 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: Make swap + ansible.builtin.command: "mkswap {{ swapfile_path }}" + when: manage_swapfile | bool + changed_when: false + +- name: Enable swap + ansible.builtin.command: "swapon {{ swapfile_path }}" + when: manage_swapfile | bool + 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 diff --git a/roles/bbb_vps_prep/templates/20auto-upgrades.j2 b/roles/bbb_vps_prep/templates/20auto-upgrades.j2 new file mode 100644 index 0000000..dfcb22c --- /dev/null +++ b/roles/bbb_vps_prep/templates/20auto-upgrades.j2 @@ -0,0 +1,5 @@ +// Managed by Ansible +APT::Periodic::Update-Package-Lists "1"; +APT::Periodic::Unattended-Upgrade "1"; +APT::Periodic::Download-Upgradeable-Packages "1"; +APT::Periodic::AutocleanInterval "7"; diff --git a/roles/bbb_vps_prep/templates/50unattended-upgrades.j2 b/roles/bbb_vps_prep/templates/50unattended-upgrades.j2 new file mode 100644 index 0000000..cd8e319 --- /dev/null +++ b/roles/bbb_vps_prep/templates/50unattended-upgrades.j2 @@ -0,0 +1,10 @@ +// Managed by Ansible +Unattended-Upgrade::Origins-Pattern { + "origin=Ubuntu,codename=${distro_codename}-security"; + "origin=Ubuntu,codename=${distro_codename}-updates"; +}; + +Unattended-Upgrade::Automatic-Reboot "false"; +Unattended-Upgrade::Remove-Unused-Kernel-Packages "true"; +Unattended-Upgrade::Remove-Unused-Dependencies "true"; +Unattended-Upgrade::SyslogEnable "true"; diff --git a/roles/bbb_vps_prep/templates/docker-daemon.json.j2 b/roles/bbb_vps_prep/templates/docker-daemon.json.j2 new file mode 100644 index 0000000..3c5394d --- /dev/null +++ b/roles/bbb_vps_prep/templates/docker-daemon.json.j2 @@ -0,0 +1,7 @@ +{ + "ip-forward-no-drop": true, + "log-driver": "local", + "log-opts": { "max-size": "50m", "max-file": "3" }, + "live-restore": true, + "userland-proxy": false +} diff --git a/roles/bbb_vps_prep/templates/hostfilter-nft.service.j2 b/roles/bbb_vps_prep/templates/hostfilter-nft.service.j2 new file mode 100644 index 0000000..87d1608 --- /dev/null +++ b/roles/bbb_vps_prep/templates/hostfilter-nft.service.j2 @@ -0,0 +1,12 @@ +[Unit] +Description=Host firewall (nftables) - hostfilter table +Wants=network-online.target +After=network-online.target docker.service + +[Service] +Type=oneshot +ExecStart=/usr/local/sbin/apply-hostfilter-nft +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/roles/bbb_vps_prep/templates/hostfilter.nft.j2 b/roles/bbb_vps_prep/templates/hostfilter.nft.j2 new file mode 100644 index 0000000..03a8ea2 --- /dev/null +++ b/roles/bbb_vps_prep/templates/hostfilter.nft.j2 @@ -0,0 +1,81 @@ +#!/usr/sbin/nft -f + +define EXT_IF = "{{ ansible_default_ipv4.interface }}" + +table inet hostfilter { + + set ssh_allow_v4 { + type ipv4_addr + flags interval + elements = { {% for c in ssh_allow_cidrs_v4 %} {{ c }}{% if not loop.last %}, {% endif %}{% endfor %} } + } + + set ssh_allow_v6 { + type ipv6_addr + flags interval + elements = { {% for c in ssh_allow_cidrs_v6 %} {{ c }}{% if not loop.last %}, {% endif %}{% endfor %} } + } + + chain input { + type filter hook input priority 0; policy drop; + + ct state invalid drop + ct state { established, related } accept + + iif "lo" accept + + # Docker internal -> host (avoid breaking container->host access) + iifname { "docker0", "br-*" } accept + + ip protocol icmp accept + ip6 nexthdr icmpv6 accept + + # SSH + tcp dport {{ ssh_port }} ip saddr @ssh_allow_v4 accept + tcp dport {{ ssh_port }} ip6 saddr @ssh_allow_v6 accept + + # BBB + tcp dport { 80, 443 } accept + udp dport {{ bbb_udp_min }}-{{ bbb_udp_max }} accept + + {% if turn_enabled | bool %} + # TURN cohabitation (no 443) + udp dport {{ turn_listen_port }} accept + tcp dport {{ turn_listen_port }} accept + tcp dport {{ turn_tls_port }} accept + udp dport {{ turn_relay_udp_min }}-{{ turn_relay_udp_max }} accept + {% endif %} + + limit rate 10/second burst 20 packets counter log prefix "nft-drop-in: " flags all drop + } + + chain forward { + # Run early; Docker keeps its chains, we enforce policy safely + type filter hook forward priority -100; policy drop; + + ct state invalid drop + ct state { established, related } accept + + # Allow Docker internal forwarding (critical) + iifname { "docker0", "br-*" } oifname { "docker0", "br-*" } accept + + # Allow containers to reach the Internet + iifname { "docker0", "br-*" } oifname $EXT_IF accept + + # Allow Internet -> published container ports we expect (guardrail) + iifname $EXT_IF oifname { "docker0", "br-*" } tcp dport { 80, 443 } accept + iifname $EXT_IF oifname { "docker0", "br-*" } udp dport {{ bbb_udp_min }}-{{ bbb_udp_max }} accept + + {% if turn_enabled | bool %} + iifname $EXT_IF oifname { "docker0", "br-*" } udp dport {{ turn_listen_port }} accept + iifname $EXT_IF oifname { "docker0", "br-*" } tcp dport { {{ turn_listen_port }}, {{ turn_tls_port }} } accept + iifname $EXT_IF oifname { "docker0", "br-*" } udp dport {{ turn_relay_udp_min }}-{{ turn_relay_udp_max }} accept + {% endif %} + + limit rate 10/second burst 20 packets counter log prefix "nft-drop-fwd: " flags all drop + } + + chain output { + type filter hook output priority 0; policy accept; + } +} diff --git a/roles/bbb_vps_prep/templates/jail.local.j2 b/roles/bbb_vps_prep/templates/jail.local.j2 new file mode 100644 index 0000000..1bfefd5 --- /dev/null +++ b/roles/bbb_vps_prep/templates/jail.local.j2 @@ -0,0 +1,12 @@ +# Managed by Ansible + +[DEFAULT] +backend = systemd +banaction = nftables-multiport +findtime = 10m +bantime = 1h +maxretry = 5 + +[sshd] +enabled = true +port = {{ ssh_port }} diff --git a/roles/bbb_vps_prep/templates/journald.conf.j2 b/roles/bbb_vps_prep/templates/journald.conf.j2 new file mode 100644 index 0000000..4747fd2 --- /dev/null +++ b/roles/bbb_vps_prep/templates/journald.conf.j2 @@ -0,0 +1,9 @@ +# Managed by Ansible +[Journal] +Storage={{ "persistent" if journald_persistent else "auto" }} +Compress=yes +SystemMaxUse=500M +RuntimeMaxUse=200M +MaxRetentionSec=1month +RateLimitIntervalSec=30s +RateLimitBurst=10000 diff --git a/roles/bbb_vps_prep/templates/sshd_config.j2 b/roles/bbb_vps_prep/templates/sshd_config.j2 new file mode 100644 index 0000000..f76da11 --- /dev/null +++ b/roles/bbb_vps_prep/templates/sshd_config.j2 @@ -0,0 +1,30 @@ +# Managed by Ansible +Port {{ ssh_port }} +Protocol 2 + +PermitRootLogin {{ permit_root_login }} +PasswordAuthentication {{ "no" if disable_password_auth else "yes" }} +KbdInteractiveAuthentication no +ChallengeResponseAuthentication no +UsePAM yes + +PubkeyAuthentication yes +AuthenticationMethods publickey + +X11Forwarding no +AllowAgentForwarding no +AllowTcpForwarding no +PermitTunnel no +PermitUserEnvironment no + +ClientAliveInterval 300 +ClientAliveCountMax 2 +LoginGraceTime 30 +MaxAuthTries 3 +MaxSessions 4 + +LogLevel VERBOSE + +{% if ssh_allow_users is defined and ssh_allow_users|length > 0 %} +AllowUsers {% for u in ssh_allow_users %}{{ u }} {% endfor %} +{% endif %} diff --git a/roles/bbb_vps_prep/templates/sysctl-hardening.conf.j2 b/roles/bbb_vps_prep/templates/sysctl-hardening.conf.j2 new file mode 100644 index 0000000..79a485d --- /dev/null +++ b/roles/bbb_vps_prep/templates/sysctl-hardening.conf.j2 @@ -0,0 +1,30 @@ +# Managed by Ansible +# Network hardening + Docker needs forwarding + +net.ipv4.ip_forward = 1 +net.ipv6.conf.all.forwarding = 1 + +net.ipv4.tcp_syncookies = 1 +net.ipv4.conf.all.rp_filter = 1 +net.ipv4.conf.default.rp_filter = 1 + +net.ipv4.conf.all.accept_redirects = 0 +net.ipv4.conf.default.accept_redirects = 0 +net.ipv4.conf.all.send_redirects = 0 +net.ipv4.conf.default.send_redirects = 0 + +net.ipv4.conf.all.accept_source_route = 0 +net.ipv4.conf.default.accept_source_route = 0 +net.ipv6.conf.all.accept_source_route = 0 +net.ipv6.conf.default.accept_source_route = 0 + +net.ipv4.icmp_echo_ignore_broadcasts = 1 +net.ipv4.icmp_ignore_bogus_error_responses = 1 + +fs.protected_hardlinks = 1 +fs.protected_symlinks = 1 +kernel.kptr_restrict = 2 +kernel.dmesg_restrict = 1 +kernel.randomize_va_space = 2 + +vm.swappiness = 10 diff --git a/site.yml b/site.yml new file mode 100644 index 0000000..01a6d9c --- /dev/null +++ b/site.yml @@ -0,0 +1,7 @@ +--- +- name: Prepare Ubuntu 22.04 VPS for BBB (nftables + hardening + Docker) + hosts: vps + become: true + gather_facts: true + roles: + - bbb_vps_prep