All checks were successful
Update Version / Update Version (push) Successful in 9s
158 lines
5.6 KiB
HCL
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
|
|
}
|