This commit is contained in:
@@ -36,14 +36,14 @@ resource "cloudflare_zero_trust_access_application" "cloudflare_aws_app_ssh_infr
|
||||
name = var.cloudflare_aws_infra_app_name
|
||||
logo_url = "https://upload.wikimedia.org/wikipedia/commons/0/01/Google-cloud-platform.svg"
|
||||
|
||||
# Tag permettant d'associer cette app à un groupe logique de ressources
|
||||
# Tag permettant d'associer cette app à un groupe logique de ressources
|
||||
tags = [cloudflare_zero_trust_access_tag.tags["engineers"].name]
|
||||
|
||||
# URLs personnalisées en cas d'accès refusé ou d'identité non reconnue
|
||||
# URLs personnalisées en cas d'accès refusé ou d'identité non reconnue
|
||||
custom_deny_url = "https://denied.tips-of-mine.org/"
|
||||
custom_non_identity_deny_url = "https://denied.tips-of-mine.org/"
|
||||
|
||||
# Définition des critères de la cible (port et protocole)
|
||||
# Définition des critères de la cible (port et protocole)
|
||||
target_criteria = [{
|
||||
port = "22",
|
||||
protocol = "SSH"
|
||||
@@ -52,26 +52,26 @@ resource "cloudflare_zero_trust_access_application" "cloudflare_aws_app_ssh_infr
|
||||
},
|
||||
}]
|
||||
|
||||
# ------------------------------------------------------
|
||||
# Définition de la politique d’accès SSH pour cette application
|
||||
# Cette politique précise :
|
||||
# - qui peut se connecter
|
||||
# - via quel fournisseur d'identité
|
||||
# - quelles méthodes d'authentification sont requises
|
||||
# - quelles règles de sécurité supplémentaires s'appliquent
|
||||
# ------------------------------------------------------
|
||||
# ------------------------------------------------------
|
||||
# Définition de la politique d’accès SSH pour cette application
|
||||
# Cette politique précise :
|
||||
# - qui peut se connecter
|
||||
# - via quel fournisseur d'identité
|
||||
# - quelles méthodes d'authentification sont requises
|
||||
# - quelles règles de sécurité supplémentaires s'appliquent
|
||||
# ------------------------------------------------------
|
||||
policies = [{
|
||||
name = "SSH GCP Infrastructure Policy"
|
||||
decision = "allow"
|
||||
|
||||
# Fournisseurs d'identité autorisés
|
||||
# Fournisseurs d'identité autorisés
|
||||
allowed_idps = [
|
||||
cloudflare_zero_trust_access_identity_provider.authentik_oidc.id,
|
||||
]
|
||||
auto_redirect_to_identity = true
|
||||
allow_authenticate_via_warp = false
|
||||
|
||||
# Groupes / domaines autorisés à accéder
|
||||
# Groupes / domaines autorisés à accéder
|
||||
include = [
|
||||
{
|
||||
saml = {
|
||||
@@ -94,7 +94,7 @@ resource "cloudflare_zero_trust_access_application" "cloudflare_aws_app_ssh_infr
|
||||
}
|
||||
]
|
||||
|
||||
# Exigences de sécurité obligatoires (MFA, posture appareil, etc.)
|
||||
# Exigences de sécurité obligatoires (MFA, posture appareil, etc.)
|
||||
require = [
|
||||
{
|
||||
device_posture = {
|
||||
@@ -108,7 +108,7 @@ resource "cloudflare_zero_trust_access_application" "cloudflare_aws_app_ssh_infr
|
||||
}
|
||||
]
|
||||
|
||||
# Méthodes explicitement interdites
|
||||
# Méthodes explicitement interdites
|
||||
exclude = [
|
||||
{
|
||||
auth_method = {
|
||||
@@ -117,7 +117,7 @@ resource "cloudflare_zero_trust_access_application" "cloudflare_aws_app_ssh_infr
|
||||
}
|
||||
]
|
||||
|
||||
# Configuration spécifique SSH (règles de connexion)
|
||||
# Configuration spécifique SSH (règles de connexion)
|
||||
connection_rules = {
|
||||
ssh = {
|
||||
allow_email_alias = true
|
||||
@@ -172,7 +172,7 @@ resource "cloudflare_zero_trust_access_application" "cloudflare_gcp_app_ssh_infr
|
||||
},
|
||||
}]
|
||||
|
||||
# Politique d’accès SSH pour GCP (mêmes principes que pour AWS)
|
||||
# Politique d’accès SSH pour GCP (mêmes principes que pour AWS)
|
||||
policies = [{
|
||||
name = "SSH GCP Infrastructure Policy"
|
||||
decision = "allow"
|
||||
@@ -183,7 +183,7 @@ resource "cloudflare_zero_trust_access_application" "cloudflare_gcp_app_ssh_infr
|
||||
auto_redirect_to_identity = true
|
||||
allow_authenticate_via_warp = false
|
||||
|
||||
# Groupes / domaines autorisés à accéder
|
||||
# Groupes / domaines autorisés à accéder
|
||||
include = [
|
||||
{
|
||||
saml = {
|
||||
@@ -206,7 +206,7 @@ resource "cloudflare_zero_trust_access_application" "cloudflare_gcp_app_ssh_infr
|
||||
}
|
||||
]
|
||||
|
||||
# Exigences de sécurité obligatoires (MFA, posture appareil, etc.)
|
||||
# Exigences de sécurité obligatoires (MFA, posture appareil, etc.)
|
||||
require = [
|
||||
{
|
||||
device_posture = {
|
||||
@@ -220,7 +220,7 @@ resource "cloudflare_zero_trust_access_application" "cloudflare_gcp_app_ssh_infr
|
||||
}
|
||||
]
|
||||
|
||||
# Méthodes explicitement interdites
|
||||
# Méthodes explicitement interdites
|
||||
exclude = [
|
||||
{
|
||||
auth_method = {
|
||||
@@ -229,7 +229,7 @@ resource "cloudflare_zero_trust_access_application" "cloudflare_gcp_app_ssh_infr
|
||||
}
|
||||
]
|
||||
|
||||
# Configuration spécifique SSH (règles de connexion)
|
||||
# Configuration spécifique SSH (règles de connexion)
|
||||
connection_rules = {
|
||||
ssh = {
|
||||
allow_email_alias = true
|
||||
|
||||
@@ -31,33 +31,33 @@ resource "cloudflare_zero_trust_access_infrastructure_target" "aws_rdp_target" {
|
||||
resource "cloudflare_zero_trust_access_application" "cloudflare_aws_app_rdp_domain" {
|
||||
account_id = local.cloudflare_account_id
|
||||
|
||||
# Type d’application (RDP, SSH, VNC, etc.)
|
||||
# Type d’application (RDP, SSH, VNC, etc.)
|
||||
type = "rdp"
|
||||
|
||||
# Nom d’affichage de l’application dans le launcher Cloudflare
|
||||
# Nom d’affichage de l’application dans le launcher Cloudflare
|
||||
name = var.cloudflare_aws_browser_rdp_app_name
|
||||
|
||||
# Afficher l’application dans l’App Launcher Cloudflare
|
||||
# Afficher l’application dans l’App Launcher Cloudflare
|
||||
app_launcher_visible = true
|
||||
|
||||
# Icône personnalisée visible par les utilisateurs
|
||||
# Icône personnalisée visible par les utilisateurs
|
||||
logo_url = "https://www.kevinsubileau.fr/wp-content/uploads/2016/05/RDP_icon.png"
|
||||
|
||||
# Attribution d’un tag pour organiser les applications (ex: équipe d’ingénieurs)
|
||||
# Attribution d’un tag pour organiser les applications (ex: équipe d’ingénieurs)
|
||||
tags = [cloudflare_zero_trust_access_tag.tags["engineers"].name]
|
||||
|
||||
# Durée de session (0s = durée illimitée tant que l’authentification reste valide)
|
||||
# Durée de session (0s = durée illimitée tant que l’authentification reste valide)
|
||||
session_duration = "0s"
|
||||
|
||||
# URL personnalisée affichée en cas de refus d’accès
|
||||
# URL personnalisée affichée en cas de refus d’accès
|
||||
custom_deny_url = "https://denied.tips-of-mine.org/"
|
||||
custom_non_identity_deny_url = "https://denied.tips-of-mine.org/"
|
||||
|
||||
# Nom de domaine public utilisé pour rendre l’interface RDP accessible via navigateur
|
||||
# Nom de domaine public utilisé pour rendre l’interface RDP accessible via navigateur
|
||||
domain = var.cloudflare_aws_subdomain_rdp
|
||||
|
||||
# Critères de correspondance pour relier cette application à la cible RDP AWS
|
||||
# Indique quel protocole et port utiliser, ainsi que la cible.
|
||||
# Critères de correspondance pour relier cette application à la cible RDP AWS
|
||||
# Indique quel protocole et port utiliser, ainsi que la cible.
|
||||
target_criteria = [{
|
||||
port = 3389
|
||||
protocol = "RDP"
|
||||
@@ -66,8 +66,8 @@ resource "cloudflare_zero_trust_access_application" "cloudflare_aws_app_rdp_doma
|
||||
}
|
||||
}]
|
||||
|
||||
# Paramètres relatifs au fournisseur d'identité (IdP)
|
||||
# Ici, Authentik via un connecteur OIDC.
|
||||
# Paramètres relatifs au fournisseur d'identité (IdP)
|
||||
# Ici, Authentik via un connecteur OIDC.
|
||||
allowed_idps = [
|
||||
cloudflare_zero_trust_access_identity_provider.authentik_oidc.id,
|
||||
]
|
||||
@@ -76,12 +76,12 @@ resource "cloudflare_zero_trust_access_application" "cloudflare_aws_app_rdp_doma
|
||||
http_only_cookie_attribute = false
|
||||
options_preflight_bypass = false
|
||||
|
||||
# Référence à la politique d’accès définie dans Access_Controls-Policies-Reusable_Policies.tf
|
||||
# Référence à la politique d’accès définie dans Access_Controls-Policies-Reusable_Policies.tf
|
||||
policies = [{
|
||||
id = cloudflare_zero_trust_access_policy.policies["domain_controller"].id
|
||||
}]
|
||||
|
||||
# Déclare explicitement que la création dépend de la cible RDP
|
||||
# Déclare explicitement que la création dépend de la cible RDP
|
||||
depends_on = [
|
||||
cloudflare_zero_trust_access_infrastructure_target.aws_rdp_target
|
||||
]
|
||||
@@ -116,33 +116,33 @@ resource "cloudflare_zero_trust_access_infrastructure_target" "gcp_rdp_target" {
|
||||
resource "cloudflare_zero_trust_access_application" "cloudflare_gcp_app_rdp_domain" {
|
||||
account_id = local.cloudflare_account_id
|
||||
|
||||
# Type d’application (RDP, SSH, VNC, etc.)
|
||||
# Type d’application (RDP, SSH, VNC, etc.)
|
||||
type = "rdp"
|
||||
|
||||
# Nom d’affichage de l’application dans le launcher Cloudflare
|
||||
# Nom d’affichage de l’application dans le launcher Cloudflare
|
||||
name = var.cloudflare_gcp_browser_rdp_app_name
|
||||
|
||||
# Afficher l’application dans l’App Launcher Cloudflare
|
||||
# Afficher l’application dans l’App Launcher Cloudflare
|
||||
app_launcher_visible = true
|
||||
|
||||
# Icône personnalisée visible par les utilisateurs
|
||||
# Icône personnalisée visible par les utilisateurs
|
||||
logo_url = "https://www.kevinsubileau.fr/wp-content/uploads/2016/05/RDP_icon.png"
|
||||
|
||||
# Attribution d’un tag pour organiser les applications (ex: équipe d’ingénieurs)
|
||||
# Attribution d’un tag pour organiser les applications (ex: équipe d’ingénieurs)
|
||||
tags = [cloudflare_zero_trust_access_tag.tags["engineers"].name]
|
||||
|
||||
# Durée de session (0s = durée illimitée tant que l’authentification reste valide)
|
||||
# Durée de session (0s = durée illimitée tant que l’authentification reste valide)
|
||||
session_duration = "0s"
|
||||
|
||||
# URL personnalisée affichée en cas de refus d’accès
|
||||
# URL personnalisée affichée en cas de refus d’accès
|
||||
custom_deny_url = "https://denied.tips-of-mine.org/"
|
||||
custom_non_identity_deny_url = "https://denied.tips-of-mine.org/"
|
||||
|
||||
# Nom de domaine public utilisé pour rendre l’interface RDP accessible via navigateur
|
||||
# Nom de domaine public utilisé pour rendre l’interface RDP accessible via navigateur
|
||||
domain = var.cloudflare_gcp_subdomain_rdp
|
||||
|
||||
# Critères de correspondance pour relier cette application à la cible RDP AWS
|
||||
# Indique quel protocole et port utiliser, ainsi que la cible.
|
||||
# Critères de correspondance pour relier cette application à la cible RDP AWS
|
||||
# Indique quel protocole et port utiliser, ainsi que la cible.
|
||||
target_criteria = [{
|
||||
port = 3389
|
||||
protocol = "RDP"
|
||||
@@ -151,8 +151,8 @@ resource "cloudflare_zero_trust_access_application" "cloudflare_gcp_app_rdp_doma
|
||||
}
|
||||
}]
|
||||
|
||||
# Paramètres relatifs au fournisseur d'identité (IdP)
|
||||
# Ici, Authentik via un connecteur OIDC.
|
||||
# Paramètres relatifs au fournisseur d'identité (IdP)
|
||||
# Ici, Authentik via un connecteur OIDC.
|
||||
allowed_idps = [
|
||||
cloudflare_zero_trust_access_identity_provider.authentik_oidc.id,
|
||||
]
|
||||
@@ -161,7 +161,7 @@ resource "cloudflare_zero_trust_access_application" "cloudflare_gcp_app_rdp_doma
|
||||
http_only_cookie_attribute = false
|
||||
options_preflight_bypass = false
|
||||
|
||||
# Référence à la politique d’accès définie dans Access_Controls-Policies-Reusable_Policies.tf
|
||||
# Référence à la politique d’accès définie dans Access_Controls-Policies-Reusable_Policies.tf
|
||||
policies = [{
|
||||
id = cloudflare_zero_trust_access_policy.policies["domain_controller"].id
|
||||
}]
|
||||
|
||||
@@ -17,12 +17,24 @@ resource "cloudflare_zero_trust_access_application" "zero_trust_access_applicati
|
||||
|
||||
type = "self_hosted"
|
||||
name = "Home Network Access Application"
|
||||
domain = "home.tips-of-mine.org"
|
||||
domain = "home.${var.cloudflare_zone}"
|
||||
session_duration = "24h"
|
||||
skip_interstitial = true
|
||||
tags = [cloudflare_zero_trust_access_tag.tags["engineers"].name]
|
||||
|
||||
# Dépend de la création des tags pour assurer qu'ils existent avant l'association
|
||||
# Politiques : humains (groupe par défaut) ET machines (service token CI).
|
||||
# La CI peut ainsi superviser l'application sans authentification humaine
|
||||
# (voir Access_Controls-Service_Auth-Service_Tokens.tf).
|
||||
policies = [
|
||||
{
|
||||
id = cloudflare_zero_trust_access_policy.allow_policie_default.id
|
||||
},
|
||||
{
|
||||
id = cloudflare_zero_trust_access_policy.allow_service_token_gitea_ci.id
|
||||
}
|
||||
]
|
||||
|
||||
# Dépend de la création des tags pour assurer qu'ils existent avant l'association
|
||||
depends_on = [
|
||||
cloudflare_zero_trust_access_tag.tags
|
||||
]
|
||||
@@ -60,20 +72,20 @@ resource "cloudflare_zero_trust_access_application" "cloudflare_app_web_competit
|
||||
custom_deny_url = "https://denied.tips-of-mine.org/"
|
||||
custom_non_identity_deny_url = "https://denied.tips-of-mine.org/"
|
||||
|
||||
# Destination publique vers laquelle Cloudflare redirige les requêtes
|
||||
# Destination publique vers laquelle Cloudflare redirige les requêtes
|
||||
destinations = [{
|
||||
type = "public"
|
||||
uri = var.cloudflare_aws_subdomain_web_sensitive
|
||||
}]
|
||||
|
||||
# Autorisation uniquement via Authentik (OIDC)
|
||||
# Autorisation uniquement via Authentik (OIDC)
|
||||
allowed_idps = [
|
||||
cloudflare_zero_trust_access_identity_provider.authentik_oidc.id,
|
||||
]
|
||||
auto_redirect_to_identity = true # Redirection immédiate vers Authentik
|
||||
allow_authenticate_via_warp = false # Interdit l’accès via WARP (client Zero Trust)
|
||||
|
||||
# Politique de sécurité spécifique à cette application (définie dans un autre fichier)
|
||||
# Politique de sécurité spécifique à cette application (définie dans un autre fichier)
|
||||
policies = [{
|
||||
id = cloudflare_zero_trust_access_policy.policies["competition_web_app"].id
|
||||
}]
|
||||
@@ -99,20 +111,20 @@ resource "cloudflare_zero_trust_access_application" "cloudflare_app_web_intranet
|
||||
custom_deny_url = "https://denied.tips-of-mine.org/"
|
||||
custom_non_identity_deny_url = "https://denied.tips-of-mine.org/"
|
||||
|
||||
# Redirection vers le domaine public hébergeant l’intranet
|
||||
# Redirection vers le domaine public hébergeant l’intranet
|
||||
destinations = [{
|
||||
type = "public"
|
||||
uri = var.cloudflare_aws_subdomain_web
|
||||
}]
|
||||
|
||||
# Authentification obligatoire via Authentik (OIDC)
|
||||
# Authentification obligatoire via Authentik (OIDC)
|
||||
allowed_idps = [
|
||||
cloudflare_zero_trust_access_identity_provider.authentik_oidc.id,
|
||||
]
|
||||
auto_redirect_to_identity = true # Redirection immédiate vers Authentik
|
||||
allow_authenticate_via_warp = false # Interdit l’accès via WARP (client Zero Trust)
|
||||
|
||||
# Politique spécifique définissant qui peut accéder à l’intranet
|
||||
# Politique spécifique définissant qui peut accéder à l’intranet
|
||||
policies = [{
|
||||
id = cloudflare_zero_trust_access_policy.policies["intranet_web_app"].id
|
||||
}]
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# =============================================================================
|
||||
# CLOUDFLARE : Access Controls : Applications > SSH (accès par clé classique)
|
||||
# =============================================================================
|
||||
# BRIQUE SSH n°2 : accès SSH traditionnel par paire de clés, publié via le
|
||||
# tunnel Cloudflare. À comparer avec la brique n°1 (certificats éphémères,
|
||||
# Access_Controls-Service_Auth-Short_Lived_Certificates.tf).
|
||||
#
|
||||
# Chaîne complète :
|
||||
# 1. Terraform génère une paire de clés ED25519 (ci-dessous)
|
||||
# 2. L'application "ssh-classic" du tfvars publie ssh://<ip>:22 dans
|
||||
# l'ingress du tunnel home + crée le DNS ssh.tips-of-mine.org
|
||||
# 3. L'application Access ci-dessous protège ce hostname (IdP + MFA)
|
||||
# 4. Côté client :
|
||||
# cloudflared access ssh-config --hostname ssh.tips-of-mine.org
|
||||
# puis connexion habituelle : ssh -i <clé privée> [email protected]
|
||||
#
|
||||
# ACTIONS EXTERNES (voir ACTIONS_EXTERNES.md) :
|
||||
# - Déposer la clé publique (terraform output ssh_classic_public_key) dans
|
||||
# ~/.ssh/authorized_keys du serveur cible
|
||||
# - Récupérer la clé privée : terraform output -raw ssh_classic_private_key
|
||||
# ATTENTION : elle est stockée dans le state Terraform (bucket S3). C'est
|
||||
# le compromis de cette approche "classique" — c'est exactement ce que la
|
||||
# brique n°1 (certificats éphémères) permet d'éviter.
|
||||
# =============================================================================
|
||||
|
||||
#======================================================
|
||||
# Paire de clés SSH d'exemple
|
||||
#======================================================
|
||||
resource "tls_private_key" "ssh_classic" {
|
||||
algorithm = "ED25519"
|
||||
}
|
||||
|
||||
#======================================================
|
||||
# Application Access protégeant le hostname SSH
|
||||
#======================================================
|
||||
resource "cloudflare_zero_trust_access_application" "cloudflare_home_app_ssh_classic" {
|
||||
account_id = local.cloudflare_account_id
|
||||
|
||||
type = "self_hosted"
|
||||
name = "SSH classique par cle : Home"
|
||||
domain = "ssh.${var.cloudflare_zone}"
|
||||
app_launcher_visible = false
|
||||
session_duration = "24h"
|
||||
tags = [cloudflare_zero_trust_access_tag.tags["engineers"].name]
|
||||
custom_deny_url = "https://denied.tips-of-mine.org/"
|
||||
custom_non_identity_deny_url = "https://denied.tips-of-mine.org/"
|
||||
|
||||
allowed_idps = [
|
||||
cloudflare_zero_trust_access_identity_provider.authentik_oidc.id,
|
||||
]
|
||||
auto_redirect_to_identity = true
|
||||
|
||||
# Réutilise la politique admin de la brique Authentik
|
||||
policies = [{
|
||||
id = cloudflare_zero_trust_access_policy.allow_policie_it_admin.id
|
||||
}]
|
||||
}
|
||||
@@ -35,7 +35,7 @@ resource "cloudflare_zero_trust_access_policy" "allow_policie_it_admin" {
|
||||
#
|
||||
resource "cloudflare_zero_trust_access_policy" "allow_policie_administrators" {
|
||||
account_id = local.cloudflare_account_id
|
||||
name = "Default Admionistratoes"
|
||||
name = "Default Administrators"
|
||||
decision = "allow"
|
||||
session_duration = "30m"
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# =============================================================================
|
||||
# CLOUDFLARE : Access Controls : Policies : Rule Groups (Azure Entra ID)
|
||||
# =============================================================================
|
||||
# Miroir Azure de la brique Authentik : mêmes concepts (rule groups +
|
||||
# politiques réutilisables), mais alimentés par les groupes Entra ID.
|
||||
# La brique est inerte tant que azure_idp_enabled = false ou que
|
||||
# azure_admin_group_id est vide.
|
||||
#
|
||||
# ACTION EXTERNE : récupérer l'Object ID du groupe Entra ID à autoriser
|
||||
# (Portail Azure > Entra ID > Groups > <groupe> > Object Id) et le renseigner
|
||||
# dans azure_admin_group_id (variables.auto.tfvars).
|
||||
# =============================================================================
|
||||
|
||||
locals {
|
||||
azure_groups_enabled = var.azure_idp_enabled && var.azure_admin_group_id != ""
|
||||
}
|
||||
|
||||
#==================================================
|
||||
# Rule group : administrateurs Entra ID
|
||||
#==================================================
|
||||
resource "cloudflare_zero_trust_access_group" "azure_admins_rule_group" {
|
||||
count = local.azure_groups_enabled ? 1 : 0
|
||||
account_id = local.cloudflare_account_id
|
||||
|
||||
name = "GL_Users_Azure Administrators"
|
||||
|
||||
include = [{
|
||||
azure_ad = {
|
||||
id = var.azure_admin_group_id
|
||||
identity_provider_id = cloudflare_zero_trust_access_identity_provider.azure_entra_id[0].id
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
#==================================================
|
||||
# Politique réutilisable : accès admin via Azure
|
||||
# (équivalent Azure de allow_policie_it_admin de la brique Authentik)
|
||||
#==================================================
|
||||
resource "cloudflare_zero_trust_access_policy" "allow_policie_azure_admin" {
|
||||
count = local.azure_groups_enabled ? 1 : 0
|
||||
account_id = local.cloudflare_account_id
|
||||
name = "Default Azure Admin"
|
||||
decision = "allow"
|
||||
session_duration = "6h"
|
||||
|
||||
include = [{
|
||||
group = {
|
||||
id = cloudflare_zero_trust_access_group.azure_admins_rule_group[0].id
|
||||
}
|
||||
}]
|
||||
|
||||
# Mêmes exigences de sécurité que la brique Authentik : MFA obligatoire
|
||||
require = [{
|
||||
auth_method = {
|
||||
auth_method = "mfa"
|
||||
}
|
||||
}]
|
||||
|
||||
exclude = [{
|
||||
auth_method = {
|
||||
auth_method = "sms"
|
||||
}
|
||||
}]
|
||||
}
|
||||
@@ -17,15 +17,15 @@ locals {
|
||||
blocked_countries = ["CN", "RU", "AF", "BY", "CD", "CU", "IR", "IQ", "KP", "MM", "SD", "SY", "UA", "ZW"]
|
||||
|
||||
main_countries = ["FR"]
|
||||
europe_countries = ["AL","AD","AT","AX","BA","BE","BG","BY","CH","CY","CZ","DE","DK","EE","ES","FI","FR","FO","GB","GG","GI","GR","HR","HU","IE","IM","IS","IT","JE","LI","LT","LU","LV","MC","MD","ME","MK","MT","NL","NO","PL","PT","RO","RS","SE","SI","SK","SM","UA","VA"]
|
||||
afrique_countries = ["AO","BF","BI","BJ","BW","CD","CF","CG","CI","CM","CV","DJ","DZ","EG","EH","ER","ET","GA","GH","GM","GN","GQ","GW","KE","KM","LR","LS","LY","MA","MG","ML","MR","MU","MW","MZ","NA","NE","NG","RE","RW","SC","SD","SH","SL","SN","SO","SS","ST","SZ","TD","TF","TG","TN","TZ","UG","YT","ZA","ZM","ZW"]
|
||||
america_north_countries = ["CA","US","MX","BM","PM","GL","UM"]
|
||||
america_central_countries = ["AG","AI","AW","BB","BZ","CR","CU","DM","DO","GD","GP","GT","HN","HT","JM","KN","KY","LC","MF","MQ","MS","NI","PA","PR","SV","SX","TC","TT","VC","VG","VI"]
|
||||
america_south_countries = ["AR","BO","BR","CL","CO","EC","FK","GF","GY","PE","PY","SR","UY","VE"]
|
||||
asie_countries = ["AF","AM","AZ","BD","BH","BN","BT","CN","GE","HK","ID","IL","IN","IQ","IR","JO","JP","KG","KH","KP","KR","KW","KZ","LA","LB","LK","MM","MN","MO","MY","NP","OM","PH","PK","PS","QA","SA","SG","SY","TH","TJ","TL","TM","TR","TW","UZ","VN","YE"]
|
||||
oceanie_countries = ["AS","AU","CK","FJ","FM","GU","HM","KI","MH","MP","NC","NF","NR","NU","NZ","PF","PG","PN","PW","SB","TK","TO","TV","UM","VU","WF","WS"]
|
||||
europe_countries = ["AL", "AD", "AT", "AX", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE", "DK", "EE", "ES", "FI", "FR", "FO", "GB", "GG", "GI", "GR", "HR", "HU", "IE", "IM", "IS", "IT", "JE", "LI", "LT", "LU", "LV", "MC", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT", "RO", "RS", "SE", "SI", "SK", "SM", "UA", "VA"]
|
||||
afrique_countries = ["AO", "BF", "BI", "BJ", "BW", "CD", "CF", "CG", "CI", "CM", "CV", "DJ", "DZ", "EG", "EH", "ER", "ET", "GA", "GH", "GM", "GN", "GQ", "GW", "KE", "KM", "LR", "LS", "LY", "MA", "MG", "ML", "MR", "MU", "MW", "MZ", "NA", "NE", "NG", "RE", "RW", "SC", "SD", "SH", "SL", "SN", "SO", "SS", "ST", "SZ", "TD", "TF", "TG", "TN", "TZ", "UG", "YT", "ZA", "ZM", "ZW"]
|
||||
america_north_countries = ["CA", "US", "MX", "BM", "PM", "GL", "UM"]
|
||||
america_central_countries = ["AG", "AI", "AW", "BB", "BZ", "CR", "CU", "DM", "DO", "GD", "GP", "GT", "HN", "HT", "JM", "KN", "KY", "LC", "MF", "MQ", "MS", "NI", "PA", "PR", "SV", "SX", "TC", "TT", "VC", "VG", "VI"]
|
||||
america_south_countries = ["AR", "BO", "BR", "CL", "CO", "EC", "FK", "GF", "GY", "PE", "PY", "SR", "UY", "VE"]
|
||||
asie_countries = ["AF", "AM", "AZ", "BD", "BH", "BN", "BT", "CN", "GE", "HK", "ID", "IL", "IN", "IQ", "IR", "JO", "JP", "KG", "KH", "KP", "KR", "KW", "KZ", "LA", "LB", "LK", "MM", "MN", "MO", "MY", "NP", "OM", "PH", "PK", "PS", "QA", "SA", "SG", "SY", "TH", "TJ", "TL", "TM", "TR", "TW", "UZ", "VN", "YE"]
|
||||
oceanie_countries = ["AS", "AU", "CK", "FJ", "FM", "GU", "HM", "KI", "MH", "MP", "NC", "NF", "NR", "NU", "NZ", "PF", "PG", "PN", "PW", "SB", "TK", "TO", "TV", "UM", "VU", "WF", "WS"]
|
||||
antarctique_countries = ["AQ", "BV"]
|
||||
other_countries = ["IO","GS"]
|
||||
other_countries = ["IO", "GS"]
|
||||
|
||||
# On fusionne *toutes* les zones dans une seule liste
|
||||
all_countries = flatten([
|
||||
@@ -53,49 +53,49 @@ locals {
|
||||
])
|
||||
|
||||
# On retire les pays "afrique"
|
||||
blocked_countries_except_afrique= compact([
|
||||
blocked_countries_except_afrique = compact([
|
||||
for code in local.all_countries :
|
||||
(contains(local.afrique_countries, code) ? null : code)
|
||||
])
|
||||
|
||||
# On retire les pays "america_north"
|
||||
blocked_countries_except_america_north= compact([
|
||||
blocked_countries_except_america_north = compact([
|
||||
for code in local.all_countries :
|
||||
(contains(local.america_north_countries, code) ? null : code)
|
||||
])
|
||||
|
||||
# On retire les pays "america_central"
|
||||
blocked_countries_except_america_central= compact([
|
||||
blocked_countries_except_america_central = compact([
|
||||
for code in local.all_countries :
|
||||
(contains(local.america_central_countries, code) ? null : code)
|
||||
])
|
||||
|
||||
# On retire les pays "america_south"
|
||||
blocked_countries_except_america_south= compact([
|
||||
blocked_countries_except_america_south = compact([
|
||||
for code in local.all_countries :
|
||||
(contains(local.america_south_countries, code) ? null : code)
|
||||
])
|
||||
|
||||
# On retire les pays "asie"
|
||||
blocked_countries_except_asie= compact([
|
||||
blocked_countries_except_asie = compact([
|
||||
for code in local.all_countries :
|
||||
(contains(local.asie_countries, code) ? null : code)
|
||||
])
|
||||
|
||||
# On retire les pays "oceanie"
|
||||
blocked_countries_except_oceanie= compact([
|
||||
blocked_countries_except_oceanie = compact([
|
||||
for code in local.all_countries :
|
||||
(contains(local.oceanie_countries, code) ? null : code)
|
||||
])
|
||||
|
||||
# On retire les pays "antarctique"
|
||||
blocked_countries_except_antarctique= compact([
|
||||
blocked_countries_except_antarctique = compact([
|
||||
for code in local.all_countries :
|
||||
(contains(local.antarctique_countries, code) ? null : code)
|
||||
])
|
||||
|
||||
# On retire les pays "other"
|
||||
blocked_countries_except_other= compact([
|
||||
blocked_countries_except_other = compact([
|
||||
for code in local.all_countries :
|
||||
(contains(local.other_countries, code) ? null : code)
|
||||
])
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
# =============================================================================
|
||||
# CLOUDFLARE : Access Controls : Service Auth : Service Tokens
|
||||
# =============================================================================
|
||||
# BRIQUE SERVICE TOKENS : accès machine-to-machine (sans utilisateur).
|
||||
#
|
||||
# Cas d'usage : une CI, une sonde de supervision ou un script doit appeler
|
||||
# une application protégée par Access. Pas d'humain pour s'authentifier via
|
||||
# l'IdP -> on émet un service token (paire Client ID / Client Secret) que la
|
||||
# machine présente en en-têtes HTTP :
|
||||
# CF-Access-Client-Id: <client_id>
|
||||
# CF-Access-Client-Secret: <client_secret>
|
||||
#
|
||||
# La politique associée utilise decision = "non_identity" : c'est la décision
|
||||
# dédiée aux authentifications par service token.
|
||||
#
|
||||
# ACTIONS EXTERNES (voir ACTIONS_EXTERNES.md) :
|
||||
# - Récupérer les credentials : terraform output -raw service_token_gitea_ci_id
|
||||
# et terraform output -raw service_token_gitea_ci_secret
|
||||
# - Les stocker comme secrets dans Gitea (et idéalement dans Vault)
|
||||
# - Le token expire (duration ci-dessous) : anticiper la rotation
|
||||
# =============================================================================
|
||||
|
||||
#======================================================
|
||||
# Service token pour la CI Gitea
|
||||
#======================================================
|
||||
resource "cloudflare_zero_trust_access_service_token" "gitea_ci" {
|
||||
account_id = local.cloudflare_account_id
|
||||
|
||||
name = "Gitea CI"
|
||||
duration = "8760h" # 1 an
|
||||
}
|
||||
|
||||
#======================================================
|
||||
# Politique réutilisable : accepter ce service token
|
||||
#======================================================
|
||||
resource "cloudflare_zero_trust_access_policy" "allow_service_token_gitea_ci" {
|
||||
account_id = local.cloudflare_account_id
|
||||
|
||||
name = "Allow service token Gitea CI"
|
||||
decision = "non_identity"
|
||||
session_duration = "24h"
|
||||
|
||||
include = [{
|
||||
service_token = {
|
||||
token_id = cloudflare_zero_trust_access_service_token.gitea_ci.id
|
||||
}
|
||||
}]
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
# =============================================================================
|
||||
# CLOUDFLARE : Access Controls : Service Auth : SSH Short-lived certificates
|
||||
# =============================================================================
|
||||
# BRIQUE SSH n°1 : certificats SSH éphémères (remplacent les clés SSH).
|
||||
#
|
||||
# Principe : Cloudflare génère une autorité de certification (CA) par
|
||||
# application SSH. À chaque connexion, l'utilisateur authentifié via Access
|
||||
# reçoit un certificat SSH de quelques secondes — plus aucune clé privée
|
||||
# longue durée à distribuer, plus de rotation à gérer.
|
||||
#
|
||||
# ACTIONS EXTERNES (sur chaque serveur SSH cible, voir ACTIONS_EXTERNES.md) :
|
||||
# 1. Récupérer la clé publique de la CA : terraform output ssh_ca_aws / ssh_ca_gcp
|
||||
# 2. La déposer sur le serveur dans /etc/ssh/ca.pub
|
||||
# 3. Ajouter dans /etc/ssh/sshd_config :
|
||||
# PubkeyAuthentication yes
|
||||
# TrustedUserCAKeys /etc/ssh/ca.pub
|
||||
# 4. Redémarrer sshd. Les comptes UNIX doivent correspondre à la partie
|
||||
# locale de l'email (allow_email_alias) ou aux usernames autorisés.
|
||||
#
|
||||
# NOTE : les applications de type "infrastructure"
|
||||
# (Access_Controls-Applications-Infrastructure.tf) utilisent le même principe
|
||||
# nativement, la CA est gérée automatiquement par Cloudflare.
|
||||
# Ici on couvre le cas des applications SSH "browser rendered".
|
||||
# =============================================================================
|
||||
|
||||
# CA de certificats éphémères pour l'app SSH browser-rendered AWS
|
||||
resource "cloudflare_zero_trust_access_short_lived_certificate" "aws_ssh_ca" {
|
||||
account_id = local.cloudflare_account_id
|
||||
app_id = cloudflare_zero_trust_access_application.cloudflare_aws_app_ssh_browser.id
|
||||
}
|
||||
|
||||
# CA de certificats éphémères pour l'app SSH browser-rendered GCP
|
||||
resource "cloudflare_zero_trust_access_short_lived_certificate" "gcp_ssh_ca" {
|
||||
account_id = local.cloudflare_account_id
|
||||
app_id = cloudflare_zero_trust_access_application.cloudflare_gcp_app_ssh_browser.id
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
# =============================================================================
|
||||
# CLOUDFLARE : Insights : Digital Experience (DEX)
|
||||
# =============================================================================
|
||||
# BRIQUE DEX : supervision de l'expérience utilisateur depuis les postes WARP.
|
||||
#
|
||||
# Les tests DEX sont exécutés PAR les clients WARP enrôlés : chaque poste
|
||||
# mesure régulièrement la latence/disponibilité des cibles ci-dessous et
|
||||
# remonte les résultats dans Insights > Digital Experience du dashboard.
|
||||
# Idéal pour répondre à "c'est lent chez moi" avec des données par site,
|
||||
# par FAI et par poste.
|
||||
#
|
||||
# (Réactivation de l'ancien fichier Insights-Digital_Experience-Test.tf.old,
|
||||
# corrigé : target_policies factices supprimées, cibles réelles du projet.)
|
||||
# =============================================================================
|
||||
|
||||
#======================================================
|
||||
# Test HTTP : l'application Home Assistant répond
|
||||
#======================================================
|
||||
resource "cloudflare_zero_trust_dex_test" "home_app_http" {
|
||||
account_id = local.cloudflare_account_id
|
||||
|
||||
name = "HTTP home application"
|
||||
description = "Verifie que home.${var.cloudflare_zone} repond depuis les postes WARP"
|
||||
enabled = true
|
||||
interval = "30m"
|
||||
|
||||
data = {
|
||||
host = "https://home.${var.cloudflare_zone}"
|
||||
kind = "http"
|
||||
method = "GET"
|
||||
}
|
||||
}
|
||||
|
||||
#======================================================
|
||||
# Test traceroute : chemin vers le réseau du site principal
|
||||
#======================================================
|
||||
resource "cloudflare_zero_trust_dex_test" "home_network_traceroute" {
|
||||
account_id = local.cloudflare_account_id
|
||||
|
||||
name = "Traceroute reseau home"
|
||||
description = "Chemin reseau depuis les postes WARP vers le LAN domestique via le tunnel"
|
||||
enabled = true
|
||||
interval = "30m"
|
||||
|
||||
data = {
|
||||
host = "10.0.4.133"
|
||||
kind = "traceroute"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
# =============================================================================
|
||||
# CLOUDFLARE : Insights : Logs : Logpush
|
||||
# =============================================================================
|
||||
# BRIQUE LOGPUSH : export continu des logs Gateway vers un stockage externe
|
||||
# (S3, R2, ou un SIEM type Elastic/Splunk/Datadog) pour l'audit long terme.
|
||||
# Le dashboard ne conserve les logs que quelques jours : Logpush est la
|
||||
# réponse "entreprise" à la rétention et à la corrélation SIEM.
|
||||
#
|
||||
# La brique est inerte tant que logpush_enabled = false.
|
||||
#
|
||||
# ACTIONS EXTERNES (voir ACTIONS_EXTERNES.md) :
|
||||
# 1. Créer le bucket de destination (ex : R2 "gateway-logs", ou S3)
|
||||
# 2. Renseigner logpush_destination_conf dans variables.auto.tfvars, ex :
|
||||
# S3 : "s3://mon-bucket/gateway?region=eu-north-1"
|
||||
# R2 : "r2://gateway-logs/{DATE}?account-id=<account>&access-key-id=...&secret-access-key=..."
|
||||
# 3. Passer logpush_enabled = true
|
||||
# =============================================================================
|
||||
|
||||
resource "cloudflare_logpush_job" "gateway_http" {
|
||||
count = var.logpush_enabled ? 1 : 0
|
||||
|
||||
account_id = local.cloudflare_account_id
|
||||
name = "gateway-http-logs"
|
||||
dataset = "gateway_http"
|
||||
destination_conf = var.logpush_destination_conf
|
||||
enabled = true
|
||||
|
||||
output_options = {
|
||||
field_names = [
|
||||
"Datetime",
|
||||
"Email",
|
||||
"Action",
|
||||
"URL",
|
||||
"HTTPHost",
|
||||
"HTTPMethod",
|
||||
"HTTPStatusCode",
|
||||
"DeviceName",
|
||||
"PolicyName",
|
||||
]
|
||||
timestamp_format = "rfc3339"
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,10 @@ resource "cloudflare_zero_trust_access_identity_provider" "gmail" {
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
#======================================================
|
||||
# BRIQUE IdP n°1 : Authentik (OIDC générique)
|
||||
# Secrets lus depuis Vault : secret/authentik
|
||||
#======================================================
|
||||
resource "cloudflare_zero_trust_access_identity_provider" "authentik_oidc" {
|
||||
account_id = local.cloudflare_account_id
|
||||
name = "Authentik OIDC"
|
||||
@@ -34,3 +37,33 @@ resource "cloudflare_zero_trust_access_identity_provider" "authentik_oidc" {
|
||||
token_url = "https://authentik.${var.cloudflare_authentik_domain}/application/o/token/"
|
||||
}
|
||||
}
|
||||
|
||||
#======================================================
|
||||
# BRIQUE IdP n°2 : Azure Entra ID (miroir de la brique Authentik)
|
||||
# Secrets lus depuis Vault : secret/azure
|
||||
# - client_id_cloudflare : Application (client) ID de l'App Registration
|
||||
# - secret_cloudflare : Client secret de l'App Registration
|
||||
# - directory_id : Directory (tenant) ID
|
||||
#
|
||||
# ACTIONS EXTERNES (voir ACTIONS_EXTERNES.md) :
|
||||
# 1. Créer une App Registration dans Azure Entra ID avec l'URL de redirection
|
||||
# https://<team_name>.cloudflareaccess.com/cdn-cgi/access/callback
|
||||
# 2. Créer le secret "secret/azure" dans Vault avec les 3 clés ci-dessus
|
||||
# 3. Passer azure_idp_enabled = true dans variables.auto.tfvars
|
||||
#======================================================
|
||||
resource "cloudflare_zero_trust_access_identity_provider" "azure_entra_id" {
|
||||
count = var.azure_idp_enabled ? 1 : 0
|
||||
|
||||
account_id = local.cloudflare_account_id
|
||||
name = "Azure Entra ID"
|
||||
type = "azureAD"
|
||||
|
||||
config = {
|
||||
client_id = local.azure_oidc_client_id_cloudflare
|
||||
client_secret = local.azure_oidc_secret_cloudflare
|
||||
directory_id = local.azure_directory_id
|
||||
# Remonter les groupes Entra ID dans les claims -> utilisables dans les
|
||||
# rule groups (Access_Controls-Policies-Rule_Groups-Azure.tf)
|
||||
support_groups = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# =============================================================================
|
||||
# CLOUDFLARE : Networks : Connectors : WARP Connector
|
||||
# =============================================================================
|
||||
# BRIQUE SITES DISTANTS (2/2) : approche "WARP Connector".
|
||||
#
|
||||
# Différence avec la brique 1 (tunnel cloudflared par site) :
|
||||
# - cloudflared = trafic ENTRANT vers le site (les clients atteignent le LAN)
|
||||
# - WARP Connector = trafic BIDIRECTIONNEL site <-> site et site <-> clients
|
||||
# (le LAN du site peut aussi initier des connexions vers le reste du
|
||||
# réseau Zero Trust : imprimantes, AD, supervision...)
|
||||
#
|
||||
# LIMITE ASSUMÉE : le provider Terraform ne sait pas encore créer un WARP
|
||||
# Connector. On suit donc la même approche que le projet de référence
|
||||
# (macharpe/terraform-cloudflare-zero-trust-demo) :
|
||||
#
|
||||
# ACTIONS EXTERNES (voir ACTIONS_EXTERNES.md) :
|
||||
# 1. Dashboard Zero Trust > Networks > Tunnels > Create > WARP Connector,
|
||||
# un par site (ex : "Site A", "Site B")
|
||||
# 2. Copier l'ID de chaque connecteur dans variables.auto.tfvars
|
||||
# (cloudflare_tunnel_warp_connector_site_a_id / _site_b_id)
|
||||
# 3. Terraform récupère alors les tokens d'installation via l'API (data
|
||||
# "http" ci-dessous) : terraform output -json warp_connector_tokens
|
||||
# 4. Installer le connecteur sur une machine Linux du site :
|
||||
# warp-cli connector new <token>
|
||||
#
|
||||
# La brique est inerte tant que les IDs sont vides.
|
||||
# =============================================================================
|
||||
|
||||
locals {
|
||||
warp_connector_sites = {
|
||||
for site, id in {
|
||||
site_a = var.cloudflare_tunnel_warp_connector_site_a_id
|
||||
site_b = var.cloudflare_tunnel_warp_connector_site_b_id
|
||||
} : site => id if id != ""
|
||||
}
|
||||
|
||||
cloudflare_api_headers = {
|
||||
"Authorization" = "Bearer ${local.cloudflare_api_token}"
|
||||
"Content-Type" = "application/json"
|
||||
}
|
||||
}
|
||||
|
||||
# Récupération du token d'installation de chaque WARP Connector
|
||||
data "http" "warp_connector_tokens" {
|
||||
for_each = local.warp_connector_sites
|
||||
|
||||
url = "https://api.cloudflare.com/client/v4/accounts/${local.cloudflare_account_id}/warp_connector/${each.value}/token"
|
||||
request_headers = local.cloudflare_api_headers
|
||||
}
|
||||
@@ -42,7 +42,7 @@ resource "cloudflare_zero_trust_dns_location" "dns_location_home" {
|
||||
account_id = local.cloudflare_account_id
|
||||
name = "Home Location"
|
||||
client_default = false
|
||||
# dns_destination_ips_id = "0e4a32c6-6fb8-4858-9296-98f51631e8e6"
|
||||
# dns_destination_ips_id = "0e4a32c6-6fb8-4858-9296-98f51631e8e6"
|
||||
ecs_support = true
|
||||
endpoints = {
|
||||
doh = {
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# =============================================================================
|
||||
# CLOUDFLARE : Networks : Routes : Remote Sites (sites distants d'entreprise)
|
||||
# =============================================================================
|
||||
# BRIQUE SITES DISTANTS (1/2) : approche "tunnel cloudflared par site".
|
||||
#
|
||||
# Scénario simulé : une grande entreprise avec plusieurs agences (Paris,
|
||||
# Lyon...). Chaque site héberge un connecteur cloudflared et publie son
|
||||
# réseau local dans Cloudflare.
|
||||
#
|
||||
# Point clé de l'exemple : les deux sites utilisent LE MÊME CIDR
|
||||
# (10.100.0.0/16). C'est un cas très réaliste (agences déployées sur le même
|
||||
# template réseau, fusions/acquisitions...). Les réseaux virtuels Cloudflare
|
||||
# permettent de faire cohabiter ces plages identiques : chaque site vit dans
|
||||
# son propre réseau virtuel, et le client WARP choisit le réseau virtuel
|
||||
# actif pour lever l'ambiguïté.
|
||||
#
|
||||
# ACTIONS EXTERNES (voir ACTIONS_EXTERNES.md) :
|
||||
# - Sur chaque site, installer cloudflared et le connecter avec son token :
|
||||
# terraform output -json remote_site_tunnel_tokens
|
||||
# cloudflared service install <token du site>
|
||||
# - Pour basculer un poste WARP d'un site à l'autre :
|
||||
# warp-cli vnet <id> (ou via l'UI du client WARP)
|
||||
# =============================================================================
|
||||
|
||||
#======================================================
|
||||
# Un réseau virtuel par site
|
||||
#======================================================
|
||||
resource "cloudflare_zero_trust_tunnel_cloudflared_virtual_network" "remote_sites" {
|
||||
for_each = var.remote_sites
|
||||
account_id = local.cloudflare_account_id
|
||||
|
||||
name = "vnet-${each.key}"
|
||||
comment = each.value.comment
|
||||
is_default_network = false
|
||||
}
|
||||
|
||||
#======================================================
|
||||
# Un tunnel cloudflared par site
|
||||
#======================================================
|
||||
resource "cloudflare_zero_trust_tunnel_cloudflared" "remote_sites" {
|
||||
for_each = var.remote_sites
|
||||
account_id = local.cloudflare_account_id
|
||||
|
||||
name = "Tunnel Site ${each.key}"
|
||||
config_src = "cloudflare"
|
||||
}
|
||||
|
||||
# Token du connecteur de chaque site (à installer sur la machine du site)
|
||||
data "cloudflare_zero_trust_tunnel_cloudflared_token" "remote_sites" {
|
||||
for_each = var.remote_sites
|
||||
account_id = local.cloudflare_account_id
|
||||
|
||||
tunnel_id = cloudflare_zero_trust_tunnel_cloudflared.remote_sites[each.key].id
|
||||
}
|
||||
|
||||
#======================================================
|
||||
# La route de chaque site, isolée dans son réseau virtuel
|
||||
# (c'est ce rattachement qui autorise les CIDR identiques)
|
||||
#======================================================
|
||||
resource "cloudflare_zero_trust_tunnel_cloudflared_route" "remote_sites" {
|
||||
for_each = var.remote_sites
|
||||
account_id = local.cloudflare_account_id
|
||||
|
||||
network = each.value.cidr
|
||||
tunnel_id = cloudflare_zero_trust_tunnel_cloudflared.remote_sites[each.key].id
|
||||
virtual_network_id = cloudflare_zero_trust_tunnel_cloudflared_virtual_network.remote_sites[each.key].id
|
||||
comment = "LAN du site ${each.key}"
|
||||
}
|
||||
@@ -8,7 +8,7 @@ resource "cloudflare_zero_trust_tunnel_cloudflared_virtual_network" "zero_trust_
|
||||
|
||||
name = "vpc-aws"
|
||||
comment = "AWS VPC for data science"
|
||||
# is_default = false
|
||||
# is_default = false
|
||||
is_default_network = false
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ resource "cloudflare_zero_trust_tunnel_cloudflared_virtual_network" "zero_trust_
|
||||
|
||||
name = "vpc-azure"
|
||||
comment = "AZURE VPC for data science"
|
||||
# is_default = false
|
||||
# is_default = false
|
||||
is_default_network = false
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ resource "cloudflare_zero_trust_tunnel_cloudflared_virtual_network" "zero_trust_
|
||||
|
||||
name = "vpc-gcp"
|
||||
comment = "GCP VPC for data science"
|
||||
# is_default = false
|
||||
# is_default = false
|
||||
is_default_network = false
|
||||
}
|
||||
|
||||
@@ -38,6 +38,6 @@ resource "cloudflare_zero_trust_tunnel_cloudflared_virtual_network" "zero_trust_
|
||||
|
||||
name = "vpc-ovh"
|
||||
comment = "OVH VPC for data science"
|
||||
# is_default = false
|
||||
# is_default = false
|
||||
is_default_network = false
|
||||
}
|
||||
+48
-13
@@ -2,7 +2,7 @@
|
||||
# CLOUDFLARE : Networks : Routes : CIDR
|
||||
# =============================================================================
|
||||
|
||||
#
|
||||
# Route du réseau domestique via le tunnel principal
|
||||
resource "cloudflare_zero_trust_tunnel_cloudflared_route" "home_tunnel_route" {
|
||||
account_id = local.cloudflare_account_id
|
||||
network = var.tunnel_network
|
||||
@@ -10,17 +10,51 @@ resource "cloudflare_zero_trust_tunnel_cloudflared_route" "home_tunnel_route" {
|
||||
comment = var.tunnel_network_comment
|
||||
}
|
||||
|
||||
#
|
||||
#data "cloudflare_zero_trust_tunnel_cloudflared_route" "home_tunnel_route_token" {
|
||||
# account_id = "699d98642c564d2e855e9661899b7252"
|
||||
# route_id = cloudflare_zero_trust_tunnel_cloudflared_route.home_tunnel_route.id
|
||||
#}
|
||||
#======================================================
|
||||
# Routes des tunnels cloud, rattachées à leur réseau virtuel
|
||||
# Chaque route publie le CIDR privé du cloud dans le réseau Cloudflare,
|
||||
# isolé dans son propre réseau virtuel (défini dans
|
||||
# Networks-Routes-Virtual_Networks.tf)
|
||||
#======================================================
|
||||
|
||||
resource "cloudflare_zero_trust_tunnel_cloudflared_route" "aws_tunnel_route" {
|
||||
account_id = local.cloudflare_account_id
|
||||
network = var.aws_private_cidr
|
||||
tunnel_id = cloudflare_zero_trust_tunnel_cloudflared.aws_tunnel.id
|
||||
virtual_network_id = cloudflare_zero_trust_tunnel_cloudflared_virtual_network.zero_trust_tunnel_cloudflared_virtual_network_aws.id
|
||||
comment = "Route AWS private subnet -> vpc-aws"
|
||||
}
|
||||
|
||||
resource "cloudflare_zero_trust_tunnel_cloudflared_route" "gcp_tunnel_route" {
|
||||
account_id = local.cloudflare_account_id
|
||||
network = var.gcp_infra_cidr
|
||||
tunnel_id = cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id
|
||||
virtual_network_id = cloudflare_zero_trust_tunnel_cloudflared_virtual_network.zero_trust_tunnel_cloudflared_virtual_network_gcp.id
|
||||
comment = "Route GCP infra subnet -> vpc-gcp"
|
||||
}
|
||||
|
||||
resource "cloudflare_zero_trust_tunnel_cloudflared_route" "azure_tunnel_route" {
|
||||
account_id = local.cloudflare_account_id
|
||||
network = var.azure_subnet_cidr
|
||||
tunnel_id = cloudflare_zero_trust_tunnel_cloudflared.azure_tunnel.id
|
||||
virtual_network_id = cloudflare_zero_trust_tunnel_cloudflared_virtual_network.zero_trust_tunnel_cloudflared_virtual_network_azure.id
|
||||
comment = "Route Azure subnet -> vpc-azure"
|
||||
}
|
||||
|
||||
resource "cloudflare_zero_trust_tunnel_cloudflared_route" "ovh_tunnel_route" {
|
||||
account_id = local.cloudflare_account_id
|
||||
network = var.ovh_private_cidr
|
||||
tunnel_id = cloudflare_zero_trust_tunnel_cloudflared.ovh_tunnel.id
|
||||
virtual_network_id = cloudflare_zero_trust_tunnel_cloudflared_virtual_network.zero_trust_tunnel_cloudflared_virtual_network_ovh.id
|
||||
comment = "Route OVH private subnet -> vpc-ovh"
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# DNS RECORDS (un par application)
|
||||
# =============================================================================
|
||||
|
||||
#
|
||||
# Ici, zone_id (l'identifiant hexadécimal, depuis Vault) est le bon usage :
|
||||
# l'API DNS attend l'ID de zone, pas le nom de domaine.
|
||||
resource "cloudflare_dns_record" "applications" {
|
||||
for_each = var.applications
|
||||
|
||||
@@ -37,7 +71,13 @@ resource "cloudflare_dns_record" "applications" {
|
||||
# TUNNEL CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
#
|
||||
# CORRECTIF : le bloc lifecycle { ignore_changes = [config] } a été retiré.
|
||||
# Terraform est désormais LA source de vérité pour les règles d'ingress :
|
||||
# - ajouter/modifier une application dans variables.auto.tfvars met bien à
|
||||
# jour le tunnel au prochain apply (ce n'était plus le cas avant) ;
|
||||
# - ACTION EXTERNE : toute modification faite à la main dans le dashboard
|
||||
# (onglet "Public Hostnames" du tunnel) sera ÉCRASÉE au prochain apply.
|
||||
# Reporter d'abord dans le tfvars ce qui a été configuré à la main.
|
||||
resource "cloudflare_zero_trust_tunnel_cloudflared_config" "home_tunnel_config" {
|
||||
account_id = local.cloudflare_account_id
|
||||
tunnel_id = cloudflare_zero_trust_tunnel_cloudflared.home_tunnel.id
|
||||
@@ -49,9 +89,4 @@ resource "cloudflare_zero_trust_tunnel_cloudflared_config" "home_tunnel_config"
|
||||
|
||||
ingress = local.ingress_rules
|
||||
}
|
||||
|
||||
lifecycle {
|
||||
# Ignorer les changements manuels dans Cloudflare Dashboard
|
||||
ignore_changes = [config]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,125 @@
|
||||
# terraform-cloudflare-tunnel-zone-org
|
||||
|
||||
Déployement de tunnel Cloudflare
|
||||
Bibliothèque d'exemples **Cloudflare Zero Trust** gérés par Terraform, construite autour d'un tunnel Cloudflare « home lab » sur la zone `tips-of-mine.org`.
|
||||
|
||||
# A propos de
|
||||
Inspiré de [macharpe/terraform-cloudflare-zero-trust-demo](https://github.com/macharpe/terraform-cloudflare-zero-trust-demo), adapté à une stack **Vault (miroir OpenBao) + Authentik + Gitea Actions**, et enrichi de briques supplémentaires.
|
||||
|
||||
> 📋 **Avant tout apply : lire [ACTIONS_EXTERNES.md](ACTIONS_EXTERNES.md)** — tout ce que Terraform ne peut pas faire (secrets Vault à créer, sshd à configurer, connecteurs à installer...) y est détaillé brique par brique.
|
||||
|
||||
# Les briques
|
||||
|
||||
Les fichiers suivent la navigation du dashboard Cloudflare Zero Trust (`Section-SousSection-Nom.tf`).
|
||||
|
||||
## Cœur — tunnel & applications
|
||||
|
||||
| Fichier | Contenu |
|
||||
|---|---|
|
||||
| `main.tf` | Secrets Vault, locals, construction des règles d'ingress |
|
||||
| `Networks-Connectors-Cloudflare_Tunnels.tf` | Tunnels cloudflared (home + clouds) |
|
||||
| `Networks-Routes-cidr.tf` | Routes réseau, DNS des applications, config d'ingress du tunnel |
|
||||
| `Networks-Routes-Virtual_Networks.tf` | Réseaux virtuels par cloud |
|
||||
| `variables.auto.tfvars` → `applications` | La map des applications exposées : ajouter une entrée = DNS + ingress créés au prochain apply |
|
||||
|
||||
## Identité — deux briques IdP interchangeables
|
||||
|
||||
| Brique | Fichiers | Activation |
|
||||
|---|---|---|
|
||||
| **Authentik (OIDC)** | `Integrations-Identity_providers.tf`, `Access_Controls-Policies-Rule_Groups.tf` | toujours active |
|
||||
| **Azure Entra ID** | `Integrations-Identity_providers.tf`, `Access_Controls-Policies-Rule_Groups-Azure.tf` | `azure_idp_enabled = true` + secret Vault `secret/azure` |
|
||||
|
||||
Les deux alimentent les mêmes concepts : rule groups, politiques réutilisables, enrôlement WARP.
|
||||
|
||||
## Accès SSH — deux approches comparées
|
||||
|
||||
| Brique | Fichier | Principe |
|
||||
|---|---|---|
|
||||
| **Certificats éphémères** | `Access_Controls-Service_Auth-Short_Lived_Certificates.tf` + apps `Infrastructure`/`ssh` | Cloudflare signe un certificat SSH de quelques secondes à chaque connexion — aucune clé à distribuer |
|
||||
| **Clé SSH classique** | `Access_Controls-Applications-ssh_key_based.tf` | Paire de clés ED25519 générée par Terraform, publiée via le tunnel — montre les limites (clé dans le state, rotation manuelle) |
|
||||
|
||||
## Agent WARP (postes de travail)
|
||||
|
||||
| Fichier | Contenu |
|
||||
|---|---|
|
||||
| `Team_Resources-Devices-Enrollment_Permissions.tf` | Qui peut enrôler son poste (app type `warp`, MFA, domaine mail) |
|
||||
| `Team_Resources-Devices-Device_posture.tf` | Postures gérées par Terraform : WARP actif, pare-feu, disque chiffré, version d'OS |
|
||||
| `Team_Resources-Devices-Device_profiles.tf` | Profils WARP par OS |
|
||||
|
||||
## Sites distants d'entreprise
|
||||
|
||||
| Brique | Fichier | Principe |
|
||||
|---|---|---|
|
||||
| **Tunnels par site** | `Networks-Routes-Remote_Sites.tf` | Un cloudflared + un réseau virtuel par agence — **CIDR identiques entre sites** (cas réel post-fusion), isolés par réseau virtuel |
|
||||
| **WARP Connector** | `Networks-Connectors-WARP_Connector.tf` | Connectivité bidirectionnelle site↔site (création manuelle du connecteur, tokens récupérés par Terraform) |
|
||||
|
||||
## Politiques de trafic (Gateway)
|
||||
|
||||
| Fichier | Contenu |
|
||||
|---|---|
|
||||
| `Traffic_Policies-Firewall_Policies-DNS.tf` | Blocage malware + liste publicitaire Pi-hole (mise à jour quotidienne par `action-update-lists.yml`) |
|
||||
| `Traffic_Policies-Firewall_Policies-HTTP.tf` | Gouvernance IA, filtrage contenu, **RBI** (webmail isolé, sous licence) |
|
||||
| `Traffic_Policies-Firewall_Policies-Network.tf` | Anti-mouvement latéral (SSH/RDP/SMB/WinRM/DB), contrôle RDP |
|
||||
| `Traffic_Policies-Firewall_Policies-DLP.tf` | Profil DLP IBAN + politique de blocage (sous licence) |
|
||||
|
||||
## Observabilité & M2M
|
||||
|
||||
| Fichier | Contenu |
|
||||
|---|---|
|
||||
| `Insights-Digital_Experience-Tests.tf` | Tests DEX (HTTP + traceroute) exécutés par les postes WARP |
|
||||
| `Insights-Logs-Logpush.tf` | Export des logs Gateway vers S3/R2/SIEM (`logpush_enabled`) |
|
||||
| `Access_Controls-Service_Auth-Service_Tokens.tf` | Accès machine-to-machine (CI) par service token |
|
||||
|
||||
# Prérequis
|
||||
|
||||
Vous avez besoin d'une installation docker fonctionnelle.
|
||||
- Terraform **>= 1.7.5** — https://developer.hashicorp.com/terraform/install
|
||||
- Un Vault (ou OpenBao) joignable avec les secrets décrits dans [ACTIONS_EXTERNES.md](ACTIONS_EXTERNES.md)
|
||||
- Un bucket S3 pour le state (`state.tf`)
|
||||
|
||||
https://developer.hashicorp.com/terraform/install?product_intent=terraform
|
||||
|
||||
# Create an API token
|
||||
## Token API Cloudflare (stocké dans Vault : `secret/cloudflare`, clé `api_token`)
|
||||
|
||||
| Type | Item | Permission | Mandatory |
|
||||
| :-------- | :------- | :------------------------- | :------- |
|
||||
| `Account` | `Cloudflare Tunnel` | **Edit** | **Required** |
|
||||
| `Account` | `Zero Trust` | **Edit** | **Required** |
|
||||
| `Account` | `Access: Apps and Policies` | **Edit** | **Required** |
|
||||
| `Account` | `Access: Service Tokens` | **Edit** | **Required** |
|
||||
| `Account` | `Zero Trust: PII` | **Read** | **Recommended** |
|
||||
| `Account` | `Account Settings` | **Read** | **Recommended** |
|
||||
| `Zone` | `DNS` | **Edit** | **Required** |
|
||||
| `Zone` | `Zone` | **Read** | **Recommended** |
|
||||
|
||||
|
||||
# Démarrage manuel
|
||||
|
||||
~~~bash
|
||||
git clone https://gitea.tips-of-mine.com/terraform-cloudflare-tunnel-zone-org.git
|
||||
cd terraform-cloudflare-tunnel-zone-org
|
||||
git clone https://gitea.tips-of-mine.com/terraform-cloudflare-tunnel-zone-org.git
|
||||
cd terraform-cloudflare-tunnel-zone-org
|
||||
~~~
|
||||
|
||||
# Utilisation du repository
|
||||
|
||||
init
|
||||
~~~bash
|
||||
terraform init
|
||||
terraform init
|
||||
~~~
|
||||
|
||||
validation
|
||||
~~~bash
|
||||
terraform validate
|
||||
terraform validate
|
||||
~~~
|
||||
|
||||
plan
|
||||
plan (le token Vault est demandé, ou via `TF_VAR_vault_token`)
|
||||
~~~bash
|
||||
terraform plan
|
||||
terraform plan
|
||||
~~~
|
||||
|
||||
# CI/CD (Gitea Actions)
|
||||
|
||||
| Workflow | Déclencheur | Rôle |
|
||||
|---|---|---|
|
||||
| `validate.yml` | pull request | fmt + validate + **plan** |
|
||||
| `cicd.yml` | push sur `main` | plan + **apply** |
|
||||
| `action-update-lists.yml` | cron quotidien | met à jour la liste Pi-hole et ouvre une PR |
|
||||
|
||||
Secrets Gitea nécessaires : `VAULT_TOKEN`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`.
|
||||
|
||||
# Buy me a coffe
|
||||
<a href='https://ko-fi.com/R5R2KNI3N' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://storage.ko-fi.com/cdn/kofi4.png?v=3' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>
|
||||
@@ -0,0 +1,116 @@
|
||||
# =============================================================================
|
||||
# CLOUDFLARE : Team & Resources : Devices : Device posture
|
||||
# =============================================================================
|
||||
# BRIQUE AGENT WARP (2/2) : vérifications de posture des postes.
|
||||
#
|
||||
# Jusqu'ici le projet consommait des posture checks créés À LA MAIN dans le
|
||||
# dashboard (variables cloudflare_*_posture_id du tfvars). Cette brique montre
|
||||
# comment les gérer entièrement en Terraform : les IDs deviennent des
|
||||
# attributs de ressources, plus rien à recopier depuis le dashboard.
|
||||
#
|
||||
# Les deux approches cohabitent volontairement (c'est un repo d'exemples) :
|
||||
# - ancienne : IDs manuels via variables (utilisés par Rule_Groups.tf)
|
||||
# - nouvelle : ressources ci-dessous, référencées par leur attribut .id
|
||||
# =============================================================================
|
||||
|
||||
#======================================================
|
||||
# Le client WARP est installé et connecté
|
||||
#======================================================
|
||||
resource "cloudflare_zero_trust_device_posture_rule" "warp_installed" {
|
||||
account_id = local.cloudflare_account_id
|
||||
|
||||
name = "TF - WARP installe"
|
||||
description = "Le poste execute le client WARP"
|
||||
type = "warp"
|
||||
|
||||
match = [
|
||||
{ platform = "windows" },
|
||||
{ platform = "mac" },
|
||||
{ platform = "linux" },
|
||||
]
|
||||
}
|
||||
|
||||
#======================================================
|
||||
# Pare-feu actif (Windows / macOS)
|
||||
#======================================================
|
||||
resource "cloudflare_zero_trust_device_posture_rule" "firewall_enabled" {
|
||||
account_id = local.cloudflare_account_id
|
||||
|
||||
name = "TF - Pare-feu actif"
|
||||
description = "Le pare-feu local du poste doit etre active"
|
||||
type = "firewall"
|
||||
schedule = "5m"
|
||||
|
||||
match = [
|
||||
{ platform = "windows" },
|
||||
{ platform = "mac" },
|
||||
]
|
||||
|
||||
input = {
|
||||
enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
#======================================================
|
||||
# Disque chiffré
|
||||
#======================================================
|
||||
resource "cloudflare_zero_trust_device_posture_rule" "disk_encrypted" {
|
||||
account_id = local.cloudflare_account_id
|
||||
|
||||
name = "TF - Disque chiffre"
|
||||
description = "Tous les volumes du poste doivent etre chiffres (BitLocker / FileVault / LUKS)"
|
||||
type = "disk_encryption"
|
||||
schedule = "5m"
|
||||
|
||||
match = [
|
||||
{ platform = "windows" },
|
||||
{ platform = "mac" },
|
||||
{ platform = "linux" },
|
||||
]
|
||||
|
||||
input = {
|
||||
require_all = true
|
||||
}
|
||||
}
|
||||
|
||||
#======================================================
|
||||
# Version d'OS minimale (exemple Windows 11 23H2)
|
||||
#======================================================
|
||||
resource "cloudflare_zero_trust_device_posture_rule" "windows_os_version" {
|
||||
account_id = local.cloudflare_account_id
|
||||
|
||||
name = "TF - Windows a jour"
|
||||
description = "Version minimale de Windows exigee"
|
||||
type = "os_version"
|
||||
schedule = "5m"
|
||||
|
||||
match = [
|
||||
{ platform = "windows" },
|
||||
]
|
||||
|
||||
input = {
|
||||
version = "10.0.22631"
|
||||
operator = ">="
|
||||
}
|
||||
}
|
||||
|
||||
#======================================================
|
||||
# Rule group Access s'appuyant sur ces postures Terraform
|
||||
# (équivalent "new generation" de latest_os_version_requirements_rule_group)
|
||||
#======================================================
|
||||
resource "cloudflare_zero_trust_access_group" "tf_managed_posture_rule_group" {
|
||||
account_id = local.cloudflare_account_id
|
||||
name = "GL_TF Managed Device Posture"
|
||||
|
||||
include = [
|
||||
for rule in [
|
||||
cloudflare_zero_trust_device_posture_rule.warp_installed,
|
||||
cloudflare_zero_trust_device_posture_rule.firewall_enabled,
|
||||
cloudflare_zero_trust_device_posture_rule.disk_encrypted,
|
||||
] : {
|
||||
device_posture = {
|
||||
integration_uid = rule.id
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -10,7 +10,7 @@ resource "cloudflare_zero_trust_device_custom_profile" "zero_trust_device_custom
|
||||
name = "Allow devices Windows"
|
||||
description = "Policy for test teams."
|
||||
precedence = 100
|
||||
# match = "identity.email == \"[email protected]\""
|
||||
# match = "identity.email == \"[email protected]\""
|
||||
match = "os.name == \"windows\""
|
||||
allow_mode_switch = true
|
||||
allow_updates = true
|
||||
@@ -18,15 +18,15 @@ resource "cloudflare_zero_trust_device_custom_profile" "zero_trust_device_custom
|
||||
auto_connect = 0
|
||||
captive_portal = 180
|
||||
disable_auto_fallback = true
|
||||
# exclude = [{
|
||||
# address = "192.0.2.0/24"
|
||||
# description = "Exclude testing domains from the tunnel"
|
||||
# }]
|
||||
# exclude = [{
|
||||
# address = "192.0.2.0/24"
|
||||
# description = "Exclude testing domains from the tunnel"
|
||||
# }]
|
||||
exclude_office_ips = true
|
||||
# include = [{
|
||||
# address = "192.0.2.0/24"
|
||||
# description = "Include testing domains in the tunnel"
|
||||
# }]
|
||||
# include = [{
|
||||
# address = "192.0.2.0/24"
|
||||
# description = "Include testing domains in the tunnel"
|
||||
# }]
|
||||
lan_allow_minutes = 30
|
||||
lan_allow_subnet_size = 24
|
||||
register_interface_ip_with_dns = true
|
||||
@@ -48,7 +48,7 @@ resource "cloudflare_zero_trust_device_custom_profile" "zero_trust_device_custom
|
||||
name = "Allow devices Linux"
|
||||
description = "Policy for test teams."
|
||||
precedence = 200
|
||||
# match = "identity.email == \"[email protected]\""
|
||||
# match = "identity.email == \"[email protected]\""
|
||||
match = "os.name == \"linux\""
|
||||
allow_mode_switch = true
|
||||
allow_updates = true
|
||||
@@ -56,15 +56,15 @@ resource "cloudflare_zero_trust_device_custom_profile" "zero_trust_device_custom
|
||||
auto_connect = 0
|
||||
captive_portal = 180
|
||||
disable_auto_fallback = true
|
||||
# exclude = [{
|
||||
# address = "192.0.2.0/24"
|
||||
# description = "Exclude testing domains from the tunnel"
|
||||
# }]
|
||||
# exclude = [{
|
||||
# address = "192.0.2.0/24"
|
||||
# description = "Exclude testing domains from the tunnel"
|
||||
# }]
|
||||
exclude_office_ips = true
|
||||
# include = [{
|
||||
# address = "192.0.2.0/24"
|
||||
# description = "Include testing domains in the tunnel"
|
||||
# }]
|
||||
# include = [{
|
||||
# address = "192.0.2.0/24"
|
||||
# description = "Include testing domains in the tunnel"
|
||||
# }]
|
||||
lan_allow_minutes = 30
|
||||
lan_allow_subnet_size = 24
|
||||
register_interface_ip_with_dns = true
|
||||
@@ -86,7 +86,7 @@ resource "cloudflare_zero_trust_device_custom_profile" "zero_trust_device_custom
|
||||
name = "Allow devices Mac"
|
||||
description = "Policy for test teams."
|
||||
precedence = 300
|
||||
# match = "identity.email == \"[email protected]\""
|
||||
# match = "identity.email == \"[email protected]\""
|
||||
match = "os.name == \"mac\""
|
||||
allow_mode_switch = true
|
||||
allow_updates = true
|
||||
@@ -94,15 +94,15 @@ resource "cloudflare_zero_trust_device_custom_profile" "zero_trust_device_custom
|
||||
auto_connect = 0
|
||||
captive_portal = 180
|
||||
disable_auto_fallback = true
|
||||
# exclude = [{
|
||||
# address = "192.0.2.0/24"
|
||||
# description = "Exclude testing domains from the tunnel"
|
||||
# }]
|
||||
# exclude = [{
|
||||
# address = "192.0.2.0/24"
|
||||
# description = "Exclude testing domains from the tunnel"
|
||||
# }]
|
||||
exclude_office_ips = true
|
||||
# include = [{
|
||||
# address = "192.0.2.0/24"
|
||||
# description = "Include testing domains in the tunnel"
|
||||
# }]
|
||||
# include = [{
|
||||
# address = "192.0.2.0/24"
|
||||
# description = "Include testing domains in the tunnel"
|
||||
# }]
|
||||
lan_allow_minutes = 30
|
||||
lan_allow_subnet_size = 24
|
||||
register_interface_ip_with_dns = true
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# =============================================================================
|
||||
# CLOUDFLARE : Team & Resources : Devices : Enrollment permissions
|
||||
# =============================================================================
|
||||
# BRIQUE AGENT WARP (1/2) : qui a le droit d'enrôler son poste ?
|
||||
#
|
||||
# L'"agent" Cloudflare installé sur les postes est le client WARP. Avant de
|
||||
# pouvoir rejoindre l'organisation Zero Trust (var.cloudflare_team_name),
|
||||
# l'utilisateur doit s'authentifier : cette application spéciale de type
|
||||
# "warp" définit les règles d'enrôlement.
|
||||
#
|
||||
# Parcours utilisateur :
|
||||
# 1. Installer le client WARP (https://one.one.one.one ou MDM)
|
||||
# 2. Préférences > Compte > "Login with Cloudflare Zero Trust"
|
||||
# 3. Saisir le nom d'équipe : var.cloudflare_team_name
|
||||
# 4. Authentification via Authentik (ou Azure si la brique est activée)
|
||||
# 5. Le poste applique alors le profil WARP correspondant
|
||||
# (Team_Resources-Devices-Device_profiles.tf) et les vérifications de
|
||||
# posture (Team_Resources-Devices-Device_posture.tf)
|
||||
# =============================================================================
|
||||
|
||||
resource "cloudflare_zero_trust_access_application" "warp_device_enrollment" {
|
||||
account_id = local.cloudflare_account_id
|
||||
|
||||
type = "warp"
|
||||
name = "Warp device enrollment"
|
||||
session_duration = "24h"
|
||||
app_launcher_visible = false
|
||||
|
||||
# Les deux briques IdP peuvent servir à l'enrôlement :
|
||||
# Authentik toujours, Azure Entra ID si la brique est activée.
|
||||
allowed_idps = concat(
|
||||
[cloudflare_zero_trust_access_identity_provider.authentik_oidc.id],
|
||||
var.azure_idp_enabled ? [cloudflare_zero_trust_access_identity_provider.azure_entra_id[0].id] : []
|
||||
)
|
||||
auto_redirect_to_identity = false
|
||||
|
||||
policies = [{
|
||||
name = "Device enrollment policy"
|
||||
decision = "allow"
|
||||
|
||||
# Seuls les membres du domaine mail de l'organisation peuvent enrôler
|
||||
# un poste. Durcir avec un rule group si besoin (ex : admins seulement).
|
||||
include = [{
|
||||
email_domain = {
|
||||
domain = var.cloudflare_email_domain
|
||||
}
|
||||
}]
|
||||
|
||||
# MFA exigée au moment de l'enrôlement
|
||||
require = [{
|
||||
auth_method = {
|
||||
auth_method = "mfa"
|
||||
}
|
||||
}]
|
||||
}]
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
# =============================================================================
|
||||
# CLOUDFLARE : Traffic Policies : Firewall Policies : DLP
|
||||
# =============================================================================
|
||||
# BRIQUE DLP (Data Loss Prevention) : détecter et bloquer l'exfiltration de
|
||||
# données sensibles dans le trafic HTTP inspecté par Gateway.
|
||||
#
|
||||
# 1. Un profil DLP définit QUOI détecter (ici : IBAN français par regex)
|
||||
# 2. Une politique Gateway HTTP définit QUE FAIRE quand le profil matche
|
||||
# (ici : bloquer les uploads contenant un IBAN)
|
||||
#
|
||||
# PRÉREQUIS : l'inspection TLS doit être active (c'est déjà le cas via
|
||||
# Traffic_Policies-Traffic_Settings-Policy_Setting.tf : tls_decrypt = true)
|
||||
# et le certificat Gateway déployé sur les postes.
|
||||
# NOTE : DLP est une fonctionnalité sous licence — la politique est livrée
|
||||
# enabled = false ; l'activer une fois l'abonnement DLP disponible sur le
|
||||
# compte.
|
||||
# =============================================================================
|
||||
|
||||
#======================================================
|
||||
# Profil DLP personnalisé : IBAN français
|
||||
#======================================================
|
||||
resource "cloudflare_zero_trust_dlp_custom_profile" "iban_fr" {
|
||||
account_id = local.cloudflare_account_id
|
||||
|
||||
name = "IBAN francais"
|
||||
description = "Detecte les IBAN FR dans le trafic HTTP inspecte"
|
||||
|
||||
allowed_match_count = 0
|
||||
|
||||
entries = [{
|
||||
name = "IBAN FR"
|
||||
enabled = true
|
||||
pattern = {
|
||||
regex = "FR[0-9]{2}[ ]?([0-9]{4}[ ]?){5}[0-9]{2}"
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
#======================================================
|
||||
# Politique Gateway : bloquer l'upload de données matchant le profil
|
||||
#======================================================
|
||||
resource "cloudflare_zero_trust_gateway_policy" "dlp_block_iban_upload" {
|
||||
account_id = local.cloudflare_account_id
|
||||
|
||||
name = "HTTP - Block: DLP IBAN upload"
|
||||
description = "Bloque les envois HTTP contenant un IBAN francais (profil DLP)"
|
||||
enabled = false # ACTION EXTERNE : activer une fois la licence DLP en place
|
||||
action = "block"
|
||||
precedence = 25200
|
||||
filters = ["http"]
|
||||
traffic = "any(dlp.profiles[*] in {\"${cloudflare_zero_trust_dlp_custom_profile.iban_fr.id}\"})"
|
||||
|
||||
rule_settings = {
|
||||
block_page_enabled = true
|
||||
block_reason = "Envoi bloque : donnees bancaires detectees (DLP)"
|
||||
notification_settings = {
|
||||
enabled = true
|
||||
msg = "Envoi bloque : donnees bancaires detectees (DLP)"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,9 @@ locals {
|
||||
# HTTP (L7) Policies - Content Filtering & DLP
|
||||
pdf_block = 25000 # Block PDF downloads for Sales Eng (identity-based DLP)
|
||||
gambling_block = 25100 # Block gambling websites (category blocking)
|
||||
|
||||
# HTTP (L7) Policies - Remote Browser Isolation
|
||||
webmail_isolate = 25300 # Isolate personal webmail in a remote browser (RBI)
|
||||
}
|
||||
|
||||
# Common rule settings for block policies
|
||||
@@ -77,6 +80,25 @@ locals {
|
||||
notification_enabled = true
|
||||
}
|
||||
|
||||
# Remote Browser Isolation (Precedence: 25300)
|
||||
# BRIQUE RBI : le site s'ouvre dans un navigateur distant exécuté chez
|
||||
# Cloudflare ; seul le rendu (pixels) atteint le poste. Le webmail
|
||||
# personnel reste accessible mais ne peut plus exfiltrer de fichiers.
|
||||
# NOTE : RBI est une fonctionnalité sous licence — politique livrée
|
||||
# enabled = false, à activer une fois l'abonnement disponible
|
||||
# (et décommenter browser_isolation dans
|
||||
# Traffic_Policies-Traffic_Settings-Policy_Setting.tf).
|
||||
isolate_webmail = {
|
||||
name = "HTTP - Isolate: Webmails personnels"
|
||||
description = "Ouvre les webmails personnels dans un navigateur distant isole (RBI)"
|
||||
enabled = false
|
||||
action = "isolate"
|
||||
precedence = local.precedence_http.webmail_isolate
|
||||
filters = ["http"]
|
||||
traffic = "any(http.request.uri.content_category[*] in {155})"
|
||||
notification_enabled = false
|
||||
}
|
||||
|
||||
allow_chatgpt_log = {
|
||||
name = "HTTP - Allow: ChatGPT logging"
|
||||
description = "Log ChatGPT requests"
|
||||
|
||||
@@ -37,15 +37,15 @@ resource "cloudflare_zero_trust_gateway_settings" "zero_trust_gateway_settings"
|
||||
inspection_mode = "deep"
|
||||
}
|
||||
|
||||
# browser_isolation = {
|
||||
# non_identity_enabled = true
|
||||
# url_browser_isolation_enabled = true
|
||||
# }
|
||||
# browser_isolation = {
|
||||
# non_identity_enabled = true
|
||||
# url_browser_isolation_enabled = true
|
||||
# }
|
||||
|
||||
# custom_certificate = {
|
||||
# enabled = true
|
||||
# id = "d1b364c5-1311-466e-a194-f0e943e0799f"
|
||||
# }
|
||||
# custom_certificate = {
|
||||
# enabled = true
|
||||
# id = "d1b364c5-1311-466e-a194-f0e943e0799f"
|
||||
# }
|
||||
|
||||
extended_email_matching = {
|
||||
enabled = true
|
||||
@@ -67,10 +67,10 @@ resource "cloudflare_zero_trust_gateway_settings" "zero_trust_gateway_settings"
|
||||
enabled = true
|
||||
}
|
||||
|
||||
# sandbox = {
|
||||
# enabled = true
|
||||
# fallback_action = "allow"
|
||||
# }
|
||||
# sandbox = {
|
||||
# enabled = true
|
||||
# fallback_action = "allow"
|
||||
# }
|
||||
|
||||
tls_decrypt = {
|
||||
enabled = true
|
||||
|
||||
@@ -10,12 +10,25 @@ data "vault_generic_secret" "authentik" {
|
||||
path = var.vault_authentik_path
|
||||
}
|
||||
|
||||
# Secrets Azure Entra ID — lus uniquement si la brique IdP Azure est activée.
|
||||
# ACTION EXTERNE : créer le secret "secret/azure" dans Vault avec les clés
|
||||
# client_id_cloudflare / secret_cloudflare / directory_id, puis passer
|
||||
# azure_idp_enabled = true dans variables.auto.tfvars
|
||||
data "vault_generic_secret" "azure" {
|
||||
count = var.azure_idp_enabled ? 1 : 0
|
||||
path = var.vault_azure_path
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# LOCALS
|
||||
# =============================================================================
|
||||
|
||||
locals {
|
||||
# Secrets Cloudflare depuis Vault
|
||||
# NOTE : zone_id_org est l'identifiant hexadécimal de la zone (utilisé par
|
||||
# les ressources DNS). Le NOM de domaine, lui, vient de var.cloudflare_zone.
|
||||
# Ne jamais mélanger les deux : un hostname se construit avec le nom,
|
||||
# jamais avec l'ID.
|
||||
cloudflare_api_token = data.vault_generic_secret.cloudflare.data["api_token"]
|
||||
cloudflare_account_id = data.vault_generic_secret.cloudflare.data["account_id"]
|
||||
cloudflare_zone_id = data.vault_generic_secret.cloudflare.data["zone_id_org"]
|
||||
@@ -24,11 +37,20 @@ locals {
|
||||
authentik_oidc_client_id_cloudflare = data.vault_generic_secret.authentik.data["client_id_cloudflare"]
|
||||
authentik_oidc_secret_cloudflare = data.vault_generic_secret.authentik.data["secret_cloudflare"]
|
||||
|
||||
# Secrets Azure Entra ID depuis Vault (brique IdP Azure, optionnelle)
|
||||
azure_oidc_client_id_cloudflare = var.azure_idp_enabled ? data.vault_generic_secret.azure[0].data["client_id_cloudflare"] : ""
|
||||
azure_oidc_secret_cloudflare = var.azure_idp_enabled ? data.vault_generic_secret.azure[0].data["secret_cloudflare"] : ""
|
||||
azure_directory_id = var.azure_idp_enabled ? data.vault_generic_secret.azure[0].data["directory_id"] : ""
|
||||
|
||||
# Construction des ingress rules pour toutes les applications
|
||||
# CORRECTIF : le hostname est construit avec le NOM de la zone
|
||||
# (var.cloudflare_zone = "tips-of-mine.org") et non plus avec l'ID de zone.
|
||||
# Avec l'ancien code, les hostnames d'ingress ne correspondaient jamais aux
|
||||
# enregistrements DNS créés dans Networks-Routes-cidr.tf.
|
||||
ingress_rules = concat(
|
||||
[
|
||||
for app_name, app_config in var.applications : {
|
||||
hostname = "${app_config.subdomain}.${local.cloudflare_zone_id}"
|
||||
hostname = "${app_config.subdomain}.${var.cloudflare_zone}"
|
||||
service = app_config.origin_url
|
||||
origin_request = {
|
||||
no_tls_verify = app_config.no_tls_verify
|
||||
|
||||
+86
-20
@@ -13,7 +13,7 @@ output "tunnel_cname" {
|
||||
}
|
||||
|
||||
output "tunnel_token" {
|
||||
description = "Token pour l'agent cloudflared (SENSIBLE)"
|
||||
description = "Token pour l'agent cloudflared (SENSIBLE). Pour la haute dispo, installer PLUSIEURS replicas cloudflared avec ce même token (2 machines = bascule automatique)."
|
||||
value = data.cloudflare_zero_trust_tunnel_cloudflared_token.home_tunnel_token.token
|
||||
sensitive = true
|
||||
}
|
||||
@@ -22,13 +22,13 @@ output "tunnel_token" {
|
||||
# APPLICATIONS OUTPUTS
|
||||
# =============================================================================
|
||||
|
||||
#output "applications_urls" {
|
||||
# description = "URLs publiques de toutes les applications"
|
||||
# value = {
|
||||
# for app_name, app_config in var.applications :
|
||||
# app_name => "https://${app_config.subdomain}.${local.cloudflare_zone_id}"
|
||||
# }
|
||||
#}
|
||||
output "applications_urls" {
|
||||
description = "URLs publiques de toutes les applications"
|
||||
value = {
|
||||
for app_name, app_config in var.applications :
|
||||
app_name => "https://${app_config.subdomain}.${var.cloudflare_zone}"
|
||||
}
|
||||
}
|
||||
|
||||
output "applications_dns_records" {
|
||||
description = "IDs des enregistrements DNS créés"
|
||||
@@ -38,15 +38,81 @@ output "applications_dns_records" {
|
||||
}
|
||||
}
|
||||
|
||||
#output "applications_details" {
|
||||
# description = "Détails de toutes les applications configurées"
|
||||
# value = {
|
||||
# for app_name, app_config in var.applications :
|
||||
# app_name => {
|
||||
# public_url = "https://${app_config.subdomain}.${local.cloudflare_zone_id}"
|
||||
# origin_url = app_config.origin_url
|
||||
# access_enabled = app_config.access_enabled
|
||||
# dns_record_id = cloudflare_dns_record.applications[app_name].id
|
||||
# }
|
||||
# }
|
||||
#}
|
||||
# =============================================================================
|
||||
# BRIQUE SITES DISTANTS
|
||||
# =============================================================================
|
||||
|
||||
output "remote_site_tunnel_tokens" {
|
||||
description = "Token cloudflared de chaque site distant (SENSIBLE) — à installer sur le connecteur du site"
|
||||
value = {
|
||||
for site, token in data.cloudflare_zero_trust_tunnel_cloudflared_token.remote_sites :
|
||||
site => token.token
|
||||
}
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "remote_site_virtual_networks" {
|
||||
description = "ID du réseau virtuel de chaque site (pour warp-cli vnet <id>)"
|
||||
value = {
|
||||
for site, vnet in cloudflare_zero_trust_tunnel_cloudflared_virtual_network.remote_sites :
|
||||
site => vnet.id
|
||||
}
|
||||
}
|
||||
|
||||
output "warp_connector_tokens" {
|
||||
description = "Token d'installation de chaque WARP Connector (SENSIBLE) — brique inerte si aucun ID renseigné"
|
||||
value = {
|
||||
for site, resp in data.http.warp_connector_tokens :
|
||||
site => try(jsondecode(resp.response_body).result, null)
|
||||
}
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# BRIQUE SSH
|
||||
# =============================================================================
|
||||
|
||||
output "ssh_classic_public_key" {
|
||||
description = "Clé publique SSH (brique SSH par clé) — à déposer dans ~/.ssh/authorized_keys du serveur cible"
|
||||
value = trimspace(tls_private_key.ssh_classic.public_key_openssh)
|
||||
}
|
||||
|
||||
output "ssh_classic_private_key" {
|
||||
description = "Clé privée SSH (SENSIBLE — présente dans le state). Récupération : terraform output -raw ssh_classic_private_key"
|
||||
value = tls_private_key.ssh_classic.private_key_openssh
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "ssh_ca_aws" {
|
||||
description = "Clé publique de la CA de certificats éphémères (app SSH AWS) — à déposer dans /etc/ssh/ca.pub du serveur"
|
||||
value = cloudflare_zero_trust_access_short_lived_certificate.aws_ssh_ca.public_key
|
||||
}
|
||||
|
||||
output "ssh_ca_gcp" {
|
||||
description = "Clé publique de la CA de certificats éphémères (app SSH GCP) — à déposer dans /etc/ssh/ca.pub du serveur"
|
||||
value = cloudflare_zero_trust_access_short_lived_certificate.gcp_ssh_ca.public_key
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# BRIQUE SERVICE TOKENS
|
||||
# =============================================================================
|
||||
|
||||
output "service_token_gitea_ci_id" {
|
||||
description = "Client ID du service token Gitea CI (en-tête CF-Access-Client-Id)"
|
||||
value = cloudflare_zero_trust_access_service_token.gitea_ci.client_id
|
||||
}
|
||||
|
||||
output "service_token_gitea_ci_secret" {
|
||||
description = "Client Secret du service token Gitea CI (SENSIBLE, en-tête CF-Access-Client-Secret)"
|
||||
value = cloudflare_zero_trust_access_service_token.gitea_ci.client_secret
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# BRIQUE IdP AZURE
|
||||
# =============================================================================
|
||||
|
||||
output "azure_idp_id" {
|
||||
description = "ID de l'identity provider Azure Entra ID (null si brique désactivée)"
|
||||
value = var.azure_idp_enabled ? cloudflare_zero_trust_access_identity_provider.azure_entra_id[0].id : null
|
||||
}
|
||||
|
||||
+16
-4
@@ -8,6 +8,17 @@ terraform {
|
||||
source = "hashicorp/vault"
|
||||
version = "~> 4.6"
|
||||
}
|
||||
# Génération de la paire de clés SSH pour la brique "accès SSH par clé"
|
||||
tls = {
|
||||
source = "hashicorp/tls"
|
||||
version = "~> 4.0"
|
||||
}
|
||||
# Récupération des tokens WARP Connector via l'API Cloudflare
|
||||
# (brique "sites distants")
|
||||
http = {
|
||||
source = "hashicorp/http"
|
||||
version = "~> 3.4"
|
||||
}
|
||||
}
|
||||
required_version = ">= 1.7.5"
|
||||
}
|
||||
@@ -20,9 +31,10 @@ provider "cloudflare" {
|
||||
provider "vault" {
|
||||
address = var.vault_url
|
||||
skip_child_token = true
|
||||
skip_tls_verify = true
|
||||
# CORRECTIF SÉCURITÉ : la vérification TLS est désormais activée par défaut.
|
||||
# ACTION EXTERNE : vault.tips-of-mine.com doit présenter un certificat valide
|
||||
# (Let's Encrypt, CA interne importée sur le runner CI, etc.).
|
||||
# En dépannage uniquement : vault_skip_tls_verify = true dans le tfvars.
|
||||
skip_tls_verify = var.vault_skip_tls_verify
|
||||
token = var.vault_token
|
||||
}
|
||||
|
||||
provider "random" {
|
||||
}
|
||||
@@ -55,6 +55,17 @@ applications = {
|
||||
no_tls_verify = false
|
||||
access_enabled = false
|
||||
}
|
||||
|
||||
# Application 4 : SSH classique par clé (brique SSH n°2)
|
||||
# Publie ssh://<serveur> dans l'ingress du tunnel + DNS ssh.tips-of-mine.org.
|
||||
# L'app Access qui protège ce hostname est dans
|
||||
# Access_Controls-Applications-ssh_key_based.tf
|
||||
"ssh-classic" = {
|
||||
subdomain = "ssh"
|
||||
origin_url = "ssh://10.0.4.140:22"
|
||||
no_tls_verify = false
|
||||
access_enabled = false
|
||||
}
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
@@ -82,6 +93,39 @@ cloudflare_team_name = "tips-of-mine"
|
||||
cloudflare_email_domain = "tips-of-mine.org"
|
||||
cloudflare_authentik_domain = "tips-of-mine.com"
|
||||
|
||||
#=====================================
|
||||
# Briques optionnelles (interrupteurs)
|
||||
#=====================================
|
||||
|
||||
# BRIQUE IdP AZURE : passer à true APRÈS avoir créé le secret Vault
|
||||
# "secret/azure" (client_id_cloudflare / secret_cloudflare / directory_id)
|
||||
# — voir ACTIONS_EXTERNES.md, sinon le plan échouera.
|
||||
azure_idp_enabled = false
|
||||
# Object ID du groupe Entra ID des admins (vide = rule group Azure non créé)
|
||||
azure_admin_group_id = ""
|
||||
|
||||
# BRIQUE SITES DISTANTS (WARP Connector) : renseigner les IDs des connecteurs
|
||||
# créés manuellement dans le dashboard (vide = brique inerte)
|
||||
cloudflare_tunnel_warp_connector_site_a_id = ""
|
||||
cloudflare_tunnel_warp_connector_site_b_id = ""
|
||||
|
||||
# BRIQUE SITES DISTANTS (tunnels cloudflared) : deux agences avec le MÊME
|
||||
# CIDR, isolées par réseau virtuel — démonstration des plages chevauchantes
|
||||
remote_sites = {
|
||||
site-paris = {
|
||||
cidr = "10.100.0.0/16"
|
||||
comment = "Site Paris - siege"
|
||||
}
|
||||
site-lyon = {
|
||||
cidr = "10.100.0.0/16"
|
||||
comment = "Site Lyon - agence (meme CIDR que Paris : isole par reseau virtuel)"
|
||||
}
|
||||
}
|
||||
|
||||
# BRIQUE LOGPUSH : renseigner la destination puis passer à true
|
||||
logpush_enabled = false
|
||||
logpush_destination_conf = ""
|
||||
|
||||
# Tunnels
|
||||
cloudflare_tunnel_name_gcp = "test-gcp-tunnel"
|
||||
#Cloudflare_tunnel_name_gcp = "Tunnel GCP (Access For Infrastructure)"
|
||||
|
||||
+211
-6
@@ -26,6 +26,18 @@ variable "vault_authentik_path" {
|
||||
default = "secret/authentik"
|
||||
}
|
||||
|
||||
variable "vault_azure_path" {
|
||||
description = "Chemin vers les secrets Azure Entra ID dans Vault (brique IdP Azure)"
|
||||
type = string
|
||||
default = "secret/azure"
|
||||
}
|
||||
|
||||
variable "vault_skip_tls_verify" {
|
||||
description = "Désactiver la vérification TLS vers Vault (dépannage uniquement — laisser à false)"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# CLOUDFLARE CONFIGURATION : AUTHENTIK SETTINGS
|
||||
# =============================================================================
|
||||
@@ -46,11 +58,14 @@ variable "vault_authentik_path" {
|
||||
# CLOUDFLARE CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
#variable "cloudflare_zone_id" {
|
||||
# description = "Domaine principal"
|
||||
# type = string
|
||||
# default = "tips-of-mine.org"
|
||||
#}
|
||||
# NOM de la zone (le domaine). À ne pas confondre avec l'ID de zone
|
||||
# (identifiant hexadécimal) qui, lui, vient de Vault (clé zone_id_org) et
|
||||
# sert aux ressources DNS. Les hostnames se construisent avec CE nom.
|
||||
variable "cloudflare_zone" {
|
||||
description = "Nom de domaine principal de la zone Cloudflare"
|
||||
type = string
|
||||
default = "tips-of-mine.org"
|
||||
}
|
||||
|
||||
variable "tunnel_name" {
|
||||
description = "Nom du tunnel Cloudflare"
|
||||
@@ -70,10 +85,14 @@ variable "tunnel_network_comment" {
|
||||
default = "tips-of-mine comment for this route."
|
||||
}
|
||||
|
||||
# NOTE : le provider Cloudflare lit désormais son token depuis Vault
|
||||
# (secret/cloudflare, clé api_token). Cette variable est conservée pour
|
||||
# compatibilité mais n'est plus utilisée — d'où le default vide.
|
||||
variable "cloudflare_api_token" {
|
||||
description = "Token d'API Cloudflare"
|
||||
description = "Token d'API Cloudflare (obsolète : le token vient de Vault)"
|
||||
type = string
|
||||
sensitive = true
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "cloudflare_access_tags" {
|
||||
@@ -627,3 +646,189 @@ variable "cloudflare_gcp_browser_rdp_app_name" {
|
||||
description = "Name of the RDP windows browser rendered App in Cloudflare"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "cloudflare_gcp_sensitive_web_app_name" {
|
||||
description = "Name of the Sensitive web App (GCP) in Cloudflare"
|
||||
type = string
|
||||
default = "Competition App : GCP"
|
||||
}
|
||||
|
||||
variable "cloudflare_gcp_intranet_web_app_name" {
|
||||
description = "Name of the Intranet web App (GCP) in Cloudflare"
|
||||
type = string
|
||||
default = "Intranet : GCP"
|
||||
}
|
||||
|
||||
#======================================================
|
||||
# BRIQUE IdP AZURE ENTRA ID
|
||||
#======================================================
|
||||
|
||||
variable "azure_idp_enabled" {
|
||||
description = "Activer la brique IdP Azure Entra ID (nécessite le secret Vault secret/azure — voir ACTIONS_EXTERNES.md)"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "azure_admin_group_id" {
|
||||
description = "Object ID du groupe Entra ID des administrateurs (vide = rule group Azure non créé)"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
#======================================================
|
||||
# BRIQUE SITES DISTANTS
|
||||
#======================================================
|
||||
|
||||
variable "remote_sites" {
|
||||
description = "Sites distants d'entreprise : un tunnel cloudflared + un réseau virtuel + une route par site. Les CIDR peuvent se chevaucher entre sites (isolation par réseau virtuel)."
|
||||
type = map(object({
|
||||
cidr = string
|
||||
comment = string
|
||||
}))
|
||||
default = {
|
||||
site-paris = {
|
||||
cidr = "10.100.0.0/16"
|
||||
comment = "Site Paris - siege"
|
||||
}
|
||||
site-lyon = {
|
||||
cidr = "10.100.0.0/16"
|
||||
comment = "Site Lyon - agence (meme CIDR que Paris : isole par reseau virtuel)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "cloudflare_tunnel_warp_connector_site_a_id" {
|
||||
description = "ID du WARP Connector du site A (créé manuellement dans le dashboard — vide = brique inerte)"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "cloudflare_tunnel_warp_connector_site_b_id" {
|
||||
description = "ID du WARP Connector du site B (créé manuellement dans le dashboard — vide = brique inerte)"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
#======================================================
|
||||
# BRIQUE LOGPUSH
|
||||
#======================================================
|
||||
|
||||
variable "logpush_enabled" {
|
||||
description = "Activer l'export Logpush des logs Gateway HTTP (nécessite logpush_destination_conf)"
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "logpush_destination_conf" {
|
||||
description = "Destination Logpush (ex : s3://bucket/gateway?region=eu-north-1 ou r2://...)"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
#======================================================
|
||||
# CIDR COMPLÉMENTAIRES (déclarations des clés du tfvars)
|
||||
# Ces plages documentent le plan d'adressage complet du lab multi-cloud ;
|
||||
# elles sont disponibles pour les politiques Gateway et les routes.
|
||||
#======================================================
|
||||
|
||||
variable "aws_infra_cidr" {
|
||||
description = "CIDR Range for AWS VMs running cloudflared"
|
||||
type = string
|
||||
default = "10.10.10.0/24"
|
||||
}
|
||||
|
||||
variable "aws_warp_cidr" {
|
||||
description = "CIDR Range for AWS VMs running warp"
|
||||
type = string
|
||||
default = "10.10.15.0/24"
|
||||
}
|
||||
|
||||
variable "aws_windows_rdp_cidr" {
|
||||
description = "CIDR Range for AWS VMs running Windows and RDP Server"
|
||||
type = string
|
||||
default = "10.10.20.0/24"
|
||||
}
|
||||
|
||||
variable "gcp_vpc_cidr" {
|
||||
description = "GCP VPC cidr"
|
||||
type = string
|
||||
default = "10.13.0.0/20"
|
||||
}
|
||||
|
||||
variable "gcp_public_cidr" {
|
||||
description = "GCP public subnet"
|
||||
type = string
|
||||
default = "10.13.0.0/24"
|
||||
}
|
||||
|
||||
variable "gcp_private_cidr" {
|
||||
description = "GCP private subnet"
|
||||
type = string
|
||||
default = "10.13.1.0/24"
|
||||
}
|
||||
|
||||
variable "azure_public_cidr" {
|
||||
description = "Azure public subnet"
|
||||
type = string
|
||||
default = "10.14.0.0/24"
|
||||
}
|
||||
|
||||
variable "azure_private_cidr" {
|
||||
description = "Azure private subnet"
|
||||
type = string
|
||||
default = "10.14.1.0/24"
|
||||
}
|
||||
|
||||
variable "azure_infra_cidr" {
|
||||
description = "CIDR Range for Azure VMs running cloudflared"
|
||||
type = string
|
||||
default = "10.14.10.0/24"
|
||||
}
|
||||
|
||||
variable "azure_warp_cidr" {
|
||||
description = "CIDR Range for Azure VMs running warp"
|
||||
type = string
|
||||
default = "10.14.15.0/24"
|
||||
}
|
||||
|
||||
variable "azure_windows_rdp_cidr" {
|
||||
description = "CIDR Range for Azure VMs running Windows and RDP Server"
|
||||
type = string
|
||||
default = "10.14.20.0/24"
|
||||
}
|
||||
|
||||
variable "ovh_vpc_cidr" {
|
||||
description = "OVH VPC cidr"
|
||||
type = string
|
||||
default = "10.16.0.0/20"
|
||||
}
|
||||
|
||||
variable "ovh_public_cidr" {
|
||||
description = "OVH public subnet"
|
||||
type = string
|
||||
default = "10.16.0.0/24"
|
||||
}
|
||||
|
||||
variable "ovh_private_cidr" {
|
||||
description = "OVH private subnet"
|
||||
type = string
|
||||
default = "10.16.1.0/24"
|
||||
}
|
||||
|
||||
variable "ovh_infra_cidr" {
|
||||
description = "CIDR Range for OVH VMs running cloudflared"
|
||||
type = string
|
||||
default = "10.16.10.0/24"
|
||||
}
|
||||
|
||||
variable "ovh_warp_cidr" {
|
||||
description = "CIDR Range for OVH VMs running warp"
|
||||
type = string
|
||||
default = "10.16.15.0/24"
|
||||
}
|
||||
|
||||
variable "ovh_windows_rdp_cidr" {
|
||||
description = "CIDR Range for OVH VMs running Windows and RDP Server"
|
||||
type = string
|
||||
default = "10.16.20.0/24"
|
||||
}
|
||||
Reference in New Issue
Block a user