Initial commit: BBB VPS prep (Ubuntu 22.04, nftables, Docker)
This commit is contained in:
commit
c59c5e9ef1
18 changed files with 680 additions and 0 deletions
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
*.retry
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
.ansible/
|
||||||
|
.cache/
|
||||||
|
.venv/
|
||||||
|
__pycache__/
|
||||||
|
group_vars/*secret*.yml
|
||||||
|
**/vault.yml
|
||||||
|
.env
|
||||||
57
Makefile
Normal file
57
Makefile
Normal file
|
|
@ -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
|
||||||
36
README.md
Normal file
36
README.md
Normal file
|
|
@ -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).
|
||||||
41
group_vars/all.yml
Normal file
41
group_vars/all.yml
Normal file
|
|
@ -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
|
||||||
2
inventory.ini
Normal file
2
inventory.ini
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
[vps]
|
||||||
|
bbb ansible_host=YOUR.VPS.IP ansible_user=ansible
|
||||||
9
roles/bbb_vps_prep/files/apply-hostfilter-nft.sh
Executable file
9
roles/bbb_vps_prep/files/apply-hostfilter-nft.sh
Executable file
|
|
@ -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"
|
||||||
31
roles/bbb_vps_prep/handlers/main.yml
Normal file
31
roles/bbb_vps_prep/handlers/main.yml
Normal file
|
|
@ -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
|
||||||
289
roles/bbb_vps_prep/tasks/main.yml
Normal file
289
roles/bbb_vps_prep/tasks/main.yml
Normal file
|
|
@ -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
|
||||||
5
roles/bbb_vps_prep/templates/20auto-upgrades.j2
Normal file
5
roles/bbb_vps_prep/templates/20auto-upgrades.j2
Normal file
|
|
@ -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";
|
||||||
10
roles/bbb_vps_prep/templates/50unattended-upgrades.j2
Normal file
10
roles/bbb_vps_prep/templates/50unattended-upgrades.j2
Normal file
|
|
@ -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";
|
||||||
7
roles/bbb_vps_prep/templates/docker-daemon.json.j2
Normal file
7
roles/bbb_vps_prep/templates/docker-daemon.json.j2
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
12
roles/bbb_vps_prep/templates/hostfilter-nft.service.j2
Normal file
12
roles/bbb_vps_prep/templates/hostfilter-nft.service.j2
Normal file
|
|
@ -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
|
||||||
81
roles/bbb_vps_prep/templates/hostfilter.nft.j2
Normal file
81
roles/bbb_vps_prep/templates/hostfilter.nft.j2
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
12
roles/bbb_vps_prep/templates/jail.local.j2
Normal file
12
roles/bbb_vps_prep/templates/jail.local.j2
Normal file
|
|
@ -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 }}
|
||||||
9
roles/bbb_vps_prep/templates/journald.conf.j2
Normal file
9
roles/bbb_vps_prep/templates/journald.conf.j2
Normal file
|
|
@ -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
|
||||||
30
roles/bbb_vps_prep/templates/sshd_config.j2
Normal file
30
roles/bbb_vps_prep/templates/sshd_config.j2
Normal file
|
|
@ -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 %}
|
||||||
30
roles/bbb_vps_prep/templates/sysctl-hardening.conf.j2
Normal file
30
roles/bbb_vps_prep/templates/sysctl-hardening.conf.j2
Normal file
|
|
@ -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
|
||||||
7
site.yml
Normal file
7
site.yml
Normal file
|
|
@ -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
|
||||||
Loading…
Reference in a new issue