WIP
This commit is contained in:
146
README.md
Normal file
146
README.md
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
# BYOW - Build Your Own WAF
|
||||||
|
|
||||||
|
Ce rôle Ansible déploie un WAF basé sur Caddy, Coraza et les OWASP Core Security Rules.
|
||||||
|
|
||||||
|
## Principes
|
||||||
|
|
||||||
|
### Go
|
||||||
|
|
||||||
|
Caddy, Coraza, le module Coraza pour Caddy et l'outil `xcaddy` sont
|
||||||
|
développés avec le langage Go.
|
||||||
|
|
||||||
|
L'outil `xcaddy` a besoin d'une version récente des outils de
|
||||||
|
développement Go. C'est pourquoi le rôle installe Go depuis
|
||||||
|
le site de téléchargement officiel et non via le gestionnaire
|
||||||
|
de paquets de la distribution Linux.
|
||||||
|
|
||||||
|
Go est installé dans le répertoire `/usr/local/go-1.25.5/go` et
|
||||||
|
un lien symbolique est créé de `/usr/local/bin/go` vers
|
||||||
|
`/usr/local/go-1.25.5/go/bin/go`.
|
||||||
|
|
||||||
|
### Caddy
|
||||||
|
|
||||||
|
Caddy est un serveur Web et reverse proxy HTTP puissant et versatile.
|
||||||
|
|
||||||
|
Étant développé en Go, son déploiement prend la forme d'un simble
|
||||||
|
binaire exécutable qui contient toutes les librairies requises. Ce
|
||||||
|
programme est construit avec l'outil `xcaddy` puis copié dans le
|
||||||
|
fichier `/usr/local/bin/caddy`. La ligne de commande utilisée pour
|
||||||
|
construire ce binaire est enregsitrée dans le fichier
|
||||||
|
`/usr/local/bin/.caddy.buil`.
|
||||||
|
|
||||||
|
Le fichier de configuration de caddy utilise le format `Caddyfile`.
|
||||||
|
Il est généré automatiquement à partir des paramètres du rôle puis
|
||||||
|
enregistré sous `/etc/caddy/Caddyfile`.
|
||||||
|
|
||||||
|
### Coraza
|
||||||
|
|
||||||
|
Coraza est directement inclus dans le binaire `caddy`
|
||||||
|
|
||||||
|
### Module Coraza pour Caddy
|
||||||
|
|
||||||
|
### xcaddy
|
||||||
|
|
||||||
|
qui permet de lier tous les modules Go requis
|
||||||
|
pour le WAF
|
||||||
|
|
||||||
|
### Plugins OWASP CRS
|
||||||
|
|
||||||
|
(À ne pas confondre avec les plugins Coraza.)
|
||||||
|
|
||||||
|
Voir: https://github.com/coreruleset/plugin-registry
|
||||||
|
|
||||||
|
- name: "drupal-rule-exclusions"
|
||||||
|
- name: "wordpress-rule-exclusions"
|
||||||
|
- name: "nextcloud-rule-exclusions"
|
||||||
|
- name: "dokuwiki-rule-exclusions"
|
||||||
|
- name: "phpmyadmin-rule-exclusions"
|
||||||
|
- name: "roundcube-rule-exclusions"
|
||||||
|
- name: "sogo-rule-exclusions"
|
||||||
|
|
||||||
|
|
||||||
|
## Paramètres du rôle Ansible
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
Fonctionnalités déjà implémentées:
|
||||||
|
|
||||||
|
- Multisite (cavec configuration des CRS par site)
|
||||||
|
- Redirection vers nom canonique
|
||||||
|
- Rediction HTTP vers HTTPS
|
||||||
|
- Certificats SSL (autmotiques via Let's Encrypt et customs)
|
||||||
|
- OWASP CRS
|
||||||
|
- Redirect (SSL et www et autres)
|
||||||
|
- Filtrage par:
|
||||||
|
- adresses IP
|
||||||
|
- réseaux IP
|
||||||
|
- Antibot par:
|
||||||
|
- défi JavaScript
|
||||||
|
- Passive backend checks
|
||||||
|
- Compression Gzip et zstd
|
||||||
|
|
||||||
|
|
||||||
|
Fomctionnalités à ajouter:
|
||||||
|
|
||||||
|
- Active backend checks
|
||||||
|
- Antibot par:
|
||||||
|
- Cookie
|
||||||
|
- Captcha
|
||||||
|
- reCAPTCHA
|
||||||
|
- hCaptcha
|
||||||
|
- Turnstile
|
||||||
|
- mCaptcha
|
||||||
|
- Changement de format de configuration: Caddyfile -> JSON
|
||||||
|
- Mise en œuvre d'un cluster Redis (ou Consul) pour le partage de informations entre les WAF d'un cluster
|
||||||
|
- Mieux tester le configuration des CRS
|
||||||
|
- Mode détection sans blocage
|
||||||
|
- Filtrage par:
|
||||||
|
- rDNS
|
||||||
|
- DNSBL
|
||||||
|
- ASN
|
||||||
|
- User-Agent (robots, crawlers, IA bots)
|
||||||
|
- méthode HTTP
|
||||||
|
- taille
|
||||||
|
- URI patterns
|
||||||
|
- pays
|
||||||
|
- (ip:danmeuk-tor-exit, ua:mitchellkrogza-bad-user-agents, /etc/hapee-1.9/blacklist.acl)
|
||||||
|
- Gestion CORS
|
||||||
|
- Client cache
|
||||||
|
- Proxy cache
|
||||||
|
- Filtrage par pays
|
||||||
|
- Compression Brotli
|
||||||
|
- En-têtes de sécurité:
|
||||||
|
- HSTS
|
||||||
|
- CSP
|
||||||
|
- X-Frame-Options
|
||||||
|
- X-Content-Type-Options
|
||||||
|
- Referrer Policy
|
||||||
|
- Permissions Policy
|
||||||
|
- Keep Headers
|
||||||
|
- Cookie Flags
|
||||||
|
- Custom headers
|
||||||
|
- Reverse scan
|
||||||
|
- Robots.txt (DarkVisitors API/Community Lists/Custom URLs/Manual Rules)
|
||||||
|
- Sessions
|
||||||
|
- Whitelist
|
||||||
|
- Bad behavior
|
||||||
|
- Auth
|
||||||
|
- Anti DoDS
|
||||||
|
- Greylist
|
||||||
|
- HTML injection
|
||||||
|
- Metrics (Elasticsearch + Metabase)
|
||||||
|
- Static files et FastCGI pour PHP
|
||||||
|
|
||||||
|
|
||||||
|
## Ressources
|
||||||
|
|
||||||
|
- [Documentation officielle Caddy](https://caddyserver.com/docs/)
|
||||||
|
- [Site Officiel de Coraza](https://coraza.io)
|
||||||
|
- [Coraza WAF Caddy module](https://github.com/corazawaf/coraza-caddy)
|
||||||
|
- [Documentation AWOSP CRS](https://coreruleset.org/docs/)
|
||||||
|
|
||||||
|
Modules
|
||||||
|
- []()
|
||||||
|
- []()
|
||||||
40
ansible/README.md
Normal file
40
ansible/README.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
### Installation d'Ansible avec pipx
|
||||||
|
|
||||||
|
1. Désinstaller *Ansible* et *ansible-lint* s'ils sont déjà installés. Par
|
||||||
|
exemple avec *Homebrew*:
|
||||||
|
```
|
||||||
|
brew uninstall ansible ansible-lint
|
||||||
|
```
|
||||||
|
1. Installer *pipx*. Par exemple avec *Homebrew*:
|
||||||
|
```
|
||||||
|
brew install pipx
|
||||||
|
```
|
||||||
|
1. Inscrire le chemin de recherche des exécutables de *pipx* dans les
|
||||||
|
scripts d'ouverture de session (fermer le shell et en ouvrir un nouveau
|
||||||
|
pour activer ce chemin de recherche):
|
||||||
|
```
|
||||||
|
pipx ensurepath
|
||||||
|
```
|
||||||
|
1. Installer *Ansible* (version 11, et au moins version core 2.16.9, pour
|
||||||
|
être compatible avec *Mitogen*) dans un environnement isolé (qui sera
|
||||||
|
nommé `ansible`) puis injecter *ansible-lint* et les librairies requises
|
||||||
|
dans cet environnement:
|
||||||
|
```
|
||||||
|
pipx install ansible==12.3.0 --include-deps
|
||||||
|
pipx inject ansible ansible-lint --include-apps
|
||||||
|
pipx inject ansible requests
|
||||||
|
pipx inject ansible pip-system-certs
|
||||||
|
pipx inject ansible passlib # pour |password_hash sur macOS (et autres)
|
||||||
|
pipx inject ansible jmespath # pour |query_result
|
||||||
|
pipx inject ansible pytz # pour plugin inventory auto
|
||||||
|
pipx inject ansible pynetbox # pour collection netbox.netbox
|
||||||
|
pipx inject ansible netaddr # pour les rôles CheckMK
|
||||||
|
pipx inject ansible mitogen # optionnel, pour utiliser Mitogen
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Vérifier l'environnement:
|
||||||
|
```
|
||||||
|
pipx list --include-injected
|
||||||
|
ansible --version -> ansible [core 2.16.9]
|
||||||
|
ansible-lint --version -> ansible-lint 24.7.0
|
||||||
|
```
|
||||||
@@ -1,3 +1,17 @@
|
|||||||
|
# Example of a ansible_local.env file that is used to customize the local
|
||||||
|
# operation of Ansible with environment variables.
|
||||||
|
|
||||||
|
# Usage:
|
||||||
|
# 1. Copy ansible/ansible_local.env.example to ansible/ansible_local.env or create a new version.
|
||||||
|
# 2. Customize the contents of the ansible_local.env file, there can only be definitions of environment variables.
|
||||||
|
# cf.: https://docs.ansible.com/ansible/latest/reference_appendices/config.html
|
||||||
|
# 3. The run_ansible.sh script will automatically source the ansible/ansible_local.env file if it exists before running ansible.
|
||||||
|
|
||||||
|
# The ansible_local.env file is ignored by Git (present in.gitignore).
|
||||||
|
|
||||||
|
# Example of using the ANSIBLE_STRATEGY_PLUGINS and ANSIBLE_STRATEGY environment variables to enable Mitogen (speeds up the execution of Ansible playbooks by a factor of 2 to 10).
|
||||||
|
# cf.: https://mitogen.networkgenomics.com/ansible_detailed.html
|
||||||
|
|
||||||
ANSIBLE_STRATEGY_PLUGINS=/home/seb/.local/share/pipx/venvs/ansible/lib/python3.13/site-packages/ansible_mitogen/plugins/strategy
|
ANSIBLE_STRATEGY_PLUGINS=/home/seb/.local/share/pipx/venvs/ansible/lib/python3.13/site-packages/ansible_mitogen/plugins/strategy
|
||||||
export ANSIBLE_STRATEGY_PLUGINS
|
export ANSIBLE_STRATEGY_PLUGINS
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
linux:
|
linux:
|
||||||
vars:
|
vars:
|
||||||
ansible_user: "root"
|
ansible_user: "root"
|
||||||
|
|
||||||
|
ubuntu24:
|
||||||
|
vars:
|
||||||
ansible_python_interpreter: "/usr/bin/python3.12"
|
ansible_python_interpreter: "/usr/bin/python3.12"
|
||||||
|
|
||||||
proxy:
|
|
||||||
hosts:
|
|
||||||
proxy1:
|
|
||||||
|
|
||||||
www:
|
|
||||||
hosts:
|
|
||||||
www1:
|
|
||||||
www2:
|
|
||||||
www3:
|
|
||||||
|
|
||||||
waf:
|
waf:
|
||||||
hosts:
|
hosts:
|
||||||
waf1:
|
waf1:
|
||||||
waf2:
|
waf2:
|
||||||
waf3:
|
waf3:
|
||||||
|
|
||||||
|
mysql:
|
||||||
|
hosts:
|
||||||
|
mysql1:
|
||||||
|
|
||||||
|
www:
|
||||||
|
hosts:
|
||||||
|
www1:
|
||||||
|
www2:
|
||||||
|
www3:
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
multipass list --format=json |jq '{
|
multipass list --format=json |jq '{
|
||||||
_meta: {hostvars: [.list[] | {(.name): {ansible_host: .ipv4[-1]}}] | add},
|
_meta: {hostvars: [.list[] | {(.name): {ansible_host: .ipv4[0]}}] | add},
|
||||||
linux: {hosts: [.list[].name]}
|
linux: {hosts: [.list[].name]},
|
||||||
|
ubuntu24: {hosts: [.list[].name]},
|
||||||
}'
|
}'
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
- "vars/common.yml"
|
- "vars/common.yml"
|
||||||
- "vars/secrets.yml"
|
- "vars/secrets.yml"
|
||||||
vars:
|
vars:
|
||||||
certs_ca_passphrase: "{{ secrets.ca.passphrase }}"
|
local_ca_certs_ca_passphrase: "{{ secrets.ca.passphrase }}"
|
||||||
certs_default_country_name: "{{ common.certificates.country_name }}"
|
local_ca_certs_default_country_name: "{{ common.certificates.country_name }}"
|
||||||
certs_default_locality_name: "{{ common.certificates.locality_name }}"
|
local_ca_certs_default_locality_name: "{{ common.certificates.locality_name }}"
|
||||||
certs_default_organization_name: "{{ common.certificates.organization_name }}"
|
local_ca_certs_default_organization_name: "{{ common.certificates.organization_name }}"
|
||||||
certs_default_email_address: "{{ common.certificates.email_address }}"
|
local_ca_certs_default_email_address: "{{ common.certificates.email_address }}"
|
||||||
certs_list:
|
local_ca_certs_list:
|
||||||
- cn: "test.lab"
|
- cn: "test.lab"
|
||||||
domains:
|
domains:
|
||||||
- "DNS:test.lab"
|
- "DNS:test.lab"
|
||||||
|
|||||||
@@ -24,20 +24,6 @@
|
|||||||
tags:
|
tags:
|
||||||
- "local_ca"
|
- "local_ca"
|
||||||
|
|
||||||
- name: Proxy server
|
|
||||||
hosts: proxy
|
|
||||||
gather_facts: false
|
|
||||||
vars_files:
|
|
||||||
- "vars/common.yml"
|
|
||||||
- "vars/secrets.yml"
|
|
||||||
vars:
|
|
||||||
squid_authorized_networks:
|
|
||||||
- "{{ ansible_default_ipv4.network }}/24"
|
|
||||||
roles:
|
|
||||||
- "squid"
|
|
||||||
tags:
|
|
||||||
- "proxy"
|
|
||||||
|
|
||||||
- name: Common
|
- name: Common
|
||||||
hosts: all
|
hosts: all
|
||||||
gather_facts: false
|
gather_facts: false
|
||||||
@@ -45,23 +31,17 @@
|
|||||||
- "vars/common.yml"
|
- "vars/common.yml"
|
||||||
- "vars/secrets.yml"
|
- "vars/secrets.yml"
|
||||||
vars:
|
vars:
|
||||||
common_proxy_server: "{{ hostvars['proxy1'].ansible_default_ipv4.address }}:3128"
|
common_default_packages:
|
||||||
|
- "net-tools"
|
||||||
|
- "htop"
|
||||||
|
- "aptitude"
|
||||||
|
- "screen"
|
||||||
|
- "tcpdump"
|
||||||
roles:
|
roles:
|
||||||
- "common"
|
- "common"
|
||||||
tags:
|
tags:
|
||||||
- "common"
|
- "common"
|
||||||
|
|
||||||
- name: HTTP Servers
|
|
||||||
hosts: www
|
|
||||||
gather_facts: false
|
|
||||||
vars_files:
|
|
||||||
- "vars/common.yml"
|
|
||||||
- "vars/secrets.yml"
|
|
||||||
roles:
|
|
||||||
- "www"
|
|
||||||
tags:
|
|
||||||
- "www"
|
|
||||||
|
|
||||||
- name: Web Application Firewalls (install)
|
- name: Web Application Firewalls (install)
|
||||||
hosts: waf
|
hosts: waf
|
||||||
gather_facts: false
|
gather_facts: false
|
||||||
@@ -69,12 +49,12 @@
|
|||||||
- "vars/common.yml"
|
- "vars/common.yml"
|
||||||
- "vars/secrets.yml"
|
- "vars/secrets.yml"
|
||||||
vars:
|
vars:
|
||||||
certs_ca_passphrase: "{{ secrets.ca.passphrase }}"
|
local_ca_certs_ca_passphrase: "{{ secrets.ca.passphrase }}"
|
||||||
certs_default_country_name: "{{ common.certificates.country_name }}"
|
local_ca_certs_default_country_name: "{{ common.certificates.country_name }}"
|
||||||
certs_default_locality_name: "{{ common.certificates.locality_name }}"
|
local_ca_certs_default_locality_name: "{{ common.certificates.locality_name }}"
|
||||||
certs_default_organization_name: "{{ common.certificates.organization_name }}"
|
local_ca_certs_default_organization_name: "{{ common.certificates.organization_name }}"
|
||||||
certs_default_email_address: "{{ common.certificates.email_address }}"
|
local_ca_certs_default_email_address: "{{ common.certificates.email_address }}"
|
||||||
certs_list:
|
local_ca_certs_list:
|
||||||
# Consul
|
# Consul
|
||||||
- cn: "server.{{ common.consul.datacenter }}.{{ common.consul.domain }}"
|
- cn: "server.{{ common.consul.datacenter }}.{{ common.consul.domain }}"
|
||||||
domains:
|
domains:
|
||||||
@@ -89,31 +69,132 @@
|
|||||||
consul_domain: "{{ common.consul.domain }}"
|
consul_domain: "{{ common.consul.domain }}"
|
||||||
consul_datacenter: "{{ common.consul.datacenter }}"
|
consul_datacenter: "{{ common.consul.datacenter }}"
|
||||||
consul_secret_key: "{{ secrets.consul.secret_key }}"
|
consul_secret_key: "{{ secrets.consul.secret_key }}"
|
||||||
consul_nodes:
|
consul_servers: ["waf1", "waf2", "waf3"]
|
||||||
- "waf1"
|
consul_server: true
|
||||||
- "waf2"
|
consul_services:
|
||||||
- "waf3"
|
- name: "in"
|
||||||
# FIXME: -> Cady modules
|
port: 443
|
||||||
waf_install_cluster: true
|
check_http: "http://{{ ansible_default_ipv4.address }}:80/"
|
||||||
vip_script_user: "caddy"
|
# keepalived_script_user: "caddy"
|
||||||
vip_monitored_process: "caddy"
|
# keepalived_monitored_process: "caddy"
|
||||||
vip_vrrp_password: "{{ secrets.keepalived.secret }}"
|
# keepalived_vrrp_password: "{{ secrets.keepalived.secret }}"
|
||||||
vip_vrrp_ips:
|
# keepalived_vrrp_ips:
|
||||||
- master: "waf1"
|
# - master: "waf1"
|
||||||
virtual_router_id: 11
|
# virtual_router_id: 251
|
||||||
addr: "{{ {{ ansible_default_ipv4.address }} | split('.')[0-3] | join('.') }}.250"
|
# addr: "{{ ansible_default_ipv4.address | regex_replace('\\.[0-9]+$', '') }}.251/24"
|
||||||
- master: "waf2"
|
# - master: "waf2"
|
||||||
virtual_router_id: 12
|
# virtual_router_id: 252
|
||||||
addr: "{{ {{ ansible_default_ipv4.address }} | split('.')[0-3] | join('.') }}.251"
|
# addr: "{{ ansible_default_ipv4.address | regex_replace('\\.[0-9]+$', '') }}.252/24"
|
||||||
- master: "waf3"
|
# - master: "waf3"
|
||||||
virtual_router_id: 13
|
# virtual_router_id: 253
|
||||||
addr: "{{ {{ ansible_default_ipv4.address }} | split('.')[0-3] | join('.') }}.252"
|
# addr: "{{ ansible_default_ipv4.address | regex_replace('\\.[0-9]+$', '') }}.253/24"
|
||||||
|
# # Multipass network doesn't seem to support well multicast.
|
||||||
|
# keepalived_unicast_peers:
|
||||||
|
# - "{{ hostvars['waf1'].ansible_default_ipv4.address }}"
|
||||||
|
# - "{{ hostvars['waf2'].ansible_default_ipv4.address }}"
|
||||||
|
# - "{{ hostvars['waf3'].ansible_default_ipv4.address }}"
|
||||||
|
unbound_listen_interfaces:
|
||||||
|
- "{{ ansible_default_ipv4.address }}"
|
||||||
|
unbound_stub_zones:
|
||||||
|
- name: "{{ common.consul.datacenter }}.{{ common.consul.domain }}"
|
||||||
|
stub_to: "127.0.0.1@8600"
|
||||||
waf_config_sites: "{{ common.sites }}"
|
waf_config_sites: "{{ common.sites }}"
|
||||||
|
ovh_ldp_cluster: "gra2.logs.ovh.com"
|
||||||
|
ovh_ldp_token: ¨{{ secrets.ovh_ldp.token }}
|
||||||
roles:
|
roles:
|
||||||
- "certs"
|
- "local_ca_certs"
|
||||||
- "consul"
|
- "consul"
|
||||||
- "waf_install"
|
- "waf_install"
|
||||||
- "vip"
|
# - "keepalived"
|
||||||
- "waf_config"
|
- "waf_config"
|
||||||
|
- "unbound"
|
||||||
|
- "ovh_ldp"
|
||||||
tags:
|
tags:
|
||||||
- "waf"
|
- "waf"
|
||||||
|
|
||||||
|
- name: MySQL servers
|
||||||
|
hosts: mysql
|
||||||
|
gather_facts: false
|
||||||
|
vars_files:
|
||||||
|
- "vars/common.yml"
|
||||||
|
- "vars/secrets.yml"
|
||||||
|
vars:
|
||||||
|
local_ca_certs_ca_passphrase: "{{ secrets.ca.passphrase }}"
|
||||||
|
local_ca_certs_default_country_name: "{{ common.certificates.country_name }}"
|
||||||
|
local_ca_certs_default_locality_name: "{{ common.certificates.locality_name }}"
|
||||||
|
local_ca_certs_default_organization_name: "{{ common.certificates.organization_name }}"
|
||||||
|
local_ca_certs_default_email_address: "{{ common.certificates.email_address }}"
|
||||||
|
local_ca_certs_list:
|
||||||
|
- cn: "{{ ansible_fqdn }}"
|
||||||
|
domains:
|
||||||
|
- "DNS:{{ ansible_fqdn }}"
|
||||||
|
- "DNS:{{ ansible_hostname }}"
|
||||||
|
- "IP:{{ ansible_default_ipv4.address }}"
|
||||||
|
mysql_databases:
|
||||||
|
- "wordpress"
|
||||||
|
mysql_users:
|
||||||
|
- name: "wordpress"
|
||||||
|
password: "{{ secrets.wordpress.mysql_password }}"
|
||||||
|
priv: "wordpress.*:ALL"
|
||||||
|
host: "{{ ansible_default_ipv4.network }}/255.255.255.0"
|
||||||
|
consul_domain: "{{ common.consul.domain }}"
|
||||||
|
consul_datacenter: "{{ common.consul.datacenter }}"
|
||||||
|
consul_secret_key: "{{ secrets.consul.secret_key }}"
|
||||||
|
consul_key_file: "/etc/ssl/private/{{ ansible_fqdn }}.key"
|
||||||
|
consul_cert_file: "/etc/ssl/certs/{{ ansible_fqdn }}.crt"
|
||||||
|
consul_servers: ["waf1", "waf2", "waf3"]
|
||||||
|
consul_services:
|
||||||
|
- name: "test-lab-mysql"
|
||||||
|
port: 3306
|
||||||
|
check_tcp: "{{ ansible_default_ipv4.address }}:3306"
|
||||||
|
roles:
|
||||||
|
- "local_ca_certs"
|
||||||
|
- "mysql"
|
||||||
|
- "consul"
|
||||||
|
tags:
|
||||||
|
- "mysql"
|
||||||
|
|
||||||
|
- name: HTTP Servers
|
||||||
|
hosts: www
|
||||||
|
gather_facts: false
|
||||||
|
vars_files:
|
||||||
|
- "vars/common.yml"
|
||||||
|
- "vars/secrets.yml"
|
||||||
|
vars:
|
||||||
|
local_ca_certs_ca_passphrase: "{{ secrets.ca.passphrase }}"
|
||||||
|
local_ca_certs_default_country_name: "{{ common.certificates.country_name }}"
|
||||||
|
local_ca_certs_default_locality_name: "{{ common.certificates.locality_name }}"
|
||||||
|
local_ca_certs_default_organization_name: "{{ common.certificates.organization_name }}"
|
||||||
|
local_ca_certs_default_email_address: "{{ common.certificates.email_address }}"
|
||||||
|
local_ca_certs_list:
|
||||||
|
- cn: "{{ ansible_fqdn }}"
|
||||||
|
domains:
|
||||||
|
- "DNS:{{ ansible_fqdn }}"
|
||||||
|
- "DNS:{{ ansible_hostname }}"
|
||||||
|
- "IP:{{ ansible_default_ipv4.address }}"
|
||||||
|
wordpress_site: "test.lab"
|
||||||
|
wordpress_url: "test.lab"
|
||||||
|
wordpress_site_title: "Lab Test"
|
||||||
|
wordpress_admin_user: "spock"
|
||||||
|
wordpress_admin_password: "{{ secrets.wordpress.admin_password }}"
|
||||||
|
wordpress_admin_email: "seb@itik.fr"
|
||||||
|
wordpress_themes: ["twentytwentyfive"]
|
||||||
|
wordpress_active_theme: "twentytwentyfive"
|
||||||
|
wordpress_db_host: "mysql1"
|
||||||
|
wordpress_db_pass: "{{ secrets.wordpress.mysql_password }}"
|
||||||
|
consul_domain: "{{ common.consul.domain }}"
|
||||||
|
consul_datacenter: "{{ common.consul.datacenter }}"
|
||||||
|
consul_secret_key: "{{ secrets.consul.secret_key }}"
|
||||||
|
consul_key_file: "/etc/ssl/private/{{ ansible_fqdn }}.key"
|
||||||
|
consul_cert_file: "/etc/ssl/certs/{{ ansible_fqdn }}.crt"
|
||||||
|
consul_servers: ["waf1", "waf2", "waf3"]
|
||||||
|
consul_services:
|
||||||
|
- name: "test-lab-www"
|
||||||
|
port: 80
|
||||||
|
check_http: "http://localhost:80/"
|
||||||
|
roles:
|
||||||
|
- "local_ca_certs"
|
||||||
|
- "wordpress"
|
||||||
|
- "consul"
|
||||||
|
tags:
|
||||||
|
- "www"
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
# Defaults
|
|
||||||
certs_default_country_name: null
|
|
||||||
certs_default_locality_name: null
|
|
||||||
certs_default_organization_name: null
|
|
||||||
certs_default_email_address: null
|
|
||||||
certs_default_not_after: "+365d" # valid for one year
|
|
||||||
certs_default_not_before: "-1d" # valid since yesterday
|
|
||||||
|
|
||||||
# Required
|
|
||||||
certs_ca_passphrase: null
|
|
||||||
certs_list: {}
|
|
||||||
# - cn: <string>
|
|
||||||
# domains: <array of strings>
|
|
||||||
# country_name: <string>
|
|
||||||
# locality_name: <string>
|
|
||||||
# organization_name: <string>
|
|
||||||
# email_address: <string>
|
|
||||||
# not_after: <string>
|
|
||||||
# not_before: <string>
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
- name: Create private key for new certificate
|
|
||||||
community.crypto.openssl_privatekey:
|
|
||||||
path: "/etc/ssl/private/{{ cert.cn }}.key"
|
|
||||||
mode: "0640"
|
|
||||||
group: "ssl-cert"
|
|
||||||
|
|
||||||
- name: Create CSR for new certificate
|
|
||||||
community.crypto.openssl_csr:
|
|
||||||
path: "/etc/ssl/certs/{{ cert.cn }}.csr"
|
|
||||||
privatekey_path: "/etc/ssl/private/{{ cert.cn }}.key"
|
|
||||||
common_name: "{{ cert.cn }}"
|
|
||||||
subject_alt_name: "{{ cert.domains }}"
|
|
||||||
country_name: "{{ cert.country_name | default(certs_default_country_name) | mandatory }}"
|
|
||||||
locality_name: "{{ cert.locality_name | default(certs_default_locality_name) | mandatory }}"
|
|
||||||
organization_name: "{{ cert.organization_name | default(certs_default_organization_name) | mandatory }}"
|
|
||||||
email_address: "{{ cert.email_address | default(certs_default_email_address) | mandatory }}"
|
|
||||||
return_content: true
|
|
||||||
register: certs_csr
|
|
||||||
|
|
||||||
- name: Sign certificate with our CA
|
|
||||||
delegate_to: "localhost"
|
|
||||||
# noqa: no-handler
|
|
||||||
when: "certs_csr.changed"
|
|
||||||
community.crypto.x509_certificate_pipe:
|
|
||||||
csr_content: "{{ certs_csr.csr }}"
|
|
||||||
provider: "ownca"
|
|
||||||
ownca_path: "ca/ca-certificate.crt"
|
|
||||||
ownca_privatekey_path: "ca/ca-certificate.key"
|
|
||||||
ownca_privatekey_passphrase: "{{ certs_ca_passphrase }}"
|
|
||||||
ownca_not_after: "{{ cert.not_after | default(certs_default_not_after) }}"
|
|
||||||
ownca_not_before: "{{ cert.not_before | default(certs_default_not_before) }}"
|
|
||||||
register: certs_certificate
|
|
||||||
|
|
||||||
- name: Write certificate file on server
|
|
||||||
# noqa: no-handler
|
|
||||||
when: "certs_csr.changed"
|
|
||||||
ansible.builtin.copy:
|
|
||||||
dest: "/etc/ssl/certs/{{ cert.cn }}.crt"
|
|
||||||
content: "{{ certs_certificate.certificate }}"
|
|
||||||
mode: "0644"
|
|
||||||
notify:
|
|
||||||
- "Reboot host"
|
|
||||||
2
ansible/roles/common/defaults/main.yml
Normal file
2
ansible/roles/common/defaults/main.yml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
common_default_packages: []
|
||||||
|
common_proxy_server: null
|
||||||
@@ -1,2 +1,7 @@
|
|||||||
- name: Setup proxy client
|
- name: Setup proxy client
|
||||||
|
when: "common_proxy_server != None"
|
||||||
ansible.builtin.include_tasks: "setup_proxy_client.yml"
|
ansible.builtin.include_tasks: "setup_proxy_client.yml"
|
||||||
|
|
||||||
|
- name: Setup packages
|
||||||
|
when: "common_default_packages | length > 0"
|
||||||
|
ansible.builtin.include_tasks: "setup_packages.yml"
|
||||||
|
|||||||
9
ansible/roles/common/tasks/setup_packages.yml
Normal file
9
ansible/roles/common/tasks/setup_packages.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
- name: Keep packages up to date
|
||||||
|
ansible.builtin.apt:
|
||||||
|
update_cache: true
|
||||||
|
upgrade: "safe"
|
||||||
|
|
||||||
|
- name: Install default packages
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: "{{ common_default_packages }}"
|
||||||
|
autoclean: true
|
||||||
@@ -2,7 +2,11 @@
|
|||||||
consul_datacenter: null
|
consul_datacenter: null
|
||||||
consul_domain: null
|
consul_domain: null
|
||||||
consul_secret_key: null
|
consul_secret_key: null
|
||||||
consul_nodes: []
|
consul_server: false
|
||||||
|
consul_servers: []
|
||||||
|
|
||||||
|
# Required for registering services
|
||||||
|
consul_services: []
|
||||||
|
|
||||||
# Defaults
|
# Defaults
|
||||||
consul_version: "1.22.2"
|
consul_version: "1.22.2"
|
||||||
@@ -12,3 +16,5 @@ consul_ca_file: "/etc/ssl/certs/local-ca.crt"
|
|||||||
consul_client_addr: "0.0.0.0"
|
consul_client_addr: "0.0.0.0"
|
||||||
consul_bind_addr: "{{ ansible_default_ipv4.address }}"
|
consul_bind_addr: "{{ ansible_default_ipv4.address }}"
|
||||||
consul_advertise_addr: "{{ ansible_default_ipv4.address }}"
|
consul_advertise_addr: "{{ ansible_default_ipv4.address }}"
|
||||||
|
consul_default_check_interval: "15s"
|
||||||
|
consul_default_check_timeout: "2s"
|
||||||
|
|||||||
@@ -3,8 +3,3 @@
|
|||||||
name: "consul"
|
name: "consul"
|
||||||
state: "restarted"
|
state: "restarted"
|
||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
|
|
||||||
- name: Reload Consul
|
|
||||||
ansible.builtin.service:
|
|
||||||
name: "consul"
|
|
||||||
state: "reloaded"
|
|
||||||
|
|||||||
6
ansible/roles/consul/notes.txt
Normal file
6
ansible/roles/consul/notes.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Security:
|
||||||
|
- Gossip protocol encryption
|
||||||
|
- Built-in ACL
|
||||||
|
- Consul agent communication
|
||||||
|
- mTLS for authentitication and encryption
|
||||||
|
- Certificate authority
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
group: "consul"
|
group: "consul"
|
||||||
mode: "0640"
|
mode: "0640"
|
||||||
notify:
|
notify:
|
||||||
- "Reload Consul"
|
- "Restart Consul"
|
||||||
|
|
||||||
- name: Deploy Consul service file
|
- name: Deploy Consul service file
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
@@ -76,3 +76,31 @@
|
|||||||
state: "started"
|
state: "started"
|
||||||
enabled: true
|
enabled: true
|
||||||
daemon_reload: true
|
daemon_reload: true
|
||||||
|
|
||||||
|
- name: Register services to consul
|
||||||
|
loop: "{{ consul_services }}"
|
||||||
|
community.general.consul_agent_service:
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
service_port: "{{ item.port }}"
|
||||||
|
tags: ="{{ item.tags | default([]) }}"
|
||||||
|
|
||||||
|
- name: Register HTTP checks to consul
|
||||||
|
loop: "{{ consul_services }}"
|
||||||
|
when: "item.check_http | default(None) != None"
|
||||||
|
community.general.consul_agent_check:
|
||||||
|
name: "{{ item.name }}_check"
|
||||||
|
service_id: "{{ item.name }}"
|
||||||
|
interval: "{{ item.check_interval | default(consul_default_check_interval) }}"
|
||||||
|
timeout: "{{ item.check_timeout | default(consul_default_check_timeout) }}"
|
||||||
|
http: "{{ item.check_http }}"
|
||||||
|
scheme: "{{ item.check_scheme | default('http') }}"
|
||||||
|
|
||||||
|
- name: Register TCP checks to consul
|
||||||
|
loop: "{{ consul_services }}"
|
||||||
|
when: "item.check_tcp | default(None) != None"
|
||||||
|
community.general.consul_agent_check:
|
||||||
|
name: "{{ item.name }}_check"
|
||||||
|
service_id: "{{ item.name }}"
|
||||||
|
interval: "{{ item.check_interval | default(consul_default_check_interval) }}"
|
||||||
|
timeout: "{{ item.check_timeout | default(consul_default_check_timeout) }}"
|
||||||
|
tcp: "{{ item.check_tcp }}"
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
{
|
{
|
||||||
"log_level": "INFO",
|
"log_level": "INFO",
|
||||||
"node_name": "{{ ansible_hostname }}",
|
|
||||||
"domain": "{{ consul_domain }}",
|
"domain": "{{ consul_domain }}",
|
||||||
"server": true,
|
"datacenter": "{{ consul_datacenter | mandatory }}",
|
||||||
"key_file": "{{ consul_key_file }}",
|
"key_file": "{{ consul_key_file }}",
|
||||||
"cert_file": "{{ consul_cert_file }}",
|
"cert_file": "{{ consul_cert_file }}",
|
||||||
"ca_file": "{{ consul_ca_file }}",
|
"ca_file": "{{ consul_ca_file }}",
|
||||||
@@ -10,17 +9,20 @@
|
|||||||
"verify_outgoing": true,
|
"verify_outgoing": true,
|
||||||
"verify_server_hostname": true,
|
"verify_server_hostname": true,
|
||||||
"encrypt": "{{ consul_secret_key }}",
|
"encrypt": "{{ consul_secret_key }}",
|
||||||
|
"data_dir": "/opt/consul",
|
||||||
|
{% if consul_server %}
|
||||||
|
"node_name": "{{ ansible_hostname }}",
|
||||||
|
"server": true,
|
||||||
"ui": true,
|
"ui": true,
|
||||||
"leave_on_terminate": true,
|
"leave_on_terminate": true,
|
||||||
"data_dir": "/opt/consul",
|
|
||||||
"datacenter": "{{ consul_datacenter | mandatory }}",
|
|
||||||
"client_addr": "{{ consul_client_addr }}",
|
"client_addr": "{{ consul_client_addr }}",
|
||||||
"bind_addr": "{{ consul_bind_addr }}",
|
"bind_addr": "{{ consul_bind_addr }}",
|
||||||
"advertise_addr": "{{ consul_advertise_addr }}",
|
"advertise_addr": "{{ consul_advertise_addr }}",
|
||||||
"bootstrap_expect": {{ consul_nodes | length }},
|
"bootstrap_expect": {{ consul_servers | length }},
|
||||||
"retry_join": ["{{ consul_nodes | join('", "') }}"],
|
|
||||||
"enable_syslog": true,
|
"enable_syslog": true,
|
||||||
"performance": {
|
"performance": {
|
||||||
"raft_multiplier": 1
|
"raft_multiplier": 1
|
||||||
}
|
},
|
||||||
|
{% endif %}
|
||||||
|
"retry_join": ["{{ consul_servers | join('", "') }}"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ Type=notify
|
|||||||
User=consul
|
User=consul
|
||||||
Group=consul
|
Group=consul
|
||||||
ExecStart=/usr/local/bin/consul agent -config-file=/etc/consul/config.json
|
ExecStart=/usr/local/bin/consul agent -config-file=/etc/consul/config.json
|
||||||
ExecReload=/usr/local/bin/consul reload
|
|
||||||
KillMode=process
|
KillMode=process
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
LimitNOFILE=65536
|
LimitNOFILE=65536
|
||||||
|
|||||||
16
ansible/roles/keepalived/defaults/main.yml
Normal file
16
ansible/roles/keepalived/defaults/main.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Required
|
||||||
|
keepalived_script_user: null
|
||||||
|
keepalived_monitored_process: null
|
||||||
|
keepalived_vrrp_password: null # Max 8 characters
|
||||||
|
keepalived_vrrp_ips: null
|
||||||
|
# - master: <string>
|
||||||
|
# virtual_router_id: <int>
|
||||||
|
# addr: <string>
|
||||||
|
|
||||||
|
# Optionnal
|
||||||
|
keepalived_alerts_to: []
|
||||||
|
keepalived_unicast_peers: []
|
||||||
|
|
||||||
|
# Required if length keepalived_alerts_to > 0
|
||||||
|
keepalived_smtp_from: null
|
||||||
|
keepalived_smtp_relay: null
|
||||||
58
ansible/roles/keepalived/templates/keepalived.conf
Normal file
58
ansible/roles/keepalived/templates/keepalived.conf
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# FIXME: NOTICE: setting config option max_auto_priority should result in better keepalived performance
|
||||||
|
|
||||||
|
global_defs {
|
||||||
|
enable_script_security
|
||||||
|
script_user "{{ keepalived_script_user }}"
|
||||||
|
{% if keepalived_alerts_to | length > 0 %}
|
||||||
|
|
||||||
|
notification_email {
|
||||||
|
{% for dest in keepalived_alerts_to %}
|
||||||
|
{{ dest }}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
notification_email_from {{ keepalived_smtp_from }}
|
||||||
|
smtp_server {{ keepalived_smtp_relay }}
|
||||||
|
smtp_connect_timeout 30
|
||||||
|
router_id {{ ansible_hostname }}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
|
||||||
|
vrrp_script chk_{{ keepalived_monitored_process }} {
|
||||||
|
script "/usr/bin/killall -0 {{ keepalived_monitored_process }}" # Check if process is running
|
||||||
|
interval 2 # Check every 2 seconds
|
||||||
|
weight 2 # Weight to influence master election
|
||||||
|
}
|
||||||
|
{% for ip in keepalived_vrrp_ips %}
|
||||||
|
|
||||||
|
# master: {{ ip.master }}
|
||||||
|
vrrp_instance VI_{{ ip.virtual_router_id }} {
|
||||||
|
state {{ (ansible_hostname==ip.master) | ternary('MASTER', 'BACKUP') }}
|
||||||
|
interface {{ ansible_default_ipv4.interface }}
|
||||||
|
virtual_router_id {{ ip.virtual_router_id }}
|
||||||
|
priority {{ (ansible_hostname==ip.master) | ternary('101', '100') }}
|
||||||
|
advert_int 1
|
||||||
|
promote_secondaries
|
||||||
|
{% if keepalived_alerts_to | length > 0 %}
|
||||||
|
smtp_alert true
|
||||||
|
{% endif %}
|
||||||
|
authentication {
|
||||||
|
auth_type PASS
|
||||||
|
auth_pass {{ keepalived_vrrp_password }}
|
||||||
|
}
|
||||||
|
virtual_ipaddress {
|
||||||
|
{{ ip.addr }}
|
||||||
|
}
|
||||||
|
{% if keepalived_unicast_peers | length > 0 %}
|
||||||
|
unicast_peer {
|
||||||
|
{% for peer in keepalived_unicast_peers %}
|
||||||
|
{% if peer != ansible_default_ipv4.address %}
|
||||||
|
{{ peer }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
track_script {
|
||||||
|
chk_{{ keepalived_monitored_process }}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
19
ansible/roles/local_ca_certs/defaults/main.yml
Normal file
19
ansible/roles/local_ca_certs/defaults/main.yml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Defaults
|
||||||
|
local_ca_certs_default_country_name: null
|
||||||
|
local_ca_certs_default_locality_name: null
|
||||||
|
local_ca_certs_default_organization_name: null
|
||||||
|
local_ca_certs_default_email_address: null
|
||||||
|
local_ca_certs_default_not_after: "+365d" # valid for one year
|
||||||
|
local_ca_certs_default_not_before: "-1d" # valid since yesterday
|
||||||
|
|
||||||
|
# Required
|
||||||
|
local_ca_certs_ca_passphrase: null
|
||||||
|
local_ca_certs_list: {}
|
||||||
|
# - cn: <string>
|
||||||
|
# domains: <array of strings>
|
||||||
|
# country_name: <string>
|
||||||
|
# locality_name: <string>
|
||||||
|
# organization_name: <string>
|
||||||
|
# email_address: <string>
|
||||||
|
# not_after: <string>
|
||||||
|
# not_before: <string>
|
||||||
42
ansible/roles/local_ca_certs/tasks/create_cert.yml
Normal file
42
ansible/roles/local_ca_certs/tasks/create_cert.yml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
- name: Create private key for new certificate
|
||||||
|
community.crypto.openssl_privatekey:
|
||||||
|
path: "/etc/ssl/private/{{ cert.cn }}.key"
|
||||||
|
mode: "0640"
|
||||||
|
group: "ssl-cert"
|
||||||
|
|
||||||
|
- name: Create CSR for new certificate
|
||||||
|
community.crypto.openssl_csr:
|
||||||
|
path: "/etc/ssl/certs/{{ cert.cn }}.csr"
|
||||||
|
privatekey_path: "/etc/ssl/private/{{ cert.cn }}.key"
|
||||||
|
common_name: "{{ cert.cn }}"
|
||||||
|
subject_alt_name: "{{ cert.domains }}"
|
||||||
|
country_name: "{{ cert.country_name | default(local_ca_certs_default_country_name) | mandatory }}"
|
||||||
|
locality_name: "{{ cert.locality_name | default(local_ca_certs_default_locality_name) | mandatory }}"
|
||||||
|
organization_name: "{{ cert.organization_name | default(local_ca_certs_default_organization_name) | mandatory }}"
|
||||||
|
email_address: "{{ cert.email_address | default(local_ca_certs_default_email_address) | mandatory }}"
|
||||||
|
return_content: true
|
||||||
|
register: local_ca_certs_csr
|
||||||
|
|
||||||
|
- name: Sign certificate with our CA
|
||||||
|
delegate_to: "localhost"
|
||||||
|
# noqa: no-handler
|
||||||
|
when: "local_ca_certs_csr.changed"
|
||||||
|
community.crypto.x509_certificate_pipe:
|
||||||
|
csr_content: "{{ local_ca_certs_csr.csr }}"
|
||||||
|
provider: "ownca"
|
||||||
|
ownca_path: "ca/ca-certificate.crt"
|
||||||
|
ownca_privatekey_path: "ca/ca-certificate.key"
|
||||||
|
ownca_privatekey_passphrase: "{{ local_ca_certs_ca_passphrase }}"
|
||||||
|
ownca_not_after: "{{ cert.not_after | default(local_ca_certs_default_not_after) }}"
|
||||||
|
ownca_not_before: "{{ cert.not_before | default(local_ca_certs_default_not_before) }}"
|
||||||
|
register: local_ca_certs_certificate
|
||||||
|
|
||||||
|
- name: Write certificate file on server
|
||||||
|
# noqa: no-handler
|
||||||
|
when: "local_ca_certs_csr.changed"
|
||||||
|
ansible.builtin.copy:
|
||||||
|
dest: "/etc/ssl/certs/{{ cert.cn }}.crt"
|
||||||
|
content: "{{ local_ca_certs_certificate.certificate }}"
|
||||||
|
mode: "0644"
|
||||||
|
notify:
|
||||||
|
- "Reboot host"
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
mode: "0644"
|
mode: "0644"
|
||||||
|
|
||||||
- name: Create certs
|
- name: Create certs
|
||||||
loop: "{{ certs_list }}"
|
loop: "{{ local_ca_certs_list }}"
|
||||||
loop_control:
|
loop_control:
|
||||||
loop_var: "cert"
|
loop_var: "cert"
|
||||||
ansible.builtin.include_tasks: "create_cert.yml"
|
ansible.builtin.include_tasks: "create_cert.yml"
|
||||||
6
ansible/roles/mysql/defaults/main.yml
Normal file
6
ansible/roles/mysql/defaults/main.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Required
|
||||||
|
mysql_databases: []
|
||||||
|
mysql_users: []
|
||||||
|
# - name: <string
|
||||||
|
# password: <string>
|
||||||
|
# priv: <string>
|
||||||
5
ansible/roles/mysql/handlers/main.yml
Normal file
5
ansible/roles/mysql/handlers/main.yml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
- name: Restart MySQL
|
||||||
|
ansible.builtin.service:
|
||||||
|
name: "mysql"
|
||||||
|
state: "restarted"
|
||||||
|
daemon_reload: true
|
||||||
31
ansible/roles/mysql/tasks/main.yml
Normal file
31
ansible/roles/mysql/tasks/main.yml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
- name: Installation de MySQL
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name:
|
||||||
|
- "mysql-server"
|
||||||
|
- "python3-pymysql"
|
||||||
|
notify:
|
||||||
|
- "Restart MySQL"
|
||||||
|
|
||||||
|
- name: Set MySQL bind_address parameter
|
||||||
|
ansible.builtin.lineinfile:
|
||||||
|
path: "/etc/mysql/mysql.conf.d/mysqld.cnf"
|
||||||
|
regexp: "^bind-address\\s+="
|
||||||
|
line: "bind-address = *"
|
||||||
|
notify:
|
||||||
|
- "Restart MySQL"
|
||||||
|
|
||||||
|
- name: Création des bases de données MySQL
|
||||||
|
loop: "{{ mysql_databases }}"
|
||||||
|
community.mysql.mysql_db:
|
||||||
|
login_unix_socket: "/var/run/mysqld/mysqld.sock"
|
||||||
|
name: "{{ item }}"
|
||||||
|
encoding: "utf8"
|
||||||
|
|
||||||
|
- name: Création des utilisateur MySQL
|
||||||
|
loop: "{{ mysql_users }}"
|
||||||
|
community.mysql.mysql_user:
|
||||||
|
login_unix_socket: "/var/run/mysqld/mysqld.sock"
|
||||||
|
name: "{{ item.name }}"
|
||||||
|
password: "{{ item.password }}"
|
||||||
|
priv: "{{ item.priv }}"
|
||||||
|
host: "{{ item.host }}"
|
||||||
6
ansible/roles/ovh_ldp/defaults/main.yml
Normal file
6
ansible/roles/ovh_ldp/defaults/main.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Required
|
||||||
|
ovh_ldp_token: null
|
||||||
|
ovh_ldp_cluster: null
|
||||||
|
|
||||||
|
# Defaults
|
||||||
|
ovh_ldp_tls_syslog_port: 6514
|
||||||
5
ansible/roles/ovh_ldp/handlers/main.yml
Normal file
5
ansible/roles/ovh_ldp/handlers/main.yml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
- name: Restart syslog-ng
|
||||||
|
ansible.builtin.service:
|
||||||
|
name: "syslog-ng"
|
||||||
|
state: "restarted"
|
||||||
|
daemon_reload: true
|
||||||
13
ansible/roles/ovh_ldp/tasks/main.yml
Normal file
13
ansible/roles/ovh_ldp/tasks/main.yml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
- name: Install syslog-ng
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name:
|
||||||
|
- "syslog-ng"
|
||||||
|
- "ca-certificates"
|
||||||
|
|
||||||
|
- name: Generate syslog-ng config file for OVH LDP
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "templates/ldp-ovh.conf"
|
||||||
|
dest: "/etc/syslog-ng/conf.d/ldp-ovh.conf"
|
||||||
|
mode: "0640"
|
||||||
|
notify:
|
||||||
|
- "Restart syslog-ng"
|
||||||
22
ansible/roles/ovh_ldp/templates/ldp-ovh.conf
Normal file
22
ansible/roles/ovh_ldp/templates/ldp-ovh.conf
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
rewrite ovh-token {
|
||||||
|
set("{{ ovh_ldp_token }}", value(".SDATA.token@29084.X-OVH-TOKEN"));
|
||||||
|
};
|
||||||
|
|
||||||
|
destination ovhPaaSLogs {
|
||||||
|
network("{{ ovh_ldp_cluster }}"
|
||||||
|
port({{ ovh_ldp_tls_syslog_port }})
|
||||||
|
transport("tls")
|
||||||
|
flags(syslog-protocol)
|
||||||
|
ts_format("iso")
|
||||||
|
frac-digits(6)
|
||||||
|
tls(peer-verify("required-trusted") ca_dir("/etc/ssl/certs/"))
|
||||||
|
keep-alive(yes)
|
||||||
|
so_keepalive(yes)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
log {
|
||||||
|
source(s_src);
|
||||||
|
rewrite(ovh-token);
|
||||||
|
destination(ovhPaaSLogs);
|
||||||
|
};
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
squid_authorized_networks: ["127.0.0.1"]
|
|
||||||
squid_cache_size: 10240
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
- name: Restart squid
|
|
||||||
ansible.builtin.systemd:
|
|
||||||
name: "squid"
|
|
||||||
state: "restarted"
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
- name: Install squid package
|
|
||||||
ansible.builtin.apt:
|
|
||||||
name: "squid"
|
|
||||||
notify:
|
|
||||||
- "Restart squid"
|
|
||||||
|
|
||||||
- name: Deploy squid configuration file
|
|
||||||
ansible.builtin.template:
|
|
||||||
src: "templates/squid.conf"
|
|
||||||
dest: "/etc/squid/squid.conf"
|
|
||||||
owner: "root"
|
|
||||||
group: "root"
|
|
||||||
mode: "0644"
|
|
||||||
notify:
|
|
||||||
- "Restart squid"
|
|
||||||
|
|
||||||
- name: Enable squid
|
|
||||||
ansible.builtin.systemd:
|
|
||||||
name: "squid"
|
|
||||||
enabled: true
|
|
||||||
daemon_reload: true
|
|
||||||
notify:
|
|
||||||
- "Restart squid"
|
|
||||||
File diff suppressed because it is too large
Load Diff
14
ansible/roles/unbound/defaults/main.yml
Normal file
14
ansible/roles/unbound/defaults/main.yml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
unbound_listen_interfaces:
|
||||||
|
- "0.0.0.0"
|
||||||
|
|
||||||
|
unbound_access_control:
|
||||||
|
- "0.0.0.0/0 allow"
|
||||||
|
|
||||||
|
unbound_forward_zones:
|
||||||
|
- name: "."
|
||||||
|
forward_to: "8.8.8.8"
|
||||||
|
|
||||||
|
unbound_stub_zones: []
|
||||||
|
# - name: <string>
|
||||||
|
# stub_to: <string>
|
||||||
|
# no_cache: no (default yes)
|
||||||
4
ansible/roles/unbound/handlers/main.yml
Normal file
4
ansible/roles/unbound/handlers/main.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
- name: Restart unbound
|
||||||
|
ansible.builtin.service:
|
||||||
|
name: "unbound"
|
||||||
|
state: "restarted"
|
||||||
14
ansible/roles/unbound/tasks/main.yml
Normal file
14
ansible/roles/unbound/tasks/main.yml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
- name: Install Unbound
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name:
|
||||||
|
- "unbound"
|
||||||
|
notify:
|
||||||
|
- "Restart unbound"
|
||||||
|
|
||||||
|
- name: Deploy unbounf config file
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "templates/unbound.conf"
|
||||||
|
dest: "/etc/unbound/unbound.conf.d/unbound.conf"
|
||||||
|
mode: "0644"
|
||||||
|
notify:
|
||||||
|
- "Restart unbound"
|
||||||
21
ansible/roles/unbound/templates/unbound.conf
Normal file
21
ansible/roles/unbound/templates/unbound.conf
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
server:
|
||||||
|
do-not-query-localhost: no
|
||||||
|
{% for interface in unbound_listen_interfaces %}
|
||||||
|
interface: {{ interface }}
|
||||||
|
{% endfor %}
|
||||||
|
{% for access in unbound_access_control %}
|
||||||
|
access-control: {{ access }}
|
||||||
|
{% endfor %}
|
||||||
|
{% for zone in unbound_stub_zones %}
|
||||||
|
|
||||||
|
stub-zone:
|
||||||
|
name: "{{ zone.name }}"
|
||||||
|
stub-addr: {{ zone.stub_to }}
|
||||||
|
stub-no-cache: {{ zone.no_cache | default('yes') }}
|
||||||
|
{% endfor %}
|
||||||
|
{% for zone in unbound_forward_zones %}
|
||||||
|
|
||||||
|
forward-zone:
|
||||||
|
name: "{{ zone.name }}"
|
||||||
|
forward-addr: {{ zone.forward_to }}
|
||||||
|
{% endfor %}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# Required
|
|
||||||
vip_script_user: null
|
|
||||||
vip_monitored_process: null
|
|
||||||
vip_vrrp_password: null # Max 8 characters
|
|
||||||
vip_vrrp_ips: null
|
|
||||||
# - master: <string>
|
|
||||||
# virtual_router_id: <int>
|
|
||||||
# addr: <string>
|
|
||||||
|
|
||||||
# Optionnal
|
|
||||||
vip_alerts_to: null
|
|
||||||
# - <string>
|
|
||||||
|
|
||||||
# Required if length vip_alerts_to > 0
|
|
||||||
vip_smtp_from: null
|
|
||||||
vip_smtp_relay: null
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
global_defs {
|
|
||||||
enable_script_security
|
|
||||||
script_user "{{ vip_script_user }}"
|
|
||||||
{% if vip_alerts_to | default([]) | length > 0 %}
|
|
||||||
|
|
||||||
notification_email {
|
|
||||||
{% for dest in vip_alerts_to %}
|
|
||||||
{{ dest }}
|
|
||||||
{% endfor %}
|
|
||||||
}
|
|
||||||
notification_email_from {{ vip_smtp_from }}
|
|
||||||
smtp_server {{ vip_smtp_relay }}
|
|
||||||
smtp_connect_timeout 30
|
|
||||||
router_id {{ ansible_hostname }}
|
|
||||||
{% endif %}
|
|
||||||
}
|
|
||||||
|
|
||||||
vrrp_script chk_{{ vip_monitored_process }} {
|
|
||||||
script "/usr/bin/killall -0 {{ vip_monitored_process }}" # Check if process is running
|
|
||||||
interval 2 # Check every 2 seconds
|
|
||||||
weight 2 # Weight to influence master election
|
|
||||||
}
|
|
||||||
{% for ip in vip_vrrp_ips %}
|
|
||||||
|
|
||||||
# master: {{ ip.master }}
|
|
||||||
vrrp_instance VI_{{ ip.virtual_router_id }} {
|
|
||||||
state {{ (ansible_hostname==ip.master) | ternary('MASTER', 'BACKUP') }}
|
|
||||||
interface eth0
|
|
||||||
virtual_router_id {{ ip.virtual_router_id }}
|
|
||||||
priority {{ (ansible_hostname==ip.master) | ternary('101', '100') }}
|
|
||||||
advert_int 1
|
|
||||||
promote_secondaries
|
|
||||||
smtp_alert true
|
|
||||||
authentication {
|
|
||||||
auth_type PASS
|
|
||||||
auth_pass {{ vip_vrrp_password }}
|
|
||||||
}
|
|
||||||
virtual_ipaddress {
|
|
||||||
{{ ip.addr }}
|
|
||||||
}
|
|
||||||
track_script {
|
|
||||||
chk_{{ vip_monitored_process }}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{% endfor %}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# Defaults
|
# Defaults
|
||||||
waf_config_owasp_crs_version: "4.21.0"
|
waf_config_owasp_crs_version: "4.22.0"
|
||||||
|
|
||||||
# Required unless not site.custom_cert
|
# Required unless not site.custom_cert
|
||||||
waf_config_email: null
|
waf_config_email: null
|
||||||
@@ -22,8 +22,6 @@ waf_config_sites: []
|
|||||||
# - ""
|
# - ""
|
||||||
# aliases:
|
# aliases:
|
||||||
# - ""
|
# - ""
|
||||||
# plugins: FIXME:TODO
|
|
||||||
# - ""
|
|
||||||
# exceptions:
|
# exceptions:
|
||||||
# before_request:
|
# before_request:
|
||||||
# - ""
|
# - ""
|
||||||
@@ -35,6 +33,7 @@ waf_config_defaults:
|
|||||||
custom_cert: true
|
custom_cert: true
|
||||||
custom_cert_file: ""
|
custom_cert_file: ""
|
||||||
custom_key_file: ""
|
custom_key_file: ""
|
||||||
|
plugins: []
|
||||||
|
|
||||||
rate_events: 1000
|
rate_events: 1000
|
||||||
rate_window: "1m"
|
rate_window: "1m"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
- name: Reload Caddy
|
- name: Restart Caddy
|
||||||
ansible.builtin.service:
|
ansible.builtin.service:
|
||||||
name: "caddy"
|
name: "caddy"
|
||||||
state: "reloaded"
|
state: "restarted"
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
- name: Create sites config directories
|
- name: Create sites config directories
|
||||||
loop: "{{ waf_config_sites }}"
|
loop: "{{ waf_config_sites }}"
|
||||||
loop_control:
|
|
||||||
loop_var: "site"
|
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: "/etc/caddy/sites/{{ site.name }}"
|
path: "/etc/caddy/sites/{{ item.name }}"
|
||||||
state: "directory"
|
state: "directory"
|
||||||
group: "caddy"
|
group: "caddy"
|
||||||
mode: "0750"
|
mode: "0750"
|
||||||
@@ -18,7 +16,7 @@
|
|||||||
group: "caddy"
|
group: "caddy"
|
||||||
mode: "0640"
|
mode: "0640"
|
||||||
notify:
|
notify:
|
||||||
- "Reload Caddy"
|
- "Restart Caddy"
|
||||||
|
|
||||||
- name: Generate sites crs-setup config file
|
- name: Generate sites crs-setup config file
|
||||||
loop: "{{ waf_config_sites }}"
|
loop: "{{ waf_config_sites }}"
|
||||||
@@ -30,7 +28,7 @@
|
|||||||
group: "caddy"
|
group: "caddy"
|
||||||
mode: "0640"
|
mode: "0640"
|
||||||
notify:
|
notify:
|
||||||
- "Reload Caddy"
|
- "Restart Caddy"
|
||||||
|
|
||||||
- name: Generate sites exclusions before request file
|
- name: Generate sites exclusions before request file
|
||||||
loop: "{{ waf_config_sites }}"
|
loop: "{{ waf_config_sites }}"
|
||||||
@@ -42,7 +40,7 @@
|
|||||||
group: "caddy"
|
group: "caddy"
|
||||||
mode: "0640"
|
mode: "0640"
|
||||||
notify:
|
notify:
|
||||||
- "Reload Caddy"
|
- "Restart Caddy"
|
||||||
|
|
||||||
- name: Generate sites exclusions after response file
|
- name: Generate sites exclusions after response file
|
||||||
loop: "{{ waf_config_sites }}"
|
loop: "{{ waf_config_sites }}"
|
||||||
@@ -54,7 +52,7 @@
|
|||||||
group: "caddy"
|
group: "caddy"
|
||||||
mode: "0640"
|
mode: "0640"
|
||||||
notify:
|
notify:
|
||||||
- "Reload Caddy"
|
- "Restart Caddy"
|
||||||
|
|
||||||
- name: Generate Caddyfile
|
- name: Generate Caddyfile
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
@@ -63,7 +61,7 @@
|
|||||||
group: "caddy"
|
group: "caddy"
|
||||||
mode: "0640"
|
mode: "0640"
|
||||||
notify:
|
notify:
|
||||||
- "Reload Caddy"
|
- "Restart Caddy"
|
||||||
|
|
||||||
- name: Copy bot barrier template
|
- name: Copy bot barrier template
|
||||||
ansible.builtin.copy:
|
ansible.builtin.copy:
|
||||||
@@ -72,4 +70,4 @@
|
|||||||
group: "caddy"
|
group: "caddy"
|
||||||
mode: "0640"
|
mode: "0640"
|
||||||
notify:
|
notify:
|
||||||
- "Reload Caddy"
|
- "Restart Caddy"
|
||||||
|
|||||||
@@ -103,12 +103,16 @@
|
|||||||
directives `
|
directives `
|
||||||
Include "/etc/caddy/sites/{{ site.name }}/coraza.conf"
|
Include "/etc/caddy/sites/{{ site.name }}/coraza.conf"
|
||||||
Include "/etc/caddy/sites/{{ site.name }}/crs-setup.conf"
|
Include "/etc/caddy/sites/{{ site.name }}/crs-setup.conf"
|
||||||
Include "/etc/caddy/plugins/*-config.conf"
|
{% for plugin in site.plugins | default(waf_config_defaults.plugins) %}
|
||||||
Include "/etc/caddy/plugins/*-before.conf"
|
Include "/etc/caddy/plugins/{{ plugin }}-config.conf"
|
||||||
|
Include "/etc/caddy/plugins/{{ plugin }}-before.conf"
|
||||||
|
{% endfor %}
|
||||||
Include "/etc/caddy/sites/{{ site.name }}/exclusions-request-before.conf"
|
Include "/etc/caddy/sites/{{ site.name }}/exclusions-request-before.conf"
|
||||||
Include "/etc/caddy/coreruleset-{{ waf_config_owasp_crs_version }}/rules/*.conf"
|
Include "/etc/caddy/coreruleset-{{ waf_config_owasp_crs_version }}/rules/*.conf"
|
||||||
Include "/etc/caddy/sites/{{ site.name }}/exclusions-response-after.conf"
|
Include "/etc/caddy/sites/{{ site.name }}/exclusions-response-after.conf"
|
||||||
Include "/etc/caddy/plugins/*-after.conf"
|
{% for plugin in site.plugins | default(waf_config_defaults.plugins) %}
|
||||||
|
Include "/etc/caddy/plugins/{{ plugin }}-after.conf"
|
||||||
|
{% endfor %}
|
||||||
SecRuleEngine On
|
SecRuleEngine On
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,7 @@ waf_install_go_version: "1.25.5"
|
|||||||
waf_install_xcaddy_version: "0.4.5"
|
waf_install_xcaddy_version: "0.4.5"
|
||||||
waf_install_caddy_version: "2.10.2"
|
waf_install_caddy_version: "2.10.2"
|
||||||
waf_install_coraza_caddy_version: "2.1.0" # Coraza v3.3.3
|
waf_install_coraza_caddy_version: "2.1.0" # Coraza v3.3.3
|
||||||
waf_install_owasp_crs_version: "4.21.0"
|
waf_install_owasp_crs_version: "4.22.0"
|
||||||
|
|
||||||
# Cluster mode (e.g. with consul)
|
|
||||||
waf_install_cluster: false
|
|
||||||
|
|
||||||
# Cf.: https://github.com/coreruleset/plugin-registry
|
# Cf.: https://github.com/coreruleset/plugin-registry
|
||||||
waf_install_crs_plugins:
|
waf_install_crs_plugins:
|
||||||
@@ -19,6 +16,7 @@ waf_install_crs_plugins:
|
|||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
- name: "phpmyadmin-rule-exclusions"
|
- name: "phpmyadmin-rule-exclusions"
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
|
has_after_config: true
|
||||||
- name: "roundcube-rule-exclusions"
|
- name: "roundcube-rule-exclusions"
|
||||||
version: "1.0.4"
|
version: "1.0.4"
|
||||||
provider: "EsadCetiner"
|
provider: "EsadCetiner"
|
||||||
|
|||||||
@@ -30,7 +30,15 @@
|
|||||||
group: "caddy"
|
group: "caddy"
|
||||||
remote_src: true
|
remote_src: true
|
||||||
notify:
|
notify:
|
||||||
- "Reload Caddy"
|
- "Restart Caddy"
|
||||||
|
|
||||||
|
- name: Create after config {{ plugin.name }}
|
||||||
|
when: "not(plugin.has_after_config | default(false))"
|
||||||
|
ansible.builtin.copy:
|
||||||
|
content: ""
|
||||||
|
dest: "/etc/caddy/plugins/{{ plugin.name }}-after.conf"
|
||||||
|
mode: "0644"
|
||||||
|
group: "caddy"
|
||||||
|
|
||||||
# FIXME: bad hack, why is it required?
|
# FIXME: bad hack, why is it required?
|
||||||
- name: Adjust /etc/caddy/plugins permissions
|
- name: Adjust /etc/caddy/plugins permissions
|
||||||
|
|||||||
@@ -30,20 +30,29 @@
|
|||||||
dest: "/usr/local/bin/go"
|
dest: "/usr/local/bin/go"
|
||||||
state: "link"
|
state: "link"
|
||||||
|
|
||||||
# FIXME: no .deb
|
- name: Create xcaddy directory
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "/usr/local/xcaddy-{{ waf_install_xcaddy_version }}"
|
||||||
|
state: "directory"
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
- name: Install xcaddy
|
- name: Install xcaddy
|
||||||
when: |
|
ansible.builtin.unarchive:
|
||||||
'xcaddy' not in ansible_facts.packages
|
src: "{{ waf_install_xcaddy_download }}/v{{ waf_install_xcaddy_version }}/xcaddy_{{ waf_install_xcaddy_version }}_linux_amd64.tar.gz"
|
||||||
or ansible_facts.packages['xcaddy'][0]['version'] != waf_install_xcaddy_version
|
dest: "/usr/local/xcaddy-{{ waf_install_xcaddy_version }}"
|
||||||
ansible.builtin.apt:
|
remote_src: true
|
||||||
deb: "{{ waf_install_xcaddy_download }}/v{{ waf_install_xcaddy_version }}/xcaddy_{{ waf_install_xcaddy_version }}_linux_amd64.deb"
|
|
||||||
force: true
|
- name: Link xcaddy
|
||||||
|
ansible.builtin.file:
|
||||||
|
src: "/usr/local/xcaddy-{{ waf_install_xcaddy_version }}/xcaddy"
|
||||||
|
dest: "/usr/local/bin/xcaddy"
|
||||||
|
state: "link"
|
||||||
|
|
||||||
- name: Create Caddy build command
|
- name: Create Caddy build command
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
waf_install_build_command: >
|
waf_install_build_command: >
|
||||||
xcaddy build v{{ waf_install_caddy_version }}
|
xcaddy build v{{ waf_install_caddy_version }}
|
||||||
{% if waf_install_cluster %} --with github.com/pteich/caddy-tlsconsul{% endif %}
|
--with github.com/pteich/caddy-tlsconsul
|
||||||
--with github.com/corazawaf/coraza-caddy/v2@v{{ waf_install_coraza_caddy_version }}
|
--with github.com/corazawaf/coraza-caddy/v2@v{{ waf_install_coraza_caddy_version }}
|
||||||
--with github.com/mholt/caddy-ratelimit
|
--with github.com/mholt/caddy-ratelimit
|
||||||
--with github.com/steffenbusch/caddy-bot-barrier
|
--with github.com/steffenbusch/caddy-bot-barrier
|
||||||
|
|||||||
23
ansible/roles/wordpress/defaults/main.yml
Normal file
23
ansible/roles/wordpress/defaults/main.yml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Required
|
||||||
|
wordpress_url: null
|
||||||
|
wordpress_site: null
|
||||||
|
wordpress_site_title: null
|
||||||
|
wordpress_admin_user: null
|
||||||
|
wordpress_admin_password: null
|
||||||
|
wordpress_admin_email: null
|
||||||
|
wordpress_db_pass: null
|
||||||
|
wordpress_themes: []
|
||||||
|
wordpress_active_theme: null
|
||||||
|
|
||||||
|
# Defaults
|
||||||
|
wordpress_user: "wordpress"
|
||||||
|
wordpress_plugins: []
|
||||||
|
wordpress_locale: "fr_FR"
|
||||||
|
wordpress_db_host: "localhost"
|
||||||
|
wordpress_db_name: "{{ wordpress_user }}"
|
||||||
|
wordpress_db_user: "{{ wordpress_user }}"
|
||||||
|
wordpress_db_prefix: "wp_"
|
||||||
|
wordpress_db_charset: "utf8mb4"
|
||||||
|
|
||||||
|
# Optionnal
|
||||||
|
wordpress_import_ocdi_indexes: []
|
||||||
17
ansible/roles/wordpress/handlers/main.yml
Normal file
17
ansible/roles/wordpress/handlers/main.yml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
- name: Restart Nginx
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: "nginx"
|
||||||
|
state: "restarted"
|
||||||
|
|
||||||
|
- name: Flush Wordpress
|
||||||
|
become: true
|
||||||
|
become_user: "{{ wordpress_user }}"
|
||||||
|
ansible.builtin.shell:
|
||||||
|
executable: "/usr/bin/bash"
|
||||||
|
chdir: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}"
|
||||||
|
cmd: |
|
||||||
|
set -o pipefail
|
||||||
|
wp cache flush
|
||||||
|
wp transient delete --all
|
||||||
|
wp rewrite flush --hard
|
||||||
|
changed_when: true
|
||||||
12
ansible/roles/wordpress/tasks/install_plugins.yml
Normal file
12
ansible/roles/wordpress/tasks/install_plugins.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
- name: Installation des plugins Wordpress
|
||||||
|
become: true
|
||||||
|
become_user: "{{ wordpress_user }}"
|
||||||
|
loop: "{{ wordpress_plugins_to_install }}"
|
||||||
|
ansible.builtin.command:
|
||||||
|
chdir: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}"
|
||||||
|
cmd: "wp plugin install {{ item }} --activate"
|
||||||
|
creates: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}/wp-content/plugins/{{ item | basename | splitext | first }}"
|
||||||
|
register: "wordpress_out_plugin_install"
|
||||||
|
changed_when: "'Success: Installed 1 of 1 plugins' in wordpress_out_plugin_install.stdout"
|
||||||
|
notify:
|
||||||
|
- "Flush Wordpress"
|
||||||
199
ansible/roles/wordpress/tasks/main.yml
Normal file
199
ansible/roles/wordpress/tasks/main.yml
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
- name: Installation des pré-requis
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name:
|
||||||
|
- "nginx"
|
||||||
|
- "php-fpm"
|
||||||
|
- "php-mysql"
|
||||||
|
- "php-curl"
|
||||||
|
- "php-json"
|
||||||
|
- "php-xml"
|
||||||
|
- "php-mbstring"
|
||||||
|
- "php-zip"
|
||||||
|
- "zip"
|
||||||
|
|
||||||
|
- name: Installation de wp-cli
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: "https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar"
|
||||||
|
dest: "/usr/local/bin/wp"
|
||||||
|
owner: "root"
|
||||||
|
group: "root"
|
||||||
|
mode: "755"
|
||||||
|
|
||||||
|
- name: Création de l'utilisateur système pour Wordpress
|
||||||
|
ansible.builtin.user:
|
||||||
|
name: "{{ wordpress_user | mandatory }}"
|
||||||
|
home: "/var/www/{{ wordpress_user }}"
|
||||||
|
group: "www-data"
|
||||||
|
shell: "/usr/bin/bash"
|
||||||
|
password_lock: true
|
||||||
|
create_home: true
|
||||||
|
|
||||||
|
- name: Création du répertoire de Wordpress
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}"
|
||||||
|
state: "directory"
|
||||||
|
owner: "{{ wordpress_user }}"
|
||||||
|
group: "www-data"
|
||||||
|
mode: "0750"
|
||||||
|
|
||||||
|
- name: Installation de Wordpress
|
||||||
|
become: true
|
||||||
|
become_user: "{{ wordpress_user }}"
|
||||||
|
ansible.builtin.command:
|
||||||
|
chdir: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}"
|
||||||
|
cmd: "wp core download --locale='{{ wordpress_locale }}' --skip-content"
|
||||||
|
creates: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}/wp-config-sample.php"
|
||||||
|
|
||||||
|
# TODO: https://make.wordpress.org/cli/handbook/references/config/#config-files
|
||||||
|
- name: Configuration de Wordpress
|
||||||
|
become: true
|
||||||
|
become_user: "{{ wordpress_user }}"
|
||||||
|
ansible.builtin.shell:
|
||||||
|
chdir: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}"
|
||||||
|
executable: "/usr/bin/bash"
|
||||||
|
cmd: |
|
||||||
|
set -o pipefail
|
||||||
|
wp config create \
|
||||||
|
--dbhost='{{ wordpress_db_host }}' \
|
||||||
|
--dbname='{{ wordpress_db_name }}' \
|
||||||
|
--dbuser='{{ wordpress_db_user }}' \
|
||||||
|
--dbpass='{{ wordpress_db_pass }}' \
|
||||||
|
--dbprefix='{{ wordpress_db_prefix }}' \
|
||||||
|
--dbcharset='{{ wordpress_db_charset }}' \
|
||||||
|
--extra-php <<PHP
|
||||||
|
define('FS_METHOD', 'direct');
|
||||||
|
define('FORCE_SSL_ADMIN', false);
|
||||||
|
define('WP_HOME', 'https://{{ wordpress_site }}');
|
||||||
|
define('WP_SITEURL', 'https://{{ wordpress_site }}');
|
||||||
|
|
||||||
|
error_reporting(0);
|
||||||
|
@ini_set('display_errors', 0);
|
||||||
|
define('DISALLOW_FILE_EDIT', true);
|
||||||
|
|
||||||
|
if (isset(\$_SERVER['HTTP_X_FORWARDED_PROTO']) && \$_SERVER['HTTP_X_FORWARDED_PROTO']=='https')
|
||||||
|
\$_SERVER['HTTPS'] = 'on';
|
||||||
|
PHP
|
||||||
|
creates: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}/wp-config.php"
|
||||||
|
notify:
|
||||||
|
- "Flush Wordpress"
|
||||||
|
|
||||||
|
- name: Create Wordpress database tables
|
||||||
|
become: true
|
||||||
|
become_user: "{{ wordpress_user }}"
|
||||||
|
run_once: true
|
||||||
|
ansible.builtin.command:
|
||||||
|
chdir: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}"
|
||||||
|
cmd: >
|
||||||
|
wp core install
|
||||||
|
--url='{{ wordpress_url }}'
|
||||||
|
--title='{{ wordpress_site_title }}'
|
||||||
|
--admin_user='{{ wordpress_admin_user }}'
|
||||||
|
--admin_password='{{ wordpress_admin_password }}'
|
||||||
|
--admin_email='{{ wordpress_admin_email }}'
|
||||||
|
--locale='{{ wordpress_locale }}'
|
||||||
|
--skip-email
|
||||||
|
creates: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}/wp-content/uploads/ }}"
|
||||||
|
register: "wordpress_out_core_install"
|
||||||
|
changed_when: "'Success: WordPress installed successfully.' in wordpress_out_core_install.stdout"
|
||||||
|
notify:
|
||||||
|
- "Flush Wordpress"
|
||||||
|
|
||||||
|
- name: Installation des thèmes Wordpress
|
||||||
|
become: true
|
||||||
|
become_user: "{{ wordpress_user }}"
|
||||||
|
loop: "{{ wordpress_themes }}"
|
||||||
|
ansible.builtin.command:
|
||||||
|
chdir: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}"
|
||||||
|
cmd: "wp theme install '{{ item }}'"
|
||||||
|
creates: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}/wp-content/themes/{{ item | basename | splitext | first }}"
|
||||||
|
register: "wordpress_out_theme_install"
|
||||||
|
changed_when: "'Success: Installed 1 of 1 themes' in wordpress_out_theme_install.stdout"
|
||||||
|
notify:
|
||||||
|
- "Flush Wordpress"
|
||||||
|
|
||||||
|
- name: Activation du thème Wordpress
|
||||||
|
become: true
|
||||||
|
become_user: "{{ wordpress_user }}"
|
||||||
|
ansible.builtin.command:
|
||||||
|
chdir: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}"
|
||||||
|
cmd: "wp theme activate '{{ wordpress_active_theme }}'"
|
||||||
|
register: "wordpress_out_theme_activate"
|
||||||
|
changed_when: "'Success: Switched to ' in wordpress_out_theme_activate.stdout"
|
||||||
|
notify:
|
||||||
|
- "Flush Wordpress"
|
||||||
|
|
||||||
|
- name: Installation des plugins wordpress-importer et one-click-demo-import
|
||||||
|
vars:
|
||||||
|
wordpress_plugins_to_install:
|
||||||
|
- "wordpress-importer"
|
||||||
|
- "one-click-demo-import"
|
||||||
|
ansible.builtin.include_tasks: "install_plugins.yml"
|
||||||
|
|
||||||
|
- name: Importation des plugins OCDI
|
||||||
|
become: true
|
||||||
|
become_user: "{{ wordpress_user }}"
|
||||||
|
loop: "{{ wordpress_import_ocdi_indexes }}"
|
||||||
|
ansible.builtin.shell:
|
||||||
|
chdir: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}"
|
||||||
|
executable: "/usr/bin/bash"
|
||||||
|
cmd: |
|
||||||
|
set -o pipefail
|
||||||
|
wp ocdi import --predefined='{{ item }}' \
|
||||||
|
&& touch /var/www/{{ wordpress_user }}/{{ wordpress_site }}/.ocdi_index{{ item }}_imported
|
||||||
|
creates: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}/.ocdi_index{{ item }}_imported"
|
||||||
|
register: "wordpress_out_ocdi_import"
|
||||||
|
changed_when: "'Success: Content import finished!' in wordpress_out_ocdi_import.stdout"
|
||||||
|
notify:
|
||||||
|
- "Flush Wordpress"
|
||||||
|
|
||||||
|
- name: Installation des plugins Wordpress
|
||||||
|
vars:
|
||||||
|
wordpress_plugins_to_install: "{{ wordpress_plugins }}"
|
||||||
|
ansible.builtin.include_tasks: "install_plugins.yml"
|
||||||
|
|
||||||
|
- name: Création du répertoire cache de Wordpress
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}/wp-content/cache"
|
||||||
|
state: "directory"
|
||||||
|
owner: "{{ wordpress_user }}"
|
||||||
|
group: "www-data"
|
||||||
|
mode: "0750"
|
||||||
|
|
||||||
|
- name: Apply permissions
|
||||||
|
loop:
|
||||||
|
- "uploads"
|
||||||
|
- "cache"
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "/var/www/{{ wordpress_user }}/{{ wordpress_site }}/wp-content/{{ item }}/"
|
||||||
|
owner: "{{ wordpress_user }}"
|
||||||
|
group: "www-data"
|
||||||
|
mode: "u=rwX,g=rwX,o=rX"
|
||||||
|
recurse: true
|
||||||
|
|
||||||
|
- name: Configuration de Nginx
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: "templates/nginx_vhost.conf"
|
||||||
|
dest: "/etc/nginx/sites-available/{{ wordpress_site }}.conf"
|
||||||
|
owner: "root"
|
||||||
|
group: "root"
|
||||||
|
mode: "0644"
|
||||||
|
notify:
|
||||||
|
- "Restart Nginx"
|
||||||
|
|
||||||
|
- name: Link Nginx vhost config
|
||||||
|
ansible.builtin.file:
|
||||||
|
src: "/etc/nginx/sites-available/{{ wordpress_site }}.conf"
|
||||||
|
dest: "/etc/nginx/sites-enabled/{{ wordpress_site }}.conf"
|
||||||
|
state: "link"
|
||||||
|
notify:
|
||||||
|
- "Restart Nginx"
|
||||||
|
|
||||||
|
- name: Unlink Nginx default vhost config
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "/etc/nginx/sites-enabled/default"
|
||||||
|
state: "absent"
|
||||||
|
notify:
|
||||||
|
- "Restart Nginx"
|
||||||
|
|
||||||
|
# FIXME: php. ini config
|
||||||
|
# Mainly: upload_max_filesize, post_max_size, max_execution_time
|
||||||
50
ansible/roles/wordpress/templates/nginx_vhost.conf
Normal file
50
ansible/roles/wordpress/templates/nginx_vhost.conf
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name {{ wordpress_site }} www.{{ wordpress_site }};
|
||||||
|
root /var/www/{{ wordpress_user }}/{{ wordpress_site }};
|
||||||
|
index index.html index.htm index.php;
|
||||||
|
client_max_body_size 50M;
|
||||||
|
|
||||||
|
location /nginx_status {
|
||||||
|
stub_status on;
|
||||||
|
access_log off;
|
||||||
|
allow 127.0.0.1;
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\.ht {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /wp-config\.php\$ {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /wp-content/uploads/.*\.php\$ {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ \.php$ {
|
||||||
|
include snippets/fastcgi-php.conf;
|
||||||
|
fastcgi_pass unix:/var/run/php/php-fpm.sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Réécritures pour WordPress
|
||||||
|
location ~ ^/index\.php$ { }
|
||||||
|
location / {
|
||||||
|
if (!-e $request_filename){
|
||||||
|
rewrite ^/(.*)$ /index.php last;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# location / {
|
||||||
|
# try_files \$uri \$uri/ /index.php?\$args;
|
||||||
|
# }
|
||||||
|
|
||||||
|
# location ~ \.php\$ {
|
||||||
|
# include snippets/fastcgi-php.conf;
|
||||||
|
# fastcgi_pass unix:/var/run/php/php-fpm.sock;
|
||||||
|
# fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
|
||||||
|
# }
|
||||||
|
}
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
- name: Install Apache 2
|
|
||||||
ansible.builtin.apt:
|
|
||||||
name:
|
|
||||||
- "apache2"
|
|
||||||
@@ -21,7 +21,11 @@ common:
|
|||||||
custom_cert: true
|
custom_cert: true
|
||||||
custom_cert_file: "/etc/ssl/certs/test.lab.crt"
|
custom_cert_file: "/etc/ssl/certs/test.lab.crt"
|
||||||
custom_key_file: "/etc/ssl/private/test.lab.key"
|
custom_key_file: "/etc/ssl/private/test.lab.key"
|
||||||
# exceptions:
|
plugins:
|
||||||
|
- "wordpress-rule-exclusions"
|
||||||
|
exceptions:
|
||||||
|
# Cf. https://github.com/owasp-modsecurity/ModSecurity/wiki/Reference-Manual-(v3.x)
|
||||||
# before_request:
|
# before_request:
|
||||||
# - 'SecRule REQUEST_URI "@beginsWith /?bin/sh" "id:1,nolog,ctl:ruleRemoveById=932160"'
|
# - 'SecRule REQUEST_FILENAME "@endsWith /wp-admin/admin-ajax.php" "id:1,nolog,ctl:ruleRemoveById=932260"'
|
||||||
# - 'SecRule REQUEST_URI "@beginsWith /?bin/sh" "id:2,nolog,ctl:ruleRemoveById=932250"'
|
after_response:
|
||||||
|
- 'SecRuleUpdateTargetById 932260 "!ARGS:tag-name"'
|
||||||
|
|||||||
@@ -1,13 +1,24 @@
|
|||||||
$ANSIBLE_VAULT;1.1;AES256
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
61643639653530613066383464333939383034643565386434616361653331656237303835653837
|
30356233646632316565363533613033376333306433626366623230633838356230363461383135
|
||||||
3431646133336265393237383366653364373039376365340a396438643530383965343865383837
|
3834363137353762316262363134643763353632306362640a613538306235633565356563613532
|
||||||
62636338366437356661356561356138383032353133396461643362323536303965353564666232
|
64643862613431313666363130376338356538323035346532636331316637646235346236333331
|
||||||
3735363038323139620a386364633933303139623436626337346535663737386264303036323866
|
6336313466313434640a316437343866343061383838383362633638363633363535363330333463
|
||||||
31303762336638313961383034386137376566383662323961336536303230303063396661333135
|
64616330333361643166663061306338616265316262646339663438306465623231336139653764
|
||||||
66646133333565656233303461356166303862303037663133383534613263383130316261643738
|
63353862303538636636306430333464383339316233326133623564613337306266353730616564
|
||||||
65656165346432636363393436646532643531393066346332326561656230316232383866643665
|
63366562626135626136663665353663323265346438346131363838383263663665653937393066
|
||||||
36626333656335626534333737633231396561633831653464383036323431616431306433623139
|
32353061396661346138386561633734353634363164663438656331383663323035613264653265
|
||||||
61346630663636313463376237353036343264613936366539393165623265656233303864386563
|
38646164633465363238623232393731633761393466326466653430316532633139636533613033
|
||||||
63386661343061373930383139376666353035373531386230306432663132653130633039616665
|
39303764363861353666356239306366633662643865663033383235336663623632373566383330
|
||||||
38383735326339353738383166623932316564636634343830326432613563306436666532313562
|
32363334653936393962633266666439623130616161373231313037343735616361303131383230
|
||||||
65326431346639636338
|
62333863313432353536363962623161383434363136316264663061343038633065316232313334
|
||||||
|
38653233356135623431343166356464666663636330373663303663326561363333633837303337
|
||||||
|
66616464393661653835636665353439353539393530623730656331316235643032336337616436
|
||||||
|
64373163373932653038613765633061306331363962333932383564333735353534646637303237
|
||||||
|
62376561303364386662343331373631343166326261636362623432323437643735663437333564
|
||||||
|
39323961626131616334323131393963323231626638393231326631306337306230666138323830
|
||||||
|
63373331636234646431666332633633353637313338353536663539303531663938336132356134
|
||||||
|
30323631646666396239323261366435656434623537343935393531633966643135633561633636
|
||||||
|
31626266366432613936643937646537636164646334316562396639363132313862616433653535
|
||||||
|
34666537653433663466323730373363363064393334386133366365383435616436623963626634
|
||||||
|
31376162306565623262663732356463616464376231356432316137373138356466393663653035
|
||||||
|
613336396164633836626637393036303336
|
||||||
|
|||||||
@@ -2,11 +2,8 @@ vms = {
|
|||||||
waf1 = {},
|
waf1 = {},
|
||||||
waf2 = {},
|
waf2 = {},
|
||||||
waf3 = {},
|
waf3 = {},
|
||||||
|
mysql1 = {},
|
||||||
www1 = {},
|
www1 = {},
|
||||||
www2 = {},
|
www2 = {},
|
||||||
www3 = {},
|
www3 = {},
|
||||||
proxy1 = {
|
|
||||||
memory = "8g"
|
|
||||||
disk = "20g"
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user