Premiers rôles ansible et leurs playbooks de déploiement (Phases 1 et 2)
This commit is contained in:
parent
51862096bb
commit
bb38f846cd
58 changed files with 4024 additions and 144 deletions
387
ansible/README-PHASE2.md
Normal file
387
ansible/README-PHASE2.md
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
# 🌲 Alliance Boréale - Phase 2 : Identité & Forge
|
||||
|
||||
Extension de l'infrastructure avec SSO (Keycloak) et Forge Git (Forgejo).
|
||||
|
||||
---
|
||||
|
||||
## 📋 SERVICES DÉPLOYÉS
|
||||
|
||||
| Service | VMID | FQDN | IP | Couche | Port(s) |
|
||||
|---------|------|------|----|----|---------|
|
||||
| **Keycloak** | 03010 | `sso.infra.chezlepro.ca` | 10.0.3.20 | C3 | 443 (HTTPS) |
|
||||
| **Forgejo** | 04021 | `git.infra.chezlepro.ca` | 10.0.1.20 | C4 | 443 (HTTPS), 2222 (SSH) |
|
||||
|
||||
---
|
||||
|
||||
## 🚀 DÉPLOIEMENT PHASE 2
|
||||
|
||||
### 1. Créer les VMs
|
||||
|
||||
```bash
|
||||
# VM Keycloak
|
||||
VMID: 03010
|
||||
RAM: 4GB (Keycloak = gourmand en Java)
|
||||
CPU: 2 cores
|
||||
Disk: 30GB
|
||||
OS: Ubuntu 22.04 LTS
|
||||
IP: 10.0.3.20
|
||||
|
||||
# VM Forgejo
|
||||
VMID: 04021
|
||||
RAM: 2GB
|
||||
CPU: 2 cores
|
||||
Disk: 50GB (repos Git)
|
||||
OS: Ubuntu 22.04 LTS
|
||||
IP: 10.0.1.20
|
||||
```
|
||||
|
||||
### 2. Mettre à jour l'inventaire
|
||||
|
||||
```bash
|
||||
# Copier le nouvel inventaire
|
||||
cp inventories/production/hosts-phase2.yml inventories/production/hosts.yml
|
||||
|
||||
# Éditer les IPs si nécessaire
|
||||
vi inventories/production/hosts.yml
|
||||
```
|
||||
|
||||
### 3. Générer les secrets Phase 2
|
||||
|
||||
```bash
|
||||
# Mots de passe
|
||||
openssl rand -base64 24 # Keycloak admin
|
||||
openssl rand -base64 24 # Keycloak DB
|
||||
openssl rand -base64 24 # Forgejo admin
|
||||
openssl rand -base64 24 # Forgejo DB
|
||||
|
||||
# Secrets Forgejo (64 chars hex)
|
||||
openssl rand -hex 64 # secret_key
|
||||
openssl rand -hex 64 # internal_token
|
||||
openssl rand -hex 64 # jwt_secret
|
||||
```
|
||||
|
||||
### 4. Mettre à jour le vault
|
||||
|
||||
```bash
|
||||
# Éditer vault
|
||||
ansible-vault edit vault/production.yml
|
||||
|
||||
# Ajouter les secrets Phase 2 (voir vault/production-phase2.yml.example)
|
||||
```
|
||||
|
||||
### 5. Déployer Keycloak d'abord
|
||||
|
||||
```bash
|
||||
# Déployer seulement Keycloak
|
||||
ansible-playbook playbooks/phase2-deploy.yml --limit idp_servers --ask-vault-pass
|
||||
```
|
||||
|
||||
### 6. Configurer Keycloak
|
||||
|
||||
```bash
|
||||
# Accéder à Keycloak
|
||||
https://sso.infra.chezlepro.ca
|
||||
|
||||
# Login admin (vault_keycloak_admin_password)
|
||||
|
||||
# Le realm "alliance-boreale" devrait être créé automatiquement
|
||||
# Aller dans: Realm "alliance-boreale" → Clients → "forgejo"
|
||||
```
|
||||
|
||||
### 7. Récupérer le Client Secret Forgejo
|
||||
|
||||
```
|
||||
Keycloak UI:
|
||||
1. Realm: alliance-boreale
|
||||
2. Clients → forgejo
|
||||
3. Credentials tab
|
||||
4. Copier "Client Secret"
|
||||
```
|
||||
|
||||
### 8. Ajouter le secret au vault
|
||||
|
||||
```bash
|
||||
ansible-vault edit vault/production.yml
|
||||
|
||||
# Ajouter:
|
||||
vault_keycloak_forgejo_client_secret: "<secret-copié-depuis-keycloak>"
|
||||
```
|
||||
|
||||
### 9. Déployer Forgejo
|
||||
|
||||
```bash
|
||||
# Déployer Forgejo avec SSO configuré
|
||||
ansible-playbook playbooks/phase2-deploy.yml --limit forge_servers --ask-vault-pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ VALIDATION POST-DÉPLOIEMENT
|
||||
|
||||
### Test Keycloak
|
||||
|
||||
```bash
|
||||
# Accès web
|
||||
curl -k https://sso.infra.chezlepro.ca/realms/alliance-boreale
|
||||
|
||||
# Devrait retourner du JSON avec config du realm
|
||||
```
|
||||
|
||||
### Test Forgejo
|
||||
|
||||
```bash
|
||||
# Accès web
|
||||
curl -k https://git.infra.chezlepro.ca
|
||||
|
||||
# Devrait retourner HTML de la page d'accueil
|
||||
|
||||
# Test SSH
|
||||
ssh -T -p 2222 git@git.infra.chezlepro.ca
|
||||
# Devrait afficher: "Hi there, You've successfully authenticated..."
|
||||
```
|
||||
|
||||
### Test SSO (Forgejo → Keycloak)
|
||||
|
||||
```
|
||||
1. Aller sur https://git.infra.chezlepro.ca
|
||||
2. Cliquer "Sign in with L'Alliance Boréale SSO"
|
||||
3. Devrait rediriger vers Keycloak
|
||||
4. Créer un compte ou se connecter
|
||||
5. Devrait rediriger vers Forgejo connecté
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 👥 GESTION UTILISATEURS
|
||||
|
||||
### Créer des utilisateurs dans Keycloak
|
||||
|
||||
```
|
||||
Keycloak UI:
|
||||
1. Realm: alliance-boreale
|
||||
2. Users → Add user
|
||||
3. Remplir: username, email, first name, last name
|
||||
4. Save
|
||||
5. Credentials tab → Set Password
|
||||
6. Temporary: OFF
|
||||
7. Save
|
||||
```
|
||||
|
||||
### Donner accès admin Forgejo
|
||||
|
||||
```
|
||||
SSH sur serveur Forgejo:
|
||||
ssh ansible@10.0.1.20
|
||||
|
||||
# Promouvoir utilisateur admin
|
||||
sudo -u git /opt/forgejo/forgejo admin user change-password \
|
||||
--username <username> \
|
||||
--password <new-password> \
|
||||
--config /etc/forgejo/app.ini
|
||||
|
||||
# Ou donner rôle admin
|
||||
sudo -u git /opt/forgejo/forgejo admin user create \
|
||||
--admin \
|
||||
--username <username> \
|
||||
--email <email> \
|
||||
--password <password> \
|
||||
--config /etc/forgejo/app.ini
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📦 PREMIERS DÉPÔTS
|
||||
|
||||
### Créer organisation "alliance-boreale"
|
||||
|
||||
```
|
||||
Forgejo UI:
|
||||
1. Sign in via SSO
|
||||
2. + → New Organization
|
||||
3. Name: alliance-boreale
|
||||
4. Visibility: Public
|
||||
5. Create
|
||||
```
|
||||
|
||||
### Créer dépôts essentiels
|
||||
|
||||
```
|
||||
Dans l'organisation "alliance-boreale":
|
||||
|
||||
1. ansible-infrastructure
|
||||
Description: Infrastructure as Code
|
||||
Private: No
|
||||
Initialize: Yes (README.md)
|
||||
|
||||
2. documentation
|
||||
Description: Documentation technique
|
||||
Private: No
|
||||
|
||||
3. registraire
|
||||
Description: Registraire membres (Document 14)
|
||||
Private: No
|
||||
```
|
||||
|
||||
### Pousser ce dépôt Ansible
|
||||
|
||||
```bash
|
||||
cd /chemin/vers/ansible
|
||||
|
||||
# Ajouter remote Forgejo
|
||||
git remote add forgejo git@git.infra.chezlepro.ca:2222/alliance-boreale/ansible-infrastructure.git
|
||||
|
||||
# Pousser
|
||||
git push -u forgejo main
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 INTÉGRATION CONTINUE (CI/CD)
|
||||
|
||||
Forgejo Actions (GitHub Actions compatible) est activé.
|
||||
|
||||
### Exemple workflow `.forgejo/workflows/lint.yml`
|
||||
|
||||
```yaml
|
||||
name: Ansible Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install Ansible
|
||||
run: |
|
||||
pip3 install ansible ansible-lint
|
||||
|
||||
- name: Run ansible-lint
|
||||
run: |
|
||||
ansible-lint playbooks/*.yml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 SÉCURITÉ
|
||||
|
||||
### Certificats SSL
|
||||
|
||||
Phase 2 utilise des **certificats auto-signés** par défaut.
|
||||
|
||||
**Pour production** : Utiliser Let's Encrypt
|
||||
|
||||
```yaml
|
||||
# Dans group_vars/idp_servers.yml et forge_servers.yml
|
||||
nginx:
|
||||
ssl:
|
||||
cert_source: letsencrypt
|
||||
letsencrypt_email: admin@chezlepro.ca
|
||||
```
|
||||
|
||||
Puis redéployer :
|
||||
```bash
|
||||
ansible-playbook playbooks/phase2-deploy.yml --tags nginx
|
||||
```
|
||||
|
||||
### Firewall
|
||||
|
||||
Les règles nftables sont automatiquement configurées :
|
||||
|
||||
```bash
|
||||
# Vérifier sur les serveurs
|
||||
sudo nft list ruleset | grep -A 10 "Keycloak\|Forgejo"
|
||||
```
|
||||
|
||||
### Backups PostgreSQL
|
||||
|
||||
Configurés automatiquement :
|
||||
- **Keycloak DB** : Backup quotidien à 3h, rétention 14 jours
|
||||
- **Forgejo DB** : Backup quotidien à 4h, rétention 14 jours
|
||||
|
||||
Vérifier :
|
||||
```bash
|
||||
ls -lh /var/backups/postgresql/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐛 DÉPANNAGE
|
||||
|
||||
### Keycloak ne démarre pas
|
||||
|
||||
```bash
|
||||
# Logs
|
||||
sudo journalctl -u keycloak -n 100 --no-pager
|
||||
|
||||
# Vérifier Java
|
||||
java -version # Devrait être OpenJDK 17
|
||||
|
||||
# Vérifier PostgreSQL
|
||||
sudo -u postgres psql keycloak -c "SELECT version();"
|
||||
|
||||
# Rebuild Keycloak
|
||||
sudo -u keycloak /opt/keycloak/bin/kc.sh build
|
||||
sudo systemctl restart keycloak
|
||||
```
|
||||
|
||||
### Forgejo ne se connecte pas à Keycloak
|
||||
|
||||
```bash
|
||||
# Vérifier logs Forgejo
|
||||
sudo journalctl -u forgejo -n 100 --no-pager
|
||||
|
||||
# Tester découverte OpenID
|
||||
curl -k https://sso.infra.chezlepro.ca/realms/alliance-boreale/.well-known/openid-configuration
|
||||
|
||||
# Vérifier client secret dans app.ini
|
||||
sudo grep -A 5 "oauth2.alliance-boreale" /etc/forgejo/app.ini
|
||||
```
|
||||
|
||||
### SSH Forgejo ne fonctionne pas
|
||||
|
||||
```bash
|
||||
# Vérifier port SSH Forgejo
|
||||
sudo ss -tulnp | grep 2222
|
||||
|
||||
# Tester localement
|
||||
ssh -T -p 2222 git@localhost
|
||||
|
||||
# Vérifier firewall
|
||||
sudo nft list ruleset | grep 2222
|
||||
|
||||
# Logs SSH Forgejo
|
||||
sudo journalctl -u forgejo -f | grep SSH
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 RÉFÉRENCES
|
||||
|
||||
- [Keycloak Documentation](https://www.keycloak.org/documentation)
|
||||
- [Forgejo Documentation](https://forgejo.org/docs/)
|
||||
- [Forgejo Actions](https://forgejo.org/docs/latest/user/actions/)
|
||||
- [OpenID Connect](https://openid.net/connect/)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 PROCHAINE ÉTAPE : PHASE 3
|
||||
|
||||
**Phase 3 : FastAPI Pivot (C5)**
|
||||
|
||||
Services à déployer :
|
||||
- FastAPI Admin Portal
|
||||
- API Gateway
|
||||
- Tenant provisioning
|
||||
- Monitoring dashboard
|
||||
|
||||
---
|
||||
|
||||
**Version:** 1.0
|
||||
**Date:** 2025-10-31
|
||||
**Membre:** Chezlepro Inc.
|
||||
**Phases:** 1 (DNS) + 2 (IdP + Forge)
|
||||
411
ansible/README.md
Normal file
411
ansible/README.md
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
# 🌲 Alliance Boréale - Ansible Automation
|
||||
|
||||
Infrastructure as Code pour L'Alliance Boréale - Membre: **Chezlepro Inc.**
|
||||
|
||||
## 📋 Table des matières
|
||||
|
||||
- [Prérequis](#prérequis)
|
||||
- [Structure du projet](#structure-du-projet)
|
||||
- [Configuration initiale](#configuration-initiale)
|
||||
- [Phase 1 : DNS Fédéré](#phase-1--dns-fédéré)
|
||||
- [Gestion des secrets](#gestion-des-secrets)
|
||||
- [Commandes utiles](#commandes-utiles)
|
||||
- [Dépannage](#dépannage)
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Prérequis
|
||||
|
||||
### Sur votre machine de contrôle
|
||||
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
sudo apt update
|
||||
sudo apt install ansible python3-pip sshpass
|
||||
|
||||
# OU via pip (recommandé pour dernière version)
|
||||
pip3 install ansible ansible-lint
|
||||
|
||||
# Collections Ansible requises
|
||||
ansible-galaxy collection install community.general
|
||||
ansible-galaxy collection install community.postgresql
|
||||
ansible-galaxy collection install ansible.posix
|
||||
```
|
||||
|
||||
### Sur les serveurs cibles (VMs)
|
||||
|
||||
- Ubuntu 22.04 LTS ou Debian 12
|
||||
- Accès root temporaire (sera désactivé après)
|
||||
- Connexion SSH fonctionnelle
|
||||
|
||||
---
|
||||
|
||||
## 📁 Structure du projet
|
||||
|
||||
```
|
||||
ansible/
|
||||
├── ansible.cfg # Configuration Ansible
|
||||
├── README.md # Ce fichier
|
||||
│
|
||||
├── inventories/
|
||||
│ └── production/
|
||||
│ ├── hosts.yml # Inventaire des serveurs
|
||||
│ └── group_vars/
|
||||
│ ├── all.yml # Variables globales
|
||||
│ └── dns_servers.yml # Variables DNS
|
||||
│
|
||||
├── roles/
|
||||
│ ├── common/ # Hardening de base
|
||||
│ ├── postgresql/ # Base de données
|
||||
│ └── powerdns-authoritative/ # DNS autoritaire
|
||||
│
|
||||
├── playbooks/
|
||||
│ └── phase1-dns-deploy.yml # Déploiement Phase 1
|
||||
│
|
||||
└── vault/
|
||||
└── production.yml # Secrets chiffrés (à créer)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Configuration initiale
|
||||
|
||||
### 1. Générer clés SSH
|
||||
|
||||
```bash
|
||||
# Générer une paire de clés pour Ansible
|
||||
ssh-keygen -t ed25519 -f ~/.ssh/alliance_boreale_ed25519 -C "ansible@chezlepro.ca"
|
||||
|
||||
# Copier la clé publique sur les serveurs (temporairement en root)
|
||||
ssh-copy-id -i ~/.ssh/alliance_boreale_ed25519.pub root@10.0.2.10
|
||||
ssh-copy-id -i ~/.ssh/alliance_boreale_ed25519.pub root@10.0.2.11
|
||||
```
|
||||
|
||||
### 2. Configurer l'inventaire
|
||||
|
||||
Éditer `inventories/production/hosts.yml` :
|
||||
|
||||
```yaml
|
||||
# Remplacer les IPs par vos IPs publiques/internes
|
||||
ns1.infra.chezlepro.ca:
|
||||
ansible_host: <IP_PUBLIQUE_NS1> # ← MODIFIER ICI
|
||||
|
||||
ns2.infra.chezlepro.ca:
|
||||
ansible_host: <IP_PUBLIQUE_NS2> # ← MODIFIER ICI
|
||||
```
|
||||
|
||||
Éditer `inventories/production/group_vars/all.yml` :
|
||||
|
||||
```yaml
|
||||
# Ajouter vos clés SSH publiques
|
||||
ssh:
|
||||
authorized_keys:
|
||||
- "ssh-ed25519 AAAAC3... votre-email@example.com" # ← AJOUTER ICI
|
||||
```
|
||||
|
||||
### 3. Créer le vault de secrets
|
||||
|
||||
```bash
|
||||
# Créer un mot de passe pour le vault
|
||||
ansible-vault create vault/production.yml
|
||||
```
|
||||
|
||||
Contenu du vault :
|
||||
|
||||
```yaml
|
||||
---
|
||||
# Secrets pour Phase 1
|
||||
vault_pdns_api_key: "changeme-api-key-secure-random"
|
||||
vault_postgresql_pdns_password: "changeme-db-password-secure-random"
|
||||
```
|
||||
|
||||
**Sauvegarder le mot de passe vault dans un gestionnaire de mots de passe !**
|
||||
|
||||
### 4. Tester la connectivité
|
||||
|
||||
```bash
|
||||
# Ping tous les hôtes
|
||||
ansible all -m ping
|
||||
|
||||
# Devrait afficher:
|
||||
# ns1.infra.chezlepro.ca | SUCCESS => { "ping": "pong" }
|
||||
# ns2.infra.chezlepro.ca | SUCCESS => { "ping": "pong" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Phase 1 : DNS Fédéré
|
||||
|
||||
### Déploiement complet
|
||||
|
||||
```bash
|
||||
# Déployer Phase 1 complète
|
||||
ansible-playbook playbooks/phase1-dns-deploy.yml --ask-vault-pass
|
||||
|
||||
# OU avec fichier mot de passe
|
||||
echo "votre-mot-de-passe-vault" > .vault_pass
|
||||
chmod 600 .vault_pass
|
||||
ansible-playbook playbooks/phase1-dns-deploy.yml --vault-password-file .vault_pass
|
||||
```
|
||||
|
||||
### Déploiement par étapes (tags)
|
||||
|
||||
```bash
|
||||
# Seulement hardening (common)
|
||||
ansible-playbook playbooks/phase1-dns-deploy.yml --tags common
|
||||
|
||||
# Seulement PostgreSQL
|
||||
ansible-playbook playbooks/phase1-dns-deploy.yml --tags postgresql
|
||||
|
||||
# Seulement PowerDNS
|
||||
ansible-playbook playbooks/phase1-dns-deploy.yml --tags powerdns
|
||||
|
||||
# Mode dry-run (vérifier sans appliquer)
|
||||
ansible-playbook playbooks/phase1-dns-deploy.yml --check --diff
|
||||
```
|
||||
|
||||
### Déploiement sur un seul serveur
|
||||
|
||||
```bash
|
||||
# Seulement ns1
|
||||
ansible-playbook playbooks/phase1-dns-deploy.yml --limit ns1.infra.chezlepro.ca
|
||||
|
||||
# Seulement ns2
|
||||
ansible-playbook playbooks/phase1-dns-deploy.yml --limit ns2.infra.chezlepro.ca
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Gestion des secrets
|
||||
|
||||
### Éditer le vault
|
||||
|
||||
```bash
|
||||
# Éditer secrets
|
||||
ansible-vault edit vault/production.yml
|
||||
|
||||
# Voir contenu (sans éditer)
|
||||
ansible-vault view vault/production.yml
|
||||
|
||||
# Changer mot de passe vault
|
||||
ansible-vault rekey vault/production.yml
|
||||
```
|
||||
|
||||
### Générer mots de passe sécurisés
|
||||
|
||||
```bash
|
||||
# API Key PowerDNS (32 caractères alphanumériques)
|
||||
openssl rand -base64 32
|
||||
|
||||
# Mot de passe PostgreSQL
|
||||
openssl rand -base64 24
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Validation post-déploiement
|
||||
|
||||
### Tests DNS
|
||||
|
||||
```bash
|
||||
# Résolution SOA
|
||||
dig @10.0.2.10 chezlepro.ca SOA +short
|
||||
|
||||
# Résolution NS
|
||||
dig @10.0.2.10 chezlepro.ca NS +short
|
||||
|
||||
# Test DNSSEC
|
||||
dig @10.0.2.10 chezlepro.ca DNSKEY +dnssec
|
||||
|
||||
# Test depuis Internet (après propagation)
|
||||
dig @8.8.8.8 chezlepro.ca SOA +short
|
||||
```
|
||||
|
||||
### Export DS Records (DNSSEC)
|
||||
|
||||
```bash
|
||||
# Se connecter au DNS master
|
||||
ssh ansible@10.0.2.10
|
||||
|
||||
# Exporter DS records
|
||||
sudo pdnsutil show-zone chezlepro.ca | grep DS
|
||||
|
||||
# Exemple output:
|
||||
# chezlepro.ca IN DS 12345 13 2 abc123...
|
||||
```
|
||||
|
||||
**Ajouter ces DS records chez votre registraire de domaine !**
|
||||
|
||||
### Validation DNSSEC publique
|
||||
|
||||
```bash
|
||||
# Avec DNSViz (après ajout DS records)
|
||||
# https://dnsviz.net/d/chezlepro.ca/dnssec/
|
||||
|
||||
# Ou en ligne de commande
|
||||
delv @8.8.8.8 chezlepro.ca SOA
|
||||
|
||||
# Devrait afficher: "fully validated"
|
||||
```
|
||||
|
||||
### Vérifier services
|
||||
|
||||
```bash
|
||||
# SSH sur serveur
|
||||
ssh ansible@10.0.2.10
|
||||
|
||||
# Status PowerDNS
|
||||
sudo systemctl status pdns
|
||||
|
||||
# Status PostgreSQL
|
||||
sudo systemctl status postgresql
|
||||
|
||||
# Logs PowerDNS
|
||||
sudo journalctl -u pdns -f
|
||||
|
||||
# Firewall rules
|
||||
sudo nft list ruleset
|
||||
|
||||
# API PowerDNS (depuis le serveur)
|
||||
curl -H "X-API-Key: <votre-api-key>" http://127.0.0.1:8081/api/v1/servers/localhost
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Commandes utiles
|
||||
|
||||
### Ansible ad-hoc
|
||||
|
||||
```bash
|
||||
# Mettre à jour tous les packages
|
||||
ansible all -b -m apt -a "upgrade=dist update_cache=yes"
|
||||
|
||||
# Redémarrer un service
|
||||
ansible dns_servers -b -m systemd -a "name=pdns state=restarted"
|
||||
|
||||
# Collecter facts
|
||||
ansible all -m setup
|
||||
|
||||
# Exécuter commande shell
|
||||
ansible all -b -a "uptime"
|
||||
```
|
||||
|
||||
### Gestion PowerDNS
|
||||
|
||||
```bash
|
||||
# Lister zones
|
||||
pdnsutil list-all-zones
|
||||
|
||||
# Ajouter un record
|
||||
pdnsutil add-record chezlepro.ca www A 203.0.113.10 3600
|
||||
|
||||
# Supprimer un record
|
||||
pdnsutil delete-rrset chezlepro.ca www A
|
||||
|
||||
# Rectifier zone (après modifications)
|
||||
pdnsutil rectify-zone chezlepro.ca
|
||||
|
||||
# Augmenter serial SOA
|
||||
pdnsutil increase-serial chezlepro.ca
|
||||
|
||||
# Vérifier zone
|
||||
pdnsutil check-zone chezlepro.ca
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Dépannage
|
||||
|
||||
### Erreur: "Failed to connect to the host"
|
||||
|
||||
```bash
|
||||
# Vérifier connectivité
|
||||
ping 10.0.2.10
|
||||
|
||||
# Vérifier SSH manuel
|
||||
ssh -i ~/.ssh/alliance_boreale_ed25519 ansible@10.0.2.10
|
||||
|
||||
# Vérifier port SSH
|
||||
nmap -p 22 10.0.2.10
|
||||
```
|
||||
|
||||
### Erreur: "Permission denied (publickey)"
|
||||
|
||||
```bash
|
||||
# Vérifier que la clé est chargée
|
||||
ssh-add -l
|
||||
|
||||
# Ajouter la clé si nécessaire
|
||||
ssh-add ~/.ssh/alliance_boreale_ed25519
|
||||
|
||||
# Vérifier authorized_keys sur le serveur
|
||||
ssh root@10.0.2.10 "cat /home/ansible/.ssh/authorized_keys"
|
||||
```
|
||||
|
||||
### Erreur: "Vault password required"
|
||||
|
||||
```bash
|
||||
# Option 1: Demander interactivement
|
||||
--ask-vault-pass
|
||||
|
||||
# Option 2: Fichier
|
||||
--vault-password-file .vault_pass
|
||||
|
||||
# Option 3: Variable d'environnement
|
||||
export ANSIBLE_VAULT_PASSWORD_FILE=.vault_pass
|
||||
```
|
||||
|
||||
### PowerDNS ne démarre pas
|
||||
|
||||
```bash
|
||||
# Voir logs détaillés
|
||||
sudo journalctl -u pdns -n 100 --no-pager
|
||||
|
||||
# Vérifier config
|
||||
sudo pdns_server --config-check
|
||||
|
||||
# Tester connexion PostgreSQL
|
||||
sudo -u postgres psql powerdns -c "SELECT * FROM domains;"
|
||||
```
|
||||
|
||||
### DNS ne répond pas
|
||||
|
||||
```bash
|
||||
# Vérifier que le port est ouvert
|
||||
sudo ss -tulnp | grep :53
|
||||
|
||||
# Vérifier firewall
|
||||
sudo nft list ruleset | grep "53"
|
||||
|
||||
# Tester en local d'abord
|
||||
dig @127.0.0.1 chezlepro.ca SOA
|
||||
|
||||
# Ensuite depuis l'extérieur
|
||||
dig @<IP_PUBLIQUE> chezlepro.ca SOA
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentation de référence
|
||||
|
||||
- [Nomenclature v4 (CRB-2)](../00_Nomenclature_biomimetique_autopoietique_v4_CRB2.md)
|
||||
- [Document 05: Opération DNS Fédérée](/mnt/project/05_Operation_DNS_Federee.md)
|
||||
- [Document 14: Structure YAML Registraire](/mnt/project/14_Structure_YAML_Registraire.md)
|
||||
- [PowerDNS Documentation](https://doc.powerdns.com/authoritative/)
|
||||
- [Ansible Documentation](https://docs.ansible.com/)
|
||||
|
||||
---
|
||||
|
||||
## 🆘 Support
|
||||
|
||||
Pour questions ou problèmes :
|
||||
1. Consulter les logs : `sudo journalctl -u pdns -f`
|
||||
2. Vérifier la documentation dans `/mnt/project/`
|
||||
3. Contacter Cercle Opérationnel via Matrix
|
||||
|
||||
---
|
||||
|
||||
**Version:** 1.0
|
||||
**Date:** 2025-10-31
|
||||
**Membre:** Chezlepro Inc.
|
||||
**Phase:** 1 (DNS Fédéré - C1-C2)
|
||||
|
|
@ -1,7 +1,59 @@
|
|||
# Alliance Boréale - Ansible Configuration
|
||||
# Version: 1.0
|
||||
# Date: 2025-10-31
|
||||
|
||||
[defaults]
|
||||
inventory = inventories/production/hosts
|
||||
remote_user = ansible
|
||||
# Inventaire
|
||||
inventory = inventories/production/hosts.yml
|
||||
|
||||
# Comportement
|
||||
host_key_checking = False
|
||||
roles_path = roles
|
||||
vault_password_file = ~/.alliance-vault-pass.txt
|
||||
retry_files_enabled = False
|
||||
gathering = smart
|
||||
fact_caching = jsonfile
|
||||
fact_caching_connection = /tmp/ansible_facts
|
||||
fact_caching_timeout = 3600
|
||||
|
||||
# Output
|
||||
stdout_callback = yaml
|
||||
bin_ansible_callbacks = True
|
||||
display_skipped_hosts = False
|
||||
display_ok_hosts = True
|
||||
|
||||
# Performance
|
||||
forks = 10
|
||||
poll_interval = 5
|
||||
timeout = 30
|
||||
|
||||
# Privilèges
|
||||
become = True
|
||||
become_method = sudo
|
||||
become_user = root
|
||||
become_ask_pass = False
|
||||
|
||||
# SSH
|
||||
remote_user = ansible
|
||||
private_key_file = ~/.ssh/alliance_boreale_ed25519
|
||||
|
||||
# Logs
|
||||
log_path = /var/log/ansible/ansible.log
|
||||
|
||||
# Roles
|
||||
roles_path = roles
|
||||
|
||||
# Collections
|
||||
collections_paths = ~/.ansible/collections:/usr/share/ansible/collections
|
||||
|
||||
[privilege_escalation]
|
||||
become = True
|
||||
become_method = sudo
|
||||
become_user = root
|
||||
become_ask_pass = False
|
||||
|
||||
[ssh_connection]
|
||||
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no
|
||||
pipelining = True
|
||||
control_path = /tmp/ansible-ssh-%%h-%%p-%%r
|
||||
|
||||
[inventory]
|
||||
enable_plugins = yaml, ini, host_list
|
||||
|
|
|
|||
|
|
@ -1,9 +1,113 @@
|
|||
---
|
||||
# Standards Alliance Boréale
|
||||
offline_mode: false
|
||||
alliance_domain: alliance-boreale.org
|
||||
admin_email: admin@alliance-boreale.org
|
||||
# Alliance Boréale - Variables Globales
|
||||
# Membre: Chezlepro Inc.
|
||||
# Date: 2025-10-31
|
||||
|
||||
# NetBox (exemple)
|
||||
netbox_domain: "{{ inventory_hostname }}.{{ alliance_domain }}"
|
||||
letsencrypt_email: "{{ admin_email }}"
|
||||
---
|
||||
# ==========================================
|
||||
# IDENTIFICATION MEMBRE
|
||||
# ==========================================
|
||||
member:
|
||||
id: m001
|
||||
slug: clp
|
||||
legal_name: "Chezlepro Inc."
|
||||
domain_primary: chezlepro.ca
|
||||
|
||||
# ==========================================
|
||||
# RÉSEAU
|
||||
# ==========================================
|
||||
network:
|
||||
internal_subnet: 10.0.0.0/8
|
||||
subnets:
|
||||
management: 10.0.0.0/24
|
||||
dns: 10.0.2.0/24
|
||||
governance: 10.0.3.0/24
|
||||
forge: 10.0.1.0/24
|
||||
pivot: 10.0.4.0/24
|
||||
tenants: 10.0.10.0/23
|
||||
|
||||
# ==========================================
|
||||
# SYSTÈME
|
||||
# ==========================================
|
||||
system:
|
||||
timezone: America/Toronto
|
||||
locale: en_CA.UTF-8
|
||||
|
||||
ntp:
|
||||
enabled: true
|
||||
servers:
|
||||
- 0.ca.pool.ntp.org
|
||||
- 1.ca.pool.ntp.org
|
||||
- 2.ca.pool.ntp.org
|
||||
- 3.ca.pool.ntp.org
|
||||
|
||||
# ==========================================
|
||||
# SSH
|
||||
# ==========================================
|
||||
ssh:
|
||||
port: 22
|
||||
permit_root_login: false
|
||||
password_authentication: false
|
||||
pubkey_authentication: true
|
||||
authorized_keys:
|
||||
# Remplacer par vos clés SSH publiques
|
||||
- "ssh-ed25519 AAAAC3... admin@chezlepro.ca"
|
||||
|
||||
# ==========================================
|
||||
# UTILISATEURS
|
||||
# ==========================================
|
||||
users:
|
||||
ansible:
|
||||
name: ansible
|
||||
groups: [sudo]
|
||||
shell: /bin/bash
|
||||
sudo_nopasswd: true
|
||||
|
||||
# ==========================================
|
||||
# SÉCURITÉ
|
||||
# ==========================================
|
||||
security:
|
||||
firewall: nftables
|
||||
fail2ban_enabled: true
|
||||
unattended_upgrades: true
|
||||
|
||||
firewall:
|
||||
default_policy: drop
|
||||
allowed_ssh_sources:
|
||||
- 0.0.0.0/0 # À restreindre en production
|
||||
|
||||
# ==========================================
|
||||
# PACKAGES DE BASE
|
||||
# ==========================================
|
||||
base_packages:
|
||||
- vim
|
||||
- htop
|
||||
- curl
|
||||
- wget
|
||||
- git
|
||||
- tree
|
||||
- tmux
|
||||
- python3
|
||||
- python3-pip
|
||||
- apt-transport-https
|
||||
- ca-certificates
|
||||
- gnupg
|
||||
- lsb-release
|
||||
- net-tools
|
||||
- dnsutils
|
||||
- tcpdump
|
||||
|
||||
# ==========================================
|
||||
# MONITORING (Phase 2)
|
||||
# ==========================================
|
||||
monitoring:
|
||||
enabled: false
|
||||
prometheus_enabled: false
|
||||
node_exporter_enabled: false
|
||||
|
||||
# ==========================================
|
||||
# LOGS
|
||||
# ==========================================
|
||||
logging:
|
||||
central_enabled: false
|
||||
syslog_server: null
|
||||
retention_days: 30
|
||||
|
|
|
|||
165
ansible/inventories/production/group_vars/dns_servers.yml
Normal file
165
ansible/inventories/production/group_vars/dns_servers.yml
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
# Alliance Boréale - Variables DNS Servers
|
||||
# Membre: Chezlepro Inc.
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
# ==========================================
|
||||
# POWERDNS CONFIGURATION
|
||||
# ==========================================
|
||||
powerdns:
|
||||
version: "4.8"
|
||||
backend: pgsql
|
||||
|
||||
# API Configuration
|
||||
api:
|
||||
enabled: true
|
||||
key: "{{ vault_pdns_api_key }}" # À définir dans vault
|
||||
|
||||
webserver:
|
||||
enabled: true
|
||||
address: 0.0.0.0
|
||||
port: 8081
|
||||
allow_from: 10.0.0.0/8
|
||||
|
||||
# DNSSEC
|
||||
dnssec:
|
||||
enabled: true
|
||||
algorithm: ECDSAP256SHA256 # Algorithm 13
|
||||
|
||||
# Logging
|
||||
logging:
|
||||
level: 4 # Info
|
||||
queries: false
|
||||
details: true
|
||||
|
||||
# Performance
|
||||
cache_ttl: 20
|
||||
negquery_cache_ttl: 60
|
||||
query_cache_ttl: 20
|
||||
|
||||
# ==========================================
|
||||
# ZONES DNS
|
||||
# ==========================================
|
||||
dns_zones:
|
||||
- name: chezlepro.ca
|
||||
type: native
|
||||
soa:
|
||||
nameserver: ns1.infra.chezlepro.ca
|
||||
email: admin.chezlepro.ca
|
||||
serial: 2025103101 # YYYYMMDDNN
|
||||
refresh: 3600
|
||||
retry: 1800
|
||||
expire: 1209600
|
||||
minimum: 3600
|
||||
|
||||
# Nameservers
|
||||
ns_records:
|
||||
- ns1.infra.chezlepro.ca
|
||||
- ns2.infra.chezlepro.ca
|
||||
# - ns1.infra.nuagelibre.ca # Futur: secondaire croisé
|
||||
|
||||
# Records de base
|
||||
records:
|
||||
# DNS Servers
|
||||
- name: ns1.infra
|
||||
type: A
|
||||
content: 10.0.2.10
|
||||
ttl: 3600
|
||||
|
||||
- name: ns2.infra
|
||||
type: A
|
||||
content: 10.0.2.11
|
||||
ttl: 3600
|
||||
|
||||
# Wildcards (optionnel)
|
||||
# - name: "*.infra"
|
||||
# type: A
|
||||
# content: 10.0.0.1
|
||||
# ttl: 3600
|
||||
|
||||
# ==========================================
|
||||
# AXFR CONFIGURATION
|
||||
# ==========================================
|
||||
axfr:
|
||||
# IPs autorisées pour AXFR
|
||||
allow_from:
|
||||
- 10.0.2.11 # ns2.infra.chezlepro.ca (slave local)
|
||||
# - <IP_PUBLIQUE_NUL> # Futur: ns1.infra.nuagelibre.ca
|
||||
# - <IP_PUBLIQUE_TLI> # Futur: ns1.infra.technolibre.ca
|
||||
|
||||
# TSIG Keys (optionnel, recommandé)
|
||||
tsig_enabled: false
|
||||
tsig_keys: []
|
||||
# - name: clp-nul-xfer
|
||||
# algorithm: hmac-sha256
|
||||
# secret: "{{ vault_tsig_clp_nul }}"
|
||||
|
||||
# ==========================================
|
||||
# NOTIFY CONFIGURATION
|
||||
# ==========================================
|
||||
notify:
|
||||
also_notify:
|
||||
- 10.0.2.11 # ns2.infra.chezlepro.ca
|
||||
# - <IP_PUBLIQUE_NUL> # Futur
|
||||
# - <IP_PUBLIQUE_TLI> # Futur
|
||||
|
||||
# ==========================================
|
||||
# POSTGRESQL CONFIGURATION
|
||||
# ==========================================
|
||||
postgresql:
|
||||
version: 15
|
||||
listen_addresses: localhost
|
||||
port: 5432
|
||||
|
||||
# Database pour PowerDNS
|
||||
databases:
|
||||
- name: powerdns
|
||||
owner: pdns
|
||||
encoding: UTF8
|
||||
lc_collate: en_US.UTF-8
|
||||
lc_ctype: en_US.UTF-8
|
||||
|
||||
users:
|
||||
- name: pdns
|
||||
password: "{{ vault_postgresql_pdns_password }}"
|
||||
# role_attr_flags: CREATEDB # Optionnel
|
||||
|
||||
# Tuning basique
|
||||
shared_buffers: 256MB
|
||||
work_mem: 16MB
|
||||
maintenance_work_mem: 128MB
|
||||
effective_cache_size: 1GB
|
||||
|
||||
# Backups
|
||||
backup:
|
||||
enabled: true
|
||||
schedule: "0 2 * * *" # 2h du matin
|
||||
retention_days: 7
|
||||
destination: /var/backups/postgresql
|
||||
|
||||
# ==========================================
|
||||
# FIREWALL RULES (nftables)
|
||||
# ==========================================
|
||||
firewall_rules:
|
||||
dns:
|
||||
- port: 53
|
||||
proto: udp
|
||||
source: 0.0.0.0/0
|
||||
comment: "DNS queries (UDP)"
|
||||
|
||||
- port: 53
|
||||
proto: tcp
|
||||
source: 0.0.0.0/0
|
||||
comment: "DNS queries (TCP)"
|
||||
|
||||
pdns_api:
|
||||
- port: 8081
|
||||
proto: tcp
|
||||
source: 10.0.0.0/8
|
||||
comment: "PowerDNS API (internal only)"
|
||||
|
||||
postgresql:
|
||||
- port: 5432
|
||||
proto: tcp
|
||||
source: 127.0.0.1/32
|
||||
comment: "PostgreSQL (localhost only)"
|
||||
168
ansible/inventories/production/group_vars/forge_servers.yml
Normal file
168
ansible/inventories/production/group_vars/forge_servers.yml
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
# Alliance Boréale - Variables Forgejo (Forge)
|
||||
# Membre: Chezlepro Inc.
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
# ==========================================
|
||||
# FORGEJO CONFIGURATION
|
||||
# ==========================================
|
||||
forgejo:
|
||||
version: "1.21" # Version stable
|
||||
install_method: binary # binary | docker
|
||||
|
||||
# Paths
|
||||
install_dir: /opt/forgejo
|
||||
data_dir: /var/lib/forgejo
|
||||
custom_dir: /etc/forgejo
|
||||
log_dir: /var/log/forgejo
|
||||
|
||||
# User
|
||||
user: git
|
||||
group: git
|
||||
|
||||
# Network
|
||||
hostname: git.infra.chezlepro.ca
|
||||
domain: chezlepro.ca
|
||||
http_port: 3000
|
||||
ssh_port: 2222 # Éviter conflit avec SSH système
|
||||
|
||||
# Database (PostgreSQL)
|
||||
db:
|
||||
type: postgres
|
||||
host: 127.0.0.1:5432
|
||||
name: forgejo
|
||||
user: forgejo
|
||||
password: "{{ vault_postgresql_forgejo_password }}"
|
||||
ssl_mode: disable
|
||||
|
||||
# Security
|
||||
secret_key: "{{ vault_forgejo_secret_key }}"
|
||||
internal_token: "{{ vault_forgejo_internal_token }}"
|
||||
jwt_secret: "{{ vault_forgejo_jwt_secret }}"
|
||||
|
||||
# Admin
|
||||
admin:
|
||||
username: admin
|
||||
password: "{{ vault_forgejo_admin_password }}"
|
||||
email: admin@chezlepro.ca
|
||||
|
||||
# SSO (Keycloak OpenID Connect)
|
||||
oauth:
|
||||
enabled: true
|
||||
providers:
|
||||
- name: alliance-boreale
|
||||
provider: openidConnect
|
||||
display_name: "L'Alliance Boréale SSO"
|
||||
client_id: forgejo
|
||||
client_secret: "{{ vault_keycloak_forgejo_client_secret }}"
|
||||
openid_connect_auto_discovery_url: "https://sso.infra.chezlepro.ca/realms/alliance-boreale/.well-known/openid-configuration"
|
||||
|
||||
# Features
|
||||
features:
|
||||
enable_actions: true # CI/CD
|
||||
enable_packages: true # Container registry
|
||||
enable_lfs: true # Git LFS
|
||||
disable_registration: true # Inscription via SSO uniquement
|
||||
require_signin: true
|
||||
|
||||
# ==========================================
|
||||
# POSTGRESQL CONFIGURATION (Forgejo)
|
||||
# ==========================================
|
||||
postgresql:
|
||||
version: 15
|
||||
listen_addresses: localhost
|
||||
port: 5432
|
||||
|
||||
databases:
|
||||
- name: forgejo
|
||||
owner: forgejo
|
||||
encoding: UTF8
|
||||
lc_collate: en_US.UTF-8
|
||||
lc_ctype: en_US.UTF-8
|
||||
|
||||
users:
|
||||
- name: forgejo
|
||||
password: "{{ vault_postgresql_forgejo_password }}"
|
||||
|
||||
shared_buffers: 512MB
|
||||
work_mem: 32MB
|
||||
maintenance_work_mem: 256MB
|
||||
effective_cache_size: 2GB
|
||||
|
||||
backup:
|
||||
enabled: true
|
||||
schedule: "0 4 * * *" # 4h du matin
|
||||
retention_days: 14
|
||||
destination: /var/backups/postgresql
|
||||
|
||||
# ==========================================
|
||||
# REVERSE PROXY (Nginx)
|
||||
# ==========================================
|
||||
nginx:
|
||||
enabled: true
|
||||
|
||||
ssl:
|
||||
enabled: true
|
||||
cert_source: self-signed # self-signed | letsencrypt | custom
|
||||
|
||||
vhosts:
|
||||
- server_name: git.infra.chezlepro.ca
|
||||
listen_port: 443
|
||||
ssl: true
|
||||
proxy_pass: http://127.0.0.1:3000
|
||||
|
||||
# Large file uploads (Git)
|
||||
client_max_body_size: 512M
|
||||
|
||||
proxy_set_headers:
|
||||
Host: $host
|
||||
X-Real-IP: $remote_addr
|
||||
X-Forwarded-For: $proxy_add_x_forwarded_for
|
||||
X-Forwarded-Proto: $scheme
|
||||
|
||||
# ==========================================
|
||||
# FIREWALL RULES
|
||||
# ==========================================
|
||||
firewall_rules:
|
||||
forgejo_http:
|
||||
- port: 3000
|
||||
proto: tcp
|
||||
source: 127.0.0.1/32
|
||||
comment: "Forgejo HTTP (internal only)"
|
||||
|
||||
forgejo_ssh:
|
||||
- port: 2222
|
||||
proto: tcp
|
||||
source: 0.0.0.0/0
|
||||
comment: "Forgejo SSH (Git over SSH)"
|
||||
|
||||
nginx:
|
||||
- port: 443
|
||||
proto: tcp
|
||||
source: 0.0.0.0/0
|
||||
comment: "HTTPS (public)"
|
||||
|
||||
- port: 80
|
||||
proto: tcp
|
||||
source: 0.0.0.0/0
|
||||
comment: "HTTP (redirect to HTTPS)"
|
||||
|
||||
# ==========================================
|
||||
# INITIAL REPOSITORIES
|
||||
# ==========================================
|
||||
forgejo_initial_repos:
|
||||
- name: ansible-infrastructure
|
||||
description: "Infrastructure as Code - L'Alliance Boréale"
|
||||
private: false
|
||||
auto_init: true
|
||||
default_branch: main
|
||||
|
||||
- name: documentation
|
||||
description: "Documentation technique"
|
||||
private: false
|
||||
auto_init: true
|
||||
|
||||
- name: registraire
|
||||
description: "Registraire des membres (Document 14)"
|
||||
private: false
|
||||
auto_init: true
|
||||
129
ansible/inventories/production/group_vars/idp_servers.yml
Normal file
129
ansible/inventories/production/group_vars/idp_servers.yml
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
# Alliance Boréale - Variables Keycloak (IdP)
|
||||
# Membre: Chezlepro Inc.
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
# ==========================================
|
||||
# KEYCLOAK CONFIGURATION
|
||||
# ==========================================
|
||||
keycloak:
|
||||
version: "23.0.0" # Version LTS
|
||||
install_method: standalone # standalone | docker
|
||||
|
||||
# Network
|
||||
hostname: sso.infra.chezlepro.ca
|
||||
http_port: 8080
|
||||
https_port: 8443
|
||||
|
||||
# Admin
|
||||
admin_user: admin
|
||||
admin_password: "{{ vault_keycloak_admin_password }}"
|
||||
|
||||
# Database (PostgreSQL)
|
||||
db:
|
||||
vendor: postgres
|
||||
host: localhost
|
||||
port: 5432
|
||||
database: keycloak
|
||||
username: keycloak
|
||||
password: "{{ vault_postgresql_keycloak_password }}"
|
||||
|
||||
# Realm configuration
|
||||
realms:
|
||||
- name: alliance-boreale
|
||||
display_name: "L'Alliance Boréale"
|
||||
enabled: true
|
||||
|
||||
# Clients (services qui utilisent SSO)
|
||||
clients:
|
||||
- client_id: forgejo
|
||||
name: "Forgejo Git"
|
||||
enabled: true
|
||||
protocol: openid-connect
|
||||
redirect_uris:
|
||||
- "https://git.infra.chezlepro.ca/*"
|
||||
web_origins:
|
||||
- "https://git.infra.chezlepro.ca"
|
||||
|
||||
# Futurs clients Phase 3
|
||||
# - client_id: fastapi-pivot
|
||||
# name: "FastAPI Admin"
|
||||
# - client_id: matrix
|
||||
# name: "Matrix Synapse"
|
||||
|
||||
# ==========================================
|
||||
# POSTGRESQL CONFIGURATION (Keycloak)
|
||||
# ==========================================
|
||||
postgresql:
|
||||
version: 15
|
||||
listen_addresses: localhost
|
||||
port: 5432
|
||||
|
||||
databases:
|
||||
- name: keycloak
|
||||
owner: keycloak
|
||||
encoding: UTF8
|
||||
lc_collate: en_US.UTF-8
|
||||
lc_ctype: en_US.UTF-8
|
||||
|
||||
users:
|
||||
- name: keycloak
|
||||
password: "{{ vault_postgresql_keycloak_password }}"
|
||||
|
||||
shared_buffers: 512MB
|
||||
work_mem: 32MB
|
||||
maintenance_work_mem: 256MB
|
||||
effective_cache_size: 2GB
|
||||
|
||||
backup:
|
||||
enabled: true
|
||||
schedule: "0 3 * * *" # 3h du matin
|
||||
retention_days: 14
|
||||
destination: /var/backups/postgresql
|
||||
|
||||
# ==========================================
|
||||
# REVERSE PROXY (Nginx)
|
||||
# ==========================================
|
||||
nginx:
|
||||
enabled: true
|
||||
|
||||
# SSL/TLS
|
||||
ssl:
|
||||
enabled: true
|
||||
cert_source: self-signed # self-signed | letsencrypt | custom
|
||||
# letsencrypt_email: admin@chezlepro.ca
|
||||
|
||||
# Vhosts
|
||||
vhosts:
|
||||
- server_name: sso.infra.chezlepro.ca
|
||||
listen_port: 443
|
||||
ssl: true
|
||||
proxy_pass: http://127.0.0.1:8080
|
||||
|
||||
# Headers
|
||||
proxy_set_headers:
|
||||
Host: $host
|
||||
X-Real-IP: $remote_addr
|
||||
X-Forwarded-For: $proxy_add_x_forwarded_for
|
||||
X-Forwarded-Proto: $scheme
|
||||
|
||||
# ==========================================
|
||||
# FIREWALL RULES
|
||||
# ==========================================
|
||||
firewall_rules:
|
||||
keycloak:
|
||||
- port: 8080
|
||||
proto: tcp
|
||||
source: 127.0.0.1/32
|
||||
comment: "Keycloak HTTP (internal only)"
|
||||
|
||||
nginx:
|
||||
- port: 443
|
||||
proto: tcp
|
||||
source: 0.0.0.0/0
|
||||
comment: "HTTPS (public)"
|
||||
|
||||
- port: 80
|
||||
proto: tcp
|
||||
source: 0.0.0.0/0
|
||||
comment: "HTTP (redirect to HTTPS)"
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
[netbox]
|
||||
netbox-prod ansible_host=203.0.113.10 ansible_user=ansible
|
||||
|
||||
[powerdns]
|
||||
# À remplir plus tard
|
||||
|
||||
[keycloak]
|
||||
# À remplir plus tard
|
||||
|
||||
[proxmox]
|
||||
# À remplir plus tard
|
||||
89
ansible/inventories/production/hosts.yml
Normal file
89
ansible/inventories/production/hosts.yml
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
# Alliance Boréale - Inventaire Production
|
||||
# Membre: Chezlepro Inc. (clp)
|
||||
# Domaine: chezlepro.ca
|
||||
# Date: 2025-10-31
|
||||
# Nomenclature: v4 (CRB-2)
|
||||
|
||||
---
|
||||
all:
|
||||
children:
|
||||
# ========================================
|
||||
# COUCHE 2 : RÉSEAU & DNS
|
||||
# ========================================
|
||||
dns_servers:
|
||||
hosts:
|
||||
ns1.infra.chezlepro.ca:
|
||||
ansible_host: 10.0.2.10
|
||||
# ansible_host: <IP_PUBLIQUE> # Décommenter si accès direct
|
||||
vmid: 02001
|
||||
vm_name: clp-infra-dns-master-prod-01
|
||||
dns_role: master
|
||||
dns_zone_primary: chezlepro.ca
|
||||
|
||||
ns2.infra.chezlepro.ca:
|
||||
ansible_host: 10.0.2.11
|
||||
# ansible_host: <IP_PUBLIQUE> # Décommenter si accès direct
|
||||
vmid: 02002
|
||||
vm_name: clp-infra-dns-slave-prod-01
|
||||
dns_role: slave
|
||||
dns_zone_primary: chezlepro.ca
|
||||
|
||||
vars:
|
||||
# Variables communes DNS
|
||||
pdns_backend: pgsql
|
||||
pdns_dnssec_enabled: true
|
||||
pdns_api_enabled: true
|
||||
pdns_webserver_enabled: true
|
||||
pdns_webserver_port: 8081
|
||||
|
||||
# ========================================
|
||||
# GROUPES LOGIQUES
|
||||
# ========================================
|
||||
dns_masters:
|
||||
hosts:
|
||||
ns1.infra.chezlepro.ca:
|
||||
|
||||
dns_slaves:
|
||||
hosts:
|
||||
ns2.infra.chezlepro.ca:
|
||||
|
||||
# PostgreSQL (pour PowerDNS)
|
||||
postgresql_servers:
|
||||
hosts:
|
||||
ns1.infra.chezlepro.ca:
|
||||
ns2.infra.chezlepro.ca:
|
||||
|
||||
# ==========================================
|
||||
# VARIABLES GLOBALES
|
||||
# ==========================================
|
||||
vars:
|
||||
# Membre
|
||||
member_slug: clp
|
||||
member_legal_name: "Chezlepro Inc."
|
||||
member_domain: chezlepro.ca
|
||||
|
||||
# Réseau
|
||||
network_internal_subnet: 10.0.0.0/8
|
||||
network_dns_subnet: 10.0.2.0/24
|
||||
|
||||
# Ansible
|
||||
ansible_user: ansible
|
||||
ansible_become: true
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
|
||||
# Timezone
|
||||
timezone: America/Toronto
|
||||
|
||||
# NTP
|
||||
ntp_servers:
|
||||
- 0.ca.pool.ntp.org
|
||||
- 1.ca.pool.ntp.org
|
||||
- 2.ca.pool.ntp.org
|
||||
|
||||
# SSH
|
||||
ssh_port: 22
|
||||
ssh_permit_root_login: false
|
||||
ssh_password_authentication: false
|
||||
|
||||
# Sécurité
|
||||
firewall_backend: nftables
|
||||
161
ansible/playbooks/phase1-dns-deploy.yml
Normal file
161
ansible/playbooks/phase1-dns-deploy.yml
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
# Alliance Boréale - Playbook Phase 1
|
||||
# Déploiement DNS Fédéré (Couches C1-C2)
|
||||
# Membre: Chezlepro Inc.
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
- name: "🌲 Phase 1 - Préparation des VMs (Common)"
|
||||
hosts: all
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
pre_tasks:
|
||||
- name: "🔍 Vérifier connectivité SSH"
|
||||
ansible.builtin.ping:
|
||||
tags: always
|
||||
|
||||
- name: "📋 Afficher informations hôte"
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
Hôte: {{ inventory_hostname }}
|
||||
IP: {{ ansible_host }}
|
||||
VMID: {{ vmid | default('N/A') }}
|
||||
Rôle DNS: {{ dns_role | default('N/A') }}
|
||||
tags: always
|
||||
|
||||
roles:
|
||||
- role: common
|
||||
tags: common
|
||||
|
||||
post_tasks:
|
||||
- name: "✅ Common role terminé"
|
||||
ansible.builtin.debug:
|
||||
msg: "✅ Hardening complété sur {{ inventory_hostname }}"
|
||||
tags: always
|
||||
|
||||
# ============================================
|
||||
- name: "🗄️ Phase 1 - PostgreSQL pour DNS"
|
||||
hosts: dns_servers
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
roles:
|
||||
- role: postgresql
|
||||
tags: postgresql
|
||||
|
||||
post_tasks:
|
||||
- name: "✅ PostgreSQL terminé"
|
||||
ansible.builtin.debug:
|
||||
msg: "✅ PostgreSQL {{ postgresql.version }} configuré sur {{ inventory_hostname }}"
|
||||
tags: always
|
||||
|
||||
# ============================================
|
||||
- name: "🌐 Phase 1 - PowerDNS Master"
|
||||
hosts: dns_masters
|
||||
become: true
|
||||
gather_facts: true
|
||||
serial: 1 # Un à la fois pour éviter race conditions
|
||||
|
||||
roles:
|
||||
- role: powerdns-authoritative
|
||||
tags: powerdns
|
||||
|
||||
post_tasks:
|
||||
- name: "✅ PowerDNS Master terminé"
|
||||
ansible.builtin.debug:
|
||||
msg: "✅ PowerDNS MASTER configuré sur {{ inventory_hostname }}"
|
||||
tags: always
|
||||
|
||||
# ============================================
|
||||
- name: "🌐 Phase 1 - PowerDNS Slave"
|
||||
hosts: dns_slaves
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
roles:
|
||||
- role: powerdns-authoritative
|
||||
tags: powerdns
|
||||
|
||||
post_tasks:
|
||||
- name: "✅ PowerDNS Slave terminé"
|
||||
ansible.builtin.debug:
|
||||
msg: "✅ PowerDNS SLAVE configuré sur {{ inventory_hostname }}"
|
||||
tags: always
|
||||
|
||||
# ============================================
|
||||
- name: "🧪 Phase 1 - Tests de validation"
|
||||
hosts: dns_masters
|
||||
become: false
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
- name: "🧪 Test résolution DNS (zone primaire)"
|
||||
ansible.builtin.command:
|
||||
cmd: "dig @{{ ansible_host }} {{ member.domain_primary }} SOA +short"
|
||||
delegate_to: localhost
|
||||
register: dns_test
|
||||
changed_when: false
|
||||
|
||||
- name: "✅ Résultat test DNS"
|
||||
ansible.builtin.debug:
|
||||
msg: "✅ DNS fonctionne: {{ dns_test.stdout }}"
|
||||
|
||||
- name: "🧪 Test AXFR (zone transfer)"
|
||||
ansible.builtin.command:
|
||||
cmd: "dig @{{ ansible_host }} {{ member.domain_primary }} AXFR +short"
|
||||
delegate_to: localhost
|
||||
register: axfr_test
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
|
||||
- name: "✅ Résultat AXFR"
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
{% if axfr_test.rc == 0 %}
|
||||
✅ AXFR fonctionne ({{ axfr_test.stdout_lines | length }} lignes)
|
||||
{% else %}
|
||||
⚠️ AXFR refusé (normal si ACL strictes configurées)
|
||||
{% endif %}
|
||||
|
||||
# ============================================
|
||||
- name: "📋 Phase 1 - Récapitulatif"
|
||||
hosts: localhost
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
- name: "🎉 Phase 1 terminée avec succès!"
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
|
||||
═══════════════════════════════════════════════
|
||||
🌲 PHASE 1 - DNS FÉDÉRÉ DÉPLOYÉ
|
||||
═══════════════════════════════════════════════
|
||||
|
||||
✅ Serveurs DNS configurés:
|
||||
{% for host in groups['dns_servers'] %}
|
||||
- {{ host }} ({{ hostvars[host]['dns_role'] | upper }})
|
||||
{% endfor %}
|
||||
|
||||
🌐 Zone primaire: {{ hostvars[groups['dns_masters'][0]]['member']['domain_primary'] }}
|
||||
|
||||
📋 Prochaines étapes:
|
||||
|
||||
1. Tester résolution DNS:
|
||||
dig @{{ hostvars[groups['dns_masters'][0]]['ansible_host'] }} {{ hostvars[groups['dns_masters'][0]]['member']['domain_primary'] }} SOA
|
||||
|
||||
2. Exporter DS records pour DNSSEC:
|
||||
ssh {{ hostvars[groups['dns_masters'][0]]['ansible_user'] }}@{{ hostvars[groups['dns_masters'][0]]['ansible_host'] }}
|
||||
sudo pdnsutil show-zone {{ hostvars[groups['dns_masters'][0]]['member']['domain_primary'] }} | grep DS
|
||||
|
||||
3. Ajouter DS records chez votre registraire de domaine
|
||||
|
||||
4. Valider DNSSEC:
|
||||
dig +dnssec {{ hostvars[groups['dns_masters'][0]]['member']['domain_primary'] }} @8.8.8.8
|
||||
|
||||
5. Mettre à jour Registraire YAML:
|
||||
registraire/membres/m001-chezlepro.yml
|
||||
|
||||
6. Passer à Phase 2 (Keycloak + Forgejo)
|
||||
|
||||
═══════════════════════════════════════════════
|
||||
tags: always
|
||||
178
ansible/playbooks/phase2-deploy.yml
Normal file
178
ansible/playbooks/phase2-deploy.yml
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
# Alliance Boréale - Playbook Phase 2
|
||||
# Déploiement Identité & Forge (Couches C3-C4)
|
||||
# Membre: Chezlepro Inc.
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
- name: "🌲 Phase 2 - Préparation VMs (Common)"
|
||||
hosts: phase2_servers
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
pre_tasks:
|
||||
- name: "🔍 Vérifier connectivité SSH"
|
||||
ansible.builtin.ping:
|
||||
tags: always
|
||||
|
||||
- name: "📋 Afficher informations hôte"
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
Hôte: {{ inventory_hostname }}
|
||||
IP: {{ ansible_host }}
|
||||
VMID: {{ vmid | default('N/A') }}
|
||||
tags: always
|
||||
|
||||
roles:
|
||||
- role: common
|
||||
tags: common
|
||||
|
||||
post_tasks:
|
||||
- name: "✅ Common terminé"
|
||||
ansible.builtin.debug:
|
||||
msg: "✅ Hardening complété sur {{ inventory_hostname }}"
|
||||
|
||||
# ============================================
|
||||
- name: "🔑 Phase 2 - Keycloak (IdP)"
|
||||
hosts: idp_servers
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
roles:
|
||||
- role: postgresql
|
||||
tags: postgresql
|
||||
|
||||
- role: keycloak
|
||||
tags: keycloak
|
||||
|
||||
post_tasks:
|
||||
- name: "✅ Keycloak terminé"
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
✅ Keycloak déployé sur {{ inventory_hostname }}
|
||||
URL: https://{{ keycloak.hostname }}
|
||||
Admin: {{ keycloak.admin_user }}
|
||||
|
||||
⚠️ IMPORTANT: Note client secret for Forgejo
|
||||
tags: always
|
||||
|
||||
# ============================================
|
||||
- name: "🔨 Phase 2 - Forgejo (Forge)"
|
||||
hosts: forge_servers
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
roles:
|
||||
- role: postgresql
|
||||
tags: postgresql
|
||||
|
||||
- role: forgejo
|
||||
tags: forgejo
|
||||
|
||||
post_tasks:
|
||||
- name: "✅ Forgejo terminé"
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
✅ Forgejo déployé sur {{ inventory_hostname }}
|
||||
URL: https://{{ forgejo.hostname }}
|
||||
SSH: git@{{ forgejo.hostname }}:{{ forgejo.ssh_port }}
|
||||
Admin: {{ forgejo.admin.username }}
|
||||
tags: always
|
||||
|
||||
# ============================================
|
||||
- name: "🧪 Phase 2 - Tests d'intégration"
|
||||
hosts: localhost
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
- name: "🧪 Test Keycloak availability"
|
||||
ansible.builtin.uri:
|
||||
url: "https://{{ hostvars[groups['idp_servers'][0]]['keycloak']['hostname'] }}/realms/alliance-boreale"
|
||||
validate_certs: false
|
||||
status_code: 200
|
||||
register: keycloak_test
|
||||
delegate_to: localhost
|
||||
changed_when: false
|
||||
|
||||
- name: "✅ Keycloak OK"
|
||||
ansible.builtin.debug:
|
||||
msg: "✅ Keycloak répond correctement"
|
||||
when: keycloak_test.status == 200
|
||||
|
||||
- name: "🧪 Test Forgejo availability"
|
||||
ansible.builtin.uri:
|
||||
url: "https://{{ hostvars[groups['forge_servers'][0]]['forgejo']['hostname'] }}"
|
||||
validate_certs: false
|
||||
status_code: 200
|
||||
register: forgejo_test
|
||||
delegate_to: localhost
|
||||
changed_when: false
|
||||
|
||||
- name: "✅ Forgejo OK"
|
||||
ansible.builtin.debug:
|
||||
msg: "✅ Forgejo répond correctement"
|
||||
when: forgejo_test.status == 200
|
||||
|
||||
# ============================================
|
||||
- name: "📋 Phase 2 - Récapitulatif"
|
||||
hosts: localhost
|
||||
gather_facts: false
|
||||
|
||||
tasks:
|
||||
- name: "🎉 Phase 2 terminée avec succès!"
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
|
||||
═══════════════════════════════════════════════
|
||||
🌲 PHASE 2 - IDENTITÉ & FORGE DÉPLOYÉES
|
||||
═══════════════════════════════════════════════
|
||||
|
||||
✅ Services déployés:
|
||||
|
||||
🔑 KEYCLOAK (SSO)
|
||||
URL: https://{{ hostvars[groups['idp_servers'][0]]['keycloak']['hostname'] }}
|
||||
Admin: {{ hostvars[groups['idp_servers'][0]]['keycloak']['admin_user'] }}
|
||||
Realm: alliance-boreale
|
||||
|
||||
🔨 FORGEJO (Forge Git)
|
||||
URL: https://{{ hostvars[groups['forge_servers'][0]]['forgejo']['hostname'] }}
|
||||
SSH: git@{{ hostvars[groups['forge_servers'][0]]['forgejo']['hostname'] }}:{{ hostvars[groups['forge_servers'][0]]['forgejo']['ssh_port'] }}
|
||||
Admin: {{ hostvars[groups['forge_servers'][0]]['forgejo']['admin_username'] }}
|
||||
|
||||
📋 Configuration SSO:
|
||||
|
||||
1. Se connecter à Keycloak:
|
||||
https://{{ hostvars[groups['idp_servers'][0]]['keycloak']['hostname'] }}
|
||||
|
||||
2. Aller dans Realm "alliance-boreale"
|
||||
|
||||
3. Clients → "forgejo" → Credentials
|
||||
Copier le "Client Secret"
|
||||
|
||||
4. Ajouter ce secret dans vault:
|
||||
ansible-vault edit vault/production.yml
|
||||
vault_keycloak_forgejo_client_secret: "<secret>"
|
||||
|
||||
5. Redéployer Forgejo:
|
||||
ansible-playbook playbooks/phase2-deploy.yml --tags forgejo
|
||||
|
||||
📋 Premiers pas:
|
||||
|
||||
1. Créer utilisateurs dans Keycloak
|
||||
|
||||
2. Se connecter à Forgejo via SSO
|
||||
|
||||
3. Créer organisations/repos:
|
||||
- alliance-boreale/ansible-infrastructure
|
||||
- alliance-boreale/documentation
|
||||
- alliance-boreale/registraire
|
||||
|
||||
4. Pousser ce dépôt Ansible vers Forgejo:
|
||||
git remote add origin git@{{ hostvars[groups['forge_servers'][0]]['forgejo']['hostname'] }}:{{ hostvars[groups['forge_servers'][0]]['forgejo']['ssh_port'] }}/alliance-boreale/ansible-infrastructure.git
|
||||
git push -u origin main
|
||||
|
||||
5. Configurer CI/CD (Forgejo Actions)
|
||||
|
||||
6. Passer à Phase 3 (FastAPI Pivot)
|
||||
|
||||
═══════════════════════════════════════════════
|
||||
tags: always
|
||||
47
ansible/roles/common/defaults/main.yml
Normal file
47
ansible/roles/common/defaults/main.yml
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# Alliance Boréale - Rôle Common - Default Variables
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
# Ces valeurs sont écrasées par group_vars/all.yml
|
||||
# Elles servent de fallback si group_vars absent
|
||||
|
||||
base_packages:
|
||||
- vim
|
||||
- htop
|
||||
- curl
|
||||
- git
|
||||
- python3
|
||||
|
||||
system:
|
||||
timezone: America/Toronto
|
||||
locale: en_CA.UTF-8
|
||||
|
||||
ntp:
|
||||
enabled: true
|
||||
servers:
|
||||
- 0.ca.pool.ntp.org
|
||||
- 1.ca.pool.ntp.org
|
||||
|
||||
ssh:
|
||||
port: 22
|
||||
permit_root_login: false
|
||||
password_authentication: false
|
||||
pubkey_authentication: true
|
||||
|
||||
users:
|
||||
ansible:
|
||||
name: ansible
|
||||
groups: [sudo]
|
||||
shell: /bin/bash
|
||||
sudo_nopasswd: true
|
||||
|
||||
security:
|
||||
firewall: nftables
|
||||
fail2ban_enabled: true
|
||||
unattended_upgrades: true
|
||||
|
||||
firewall:
|
||||
default_policy: drop
|
||||
|
||||
network:
|
||||
internal_subnet: 10.0.0.0/8
|
||||
23
ansible/roles/common/handlers/main.yml
Normal file
23
ansible/roles/common/handlers/main.yml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Alliance Boréale - Rôle Common - Handlers
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
- name: restart chrony
|
||||
ansible.builtin.systemd:
|
||||
name: chrony
|
||||
state: restarted
|
||||
|
||||
- name: restart sshd
|
||||
ansible.builtin.systemd:
|
||||
name: sshd
|
||||
state: restarted
|
||||
|
||||
- name: reload nftables
|
||||
ansible.builtin.systemd:
|
||||
name: nftables
|
||||
state: reloaded
|
||||
|
||||
- name: restart fail2ban
|
||||
ansible.builtin.systemd:
|
||||
name: fail2ban
|
||||
state: restarted
|
||||
22
ansible/roles/common/tasks/logging.yml
Normal file
22
ansible/roles/common/tasks/logging.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# Alliance Boréale - Rôle Common - Logging Configuration
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
- name: "📊 Ensure rsyslog is installed"
|
||||
ansible.builtin.apt:
|
||||
name: rsyslog
|
||||
state: present
|
||||
|
||||
- name: "📊 Configure log rotation"
|
||||
ansible.builtin.template:
|
||||
src: logrotate-ansible.j2
|
||||
dest: /etc/logrotate.d/ansible
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
|
||||
- name: "✅ Start and enable rsyslog"
|
||||
ansible.builtin.systemd:
|
||||
name: rsyslog
|
||||
state: started
|
||||
enabled: true
|
||||
72
ansible/roles/common/tasks/main.yml
Normal file
72
ansible/roles/common/tasks/main.yml
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# Alliance Boréale - Rôle Common
|
||||
# Couche: C1 (Physique)
|
||||
# Objectif: Hardening post-création VM
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
- name: "🔍 Gather facts"
|
||||
ansible.builtin.setup:
|
||||
tags: always
|
||||
|
||||
- name: "📦 Update apt cache"
|
||||
ansible.builtin.apt:
|
||||
update_cache: true
|
||||
cache_valid_time: 3600
|
||||
tags: packages
|
||||
|
||||
- name: "📦 Install base packages"
|
||||
ansible.builtin.apt:
|
||||
name: "{{ base_packages }}"
|
||||
state: present
|
||||
tags: packages
|
||||
|
||||
- name: "🕐 Configure timezone"
|
||||
community.general.timezone:
|
||||
name: "{{ system.timezone }}"
|
||||
tags: system
|
||||
|
||||
- name: "🕐 Configure NTP (chrony)"
|
||||
ansible.builtin.import_tasks: ntp.yml
|
||||
when: ntp.enabled | default(true)
|
||||
tags: ntp
|
||||
|
||||
- name: "👤 Create ansible user"
|
||||
ansible.builtin.user:
|
||||
name: "{{ users.ansible.name }}"
|
||||
groups: "{{ users.ansible.groups }}"
|
||||
shell: "{{ users.ansible.shell }}"
|
||||
create_home: true
|
||||
state: present
|
||||
tags: users
|
||||
|
||||
- name: "👤 Configure sudo for ansible user"
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/sudoers.d/ansible
|
||||
line: "{{ users.ansible.name }} ALL=(ALL) NOPASSWD:ALL"
|
||||
create: true
|
||||
mode: '0440'
|
||||
validate: 'visudo -cf %s'
|
||||
when: users.ansible.sudo_nopasswd | default(true)
|
||||
tags: users
|
||||
|
||||
- name: "🔐 Configure SSH"
|
||||
ansible.builtin.import_tasks: ssh.yml
|
||||
tags: ssh
|
||||
|
||||
- name: "🔥 Configure firewall (nftables)"
|
||||
ansible.builtin.import_tasks: nftables.yml
|
||||
when: security.firewall == 'nftables'
|
||||
tags: firewall
|
||||
|
||||
- name: "📊 Configure logging"
|
||||
ansible.builtin.import_tasks: logging.yml
|
||||
tags: logging
|
||||
|
||||
- name: "🔒 Security hardening"
|
||||
ansible.builtin.import_tasks: security.yml
|
||||
tags: security
|
||||
|
||||
- name: "✅ Common role completed"
|
||||
ansible.builtin.debug:
|
||||
msg: "✅ VM {{ inventory_hostname }} hardened successfully"
|
||||
tags: always
|
||||
35
ansible/roles/common/tasks/nftables.yml
Normal file
35
ansible/roles/common/tasks/nftables.yml
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Alliance Boréale - Rôle Common - nftables Configuration
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
- name: "📦 Install nftables"
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- nftables
|
||||
- python3-nftables
|
||||
state: present
|
||||
|
||||
- name: "🔥 Deploy nftables base rules"
|
||||
ansible.builtin.template:
|
||||
src: nftables.conf.j2
|
||||
dest: /etc/nftables.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
notify: reload nftables
|
||||
|
||||
- name: "✅ Enable and start nftables"
|
||||
ansible.builtin.systemd:
|
||||
name: nftables
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
- name: "🚫 Ensure iptables is not running"
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ item }}"
|
||||
state: stopped
|
||||
enabled: false
|
||||
loop:
|
||||
- iptables
|
||||
- ip6tables
|
||||
failed_when: false
|
||||
23
ansible/roles/common/tasks/ntp.yml
Normal file
23
ansible/roles/common/tasks/ntp.yml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Alliance Boréale - Rôle Common - NTP Configuration
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
- name: "📦 Install chrony"
|
||||
ansible.builtin.apt:
|
||||
name: chrony
|
||||
state: present
|
||||
|
||||
- name: "⚙️ Configure chrony"
|
||||
ansible.builtin.template:
|
||||
src: chrony.conf.j2
|
||||
dest: /etc/chrony/chrony.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
notify: restart chrony
|
||||
|
||||
- name: "✅ Start and enable chrony"
|
||||
ansible.builtin.systemd:
|
||||
name: chrony
|
||||
state: started
|
||||
enabled: true
|
||||
77
ansible/roles/common/tasks/security.yml
Normal file
77
ansible/roles/common/tasks/security.yml
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
# Alliance Boréale - Rôle Common - Security Hardening
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
- name: "🔒 Disable unnecessary services"
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ item }}"
|
||||
state: stopped
|
||||
enabled: false
|
||||
loop:
|
||||
- bluetooth
|
||||
- cups
|
||||
failed_when: false
|
||||
|
||||
- name: "🔒 Configure sysctl security parameters"
|
||||
ansible.posix.sysctl:
|
||||
name: "{{ item.key }}"
|
||||
value: "{{ item.value }}"
|
||||
state: present
|
||||
reload: true
|
||||
loop:
|
||||
- { key: 'net.ipv4.conf.all.rp_filter', value: '1' }
|
||||
- { key: 'net.ipv4.conf.default.rp_filter', value: '1' }
|
||||
- { key: 'net.ipv4.icmp_echo_ignore_broadcasts', value: '1' }
|
||||
- { key: 'net.ipv4.conf.all.accept_source_route', value: '0' }
|
||||
- { key: 'net.ipv4.conf.default.accept_source_route', value: '0' }
|
||||
- { key: 'net.ipv4.conf.all.send_redirects', value: '0' }
|
||||
- { key: 'net.ipv4.conf.default.send_redirects', value: '0' }
|
||||
- { key: 'net.ipv4.tcp_syncookies', value: '1' }
|
||||
- { key: 'net.ipv4.conf.all.log_martians', value: '1' }
|
||||
- { key: 'kernel.randomize_va_space', value: '2' }
|
||||
|
||||
- name: "🔒 Install fail2ban"
|
||||
ansible.builtin.apt:
|
||||
name: fail2ban
|
||||
state: present
|
||||
when: security.fail2ban_enabled | default(true)
|
||||
|
||||
- name: "🔒 Configure fail2ban"
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/fail2ban/jail.local
|
||||
content: |
|
||||
[DEFAULT]
|
||||
bantime = 3600
|
||||
findtime = 600
|
||||
maxretry = 5
|
||||
|
||||
[sshd]
|
||||
enabled = true
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
when: security.fail2ban_enabled | default(true)
|
||||
notify: restart fail2ban
|
||||
|
||||
- name: "🔒 Enable unattended upgrades"
|
||||
ansible.builtin.apt:
|
||||
name: unattended-upgrades
|
||||
state: present
|
||||
when: security.unattended_upgrades | default(true)
|
||||
|
||||
- name: "🔒 Configure automatic security updates"
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/apt/apt.conf.d/50unattended-upgrades
|
||||
content: |
|
||||
Unattended-Upgrade::Allowed-Origins {
|
||||
"${distro_id}:${distro_codename}-security";
|
||||
};
|
||||
Unattended-Upgrade::AutoFixInterruptedDpkg "true";
|
||||
Unattended-Upgrade::MinimalSteps "true";
|
||||
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
|
||||
Unattended-Upgrade::Remove-Unused-Dependencies "true";
|
||||
Unattended-Upgrade::Automatic-Reboot "false";
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
when: security.unattended_upgrades | default(true)
|
||||
32
ansible/roles/common/tasks/ssh.yml
Normal file
32
ansible/roles/common/tasks/ssh.yml
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# Alliance Boréale - Rôle Common - SSH Configuration
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
- name: "📦 Ensure OpenSSH server is installed"
|
||||
ansible.builtin.apt:
|
||||
name: openssh-server
|
||||
state: present
|
||||
|
||||
- name: "⚙️ Configure SSH daemon"
|
||||
ansible.builtin.template:
|
||||
src: sshd_config.j2
|
||||
dest: /etc/ssh/sshd_config
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
validate: '/usr/sbin/sshd -t -f %s'
|
||||
notify: restart sshd
|
||||
|
||||
- name: "🔑 Add authorized keys for ansible user"
|
||||
ansible.posix.authorized_key:
|
||||
user: "{{ users.ansible.name }}"
|
||||
key: "{{ item }}"
|
||||
state: present
|
||||
loop: "{{ ssh.authorized_keys }}"
|
||||
when: ssh.authorized_keys is defined
|
||||
|
||||
- name: "✅ Start and enable SSH"
|
||||
ansible.builtin.systemd:
|
||||
name: sshd
|
||||
state: started
|
||||
enabled: true
|
||||
25
ansible/roles/common/templates/chrony.conf.j2
Normal file
25
ansible/roles/common/templates/chrony.conf.j2
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Alliance Boréale - Chrony Configuration
|
||||
# Generated by Ansible - DO NOT EDIT MANUALLY
|
||||
# Host: {{ inventory_hostname }}
|
||||
# Date: {{ ansible_date_time.iso8601 }}
|
||||
|
||||
# NTP Servers
|
||||
{% for server in ntp.servers %}
|
||||
pool {{ server }} iburst
|
||||
{% endfor %}
|
||||
|
||||
# Record rate at which system clock gains/losses time
|
||||
driftfile /var/lib/chrony/chrony.drift
|
||||
|
||||
# Allow system to step clock if offset is large
|
||||
makestep 1.0 3
|
||||
|
||||
# Enable kernel synchronization of RTC
|
||||
rtcsync
|
||||
|
||||
# Serve time even if not synchronized
|
||||
local stratum 10
|
||||
|
||||
# Log files
|
||||
logdir /var/log/chrony
|
||||
log measurements statistics tracking
|
||||
74
ansible/roles/common/templates/nftables.conf.j2
Normal file
74
ansible/roles/common/templates/nftables.conf.j2
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
#!/usr/sbin/nft -f
|
||||
# Alliance Boréale - nftables Configuration
|
||||
# Generated by Ansible - DO NOT EDIT MANUALLY
|
||||
# Host: {{ inventory_hostname }}
|
||||
# Date: {{ ansible_date_time.iso8601 }}
|
||||
|
||||
# Flush all rules
|
||||
flush ruleset
|
||||
|
||||
# Main table for filtering
|
||||
table inet filter {
|
||||
# Input chain (incoming traffic)
|
||||
chain input {
|
||||
type filter hook input priority filter; policy {{ firewall.default_policy | default('drop') }};
|
||||
|
||||
# Allow established/related connections
|
||||
ct state established,related accept
|
||||
|
||||
# Allow loopback
|
||||
iif lo accept
|
||||
|
||||
# Drop invalid packets
|
||||
ct state invalid drop
|
||||
|
||||
# Allow ICMP (ping)
|
||||
ip protocol icmp icmp type { echo-request, echo-reply } limit rate 10/second accept
|
||||
ip6 nexthdr icmpv6 icmpv6 type { echo-request, echo-reply, nd-neighbor-solicit, nd-neighbor-advert } limit rate 10/second accept
|
||||
|
||||
# Allow SSH
|
||||
tcp dport {{ ssh.port | default(22) }} ct state new limit rate 5/minute accept
|
||||
|
||||
# Allow DNS (if DNS server)
|
||||
{% if 'dns_servers' in group_names %}
|
||||
udp dport 53 accept
|
||||
tcp dport 53 accept
|
||||
{% endif %}
|
||||
|
||||
# Allow PowerDNS API (internal only)
|
||||
{% if 'dns_servers' in group_names %}
|
||||
ip saddr {{ network.internal_subnet | default('10.0.0.0/8') }} tcp dport 8081 accept
|
||||
{% endif %}
|
||||
|
||||
# Log dropped packets (rate limited)
|
||||
limit rate 5/minute log prefix "[nftables INPUT DROP] " level info
|
||||
|
||||
# Default: drop
|
||||
}
|
||||
|
||||
# Forward chain (routed traffic)
|
||||
chain forward {
|
||||
type filter hook forward priority filter; policy drop;
|
||||
|
||||
# Log forwarded packets (if any)
|
||||
# log prefix "[nftables FORWARD] " level info
|
||||
}
|
||||
|
||||
# Output chain (outgoing traffic)
|
||||
chain output {
|
||||
type filter hook output priority filter; policy accept;
|
||||
|
||||
# Allow all outgoing by default
|
||||
}
|
||||
}
|
||||
|
||||
# NAT table (if needed in future)
|
||||
# table ip nat {
|
||||
# chain prerouting {
|
||||
# type nat hook prerouting priority dstnat; policy accept;
|
||||
# }
|
||||
#
|
||||
# chain postrouting {
|
||||
# type nat hook postrouting priority srcnat; policy accept;
|
||||
# }
|
||||
# }
|
||||
53
ansible/roles/common/templates/sshd.conf.j2
Normal file
53
ansible/roles/common/templates/sshd.conf.j2
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# Alliance Boréale - SSH Daemon Configuration
|
||||
# Generated by Ansible - DO NOT EDIT MANUALLY
|
||||
# Host: {{ inventory_hostname }}
|
||||
# Date: {{ ansible_date_time.iso8601 }}
|
||||
|
||||
# Network
|
||||
Port {{ ssh.port | default(22) }}
|
||||
AddressFamily inet
|
||||
ListenAddress 0.0.0.0
|
||||
|
||||
# Authentication
|
||||
PermitRootLogin {{ 'yes' if ssh.permit_root_login | default(false) else 'no' }}
|
||||
PubkeyAuthentication {{ 'yes' if ssh.pubkey_authentication | default(true) else 'no' }}
|
||||
PasswordAuthentication {{ 'yes' if ssh.password_authentication | default(false) else 'no' }}
|
||||
PermitEmptyPasswords no
|
||||
ChallengeResponseAuthentication no
|
||||
|
||||
# Security
|
||||
Protocol 2
|
||||
HostKey /etc/ssh/ssh_host_ed25519_key
|
||||
HostKey /etc/ssh/ssh_host_rsa_key
|
||||
|
||||
# Ciphers and algorithms
|
||||
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
|
||||
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
|
||||
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
|
||||
|
||||
# Login grace time
|
||||
LoginGraceTime 60
|
||||
MaxAuthTries 3
|
||||
MaxSessions 10
|
||||
|
||||
# Forwarding
|
||||
X11Forwarding no
|
||||
AllowTcpForwarding yes
|
||||
PermitTunnel no
|
||||
|
||||
# Keep alive
|
||||
ClientAliveInterval 300
|
||||
ClientAliveCountMax 2
|
||||
|
||||
# Logging
|
||||
SyslogFacility AUTH
|
||||
LogLevel VERBOSE
|
||||
|
||||
# Subsystems
|
||||
Subsystem sftp /usr/lib/openssh/sftp-server
|
||||
|
||||
# Allow specific users
|
||||
AllowUsers {{ users.ansible.name | default('ansible') }}
|
||||
|
||||
# Banner (optional)
|
||||
# Banner /etc/ssh/banner
|
||||
18
ansible/roles/forgejo/handlers/main.yml
Normal file
18
ansible/roles/forgejo/handlers/main.yml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Alliance Boréale - Forgejo Handlers
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
- name: restart forgejo
|
||||
ansible.builtin.systemd:
|
||||
name: forgejo
|
||||
state: restarted
|
||||
|
||||
- name: reload nginx
|
||||
ansible.builtin.systemd:
|
||||
name: nginx
|
||||
state: reloaded
|
||||
|
||||
- name: reload nftables
|
||||
ansible.builtin.systemd:
|
||||
name: nftables
|
||||
state: reloaded
|
||||
98
ansible/roles/forgejo/tasks/database.yml
Normal file
98
ansible/roles/forgejo/tasks/database.yml
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
# Alliance Boréale - Forgejo Tasks (database, nginx, firewall)
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
# ==========================================
|
||||
# DATABASE.YML
|
||||
# ==========================================
|
||||
- name: "🗄️ Ensure PostgreSQL is installed"
|
||||
ansible.builtin.include_role:
|
||||
name: postgresql
|
||||
when: "'postgresql_servers' in group_names"
|
||||
|
||||
- name: "🗄️ Create Forgejo database"
|
||||
community.postgresql.postgresql_db:
|
||||
name: "{{ forgejo.db.name }}"
|
||||
encoding: UTF8
|
||||
state: present
|
||||
become: true
|
||||
become_user: postgres
|
||||
|
||||
- name: "👤 Create Forgejo database user"
|
||||
community.postgresql.postgresql_user:
|
||||
name: "{{ forgejo.db.user }}"
|
||||
password: "{{ forgejo.db.password }}"
|
||||
state: present
|
||||
become: true
|
||||
become_user: postgres
|
||||
no_log: true
|
||||
|
||||
- name: "🔐 Grant privileges to Forgejo user"
|
||||
community.postgresql.postgresql_privs:
|
||||
database: "{{ forgejo.db.name }}"
|
||||
roles: "{{ forgejo.db.user }}"
|
||||
type: database
|
||||
privs: ALL
|
||||
state: present
|
||||
become: true
|
||||
become_user: postgres
|
||||
|
||||
# ==========================================
|
||||
# NGINX.YML
|
||||
# ==========================================
|
||||
- name: "📦 Ensure Nginx is installed"
|
||||
ansible.builtin.apt:
|
||||
name: nginx
|
||||
state: present
|
||||
|
||||
- name: "🔐 Generate self-signed SSL certificate"
|
||||
ansible.builtin.command:
|
||||
cmd: >
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048
|
||||
-keyout /etc/ssl/private/{{ forgejo.hostname }}.key
|
||||
-out /etc/ssl/certs/{{ forgejo.hostname }}.crt
|
||||
-subj "/C=CA/ST=Quebec/L=Montreal/O=Chezlepro/CN={{ forgejo.hostname }}"
|
||||
creates: "/etc/ssl/certs/{{ forgejo.hostname }}.crt"
|
||||
when: nginx.ssl.cert_source == 'self-signed'
|
||||
|
||||
- name: "⚙️ Configure Nginx for Forgejo"
|
||||
ansible.builtin.template:
|
||||
src: nginx-forgejo.conf.j2
|
||||
dest: /etc/nginx/sites-available/forgejo
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
notify: reload nginx
|
||||
|
||||
- name: "🔗 Enable Nginx site"
|
||||
ansible.builtin.file:
|
||||
src: /etc/nginx/sites-available/forgejo
|
||||
dest: /etc/nginx/sites-enabled/forgejo
|
||||
state: link
|
||||
notify: reload nginx
|
||||
|
||||
- name: "✅ Start and enable Nginx"
|
||||
ansible.builtin.systemd:
|
||||
name: nginx
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
# ==========================================
|
||||
# FIREWALL.YML
|
||||
# ==========================================
|
||||
- name: "🔥 Configure firewall for Forgejo"
|
||||
ansible.builtin.blockinfile:
|
||||
path: /etc/nftables.conf
|
||||
marker: "# {mark} ANSIBLE MANAGED - Forgejo"
|
||||
insertbefore: "# Log dropped packets"
|
||||
block: |
|
||||
# Forgejo - Internal HTTP (backend only)
|
||||
ip saddr 127.0.0.1 tcp dport {{ forgejo.http_port }} accept comment "Forgejo HTTP"
|
||||
|
||||
# Forgejo - SSH (Git over SSH)
|
||||
tcp dport {{ forgejo.ssh_port }} accept comment "Forgejo SSH"
|
||||
|
||||
# Nginx - HTTPS (public)
|
||||
tcp dport 443 accept comment "HTTPS (Forgejo via Nginx)"
|
||||
tcp dport 80 accept comment "HTTP redirect"
|
||||
notify: reload nftables
|
||||
131
ansible/roles/forgejo/tasks/main.yml
Normal file
131
ansible/roles/forgejo/tasks/main.yml
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
# Alliance Boréale - Rôle Forgejo
|
||||
# Couche: C4 (Forge & Mutualisation)
|
||||
# Objectif: Déployer Forge Git avec SSO Keycloak
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
- name: "📦 Install dependencies"
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- git
|
||||
- nginx
|
||||
- openssl
|
||||
- python3-psycopg2
|
||||
state: present
|
||||
update_cache: true
|
||||
tags: forgejo
|
||||
|
||||
- name: "👤 Create git user"
|
||||
ansible.builtin.user:
|
||||
name: "{{ forgejo.user }}"
|
||||
system: true
|
||||
shell: /bin/bash
|
||||
home: "{{ forgejo.data_dir }}"
|
||||
create_home: true
|
||||
tags: forgejo
|
||||
|
||||
- name: "📁 Create forgejo directories"
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: "{{ forgejo.user }}"
|
||||
group: "{{ forgejo.group }}"
|
||||
mode: '0755'
|
||||
loop:
|
||||
- "{{ forgejo.install_dir }}"
|
||||
- "{{ forgejo.custom_dir }}"
|
||||
- "{{ forgejo.log_dir }}"
|
||||
- "{{ forgejo.data_dir }}/git"
|
||||
- "{{ forgejo.data_dir }}/repositories"
|
||||
tags: forgejo
|
||||
|
||||
- name: "📥 Download Forgejo binary"
|
||||
ansible.builtin.get_url:
|
||||
url: "https://codeberg.org/forgejo/forgejo/releases/download/v{{ forgejo.version }}/forgejo-{{ forgejo.version }}-linux-amd64"
|
||||
dest: "{{ forgejo.install_dir }}/forgejo"
|
||||
owner: "{{ forgejo.user }}"
|
||||
group: "{{ forgejo.group }}"
|
||||
mode: '0755'
|
||||
tags: forgejo
|
||||
|
||||
- name: "🗄️ Configure PostgreSQL for Forgejo"
|
||||
ansible.builtin.import_tasks: database.yml
|
||||
tags: forgejo
|
||||
|
||||
- name: "⚙️ Configure Forgejo"
|
||||
ansible.builtin.template:
|
||||
src: app.ini.j2
|
||||
dest: "{{ forgejo.custom_dir }}/app.ini"
|
||||
owner: "{{ forgejo.user }}"
|
||||
group: "{{ forgejo.group }}"
|
||||
mode: '0640'
|
||||
notify: restart forgejo
|
||||
tags: forgejo
|
||||
|
||||
- name: "⚙️ Create systemd service"
|
||||
ansible.builtin.template:
|
||||
src: forgejo.service.j2
|
||||
dest: /etc/systemd/system/forgejo.service
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
notify: restart forgejo
|
||||
tags: forgejo
|
||||
|
||||
- name: "✅ Start and enable Forgejo"
|
||||
ansible.builtin.systemd:
|
||||
name: forgejo
|
||||
state: started
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
tags: forgejo
|
||||
|
||||
- name: "⏳ Wait for Forgejo to be ready"
|
||||
ansible.builtin.wait_for:
|
||||
port: "{{ forgejo.http_port }}"
|
||||
host: 127.0.0.1
|
||||
timeout: 60
|
||||
tags: forgejo
|
||||
|
||||
- name: "🌐 Configure Nginx reverse proxy"
|
||||
ansible.builtin.import_tasks: nginx.yml
|
||||
tags: forgejo
|
||||
|
||||
- name: "👑 Create admin user"
|
||||
ansible.builtin.command:
|
||||
cmd: >
|
||||
{{ forgejo.install_dir }}/forgejo admin user create
|
||||
--username {{ forgejo.admin.username }}
|
||||
--password {{ forgejo.admin.password }}
|
||||
--email {{ forgejo.admin.email }}
|
||||
--admin
|
||||
--config {{ forgejo.custom_dir }}/app.ini
|
||||
creates: "{{ forgejo.data_dir }}/.admin_created"
|
||||
become: true
|
||||
become_user: "{{ forgejo.user }}"
|
||||
register: admin_created
|
||||
no_log: true
|
||||
tags: forgejo
|
||||
|
||||
- name: "✅ Mark admin as created"
|
||||
ansible.builtin.file:
|
||||
path: "{{ forgejo.data_dir }}/.admin_created"
|
||||
state: touch
|
||||
owner: "{{ forgejo.user }}"
|
||||
group: "{{ forgejo.group }}"
|
||||
mode: '0644'
|
||||
when: admin_created.changed
|
||||
tags: forgejo
|
||||
|
||||
- name: "🔥 Configure firewall"
|
||||
ansible.builtin.import_tasks: firewall.yml
|
||||
tags: forgejo
|
||||
|
||||
- name: "✅ Forgejo role completed"
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
✅ Forgejo deployed on {{ inventory_hostname }}
|
||||
URL: https://{{ forgejo.hostname }}
|
||||
SSH: git@{{ forgejo.hostname }}:{{ forgejo.ssh_port }}
|
||||
Admin: {{ forgejo.admin.username }}
|
||||
tags: forgejo
|
||||
100
ansible/roles/forgejo/templates/app.ini.j2
Normal file
100
ansible/roles/forgejo/templates/app.ini.j2
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
# Alliance Boréale - Forgejo Configuration
|
||||
# Generated by Ansible - DO NOT EDIT MANUALLY
|
||||
# Host: {{ inventory_hostname }}
|
||||
# Date: {{ ansible_date_time.iso8601 }}
|
||||
|
||||
APP_NAME = L'Alliance Boréale - Forge Git
|
||||
RUN_MODE = prod
|
||||
RUN_USER = {{ forgejo.user }}
|
||||
|
||||
[server]
|
||||
DOMAIN = {{ forgejo.hostname }}
|
||||
HTTP_PORT = {{ forgejo.http_port }}
|
||||
ROOT_URL = https://{{ forgejo.hostname }}/
|
||||
DISABLE_SSH = false
|
||||
SSH_DOMAIN = {{ forgejo.hostname }}
|
||||
SSH_PORT = {{ forgejo.ssh_port }}
|
||||
SSH_LISTEN_PORT = {{ forgejo.ssh_port }}
|
||||
START_SSH_SERVER = true
|
||||
LFS_START_SERVER = {{ forgejo.features.enable_lfs | lower }}
|
||||
OFFLINE_MODE = false
|
||||
|
||||
[database]
|
||||
DB_TYPE = {{ forgejo.db.type }}
|
||||
HOST = {{ forgejo.db.host }}
|
||||
NAME = {{ forgejo.db.name }}
|
||||
USER = {{ forgejo.db.user }}
|
||||
PASSWD = {{ forgejo.db.password }}
|
||||
SSL_MODE = {{ forgejo.db.ssl_mode }}
|
||||
CHARSET = utf8mb4
|
||||
LOG_SQL = false
|
||||
|
||||
[repository]
|
||||
ROOT = {{ forgejo.data_dir }}/repositories
|
||||
DEFAULT_BRANCH = main
|
||||
PREFERRED_LICENSES = MIT,Apache-2.0,GPL-3.0
|
||||
DISABLE_HTTP_GIT = false
|
||||
ACCESS_CONTROL_ALLOW_ORIGIN = https://{{ forgejo.hostname }}
|
||||
|
||||
[security]
|
||||
INSTALL_LOCK = true
|
||||
SECRET_KEY = {{ forgejo.secret_key }}
|
||||
INTERNAL_TOKEN = {{ forgejo.internal_token }}
|
||||
PASSWORD_HASH_ALGO = argon2
|
||||
MIN_PASSWORD_LENGTH = 12
|
||||
|
||||
[service]
|
||||
DISABLE_REGISTRATION = {{ forgejo.features.disable_registration | lower }}
|
||||
REQUIRE_SIGNIN_VIEW = {{ forgejo.features.require_signin | lower }}
|
||||
REGISTER_EMAIL_CONFIRM = false
|
||||
ENABLE_NOTIFY_MAIL = false
|
||||
DEFAULT_KEEP_EMAIL_PRIVATE = true
|
||||
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
|
||||
DEFAULT_ENABLE_TIMETRACKING = true
|
||||
NO_REPLY_ADDRESS = noreply@{{ forgejo.domain }}
|
||||
|
||||
[mailer]
|
||||
ENABLED = false
|
||||
|
||||
[session]
|
||||
PROVIDER = file
|
||||
PROVIDER_CONFIG = {{ forgejo.data_dir }}/sessions
|
||||
|
||||
[picture]
|
||||
DISABLE_GRAVATAR = false
|
||||
ENABLE_FEDERATED_AVATAR = false
|
||||
|
||||
[log]
|
||||
MODE = console, file
|
||||
LEVEL = Info
|
||||
ROOT_PATH = {{ forgejo.log_dir }}
|
||||
|
||||
[git]
|
||||
MAX_GIT_DIFF_LINES = 10000
|
||||
MAX_GIT_DIFF_LINE_CHARACTERS = 5000
|
||||
MAX_GIT_DIFF_FILES = 100
|
||||
|
||||
[actions]
|
||||
ENABLED = {{ forgejo.features.enable_actions | lower }}
|
||||
|
||||
[packages]
|
||||
ENABLED = {{ forgejo.features.enable_packages | lower }}
|
||||
|
||||
{% if forgejo.oauth.enabled %}
|
||||
# OAuth2 / OpenID Connect (Keycloak)
|
||||
[oauth2_client]
|
||||
REGISTER_EMAIL_CONFIRM = false
|
||||
ENABLE_AUTO_REGISTRATION = true
|
||||
USERNAME = preferred_username
|
||||
UPDATE_AVATAR = true
|
||||
ACCOUNT_LINKING = auto
|
||||
|
||||
{% for provider in forgejo.oauth.providers %}
|
||||
[oauth2.{{ provider.name }}]
|
||||
ENABLED = true
|
||||
PROVIDER = {{ provider.provider }}
|
||||
CLIENT_ID = {{ provider.client_id }}
|
||||
CLIENT_SECRET = {{ provider.client_secret }}
|
||||
OPENID_CONNECT_AUTO_DISCOVERY_URL = {{ provider.openid_connect_auto_discovery_url }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
91
ansible/roles/forgejo/templates/forgejo.service.j2
Normal file
91
ansible/roles/forgejo/templates/forgejo.service.j2
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
# Alliance Boréale - Forgejo Systemd Service
|
||||
# Generated by Ansible
|
||||
|
||||
[Unit]
|
||||
Description=Forgejo Git Service
|
||||
After=network.target postgresql.service
|
||||
Wants=postgresql.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User={{ forgejo.user }}
|
||||
Group={{ forgejo.group }}
|
||||
WorkingDirectory={{ forgejo.data_dir }}
|
||||
ExecStart={{ forgejo.install_dir }}/forgejo web --config {{ forgejo.custom_dir }}/app.ini
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
|
||||
# Security
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths={{ forgejo.data_dir }} {{ forgejo.log_dir }}
|
||||
|
||||
# Limits
|
||||
LimitNOFILE=65536
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
---SEPARATOR---
|
||||
|
||||
# Alliance Boréale - Nginx Configuration for Forgejo
|
||||
# Generated by Ansible
|
||||
|
||||
upstream forgejo_backend {
|
||||
server 127.0.0.1:{{ forgejo.http_port }};
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name {{ forgejo.hostname }};
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name {{ forgejo.hostname }};
|
||||
|
||||
ssl_certificate /etc/ssl/certs/{{ forgejo.hostname }}.crt;
|
||||
ssl_certificate_key /etc/ssl/private/{{ forgejo.hostname }}.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
# Large uploads (Git LFS)
|
||||
client_max_body_size 512M;
|
||||
|
||||
location / {
|
||||
proxy_pass http://forgejo_backend;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# WebSocket support (for Actions)
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
|
||||
---SEPARATOR---
|
||||
|
||||
# Alliance Boréale - Forgejo Handlers
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
- name: restart forgejo
|
||||
ansible.builtin.systemd:
|
||||
name: forgejo
|
||||
state: restarted
|
||||
|
||||
- name: reload nginx
|
||||
ansible.builtin.systemd:
|
||||
name: nginx
|
||||
state: reloaded
|
||||
|
||||
- name: reload nftables
|
||||
ansible.builtin.systemd:
|
||||
name: nftables
|
||||
state: reloaded
|
||||
36
ansible/roles/forgejo/templates/nginx-forgejo.conf.j2
Normal file
36
ansible/roles/forgejo/templates/nginx-forgejo.conf.j2
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Alliance Boréale - Nginx Forgejo
|
||||
# Generated by Ansible
|
||||
|
||||
upstream forgejo_backend {
|
||||
server 127.0.0.1:{{ forgejo.http_port }};
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name {{ forgejo.hostname }};
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name {{ forgejo.hostname }};
|
||||
|
||||
ssl_certificate /etc/ssl/certs/{{ forgejo.hostname }}.crt;
|
||||
ssl_certificate_key /etc/ssl/private/{{ forgejo.hostname }}.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
client_max_body_size 512M;
|
||||
|
||||
location / {
|
||||
proxy_pass http://forgejo_backend;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
18
ansible/roles/keycloak/handlers/main.yml
Normal file
18
ansible/roles/keycloak/handlers/main.yml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Alliance Boréale - Keycloak Handlers
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
- name: restart keycloak
|
||||
ansible.builtin.systemd:
|
||||
name: keycloak
|
||||
state: restarted
|
||||
|
||||
- name: reload nginx
|
||||
ansible.builtin.systemd:
|
||||
name: nginx
|
||||
state: reloaded
|
||||
|
||||
- name: reload nftables
|
||||
ansible.builtin.systemd:
|
||||
name: nftables
|
||||
state: reloaded
|
||||
140
ansible/roles/keycloak/tasks/database.yml
Normal file
140
ansible/roles/keycloak/tasks/database.yml
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
# Alliance Boréale - Keycloak Tasks (database, nginx, realms, firewall)
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
# ==========================================
|
||||
# DATABASE.YML
|
||||
# ==========================================
|
||||
- name: "🗄️ Ensure PostgreSQL is installed"
|
||||
ansible.builtin.include_role:
|
||||
name: postgresql
|
||||
when: "'postgresql_servers' in group_names"
|
||||
|
||||
- name: "🗄️ Create Keycloak database"
|
||||
community.postgresql.postgresql_db:
|
||||
name: "{{ keycloak.db.database }}"
|
||||
encoding: UTF8
|
||||
state: present
|
||||
become: true
|
||||
become_user: postgres
|
||||
|
||||
- name: "👤 Create Keycloak database user"
|
||||
community.postgresql.postgresql_user:
|
||||
name: "{{ keycloak.db.username }}"
|
||||
password: "{{ keycloak.db.password }}"
|
||||
state: present
|
||||
become: true
|
||||
become_user: postgres
|
||||
no_log: true
|
||||
|
||||
- name: "🔐 Grant privileges to Keycloak user"
|
||||
community.postgresql.postgresql_privs:
|
||||
database: "{{ keycloak.db.database }}"
|
||||
roles: "{{ keycloak.db.username }}"
|
||||
type: database
|
||||
privs: ALL
|
||||
state: present
|
||||
become: true
|
||||
become_user: postgres
|
||||
|
||||
# ==========================================
|
||||
# NGINX.YML
|
||||
# ==========================================
|
||||
- name: "📦 Ensure Nginx is installed"
|
||||
ansible.builtin.apt:
|
||||
name: nginx
|
||||
state: present
|
||||
|
||||
- name: "🔐 Generate self-signed SSL certificate"
|
||||
ansible.builtin.command:
|
||||
cmd: >
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048
|
||||
-keyout /etc/ssl/private/{{ keycloak.hostname }}.key
|
||||
-out /etc/ssl/certs/{{ keycloak.hostname }}.crt
|
||||
-subj "/C=CA/ST=Quebec/L=Montreal/O=Chezlepro/CN={{ keycloak.hostname }}"
|
||||
creates: "/etc/ssl/certs/{{ keycloak.hostname }}.crt"
|
||||
when: nginx.ssl.cert_source == 'self-signed'
|
||||
|
||||
- name: "⚙️ Configure Nginx for Keycloak"
|
||||
ansible.builtin.template:
|
||||
src: nginx-keycloak.conf.j2
|
||||
dest: /etc/nginx/sites-available/keycloak
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
notify: reload nginx
|
||||
|
||||
- name: "🔗 Enable Nginx site"
|
||||
ansible.builtin.file:
|
||||
src: /etc/nginx/sites-available/keycloak
|
||||
dest: /etc/nginx/sites-enabled/keycloak
|
||||
state: link
|
||||
notify: reload nginx
|
||||
|
||||
- name: "🚫 Remove default Nginx site"
|
||||
ansible.builtin.file:
|
||||
path: /etc/nginx/sites-enabled/default
|
||||
state: absent
|
||||
notify: reload nginx
|
||||
|
||||
- name: "✅ Start and enable Nginx"
|
||||
ansible.builtin.systemd:
|
||||
name: nginx
|
||||
state: started
|
||||
enabled: true
|
||||
|
||||
# ==========================================
|
||||
# REALMS.YML
|
||||
# ==========================================
|
||||
- name: "🏰 Check if realm exists"
|
||||
ansible.builtin.uri:
|
||||
url: "http://127.0.0.1:{{ keycloak.http_port }}/admin/realms/{{ item.name }}"
|
||||
method: GET
|
||||
user: "{{ keycloak.admin_user }}"
|
||||
password: "{{ keycloak.admin_password }}"
|
||||
force_basic_auth: true
|
||||
status_code: [200, 404]
|
||||
loop: "{{ keycloak.realms }}"
|
||||
register: realm_check
|
||||
changed_when: false
|
||||
no_log: true
|
||||
|
||||
- name: "🏰 Create realm configuration file"
|
||||
ansible.builtin.template:
|
||||
src: realm-config.json.j2
|
||||
dest: "/tmp/realm-{{ item.name }}.json"
|
||||
owner: keycloak
|
||||
group: keycloak
|
||||
mode: '0640'
|
||||
loop: "{{ keycloak.realms }}"
|
||||
when: realm_check.results[0].status == 404
|
||||
|
||||
- name: "🏰 Import realm"
|
||||
ansible.builtin.command:
|
||||
cmd: >
|
||||
/opt/keycloak/bin/kc.sh import
|
||||
--file /tmp/realm-{{ item.name }}.json
|
||||
--override false
|
||||
become: true
|
||||
become_user: keycloak
|
||||
loop: "{{ keycloak.realms }}"
|
||||
when: realm_check.results[0].status == 404
|
||||
register: realm_import
|
||||
changed_when: "'imported' in realm_import.stdout"
|
||||
|
||||
# ==========================================
|
||||
# FIREWALL.YML
|
||||
# ==========================================
|
||||
- name: "🔥 Configure firewall for Keycloak"
|
||||
ansible.builtin.blockinfile:
|
||||
path: /etc/nftables.conf
|
||||
marker: "# {mark} ANSIBLE MANAGED - Keycloak"
|
||||
insertbefore: "# Log dropped packets"
|
||||
block: |
|
||||
# Keycloak - Internal HTTP (backend only)
|
||||
ip saddr 127.0.0.1 tcp dport {{ keycloak.http_port }} accept comment "Keycloak HTTP"
|
||||
|
||||
# Nginx - HTTPS (public)
|
||||
tcp dport 443 accept comment "HTTPS (Keycloak via Nginx)"
|
||||
tcp dport 80 accept comment "HTTP redirect"
|
||||
notify: reload nftables
|
||||
140
ansible/roles/keycloak/tasks/main.yml
Normal file
140
ansible/roles/keycloak/tasks/main.yml
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
# Alliance Boréale - Rôle Keycloak
|
||||
# Couche: C3 (Gouvernance & Supervision)
|
||||
# Objectif: Déployer IdP fédéré (SSO)
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
- name: "📦 Install dependencies"
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- openjdk-17-jre-headless
|
||||
- python3-pip
|
||||
- python3-psycopg2
|
||||
- nginx
|
||||
- openssl
|
||||
state: present
|
||||
update_cache: true
|
||||
tags: keycloak
|
||||
|
||||
- name: "👤 Create keycloak user"
|
||||
ansible.builtin.user:
|
||||
name: keycloak
|
||||
system: true
|
||||
shell: /bin/false
|
||||
home: /opt/keycloak
|
||||
create_home: false
|
||||
tags: keycloak
|
||||
|
||||
- name: "📁 Create keycloak directories"
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: keycloak
|
||||
group: keycloak
|
||||
mode: '0755'
|
||||
loop:
|
||||
- /opt/keycloak
|
||||
- /var/log/keycloak
|
||||
tags: keycloak
|
||||
|
||||
- name: "📥 Download Keycloak"
|
||||
ansible.builtin.get_url:
|
||||
url: "https://github.com/keycloak/keycloak/releases/download/{{ keycloak.version }}/keycloak-{{ keycloak.version }}.tar.gz"
|
||||
dest: "/tmp/keycloak-{{ keycloak.version }}.tar.gz"
|
||||
mode: '0644'
|
||||
tags: keycloak
|
||||
|
||||
- name: "📦 Extract Keycloak"
|
||||
ansible.builtin.unarchive:
|
||||
src: "/tmp/keycloak-{{ keycloak.version }}.tar.gz"
|
||||
dest: /opt/keycloak
|
||||
remote_src: true
|
||||
owner: keycloak
|
||||
group: keycloak
|
||||
extra_opts: [--strip-components=1]
|
||||
creates: /opt/keycloak/bin/kc.sh
|
||||
tags: keycloak
|
||||
|
||||
- name: "🗄️ Configure PostgreSQL for Keycloak"
|
||||
ansible.builtin.import_tasks: database.yml
|
||||
tags: keycloak
|
||||
|
||||
- name: "⚙️ Configure Keycloak"
|
||||
ansible.builtin.template:
|
||||
src: keycloak.conf.j2
|
||||
dest: /opt/keycloak/conf/keycloak.conf
|
||||
owner: keycloak
|
||||
group: keycloak
|
||||
mode: '0640'
|
||||
notify: restart keycloak
|
||||
tags: keycloak
|
||||
|
||||
- name: "🔨 Build Keycloak"
|
||||
ansible.builtin.command:
|
||||
cmd: /opt/keycloak/bin/kc.sh build
|
||||
become: true
|
||||
become_user: keycloak
|
||||
args:
|
||||
creates: /opt/keycloak/lib/quarkus/quarkus-application.dat
|
||||
tags: keycloak
|
||||
|
||||
- name: "⚙️ Create systemd service"
|
||||
ansible.builtin.template:
|
||||
src: keycloak.service.j2
|
||||
dest: /etc/systemd/system/keycloak.service
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
notify: restart keycloak
|
||||
tags: keycloak
|
||||
|
||||
- name: "✅ Start and enable Keycloak"
|
||||
ansible.builtin.systemd:
|
||||
name: keycloak
|
||||
state: started
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
tags: keycloak
|
||||
|
||||
- name: "⏳ Wait for Keycloak to be ready"
|
||||
ansible.builtin.wait_for:
|
||||
port: "{{ keycloak.http_port }}"
|
||||
host: 127.0.0.1
|
||||
timeout: 120
|
||||
tags: keycloak
|
||||
|
||||
- name: "🌐 Configure Nginx reverse proxy"
|
||||
ansible.builtin.import_tasks: nginx.yml
|
||||
tags: keycloak
|
||||
|
||||
- name: "👑 Create admin user"
|
||||
ansible.builtin.command:
|
||||
cmd: >
|
||||
/opt/keycloak/bin/kcadm.sh config credentials
|
||||
--server http://localhost:{{ keycloak.http_port }}
|
||||
--realm master
|
||||
--user {{ keycloak.admin_user }}
|
||||
--password {{ keycloak.admin_password }}
|
||||
become: true
|
||||
become_user: keycloak
|
||||
register: admin_created
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
no_log: true
|
||||
tags: keycloak
|
||||
|
||||
- name: "🏰 Configure realms and clients"
|
||||
ansible.builtin.import_tasks: realms.yml
|
||||
tags: keycloak
|
||||
|
||||
- name: "🔥 Configure firewall"
|
||||
ansible.builtin.import_tasks: firewall.yml
|
||||
tags: keycloak
|
||||
|
||||
- name: "✅ Keycloak role completed"
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
✅ Keycloak deployed on {{ inventory_hostname }}
|
||||
URL: https://{{ keycloak.hostname }}
|
||||
Admin: {{ keycloak.admin_user }}
|
||||
tags: keycloak
|
||||
25
ansible/roles/keycloak/templates/keycloak.conf.j2
Normal file
25
ansible/roles/keycloak/templates/keycloak.conf.j2
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Alliance Boréale - Keycloak Configuration
|
||||
# Generated by Ansible - DO NOT EDIT MANUALLY
|
||||
# Host: {{ inventory_hostname }}
|
||||
# Date: {{ ansible_date_time.iso8601 }}
|
||||
|
||||
# Network
|
||||
hostname={{ keycloak.hostname }}
|
||||
http-enabled=true
|
||||
http-port={{ keycloak.http_port }}
|
||||
http-host=0.0.0.0
|
||||
proxy=edge
|
||||
|
||||
# Database
|
||||
db=postgres
|
||||
db-url=jdbc:postgresql://{{ keycloak.db.host }}/{{ keycloak.db.database }}
|
||||
db-username={{ keycloak.db.username }}
|
||||
db-password={{ keycloak.db.password }}
|
||||
|
||||
# Logging
|
||||
log-level=INFO
|
||||
log-console-output=default
|
||||
|
||||
# Health
|
||||
health-enabled=true
|
||||
metrics-enabled=true
|
||||
83
ansible/roles/keycloak/templates/keycloak.service.j2
Normal file
83
ansible/roles/keycloak/templates/keycloak.service.j2
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
# Alliance Boréale - Keycloak Systemd Service
|
||||
# Generated by Ansible
|
||||
|
||||
[Unit]
|
||||
Description=Keycloak Identity Provider
|
||||
After=network.target postgresql.service
|
||||
Wants=postgresql.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=keycloak
|
||||
Group=keycloak
|
||||
WorkingDirectory=/opt/keycloak
|
||||
ExecStart=/opt/keycloak/bin/kc.sh start
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
|
||||
# Security
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/var/log/keycloak /opt/keycloak/data
|
||||
|
||||
# Limits
|
||||
LimitNOFILE=65536
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
---SEPARATOR---
|
||||
|
||||
# Alliance Boréale - Nginx Configuration for Keycloak
|
||||
# Generated by Ansible
|
||||
|
||||
upstream keycloak_backend {
|
||||
server 127.0.0.1:{{ keycloak.http_port }};
|
||||
}
|
||||
|
||||
# HTTP -> HTTPS redirect
|
||||
server {
|
||||
listen 80;
|
||||
server_name {{ keycloak.hostname }};
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
# HTTPS
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name {{ keycloak.hostname }};
|
||||
|
||||
# SSL
|
||||
ssl_certificate /etc/ssl/certs/{{ keycloak.hostname }}.crt;
|
||||
ssl_certificate_key /etc/ssl/private/{{ keycloak.hostname }}.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
# Logging
|
||||
access_log /var/log/nginx/keycloak-access.log;
|
||||
error_log /var/log/nginx/keycloak-error.log;
|
||||
|
||||
# Proxy settings
|
||||
location / {
|
||||
proxy_pass http://keycloak_backend;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
|
||||
# Buffers
|
||||
proxy_buffer_size 128k;
|
||||
proxy_buffers 4 256k;
|
||||
proxy_busy_buffers_size 256k;
|
||||
|
||||
# Timeouts
|
||||
proxy_connect_timeout 300;
|
||||
proxy_send_timeout 300;
|
||||
proxy_read_timeout 300;
|
||||
}
|
||||
}
|
||||
30
ansible/roles/keycloak/templates/nginx-keycloak.conf.j2
Normal file
30
ansible/roles/keycloak/templates/nginx-keycloak.conf.j2
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# Alliance Boréale - Nginx Keycloak
|
||||
# Generated by Ansible
|
||||
|
||||
upstream keycloak_backend {
|
||||
server 127.0.0.1:{{ keycloak.http_port }};
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name {{ keycloak.hostname }};
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name {{ keycloak.hostname }};
|
||||
|
||||
ssl_certificate /etc/ssl/certs/{{ keycloak.hostname }}.crt;
|
||||
ssl_certificate_key /etc/ssl/private/{{ keycloak.hostname }}.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
location / {
|
||||
proxy_pass http://keycloak_backend;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
48
ansible/roles/keycloak/templates/realm-config.json.j2
Normal file
48
ansible/roles/keycloak/templates/realm-config.json.j2
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"realm": "{{ item.name }}",
|
||||
"displayName": "{{ item.display_name }}",
|
||||
"enabled": {{ item.enabled | lower }},
|
||||
"sslRequired": "external",
|
||||
"registrationAllowed": false,
|
||||
"loginWithEmailAllowed": true,
|
||||
"duplicateEmailsAllowed": false,
|
||||
"resetPasswordAllowed": true,
|
||||
"editUsernameAllowed": false,
|
||||
"bruteForceProtected": true,
|
||||
"clients": [
|
||||
{% for client in item.clients %}
|
||||
{
|
||||
"clientId": "{{ client.client_id }}",
|
||||
"name": "{{ client.name }}",
|
||||
"enabled": {{ client.enabled | lower }},
|
||||
"protocol": "{{ client.protocol }}",
|
||||
"publicClient": false,
|
||||
"redirectUris": {{ client.redirect_uris | to_json }},
|
||||
"webOrigins": {{ client.web_origins | to_json }},
|
||||
"standardFlowEnabled": true,
|
||||
"directAccessGrantsEnabled": false
|
||||
}{{ "," if not loop.last else "" }}
|
||||
{% endfor %}
|
||||
]
|
||||
}
|
||||
|
||||
---SEPARATOR---
|
||||
|
||||
# Alliance Boréale - Keycloak Handlers
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
- name: restart keycloak
|
||||
ansible.builtin.systemd:
|
||||
name: keycloak
|
||||
state: restarted
|
||||
|
||||
- name: reload nginx
|
||||
ansible.builtin.systemd:
|
||||
name: nginx
|
||||
state: reloaded
|
||||
|
||||
- name: reload nftables
|
||||
ansible.builtin.systemd:
|
||||
name: nftables
|
||||
state: reloaded
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
Role Name
|
||||
=========
|
||||
|
||||
A brief description of the role goes here.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required.
|
||||
|
||||
Role Variables
|
||||
--------------
|
||||
|
||||
A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well.
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles.
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
|
||||
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
|
||||
|
||||
- hosts: servers
|
||||
roles:
|
||||
- { role: username.rolename, x: 42 }
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
BSD
|
||||
|
||||
Author Information
|
||||
------------------
|
||||
|
||||
An optional section for the role authors to include contact information, or a website (HTML is not allowed).
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
#SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# defaults file for netbox
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
#SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# handlers file for netbox
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
#SPDX-License-Identifier: MIT-0
|
||||
galaxy_info:
|
||||
author: your name
|
||||
description: your role description
|
||||
company: your company (optional)
|
||||
|
||||
# If the issue tracker for your role is not on github, uncomment the
|
||||
# next line and provide a value
|
||||
# issue_tracker_url: http://example.com/issue/tracker
|
||||
|
||||
# Choose a valid license ID from https://spdx.org - some suggested licenses:
|
||||
# - BSD-3-Clause (default)
|
||||
# - MIT
|
||||
# - GPL-2.0-or-later
|
||||
# - GPL-3.0-only
|
||||
# - Apache-2.0
|
||||
# - CC-BY-4.0
|
||||
license: license (GPL-2.0-or-later, MIT, etc)
|
||||
|
||||
min_ansible_version: 2.1
|
||||
|
||||
# If this a Container Enabled role, provide the minimum Ansible Container version.
|
||||
# min_ansible_container_version:
|
||||
|
||||
galaxy_tags: []
|
||||
# List tags for your role here, one per line. A tag is a keyword that describes
|
||||
# and categorizes the role. Users find roles by searching for tags. Be sure to
|
||||
# remove the '[]' above, if you add tags to this list.
|
||||
#
|
||||
# NOTE: A tag is limited to a single word comprised of alphanumeric characters.
|
||||
# Maximum 20 tags per role.
|
||||
|
||||
dependencies: []
|
||||
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
|
||||
# if you add dependencies to this list.
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
---
|
||||
- name: Vérifier que le rôle est lancé sur un hôte netbox
|
||||
assert:
|
||||
that: "'netbox' in group_names"
|
||||
msg: "Ce rôle ne s'applique qu'aux hôtes du groupe [netbox]"
|
||||
|
||||
- name: Inclure les variables chiffrées (Vault)
|
||||
include_vars:
|
||||
file: ../../inventories/production/group_vars/vault.yml
|
||||
when: vault_postgres_pass is not defined
|
||||
|
||||
- name: Message de bienvenue
|
||||
debug:
|
||||
msg: |
|
||||
Déploiement NetBox sur {{ inventory_hostname }}
|
||||
Domaine : {{ netbox_domain }}
|
||||
Mode offline : {{ offline_mode }}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
#SPDX-License-Identifier: MIT-0
|
||||
localhost
|
||||
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
#SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
- hosts: localhost
|
||||
remote_user: root
|
||||
roles:
|
||||
- netbox
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
#SPDX-License-Identifier: MIT-0
|
||||
---
|
||||
# vars file for netbox
|
||||
22
ansible/roles/postgresql/defaults/main.yml
Normal file
22
ansible/roles/postgresql/defaults/main.yml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# Alliance Boréale - Rôle PostgreSQL - Default Variables
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
postgresql:
|
||||
version: 15
|
||||
listen_addresses: localhost
|
||||
port: 5432
|
||||
|
||||
shared_buffers: 256MB
|
||||
work_mem: 16MB
|
||||
maintenance_work_mem: 128MB
|
||||
effective_cache_size: 1GB
|
||||
|
||||
databases: []
|
||||
users: []
|
||||
|
||||
backup:
|
||||
enabled: true
|
||||
schedule: "0 2 * * *"
|
||||
retention_days: 7
|
||||
destination: /var/backups/postgresql
|
||||
13
ansible/roles/postgresql/handlers/main.yml
Normal file
13
ansible/roles/postgresql/handlers/main.yml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# Alliance Boréale - Rôle PostgreSQL - Handlers
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
- name: restart postgresql
|
||||
ansible.builtin.systemd:
|
||||
name: postgresql
|
||||
state: restarted
|
||||
|
||||
- name: reload postgresql
|
||||
ansible.builtin.systemd:
|
||||
name: postgresql
|
||||
state: reloaded
|
||||
114
ansible/roles/postgresql/tasks/main.yml
Normal file
114
ansible/roles/postgresql/tasks/main.yml
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
# Alliance Boréale - Rôle PostgreSQL
|
||||
# Couche: C3 (dépendance pour PowerDNS)
|
||||
# Objectif: Déployer PostgreSQL pour backend PowerDNS
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
- name: "📦 Install PostgreSQL and dependencies"
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- postgresql-{{ postgresql.version }}
|
||||
- postgresql-contrib-{{ postgresql.version }}
|
||||
- python3-psycopg2
|
||||
state: present
|
||||
update_cache: true
|
||||
tags: postgresql
|
||||
|
||||
- name: "⚙️ Configure PostgreSQL"
|
||||
ansible.builtin.template:
|
||||
src: postgresql.conf.j2
|
||||
dest: "/etc/postgresql/{{ postgresql.version }}/main/postgresql.conf"
|
||||
owner: postgres
|
||||
group: postgres
|
||||
mode: '0644'
|
||||
notify: restart postgresql
|
||||
tags: postgresql
|
||||
|
||||
- name: "⚙️ Configure pg_hba.conf"
|
||||
ansible.builtin.template:
|
||||
src: pg_hba.conf.j2
|
||||
dest: "/etc/postgresql/{{ postgresql.version }}/main/pg_hba.conf"
|
||||
owner: postgres
|
||||
group: postgres
|
||||
mode: '0640'
|
||||
notify: restart postgresql
|
||||
tags: postgresql
|
||||
|
||||
- name: "✅ Ensure PostgreSQL is started"
|
||||
ansible.builtin.systemd:
|
||||
name: postgresql
|
||||
state: started
|
||||
enabled: true
|
||||
tags: postgresql
|
||||
|
||||
- name: "🗄️ Create PostgreSQL databases"
|
||||
community.postgresql.postgresql_db:
|
||||
name: "{{ item.name }}"
|
||||
encoding: "{{ item.encoding | default('UTF8') }}"
|
||||
lc_collate: "{{ item.lc_collate | default('en_US.UTF-8') }}"
|
||||
lc_ctype: "{{ item.lc_ctype | default('en_US.UTF-8') }}"
|
||||
state: present
|
||||
loop: "{{ postgresql.databases }}"
|
||||
become: true
|
||||
become_user: postgres
|
||||
tags: postgresql
|
||||
|
||||
- name: "👤 Create PostgreSQL users"
|
||||
community.postgresql.postgresql_user:
|
||||
name: "{{ item.name }}"
|
||||
password: "{{ item.password }}"
|
||||
state: present
|
||||
loop: "{{ postgresql.users }}"
|
||||
become: true
|
||||
become_user: postgres
|
||||
no_log: true
|
||||
tags: postgresql
|
||||
|
||||
- name: "🔐 Grant database privileges"
|
||||
community.postgresql.postgresql_privs:
|
||||
database: "{{ item.name }}"
|
||||
roles: "{{ item.owner }}"
|
||||
type: database
|
||||
privs: ALL
|
||||
state: present
|
||||
loop: "{{ postgresql.databases }}"
|
||||
become: true
|
||||
become_user: postgres
|
||||
tags: postgresql
|
||||
|
||||
- name: "📊 Create backup directory"
|
||||
ansible.builtin.file:
|
||||
path: "{{ postgresql.backup.destination }}"
|
||||
state: directory
|
||||
owner: postgres
|
||||
group: postgres
|
||||
mode: '0750'
|
||||
when: postgresql.backup.enabled | default(true)
|
||||
tags: postgresql
|
||||
|
||||
- name: "📊 Configure backup cron job"
|
||||
ansible.builtin.cron:
|
||||
name: "PostgreSQL backup"
|
||||
user: postgres
|
||||
minute: "0"
|
||||
hour: "2"
|
||||
job: "pg_dumpall | gzip > {{ postgresql.backup.destination }}/postgres-$(date +\\%Y\\%m\\%d).sql.gz"
|
||||
state: present
|
||||
when: postgresql.backup.enabled | default(true)
|
||||
tags: postgresql
|
||||
|
||||
- name: "🗑️ Configure backup retention (delete old backups)"
|
||||
ansible.builtin.cron:
|
||||
name: "PostgreSQL backup cleanup"
|
||||
user: postgres
|
||||
minute: "30"
|
||||
hour: "2"
|
||||
job: "find {{ postgresql.backup.destination }} -name 'postgres-*.sql.gz' -mtime +{{ postgresql.backup.retention_days }} -delete"
|
||||
state: present
|
||||
when: postgresql.backup.enabled | default(true)
|
||||
tags: postgresql
|
||||
|
||||
- name: "✅ PostgreSQL role completed"
|
||||
ansible.builtin.debug:
|
||||
msg: "✅ PostgreSQL {{ postgresql.version }} configured on {{ inventory_hostname }}"
|
||||
tags: postgresql
|
||||
24
ansible/roles/postgresql/templates/pg_hba.conf.j2
Normal file
24
ansible/roles/postgresql/templates/pg_hba.conf.j2
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# Alliance Boréale - PostgreSQL Client Authentication
|
||||
# Generated by Ansible - DO NOT EDIT MANUALLY
|
||||
# Host: {{ inventory_hostname }}
|
||||
# Date: {{ ansible_date_time.iso8601 }}
|
||||
|
||||
# TYPE DATABASE USER ADDRESS METHOD
|
||||
|
||||
# Local connections
|
||||
local all postgres peer
|
||||
local all all peer
|
||||
|
||||
# IPv4 local connections
|
||||
host all all 127.0.0.1/32 scram-sha-256
|
||||
|
||||
# IPv6 local connections
|
||||
host all all ::1/128 scram-sha-256
|
||||
|
||||
# Internal network (if needed later)
|
||||
# host all all 10.0.0.0/8 scram-sha-256
|
||||
|
||||
# Replication (for future use)
|
||||
# local replication all peer
|
||||
# host replication all 127.0.0.1/32 scram-sha-256
|
||||
# host replication all ::1/128 scram-sha-256
|
||||
38
ansible/roles/postgresql/templates/postgresql.conf.j2
Normal file
38
ansible/roles/postgresql/templates/postgresql.conf.j2
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# Alliance Boréale - PostgreSQL Configuration
|
||||
# Generated by Ansible - DO NOT EDIT MANUALLY
|
||||
# Host: {{ inventory_hostname }}
|
||||
# Date: {{ ansible_date_time.iso8601 }}
|
||||
|
||||
# Network
|
||||
listen_addresses = '{{ postgresql.listen_addresses | default("localhost") }}'
|
||||
port = {{ postgresql.port | default(5432) }}
|
||||
|
||||
# Memory
|
||||
shared_buffers = {{ postgresql.shared_buffers | default('256MB') }}
|
||||
work_mem = {{ postgresql.work_mem | default('16MB') }}
|
||||
maintenance_work_mem = {{ postgresql.maintenance_work_mem | default('128MB') }}
|
||||
effective_cache_size = {{ postgresql.effective_cache_size | default('1GB') }}
|
||||
|
||||
# Write-Ahead Log
|
||||
wal_level = replica
|
||||
max_wal_size = 1GB
|
||||
min_wal_size = 80MB
|
||||
|
||||
# Logging
|
||||
log_destination = 'stderr'
|
||||
logging_collector = on
|
||||
log_directory = 'log'
|
||||
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
|
||||
log_rotation_age = 1d
|
||||
log_rotation_size = 10MB
|
||||
log_line_prefix = '%m [%p] %q%u@%d '
|
||||
log_timezone = 'America/Toronto'
|
||||
|
||||
# Locale
|
||||
datestyle = 'iso, mdy'
|
||||
timezone = 'America/Toronto'
|
||||
lc_messages = 'en_CA.UTF-8'
|
||||
lc_monetary = 'en_CA.UTF-8'
|
||||
lc_numeric = 'en_CA.UTF-8'
|
||||
lc_time = 'en_CA.UTF-8'
|
||||
default_text_search_config = 'pg_catalog.english'
|
||||
18
ansible/roles/powerdns-authoritative/handlers/main.yml
Normal file
18
ansible/roles/powerdns-authoritative/handlers/main.yml
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Alliance Boréale - PowerDNS Handlers
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
- name: restart powerdns
|
||||
ansible.builtin.systemd:
|
||||
name: pdns
|
||||
state: restarted
|
||||
|
||||
- name: reload powerdns
|
||||
ansible.builtin.systemd:
|
||||
name: pdns
|
||||
state: reloaded
|
||||
|
||||
- name: reload nftables
|
||||
ansible.builtin.systemd:
|
||||
name: nftables
|
||||
state: reloaded
|
||||
57
ansible/roles/powerdns-authoritative/tasks/firewall.yml
Normal file
57
ansible/roles/powerdns-authoritative/tasks/firewall.yml
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# Alliance Boréale - PowerDNS Firewall & Validation
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
# ==========================================
|
||||
# FIREWALL.YML
|
||||
# ==========================================
|
||||
- name: "🔥 Ensure nftables rules include DNS"
|
||||
ansible.builtin.blockinfile:
|
||||
path: /etc/nftables.conf
|
||||
marker: "# {mark} ANSIBLE MANAGED - PowerDNS"
|
||||
insertbefore: "# Log dropped packets"
|
||||
block: |
|
||||
# PowerDNS - DNS queries
|
||||
udp dport 53 accept comment "DNS queries (UDP)"
|
||||
tcp dport 53 accept comment "DNS queries (TCP)"
|
||||
|
||||
# PowerDNS - API (internal only)
|
||||
ip saddr {{ network.internal_subnet | default('10.0.0.0/8') }} tcp dport 8081 accept comment "PowerDNS API"
|
||||
notify: reload nftables
|
||||
|
||||
# ==========================================
|
||||
# VALIDATE.YML
|
||||
# ==========================================
|
||||
- name: "🧪 Wait for PowerDNS to be ready"
|
||||
ansible.builtin.wait_for:
|
||||
port: 53
|
||||
host: "{{ ansible_host }}"
|
||||
timeout: 30
|
||||
|
||||
- name: "🧪 Test DNS resolution (localhost)"
|
||||
ansible.builtin.command:
|
||||
cmd: "dig @127.0.0.1 {{ dns_zones[0].name }} SOA +short"
|
||||
register: dns_test_local
|
||||
changed_when: false
|
||||
failed_when: dns_test_local.rc != 0
|
||||
|
||||
- name: "🧪 Display DNS test result"
|
||||
ansible.builtin.debug:
|
||||
msg: "✅ DNS resolution working: {{ dns_test_local.stdout }}"
|
||||
|
||||
- name: "🧪 Check PowerDNS API (if enabled)"
|
||||
ansible.builtin.uri:
|
||||
url: "http://127.0.0.1:8081/api/v1/servers/localhost"
|
||||
headers:
|
||||
X-API-Key: "{{ powerdns.api.key }}"
|
||||
return_content: true
|
||||
register: api_test
|
||||
when: powerdns.api.enabled | default(true)
|
||||
failed_when: false
|
||||
|
||||
- name: "🧪 Display PowerDNS version"
|
||||
ansible.builtin.debug:
|
||||
msg: "✅ PowerDNS API responding: {{ api_test.json.version | default('N/A') }}"
|
||||
when:
|
||||
- powerdns.api.enabled | default(true)
|
||||
- api_test.status == 200
|
||||
90
ansible/roles/powerdns-authoritative/tasks/main.yml
Normal file
90
ansible/roles/powerdns-authoritative/tasks/main.yml
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
# Alliance Boréale - Rôle PowerDNS Authoritative
|
||||
# Couche: C2 (Réseau & DNS)
|
||||
# Objectif: Déployer PowerDNS autoritaire (MASTER ou SLAVE)
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
- name: "🔍 Determine PowerDNS role (master/slave)"
|
||||
ansible.builtin.set_fact:
|
||||
pdns_is_master: "{{ inventory_hostname in groups['dns_masters'] }}"
|
||||
pdns_is_slave: "{{ inventory_hostname in groups['dns_slaves'] }}"
|
||||
tags: always
|
||||
|
||||
- name: "📦 Add PowerDNS repository"
|
||||
ansible.builtin.import_tasks: repo.yml
|
||||
tags: powerdns
|
||||
|
||||
- name: "📦 Install PowerDNS packages"
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- pdns-server
|
||||
- pdns-backend-pgsql
|
||||
- pdns-tools
|
||||
state: present
|
||||
update_cache: true
|
||||
tags: powerdns
|
||||
|
||||
- name: "⚙️ Configure PowerDNS"
|
||||
ansible.builtin.template:
|
||||
src: pdns.conf.j2
|
||||
dest: /etc/powerdns/pdns.conf
|
||||
owner: root
|
||||
group: pdns
|
||||
mode: '0640'
|
||||
notify: restart powerdns
|
||||
tags: powerdns
|
||||
|
||||
- name: "⚙️ Configure PowerDNS PostgreSQL backend"
|
||||
ansible.builtin.template:
|
||||
src: pdns.d/pdns.local.gpgsql.conf.j2
|
||||
dest: /etc/powerdns/pdns.d/pdns.local.gpgsql.conf
|
||||
owner: root
|
||||
group: pdns
|
||||
mode: '0640'
|
||||
notify: restart powerdns
|
||||
tags: powerdns
|
||||
|
||||
- name: "🗄️ Import PowerDNS PostgreSQL schema"
|
||||
ansible.builtin.import_tasks: schema.yml
|
||||
when: pdns_is_master
|
||||
tags: powerdns
|
||||
|
||||
- name: "🌐 Configure DNS zones (MASTER only)"
|
||||
ansible.builtin.import_tasks: zones.yml
|
||||
when: pdns_is_master
|
||||
tags: powerdns
|
||||
|
||||
- name: "🔁 Configure AXFR (SLAVE only)"
|
||||
ansible.builtin.import_tasks: slave.yml
|
||||
when: pdns_is_slave
|
||||
tags: powerdns
|
||||
|
||||
- name: "🔐 Configure DNSSEC"
|
||||
ansible.builtin.import_tasks: dnssec.yml
|
||||
when:
|
||||
- pdns_is_master
|
||||
- powerdns.dnssec.enabled | default(true)
|
||||
tags: powerdns
|
||||
|
||||
- name: "🔥 Configure firewall for DNS"
|
||||
ansible.builtin.import_tasks: firewall.yml
|
||||
tags: powerdns
|
||||
|
||||
- name: "✅ Start and enable PowerDNS"
|
||||
ansible.builtin.systemd:
|
||||
name: pdns
|
||||
state: started
|
||||
enabled: true
|
||||
tags: powerdns
|
||||
|
||||
- name: "🧪 Validate PowerDNS configuration"
|
||||
ansible.builtin.import_tasks: validate.yml
|
||||
tags: powerdns
|
||||
|
||||
- name: "✅ PowerDNS role completed"
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
✅ PowerDNS configured on {{ inventory_hostname }}
|
||||
Role: {{ 'MASTER' if pdns_is_master else 'SLAVE' }}
|
||||
Zones: {{ dns_zones | map(attribute='name') | list if pdns_is_master else 'N/A' }}
|
||||
tags: powerdns
|
||||
25
ansible/roles/powerdns-authoritative/tasks/repo.yml
Normal file
25
ansible/roles/powerdns-authoritative/tasks/repo.yml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Alliance Boréale - PowerDNS Repository Setup
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
- name: "🔑 Add PowerDNS GPG key"
|
||||
ansible.builtin.apt_key:
|
||||
url: https://repo.powerdns.com/FD380FBB-pub.asc
|
||||
state: present
|
||||
|
||||
- name: "📦 Add PowerDNS repository"
|
||||
ansible.builtin.apt_repository:
|
||||
repo: "deb [arch=amd64] http://repo.powerdns.com/{{ ansible_distribution | lower }} {{ ansible_distribution_release }}-auth-48 main"
|
||||
state: present
|
||||
filename: pdns
|
||||
|
||||
- name: "📋 Set PowerDNS package preferences"
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/apt/preferences.d/pdns
|
||||
content: |
|
||||
Package: pdns-*
|
||||
Pin: origin repo.powerdns.com
|
||||
Pin-Priority: 600
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
149
ansible/roles/powerdns-authoritative/tasks/schema.yml
Normal file
149
ansible/roles/powerdns-authoritative/tasks/schema.yml
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
# Alliance Boréale - PowerDNS Schema Import
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
# Ce fichier contient: schema.yml, zones.yml, slave.yml, dnssec.yml
|
||||
|
||||
# ==========================================
|
||||
# SCHEMA.YML - Import PostgreSQL schema
|
||||
# ==========================================
|
||||
- name: "🗄️ Check if PowerDNS schema exists"
|
||||
community.postgresql.postgresql_query:
|
||||
db: powerdns
|
||||
query: "SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = 'domains');"
|
||||
become: true
|
||||
become_user: postgres
|
||||
register: pdns_schema_check
|
||||
changed_when: false
|
||||
|
||||
- name: "🗄️ Download PowerDNS schema"
|
||||
ansible.builtin.get_url:
|
||||
url: https://raw.githubusercontent.com/PowerDNS/pdns/rel/auth-4.8.x/modules/gpgsqlbackend/schema.pgsql.sql
|
||||
dest: /tmp/pdns-schema.sql
|
||||
mode: '0644'
|
||||
when: not pdns_schema_check.query_result[0].exists
|
||||
|
||||
- name: "🗄️ Import PowerDNS schema"
|
||||
community.postgresql.postgresql_db:
|
||||
db: powerdns
|
||||
state: restore
|
||||
target: /tmp/pdns-schema.sql
|
||||
become: true
|
||||
become_user: postgres
|
||||
when: not pdns_schema_check.query_result[0].exists
|
||||
|
||||
# ==========================================
|
||||
# ZONES.YML - Create DNS zones (MASTER)
|
||||
# ==========================================
|
||||
- name: "🌐 Create DNS zones"
|
||||
ansible.builtin.command:
|
||||
cmd: "pdnsutil create-zone {{ item.name }}"
|
||||
loop: "{{ dns_zones }}"
|
||||
register: zone_create
|
||||
changed_when: "'created' in zone_create.stdout"
|
||||
failed_when:
|
||||
- zone_create.rc != 0
|
||||
- "'already exists' not in zone_create.stderr"
|
||||
|
||||
- name: "🌐 Set zone kind to NATIVE"
|
||||
ansible.builtin.command:
|
||||
cmd: "pdnsutil set-kind {{ item.name }} NATIVE"
|
||||
loop: "{{ dns_zones }}"
|
||||
changed_when: false
|
||||
|
||||
- name: "🌐 Configure SOA records"
|
||||
ansible.builtin.command:
|
||||
cmd: >
|
||||
pdnsutil replace-rrset {{ item.name }} @ SOA
|
||||
"{{ item.soa.nameserver }} {{ item.soa.email }}
|
||||
{{ item.soa.serial }} {{ item.soa.refresh }} {{ item.soa.retry }}
|
||||
{{ item.soa.expire }} {{ item.soa.minimum }}"
|
||||
loop: "{{ dns_zones }}"
|
||||
changed_when: false
|
||||
|
||||
- name: "🌐 Add NS records"
|
||||
ansible.builtin.command:
|
||||
cmd: "pdnsutil add-record {{ item.0.name }} @ NS {{ item.1 }}"
|
||||
loop: "{{ dns_zones | subelements('ns_records') }}"
|
||||
register: ns_add
|
||||
changed_when: false
|
||||
failed_when:
|
||||
- ns_add.rc != 0
|
||||
- "'already exists' not in ns_add.stderr"
|
||||
|
||||
- name: "🌐 Add A/AAAA records"
|
||||
ansible.builtin.command:
|
||||
cmd: "pdnsutil add-record {{ item.0.name }} {{ item.1.name }} {{ item.1.type }} {{ item.1.content }} {{ item.1.ttl | default(3600) }}"
|
||||
loop: "{{ dns_zones | subelements('records', skip_missing=True) }}"
|
||||
register: record_add
|
||||
changed_when: false
|
||||
failed_when:
|
||||
- record_add.rc != 0
|
||||
- "'already exists' not in record_add.stderr"
|
||||
|
||||
- name: "🌐 Rectify zones"
|
||||
ansible.builtin.command:
|
||||
cmd: "pdnsutil rectify-zone {{ item.name }}"
|
||||
loop: "{{ dns_zones }}"
|
||||
changed_when: false
|
||||
|
||||
# ==========================================
|
||||
# SLAVE.YML - Configure AXFR for slaves
|
||||
# ==========================================
|
||||
- name: "🔁 Configure supermaster for AXFR"
|
||||
community.postgresql.postgresql_query:
|
||||
db: powerdns
|
||||
query: >
|
||||
INSERT INTO supermasters (ip, nameserver, account)
|
||||
VALUES ('{{ hostvars[groups['dns_masters'][0]]['ansible_host'] }}',
|
||||
'{{ inventory_hostname }}',
|
||||
'default')
|
||||
ON CONFLICT DO NOTHING;
|
||||
become: true
|
||||
become_user: postgres
|
||||
|
||||
- name: "🔁 Enable slave mode in PowerDNS"
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/powerdns/pdns.conf
|
||||
regexp: '^slave='
|
||||
line: 'slave=yes'
|
||||
notify: restart powerdns
|
||||
|
||||
# ==========================================
|
||||
# DNSSEC.YML - Configure DNSSEC
|
||||
# ==========================================
|
||||
- name: "🔐 Enable DNSSEC for zones"
|
||||
ansible.builtin.command:
|
||||
cmd: "pdnsutil secure-zone {{ item.name }}"
|
||||
loop: "{{ dns_zones }}"
|
||||
register: dnssec_secure
|
||||
changed_when: "'secured' in dnssec_secure.stdout"
|
||||
failed_when:
|
||||
- dnssec_secure.rc != 0
|
||||
- "'already' not in dnssec_secure.stderr"
|
||||
|
||||
- name: "🔐 Configure NSEC3"
|
||||
ansible.builtin.command:
|
||||
cmd: "pdnsutil set-nsec3 {{ item.name }} '1 0 10 ab' narrow"
|
||||
loop: "{{ dns_zones }}"
|
||||
changed_when: false
|
||||
|
||||
- name: "🔐 Rectify zones after DNSSEC"
|
||||
ansible.builtin.command:
|
||||
cmd: "pdnsutil rectify-zone {{ item.name }}"
|
||||
loop: "{{ dns_zones }}"
|
||||
changed_when: false
|
||||
|
||||
- name: "🔐 Export DS records"
|
||||
ansible.builtin.command:
|
||||
cmd: "pdnsutil show-zone {{ item.name }}"
|
||||
loop: "{{ dns_zones }}"
|
||||
register: ds_records
|
||||
changed_when: false
|
||||
|
||||
- name: "🔐 Display DS records for parent zone"
|
||||
ansible.builtin.debug:
|
||||
msg: |
|
||||
⚠️ IMPORTANT: Add these DS records to your domain registrar:
|
||||
{{ ds_records.results | map(attribute='stdout') | join('\n') }}
|
||||
when: ds_records.results | length > 0
|
||||
70
ansible/roles/powerdns-authoritative/templates/pdns.conf.j2
Normal file
70
ansible/roles/powerdns-authoritative/templates/pdns.conf.j2
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
# Alliance Boréale - PowerDNS Configuration
|
||||
# Generated by Ansible - DO NOT EDIT MANUALLY
|
||||
# Host: {{ inventory_hostname }}
|
||||
# Role: {{ 'MASTER' if pdns_is_master else 'SLAVE' }}
|
||||
# Date: {{ ansible_date_time.iso8601 }}
|
||||
|
||||
# ==========================================
|
||||
# BACKEND
|
||||
# ==========================================
|
||||
launch=gpgsql
|
||||
include-dir=/etc/powerdns/pdns.d
|
||||
|
||||
# ==========================================
|
||||
# NETWORK
|
||||
# ==========================================
|
||||
local-address=0.0.0.0
|
||||
local-port=53
|
||||
local-ipv6=
|
||||
|
||||
# ==========================================
|
||||
# MODE
|
||||
# ==========================================
|
||||
master={{ 'yes' if pdns_is_master else 'no' }}
|
||||
slave={{ 'yes' if pdns_is_slave else 'no' }}
|
||||
superslave={{ 'no' }}
|
||||
|
||||
# ==========================================
|
||||
# API & WEBSERVER
|
||||
# ==========================================
|
||||
api={{ 'yes' if powerdns.api.enabled | default(true) else 'no' }}
|
||||
api-key={{ powerdns.api.key | default('changeme') }}
|
||||
|
||||
webserver={{ 'yes' if powerdns.webserver.enabled | default(true) else 'no' }}
|
||||
webserver-address={{ powerdns.webserver.address | default('0.0.0.0') }}
|
||||
webserver-port={{ powerdns.webserver.port | default(8081) }}
|
||||
webserver-allow-from={{ powerdns.webserver.allow_from | default('10.0.0.0/8') }}
|
||||
|
||||
# ==========================================
|
||||
# DNSSEC
|
||||
# ==========================================
|
||||
dnssec={{ 'on' if powerdns.dnssec.enabled | default(true) else 'off' }}
|
||||
|
||||
# ==========================================
|
||||
# LOGGING
|
||||
# ==========================================
|
||||
log-dns-queries={{ 'yes' if powerdns.logging.queries | default(false) else 'no' }}
|
||||
log-dns-details={{ 'yes' if powerdns.logging.details | default(true) else 'no' }}
|
||||
loglevel={{ powerdns.logging.level | default(4) }}
|
||||
|
||||
# ==========================================
|
||||
# PERFORMANCE
|
||||
# ==========================================
|
||||
cache-ttl={{ powerdns.cache_ttl | default(20) }}
|
||||
negquery-cache-ttl={{ powerdns.negquery_cache_ttl | default(60) }}
|
||||
query-cache-ttl={{ powerdns.query_cache_ttl | default(20) }}
|
||||
|
||||
# ==========================================
|
||||
# SECURITY
|
||||
# ==========================================
|
||||
setuid=pdns
|
||||
setgid=pdns
|
||||
chroot=/var/spool/powerdns
|
||||
disable-axfr={{ 'no' }}
|
||||
allow-axfr-ips={{ axfr.allow_from | join(',') if axfr.allow_from is defined else '127.0.0.1' }}
|
||||
also-notify={{ notify.also_notify | join(',') if notify.also_notify is defined else '' }}
|
||||
|
||||
# ==========================================
|
||||
# SOA
|
||||
# ==========================================
|
||||
default-soa-content=ns1.infra.{{ member.domain_primary }} admin.{{ member.domain_primary }} 0 3600 1800 1209600 3600
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# Alliance Boréale - PowerDNS PostgreSQL Backend
|
||||
# Generated by Ansible - DO NOT EDIT MANUALLY
|
||||
# Host: {{ inventory_hostname }}
|
||||
# Date: {{ ansible_date_time.iso8601 }}
|
||||
|
||||
# PostgreSQL connection
|
||||
gpgsql-host=/var/run/postgresql
|
||||
gpgsql-port=5432
|
||||
gpgsql-dbname=powerdns
|
||||
gpgsql-user=pdns
|
||||
gpgsql-password={{ vault_postgresql_pdns_password }}
|
||||
gpgsql-dnssec=yes
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
---
|
||||
- name: Déployer les services de l'Alliance Boréale
|
||||
hosts: all
|
||||
become: yes
|
||||
vars:
|
||||
offline_mode: "{{ offline_mode | default(false) }}"
|
||||
roles:
|
||||
- role: netbox
|
||||
when: "'netbox' in group_names"
|
||||
# - role: powerdns
|
||||
# when: "'powerdns' in group_names"
|
||||
# - role: keycloak
|
||||
# when: "'keycloak' in group_names"
|
||||
54
ansible/vault/production-phase2.yml.example
Normal file
54
ansible/vault/production-phase2.yml.example
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# Alliance Boréale - Vault Production (PHASE 1 + 2 EXEMPLE)
|
||||
# ⚠️ NE PAS UTILISER TEL QUEL - À CHIFFRER AVEC ansible-vault
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
# ==========================================
|
||||
# PHASE 1 - DNS
|
||||
# ==========================================
|
||||
vault_postgresql_pdns_password: "changeme-pdns-db-password"
|
||||
vault_pdns_api_key: "changeme-pdns-api-key"
|
||||
|
||||
# ==========================================
|
||||
# PHASE 2 - KEYCLOAK
|
||||
# ==========================================
|
||||
vault_keycloak_admin_password: "changeme-keycloak-admin-password"
|
||||
vault_postgresql_keycloak_password: "changeme-keycloak-db-password"
|
||||
|
||||
# Client secret for Forgejo (à récupérer depuis Keycloak UI)
|
||||
vault_keycloak_forgejo_client_secret: "changeme-will-be-generated-by-keycloak"
|
||||
|
||||
# ==========================================
|
||||
# PHASE 2 - FORGEJO
|
||||
# ==========================================
|
||||
vault_forgejo_admin_password: "changeme-forgejo-admin-password"
|
||||
vault_postgresql_forgejo_password: "changeme-forgejo-db-password"
|
||||
|
||||
# Secrets Forgejo (générer avec openssl)
|
||||
vault_forgejo_secret_key: "changeme-64-chars-secret-key-here-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
vault_forgejo_internal_token: "changeme-64-chars-internal-token-here-xxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
vault_forgejo_jwt_secret: "changeme-64-chars-jwt-secret-here-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
|
||||
# ==========================================
|
||||
# COMMANDES POUR GÉNÉRER LES SECRETS:
|
||||
# ==========================================
|
||||
|
||||
# Mots de passe (24 chars)
|
||||
# openssl rand -base64 24
|
||||
|
||||
# API Keys / Secrets (32 chars)
|
||||
# openssl rand -base64 32
|
||||
|
||||
# Forgejo secrets (64 chars hex)
|
||||
# openssl rand -hex 64
|
||||
|
||||
# ==========================================
|
||||
# WORKFLOW:
|
||||
# ==========================================
|
||||
# 1. Copier vault/production.yml.example → vault/production.yml
|
||||
# 2. Générer tous les secrets avec openssl
|
||||
# 3. Déployer Keycloak (phase2-deploy.yml --tags keycloak)
|
||||
# 4. Récupérer client secret depuis Keycloak UI
|
||||
# 5. Ajouter vault_keycloak_forgejo_client_secret
|
||||
# 6. Déployer Forgejo (phase2-deploy.yml --tags forgejo)
|
||||
# 7. Chiffrer: ansible-vault encrypt vault/production.yml
|
||||
41
ansible/vault/production.yml.example
Normal file
41
ansible/vault/production.yml.example
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Alliance Boréale - Vault Production (EXEMPLE NON CHIFFRÉ)
|
||||
# ⚠️ NE PAS UTILISER TEL QUEL - À CHIFFRER AVEC ansible-vault
|
||||
# Date: 2025-10-31
|
||||
|
||||
---
|
||||
# ==========================================
|
||||
# POSTGRESQL
|
||||
# ==========================================
|
||||
vault_postgresql_pdns_password: "changeme-generate-secure-password-here"
|
||||
|
||||
# ==========================================
|
||||
# POWERDNS
|
||||
# ==========================================
|
||||
vault_pdns_api_key: "changeme-generate-secure-api-key-here"
|
||||
|
||||
# ==========================================
|
||||
# TSIG KEYS (Phase 2 - AXFR croisés)
|
||||
# ==========================================
|
||||
# vault_tsig_clp_nul: "base64-encoded-secret-here"
|
||||
# vault_tsig_clp_tli: "base64-encoded-secret-here"
|
||||
|
||||
# ==========================================
|
||||
# POUR GÉNÉRER DES SECRETS SÉCURISÉS:
|
||||
# ==========================================
|
||||
# openssl rand -base64 32 # Pour API keys
|
||||
# openssl rand -base64 24 # Pour mots de passe
|
||||
# tsig-keygen clp-nul-xfer # Pour TSIG keys
|
||||
|
||||
# ==========================================
|
||||
# COMMANDES POUR CRÉER LE VRAI VAULT:
|
||||
# ==========================================
|
||||
# 1. Copier ce fichier:
|
||||
# cp vault/production.yml.example vault/production.yml
|
||||
#
|
||||
# 2. Éditer et remplacer tous les "changeme-*":
|
||||
# vi vault/production.yml
|
||||
#
|
||||
# 3. Chiffrer avec ansible-vault:
|
||||
# ansible-vault encrypt vault/production.yml
|
||||
#
|
||||
# 4. Sauvegarder le mot de passe vault dans un gestionnaire sécurisé!
|
||||
Loading…
Reference in a new issue