opentofu-common/cloudflare.tf
GregoryDosh daf26312d1
All checks were successful
Update Version / Update Version (push) Successful in 9s
feat: consistency sweeping w/ other repos to add release/tag process + license checking
2025-05-13 10:11:01 -05:00

158 lines
5.6 KiB
HCL

# git.auengun.net/homelab/opentofu-common
# Copyright (C) 2024 GregoryDosh
# SPDX-License-Identifier: AGPL-3.0-or-later
# SPDX-FileCopyrightText: 2024 GregoryDosh
variable "cloudflare" {
description = "Options to configure the Cloudflare module."
type = object({
# Create a cloudflare tunnel to this app?
enabled = optional(bool, false)
# This sets Cloudflare up to bypass checking for
# authentication headers before sending traffic
# through the tunnel and to the application.
# This is mostly for blog/external facing things.
direct_access = optional(string, "")
# If set, this tells Cloudflare to require an
# auth token from auth.auengun.net (Authentik)
# before allowing access. This isn't tied to
# any group inside Authentik beyond simple user.
# Any additional route protection will be handled
# via Caddy/Authentik path and forward auth modules
# to handle split horizon domains appropriately.
protected_path = optional(string, "")
account_id = string
zone_id = string
})
}
# Randomized Tunnel Secret
# https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password
resource "random_password" "tunnel_secret" {
length = 64
count = var.cloudflare.enabled == true ? 1 : 0
}
# Cloudflare Tunnel
# https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/tunnel
resource "cloudflare_tunnel" "tunnel" {
account_id = var.cloudflare.account_id
name = local.external_hostname
secret = base64sha256(random_password.tunnel_secret[count.index].result)
config_src = "cloudflare"
count = var.cloudflare.enabled == true ? 1 : 0
depends_on = [random_password.tunnel_secret]
}
# Tunnel Configs
# https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/tunnel_config
resource "cloudflare_tunnel_config" "tunnel" {
tunnel_id = cloudflare_tunnel.tunnel[count.index].id
account_id = var.cloudflare.account_id
config {
ingress_rule {
hostname = local.external_hostname
service = "https://${local.internal_hostname}"
origin_request {
http_host_header = local.external_hostname
no_tls_verify = true
}
}
ingress_rule {
service = "http_status:404"
}
}
count = var.cloudflare.enabled == true ? 1 : 0
depends_on = [cloudflare_tunnel.tunnel]
}
# CNAME Proxies to the tunnel
# https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/record
resource "cloudflare_record" "cname" {
zone_id = var.cloudflare.zone_id
name = local.external_hostname
value = cloudflare_tunnel.tunnel[count.index].cname
type = "CNAME"
proxied = true
count = var.cloudflare.enabled == true ? 1 : 0
depends_on = [cloudflare_tunnel_config.tunnel]
}
# Application Access
# https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/access_application
resource "cloudflare_access_application" "bypass_auth_to_local_app" {
zone_id = var.cloudflare.zone_id
name = "${local.external_hostname} (bypass auth)"
domain = var.cloudflare.direct_access
session_duration = "24h"
app_launcher_visible = false
http_only_cookie_attribute = true
count = length(var.cloudflare.direct_access) > 0 ? 1 : 0
depends_on = [cloudflare_tunnel.tunnel, cloudflare_record.cname]
}
# Access Policy
# https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/access_policy
resource "cloudflare_access_policy" "bypass_auth_access_policy" {
application_id = cloudflare_access_application.bypass_auth_to_local_app[count.index].id
zone_id = var.cloudflare.zone_id
name = "${local.external_hostname} (bypass auth)"
precedence = "1"
decision = "bypass"
include {
everyone = true
}
count = length(var.cloudflare.direct_access) > 0 ? 1 : 0
depends_on = [cloudflare_access_application.bypass_auth_to_local_app]
}
# Application Access
# https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/access_application
resource "cloudflare_access_application" "logged_in_to_authentik" {
zone_id = var.cloudflare.zone_id
name = "${local.external_hostname} (logged-in user)"
domain = var.cloudflare.protected_path
session_duration = "24h"
app_launcher_visible = false
http_only_cookie_attribute = true
count = length(var.cloudflare.protected_path) > 0 ? 1 : 0
depends_on = [cloudflare_tunnel.tunnel, cloudflare_record.cname]
}
# Access Policy
# https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs/resources/access_policy
resource "cloudflare_access_policy" "logged_in_access_policy" {
application_id = cloudflare_access_application.logged_in_to_authentik[count.index].id
zone_id = var.cloudflare.zone_id
name = "${local.external_hostname} (logged-in user)"
precedence = "1"
decision = "allow"
include {
# This is a magic number. But it was pulle from an existing
# group defined on the authentication provider for the
# Cloudflared <-> Authentik (auth.auengun.net) interaction.
group = [ "a9ea2f63-1843-4886-afd8-e2bac2348d34" ]
}
count = length(var.cloudflare.protected_path) > 0 ? 1 : 0
depends_on = [cloudflare_access_application.logged_in_to_authentik]
}
output "CF_TUNNEL_TOKEN" {
value = one(cloudflare_tunnel.tunnel[*].tunnel_token)
depends_on = [cloudflare_tunnel.tunnel]
sensitive = true
}