Commit initial : infrastructure Ansible pour homeserver

- Playbooks Ansible avec rôles (common, cockpit, docker, services)
- 30+ stacks Docker Compose avec reverse proxy Traefik
- Ansible Vault pour gestion secrets
- Intégration CrowdSec pour détection intrusions
- Versions images Docker fixées pour reproductibilité
This commit is contained in:
Tellsanguis 2025-11-23 19:40:17 +01:00
commit fd01ea59ee
125 changed files with 4768 additions and 0 deletions

View file

@ -0,0 +1,9 @@
FROM python:3.11-slim
WORKDIR /app
COPY generate_containers_block.py .
RUN pip install docker pyyaml requests beautifulsoup4
CMD ["python3", "generate_containers_block.py"]

View file

@ -0,0 +1,124 @@
#!/usr/bin/env python3
import docker
import yaml
import os
import json
import requests
from bs4 import BeautifulSoup
client = docker.from_env()
containers = client.containers.list()
output = {}
print(f"[•] Détection de {len(containers)} conteneur(s)...\n")
override_path = "/app/config/icon_overrides.json"
overrides = {}
# Charger les overrides si présents
if os.path.exists(override_path):
with open(override_path, "r", encoding="utf-8") as f:
overrides = json.load(f)
def extract_image_name(image):
name = image.split("/")[-1]
return name.split(":")[0] if ":" in name else name
def find_favicon(url, project_name, image_name):
print(f"[→] Recherche favicon pour {url}")
# Override manuel ?
if project_name in overrides and overrides[project_name]:
print(f"[✓] Favicon forcé depuis override : {overrides[project_name]}")
return overrides[project_name]
icon_url = None
try:
r = requests.get(url, timeout=5)
r.raise_for_status()
soup = BeautifulSoup(r.text, 'html.parser')
icons = soup.find_all("link", rel=lambda x: x and 'icon' in x.lower())
if icons:
href = icons[0].get("href")
if href:
icon_url = href if href.startswith("http") else url.rstrip("/") + "/" + href.lstrip("/")
print(f"[✓] Favicon trouvé via HTML : {icon_url}")
return icon_url
else:
print("[✗] Aucun favicon trouvé dans HTML")
except Exception:
print(f"[✗] Impossible de charger HTML depuis {url}")
test_favicon = f"{url.rstrip('/')}/favicon.ico"
try:
r = requests.get(test_favicon, timeout=5)
if r.ok:
print(f"[✓] Favicon trouvé via /favicon.ico : {test_favicon}")
return test_favicon
except:
pass
github_icon = f"https://raw.githubusercontent.com/selfhst/icons/refs/heads/main/png/{project_name}.png"
try:
r = requests.get(github_icon, timeout=5)
if r.ok:
print(f"[✓] Favicon fallback via GitHub (project_name) : {github_icon}")
return github_icon
except:
print(f"[✗] Pas dicône GitHub pour {project_name}")
github_icon = f"https://raw.githubusercontent.com/selfhst/icons/refs/heads/main/png/{image_name}.png"
try:
r = requests.get(github_icon, timeout=5)
if r.ok:
print(f"[✓] Favicon fallback via GitHub (image) : {github_icon}")
return github_icon
except:
print(f"[✗] Pas dicône GitHub pour {image_name}")
print("[✗] Aucun favicon disponible\n")
return None
for container in containers:
labels = container.labels
name = container.name
image_name = extract_image_name(container.image.tags[0]) if container.image.tags else container.image.short_id
project_name = labels.get("com.docker.compose.project", name).lower()
print(f"[•] Analyse du conteneur : {name} (projet : {project_name})")
domain = None
for key, value in labels.items():
if key.startswith(f"traefik.http.routers.{project_name}-prod.rule") and "Host(`" in value:
domain = value.split("Host(`")[1].split("`)")[0]
break
if domain:
url = f"https://{domain}"
icon = find_favicon(url, project_name, image_name)
output[name] = {
"name": project_name.capitalize(),
"url": url,
"icon": icon or "mdi:web",
"hide": False
}
if project_name not in overrides:
overrides[project_name] = ""
print(f"[✓] Conteneur ajouté : {project_name}{url}\n")
else:
print("[!] Aucune règle Traefik -prod trouvée pour ce conteneur.\n")
# Générer fichiers
os.makedirs("/output", exist_ok=True)
with open("/output/containers.yml", "w", encoding="utf-8") as f:
yaml.dump({"containers": output}, f, sort_keys=False)
with open(override_path, "w", encoding="utf-8") as f:
json.dump(overrides, f, indent=2, ensure_ascii=False)
print("✅ Fichier containers.yml généré : /output/containers.yml")
print(f"✅ Fichier overrides mis à jour : {override_path}")

View file

@ -0,0 +1,32 @@
{
"glance": "",
"paperless": "",
"librechat": "https://raw.githubusercontent.com/selfhst/icons/refs/heads/main/png/librechat.png",
"blog": "",
"larabouillere": "",
"yamtrack": "",
"photoprism": "",
"plex": "",
"stirlingpdf": "https://raw.githubusercontent.com/Stirling-Tools/Stirling-PDF/main/docs/stirling.png",
"etesync": "",
"crafty": "",
"bin": "https://microbin.eu/img/logo-square.png",
"beszel": "",
"audiobookshelf": "",
"gotify": "",
"clipcascade": "",
"freshrss": "",
"feedropolis": "https://raw.githubusercontent.com/selfhst/icons/refs/heads/main/png/rss-com.png",
"joal": "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/joal.png",
"qbittorrent": "https://raw.githubusercontent.com/selfhst/icons/refs/heads/main/png/qbittorrent.png",
"kavita": "",
"whoogle": "",
"pingvin": "",
"vikunja": "",
"vaultwarden": "",
"mobilizon": "",
"random-draft": "",
"pc-builds": "",
"searxng": "",
"webdav": ""
}