Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BAUeCFpDkRD6tU5wGsc1C
158 lines
4.5 KiB
HCL
158 lines
4.5 KiB
HCL
# =============================================================================
|
|
# Firewalls Hetzner Cloud (interfaces publiques uniquement)
|
|
# =============================================================================
|
|
# Politique : tout est ferme par defaut. On n'ouvre que le strict necessaire,
|
|
# et jamais vers 0.0.0.0/0 sauf pour le trafic web derriere Cloudflare.
|
|
|
|
locals {
|
|
# Rangs d'IP Cloudflare. A rafraichir avec :
|
|
# curl -s https://www.cloudflare.com/ips-v4
|
|
# curl -s https://www.cloudflare.com/ips-v6
|
|
# Verifiez cette liste a chaque `terraform apply` (cf. scripts/refresh-cloudflare-ips.sh).
|
|
cloudflare_ipv4 = [
|
|
"173.245.48.0/20",
|
|
"103.21.244.0/22",
|
|
"103.22.200.0/22",
|
|
"103.31.4.0/22",
|
|
"141.101.64.0/18",
|
|
"108.162.192.0/18",
|
|
"190.93.240.0/20",
|
|
"188.114.96.0/20",
|
|
"197.234.240.0/22",
|
|
"198.41.128.0/17",
|
|
"162.158.0.0/15",
|
|
"104.16.0.0/13",
|
|
"104.24.0.0/14",
|
|
"172.64.0.0/13",
|
|
"131.0.72.0/22",
|
|
]
|
|
|
|
cloudflare_ipv6 = [
|
|
"2400:cb00::/32",
|
|
"2606:4700::/32",
|
|
"2803:f800::/32",
|
|
"2405:b500::/32",
|
|
"2405:8100::/32",
|
|
"2a06:98c0::/29",
|
|
"2c0f:f248::/32",
|
|
]
|
|
|
|
http_sources_v4 = var.restrict_http_to_cloudflare ? local.cloudflare_ipv4 : ["0.0.0.0/0"]
|
|
http_sources_v6 = var.restrict_http_to_cloudflare ? local.cloudflare_ipv6 : ["::/0"]
|
|
http_sources = concat(local.http_sources_v4, local.http_sources_v6)
|
|
}
|
|
|
|
# --- Noeud applicatif (k3s server) ------------------------------------------
|
|
|
|
resource "hcloud_firewall" "app" {
|
|
name = "${var.project_name}-fw-app"
|
|
|
|
# SSH : uniquement depuis les IPs d'administration.
|
|
rule {
|
|
direction = "in"
|
|
protocol = "tcp"
|
|
port = "22"
|
|
source_ips = var.admin_ip_allowlist
|
|
description = "SSH administrateur"
|
|
}
|
|
|
|
# API Kubernetes : uniquement depuis les IPs d'administration.
|
|
# Les runners GitHub Actions n'ont pas d'IP fixe : le deploiement passe donc
|
|
# par SSH + kubectl local sur le serveur, pas par 6443 expose.
|
|
# Cf. .github/workflows/cd-main.yml.
|
|
rule {
|
|
direction = "in"
|
|
protocol = "tcp"
|
|
port = "6443"
|
|
source_ips = var.admin_ip_allowlist
|
|
description = "API k3s (kubectl administrateur)"
|
|
}
|
|
|
|
rule {
|
|
direction = "in"
|
|
protocol = "tcp"
|
|
port = "80"
|
|
source_ips = local.http_sources
|
|
description = "HTTP (redirection 301 + ACME HTTP-01 de secours)"
|
|
}
|
|
|
|
rule {
|
|
direction = "in"
|
|
protocol = "tcp"
|
|
port = "443"
|
|
source_ips = local.http_sources
|
|
description = "HTTPS via Cloudflare"
|
|
}
|
|
|
|
# ICMP : utile pour le diagnostic reseau, sans risque notable.
|
|
rule {
|
|
direction = "in"
|
|
protocol = "icmp"
|
|
source_ips = ["0.0.0.0/0", "::/0"]
|
|
description = "ICMP (ping / MTU discovery)"
|
|
}
|
|
|
|
labels = {
|
|
project = var.project_name
|
|
managed = "terraform"
|
|
}
|
|
}
|
|
|
|
# --- Ouverture temporaire pour la CI/CD -------------------------------------
|
|
# Les runners GitHub Actions n'ont pas d'IP fixe : impossible de les inscrire
|
|
# une fois pour toutes dans une liste blanche, et ouvrir SSH au monde entier
|
|
# n'est pas une option.
|
|
#
|
|
# Ce firewall est attache a app-01 et reste VIDE en regime nominal. Le workflow
|
|
# cd-main.yml y injecte l'IP du runner juste avant le deploiement, puis le vide
|
|
# systematiquement (etape `if: always()`). La fenetre d'exposition dure le temps
|
|
# du deploiement, pour une seule IP, sur le seul port 22.
|
|
#
|
|
# `ignore_changes = [rule]` est indispensable : sans lui, le prochain
|
|
# `terraform apply` supprimerait une regle posee par la CI en cours d'execution.
|
|
|
|
resource "hcloud_firewall" "cicd" {
|
|
name = "${var.project_name}-fw-cicd"
|
|
|
|
# Aucune regle : l'etat au repos est "ferme".
|
|
|
|
labels = {
|
|
project = var.project_name
|
|
managed = "terraform"
|
|
purpose = "cicd-temporaire"
|
|
}
|
|
|
|
lifecycle {
|
|
ignore_changes = [rule]
|
|
}
|
|
}
|
|
|
|
# --- Noeud de donnees --------------------------------------------------------
|
|
# Aucun port applicatif expose publiquement. PostgreSQL et Redis n'ecoutent que
|
|
# sur l'IP privee (cf. conf/postgresql.conf et conf/redis.conf) et sont en plus
|
|
# filtres par nftables. Ce firewall ne laisse passer que le SSH d'administration.
|
|
|
|
resource "hcloud_firewall" "db" {
|
|
name = "${var.project_name}-fw-db"
|
|
|
|
rule {
|
|
direction = "in"
|
|
protocol = "tcp"
|
|
port = "22"
|
|
source_ips = var.admin_ip_allowlist
|
|
description = "SSH administrateur"
|
|
}
|
|
|
|
rule {
|
|
direction = "in"
|
|
protocol = "icmp"
|
|
source_ips = ["0.0.0.0/0", "::/0"]
|
|
description = "ICMP (ping / MTU discovery)"
|
|
}
|
|
|
|
labels = {
|
|
project = var.project_name
|
|
managed = "terraform"
|
|
}
|
|
}
|