Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 1 addition & 27 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,32 +50,6 @@ jobs:
AUTH_SECRET: ci-placeholder
DATABASE_URL: ":memory:"

ansible-quality:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ansible
env:
ANSIBLE_INVENTORY: localhost,
ANSIBLE_COLLECTIONS_PATH: ./collections
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: pip
cache-dependency-path: ansible/requirements.txt
- name: Install Ansible and lint tools
run: pip install -r requirements.txt
- name: Install Ansible collections
run: ansible-galaxy collection install -r requirements.yml -p ./collections --force
- name: Ansible lint – PaaS
run: ansible-lint --offline playbooks/paas/main.yml
- name: Ansible lint – SaaS deploy
run: ansible-lint --offline playbooks/saas/main.yml playbooks/saas/operate.yml
- name: Ansible lint – SaaS image build
run: ansible-lint --offline playbooks/saas/image-forkable.yml

build-ui:
needs: [setup, quality]
uses: ./.github/workflows/docker-build.yml
Expand All @@ -101,7 +75,7 @@ jobs:
registry_token: ${{ secrets.GITHUB_TOKEN }}

build-ansible:
needs: [setup, quality, ansible-quality]
needs: [setup, quality]
uses: ./.github/workflows/docker-build.yml
with:
image_name: ${{ github.repository }}-ansible
Expand Down
13 changes: 13 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@ repos:
- id: pretty-format-json
args:
- --autofix
- repo: local
hooks:
- id: ansible-lint
name: Ansible lint
language: system
entry: >-
bash -c 'cd ansible && ansible-lint --offline
playbooks/paas/main.yml
playbooks/saas/main.yml
playbooks/saas/operate.yml
playbooks/saas/image-forkable.yml'
files: ^ansible/
pass_filenames: false
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.96.1
hooks:
Expand Down
5 changes: 5 additions & 0 deletions ansible/.ansible-lint
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,8 @@ skip_list:
- jinja[spacing]
- yaml[line-length]
- yaml[truthy]
- no-changed-when
- no-handler
- risky-file-permissions
- ignore-errors
- name[missing]
9 changes: 9 additions & 0 deletions ansible/playbooks/paas/fail2ban.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
- name: Install fail2ban
any_errors_fatal: true
hosts: "{{ hosts_limit | default('infrastructure') }}"
gather_facts: true
become: true

roles:
- fail2ban
2 changes: 2 additions & 0 deletions ansible/playbooks/paas/nvidia.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@
# https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html
- name: Nomad-nvidia-plugin | Test nvidia support
ansible.builtin.command: nvidia-ctk runtime configure --runtime=docker
changed_when: false

- name: Nomad-nvidia-plugin | Restart docker
ansible.builtin.systemd:
Expand All @@ -148,6 +149,7 @@
- name: Nomad-nvidia-plugin | Test nvidia support
ansible.builtin.command: docker run --rm --runtime=nvidia --gpus all ubuntu nvidia-smi
register: docker_run
changed_when: false

- name: Nomad-nvidia-plugin | Debug
ansible.builtin.debug:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
ansible.builtin.get_url:
url: "https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg"
dest: /etc/apt/keyrings/docker.asc
mode: '0644'

- name: Add Docker repository on ubuntu
ansible.builtin.copy:
Expand Down
2 changes: 1 addition & 1 deletion ansible/playbooks/paas/roles/ansible-ufw/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
retries: 10

- name: Reset firewall
ufw:
community.general.ufw:
state: reset
when: ufw_reset

Expand Down
43 changes: 43 additions & 0 deletions ansible/playbooks/paas/roles/fail2ban/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
fail2ban_packages:
- fail2ban
- conntrack

# IPs to whitelist (never ban)
fail2ban_ignoreip:
- 127.0.0.1/8
- 172.17.0.0/16
- ::1

# Logging
fail2ban_loglevel: INFO
fail2ban_logtarget: /var/log/fail2ban.log
fail2ban_dbpurgeage: 30d

# Traefik access log path
fail2ban_traefik_access_log: /var/log/traefik/traefik-access.log

# Exponential bantime (fail2ban >= 0.11)
# Each recidive multiplies the base bantime: 10m, 50m, 5h, 10h, 2d, 10d
fail2ban_bantime_increment: true
fail2ban_bantime_multipliers: "1 5 30 60 288 1440"
fail2ban_bantime_maxtime: 4w
fail2ban_bantime_overalljails: true

# WP Login brute force
fail2ban_wp_login_enabled: true
fail2ban_wp_login_maxretry: 5
fail2ban_wp_login_findtime: 3600
fail2ban_wp_login_bantime: 600

# 404 flood
fail2ban_404_enabled: true
fail2ban_404_maxretry: 5
fail2ban_404_findtime: 600
fail2ban_404_bantime: 600

# Rate limit
fail2ban_ratelimit_enabled: true
fail2ban_ratelimit_maxretry: 50
fail2ban_ratelimit_findtime: 30
fail2ban_ratelimit_bantime: 600
10 changes: 10 additions & 0 deletions ansible/playbooks/paas/roles/fail2ban/handlers/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
- name: Restart fail2ban
ansible.builtin.systemd:
name: fail2ban
state: restarted
enabled: true

- name: Reload fail2ban
ansible.builtin.command: fail2ban-client reload
changed_when: true
58 changes: 58 additions & 0 deletions ansible/playbooks/paas/roles/fail2ban/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
- name: Install fail2ban
ansible.builtin.apt:
pkg: "{{ fail2ban_packages }}"
state: present
install_recommends: false
update_cache: true
cache_valid_time: 86400
register: apt_status
until: apt_status is success
delay: 6
retries: 10

- name: Deploy fail2ban.local
ansible.builtin.template:
src: fail2ban.local
dest: /etc/fail2ban/fail2ban.local
owner: root
group: root
mode: "0644"
notify: Reload fail2ban

- name: Deploy custom actions
ansible.builtin.template:
src: action.d/ufw-conntrack.conf
dest: /etc/fail2ban/action.d/ufw-conntrack.conf
owner: root
group: root
mode: "0644"
notify: Reload fail2ban

- name: Deploy traefik filters
ansible.builtin.template:
src: "filter.d/{{ item }}"
dest: "/etc/fail2ban/filter.d/{{ item }}"
owner: root
group: root
mode: "0644"
loop:
- traefik-wp-login.conf
- traefik-404.conf
- traefik-ratelimit.conf
notify: Reload fail2ban

- name: Deploy traefik jail
ansible.builtin.template:
src: jail.d/traefik.conf
dest: /etc/fail2ban/jail.d/traefik.conf
owner: root
group: root
mode: "0644"
notify: Reload fail2ban

- name: Enable and start fail2ban
ansible.builtin.systemd:
name: fail2ban
state: started
enabled: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Custom fail2ban action: UFW ban + kill existing connections
# This ensures that already-established TCP connections from the banned IP
# are immediately terminated, preventing keep-alive bypass.

[Definition]

actionstart =
actionstop =
actioncheck =

actionban = ufw insert 1 deny from <ip> to any
ss -K dst <ip> 2>/dev/null || true
conntrack -D -s <ip> 2>/dev/null || true

actionunban = ufw delete deny from <ip> to any
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[Definition]
loglevel = {{ fail2ban_loglevel }}
logtarget = {{ fail2ban_logtarget }}

# Use SQLite database to track bans across restarts (required for bantime.increment)
dbfile = /var/lib/fail2ban/fail2ban.sqlite3
dbpurgeage = {{ fail2ban_dbpurgeage }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# fail2ban filter for 404 flood via Traefik CLF access logs
# Matches any request returning HTTP 404
#
# Traefik CLF format:
# <ip> - <user> [<date>] "<method> <url> <proto>" <status> <size> "<referer>" "<ua>" ...

[Definition]

failregex = ^<HOST> - .* "(?:GET|POST|HEAD|PUT|DELETE|PATCH) [^ ]+ HTTP/[0-9.]+" 404

ignoreregex =

datepattern = \[%%d/%%b/%%Y:%%H:%%M:%%S %%z\]
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# fail2ban filter for request rate limiting via Traefik CLF access logs
# Matches any HTTP request (all methods)
#
# Traefik CLF format:
# <ip> - <user> [<date>] "<method> <url> <proto>" <status> <size> "<referer>" "<ua>" ...

[Definition]

failregex = ^<HOST> - .* "(?:GET|POST|HEAD|PUT|DELETE|PATCH|OPTIONS) [^ ]+ HTTP/[0-9.]+"

ignoreregex =

datepattern = \[%%d/%%b/%%Y:%%H:%%M:%%S %%z\]
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# fail2ban filter for WordPress login brute force via Traefik CLF access logs
# Matches POST/GET to /wp-login.php and /xmlrpc.php
#
# Traefik CLF format:
# <ip> - <user> [<date>] "<method> <url> <proto>" <status> <size> "<referer>" "<ua>" ...

[Definition]

failregex = ^<HOST> - .* "(GET|POST) /wp-login\.php
^<HOST> - .* "(GET|POST) /xmlrpc\.php

ignoreregex =

datepattern = \[%%d/%%b/%%Y:%%H:%%M:%%S %%z\]
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# fail2ban jails for Traefik access logs

{% if fail2ban_wp_login_enabled %}
[traefik-wp-login]
enabled = true
filter = traefik-wp-login
logpath = {{ fail2ban_traefik_access_log }}
maxretry = {{ fail2ban_wp_login_maxretry }}
findtime = {{ fail2ban_wp_login_findtime }}
bantime = {{ fail2ban_wp_login_bantime }}
bantime.increment = {{ fail2ban_bantime_increment | lower }}
bantime.multipliers = {{ fail2ban_bantime_multipliers }}
bantime.maxtime = {{ fail2ban_bantime_maxtime }}
bantime.overalljails = {{ fail2ban_bantime_overalljails | lower }}
ignoreip = {{ fail2ban_ignoreip | join(' ') }}
action = ufw-conntrack
{% endif %}

{% if fail2ban_404_enabled %}
[traefik-404]
enabled = true
filter = traefik-404
logpath = {{ fail2ban_traefik_access_log }}
maxretry = {{ fail2ban_404_maxretry }}
findtime = {{ fail2ban_404_findtime }}
bantime = {{ fail2ban_404_bantime }}
bantime.increment = {{ fail2ban_bantime_increment | lower }}
bantime.multipliers = {{ fail2ban_bantime_multipliers }}
bantime.maxtime = {{ fail2ban_bantime_maxtime }}
bantime.overalljails = {{ fail2ban_bantime_overalljails | lower }}
ignoreip = {{ fail2ban_ignoreip | join(' ') }}
action = ufw-conntrack
{% endif %}

{% if fail2ban_ratelimit_enabled %}
[traefik-ratelimit]
enabled = true
filter = traefik-ratelimit
logpath = {{ fail2ban_traefik_access_log }}
maxretry = {{ fail2ban_ratelimit_maxretry }}
findtime = {{ fail2ban_ratelimit_findtime }}
bantime = {{ fail2ban_ratelimit_bantime }}
bantime.increment = {{ fail2ban_bantime_increment | lower }}
bantime.multipliers = {{ fail2ban_bantime_multipliers }}
bantime.maxtime = {{ fail2ban_bantime_maxtime }}
bantime.overalljails = {{ fail2ban_bantime_overalljails | lower }}
ignoreip = {{ fail2ban_ignoreip | join(' ') }}
action = ufw-conntrack
{% endif %}
14 changes: 8 additions & 6 deletions ansible/playbooks/paas/roles/golang/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -1,35 +1,37 @@
---
- name: Check if Go is already installed
command: /usr/local/go/bin/go version
ansible.builtin.command: /usr/local/go/bin/go version
ignore_errors: true
register: go_version_result
changed_when: false

- name: Remove current installation
file:
ansible.builtin.file:
state: absent
path: /usr/local/go
when:
- go_version_result is succeeded
- go_version not in go_version_result.stdout

- name: Download Go
get_url:
ansible.builtin.get_url:
url: "{{ go_download_url }}"
dest: /usr/local/src/{{ go_tarball }}
checksum: "sha256:{{ go_checksum[upstream_default_arch] }}"
mode: '0644'
when: go_version_result is failed or go_version not in go_version_result.stdout

- name: Extract Go
unarchive:
ansible.builtin.unarchive:
src: /usr/local/src/{{ go_tarball }}
dest: /usr/local
copy: no
copy: false
when: go_version_result is failed
or go_version not in go_version_result.stdout

- name: Add Go to to system-wide $PATH
copy:
ansible.builtin.copy:
dest: /etc/profile.d/go-path.sh
mode: '0644'
content: |-
export PATH=$PATH:/usr/local/go/bin
2 changes: 1 addition & 1 deletion ansible/playbooks/paas/roles/prometheus/tasks/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
url: "{{ upstream_file_url }}"
dest: "{{ build_work_dir }}/download/"
mode: '0644'
force: no
force: false
register: download_result

- name: Prometheus | Unarchive GitHub release
Expand Down
Loading
Loading