跳转到内容
搜索文档

Terraform

最后更新 查看 MarkdownAgent 设置

Terraform 是一种基础设施即代码(infrastructure as code)软件工具,允许您使用标准化的配置语法部署来自不同提供商的服务。创建 Terraform 配置文件时,您定义的是配置的最终状态,而不是逐步过程。这使您能够轻松地与其他基础设施一起部署、修改和管理您的 Tunnel。

在本指南中,您将使用 Terraform 部署:

  • 一个运行 HTTP 测试服务器的 Google Cloud Project (GCP) 虚拟机
  • 一个使服务器在互联网上可用的 Cloudflare Tunnel
  • 一个定义谁可以连接到服务器的 Cloudflare Access 策略

前提条件

要完成以下步骤,您需要:

1. 安装 Terraform

请参阅适用于您的操作系统的 Terraform 安装指南

2. 安装 gcloud CLI

  1. 安装 gcloud CLI,以便 Terraform 可以与您的 GCP 账户进行交互。

  2. 通过运行以下命令进行 CLI 身份验证:

    gcloud auth application-default login

3. 创建 Cloudflare API token

创建 API 令牌,以便 Terraform 可以与您的 Cloudflare 账户进行交互。您的令牌至少应包含以下权限:

类型 项目 权限
账户 Cloudflare Tunnel 编辑
账户 Access:应用程序和策略 编辑
区域 DNS 编辑

4. 创建配置目录

Terraform 通过包含配置文件的工作目录发挥作用。您可以将配置存储在多个文件中,也可以仅存储在一个文件中——Terraform 将把目录中的所有配置文件视为处于单个文档中进行评估。

  1. 为您的 Terraform 配置创建一个文件夹:

    mkdir cloudflare-tf
  2. 进入该目录:

    cd cloudflare-tf

5. 创建 Terraform 配置文件

定义输入变量

以下变量将被传递到您的 GCP 和 Cloudflare 配置中。

  1. 在您的配置目录中,创建一个 .tf 文件:

    touch variables.tf
  2. 在文本编辑器中打开该文件并复制并粘贴以下内容:

    # GCP 变量
    variable "gcp_project_id" {
      description = "Google Cloud Platform (GCP) 项目 ID"
      type        = string
    }
    
    variable "zone" {
      description = "GCP VM 实例的地理区域"
      type        = string
    }
    
    variable "machine_type" {
      description = "GCP VM 实例的机器类型"
      type        = string
    }
    
    # Cloudflare 变量
    variable "cloudflare_zone" {
      description = "用于向互联网公开 GCP VM 实例的域名"
      type        = string
    }
    
    variable "cloudflare_zone_id" {
      description = "您域的区域 (Zone) ID"
      type        = string
    }
    
    variable "cloudflare_account_id" {
      description = "您 Cloudflare 账户(account)的账户 ID"
      type        = string
      sensitive   = true
    }
    
    variable "cloudflare_email" {
      description = "您 Cloudflare 账户(account)的电子邮件地址"
      type        = string
      sensitive   = true
    }
    
    variable "cloudflare_token" {
      description = "Cloudflare API 令牌"
      type        = string
      sensitive   = true
    }

为变量分配值

  1. 在您的配置目录中,创建一个 .tfvars 文件:

    touch terraform.tfvars

    如果该文件命名为 terraform.tfvars,Terraform 将自动使用这些变量,否则需要手动传入该变量文件。

  2. 将以下变量添加到 terraform.tfvars 中。请确保使用您自己的值来修改示例。

    cloudflare_zone           = "example.com"
    cloudflare_zone_id        = "023e105f4ecef8ad9ca31a8372d0c353"
    cloudflare_account_id     = "372e67954025e0ba6aaa6d586b9e0b59"
    cloudflare_email          = "user@example.com"
    cloudflare_token          = "y3AalHS_E7Vabk3c3lX950F90_Xl7YtjSlzyFn_X"
    gcp_project_id            = "testvm-123"
    zone                      = "us-central1-a"
    machine_type              = "e2-medium"

配置 Terraform 提供商

您需要声明用于配置基础设施的 提供程序(providers)

  1. 在您的配置目录中,创建一个 .tf 文件:

    touch providers.tf
  2. 将以下提供程序添加到 providers.tf 中。random 提供程序用于生成隧道机密(secret)。

    terraform {
    	required_providers {
    		cloudflare = {
    			source = "cloudflare/cloudflare"
    			version = ">= 5.8.2"
    		}
    		google = {
    			source = "hashicorp/google"
    		}
    	}
    	required_version = ">= 1.2"
    }
    
    # Providers
    provider "cloudflare" {
    	api_token    = var.cloudflare_token
    }
    provider "google" {
    	project    = var.gcp_project_id
    }
    provider "random" {
    }
    terraform {
    	required_providers {
    		cloudflare = {
    			source = "cloudflare/cloudflare"
    			version = ">= 4.40.0, < 5.0.0"
    		}
    		google = {
    			source = "hashicorp/google"
    		}
    		random = {
    			source = "hashicorp/random"
    		}
    	}
    	required_version = ">= 1.2"
    }
    
    # Providers
    provider "cloudflare" {
    	api_token    = var.cloudflare_token
    }
    provider "google" {
    	project    = var.gcp_project_id
    }
    provider "random" {
    }

配置 Cloudflare 资源

以下配置将修改您 Cloudflare 账户(account)中的设置。

  1. 在您的配置目录中,创建一个 .tf 文件:

    touch Cloudflare-config.tf
  2. 将以下资源添加到 Cloudflare-config.tf 中:

    
    # 为 GCP VM 创建一个新的远程管理隧道。
    resource "cloudflare_zero_trust_tunnel_cloudflared" "gcp_tunnel" {
    	account_id    = var.cloudflare_account_id
    	name          = "Terraform GCP tunnel"
    	config_src    = "cloudflare"
    }
    
    # 读取用于在服务器上运行该隧道的令牌。
    data "cloudflare_zero_trust_tunnel_cloudflared_token" "gcp_tunnel_token" {
    	account_id 	= var.cloudflare_account_id
    	tunnel_id 	= cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id
    }
    
    # 创建将 http_app.${var.cloudflare_zone} 路由到该隧道的 CNAME 记录。
    resource "cloudflare_dns_record" "http_app" {
    	zone_id = var.cloudflare_zone_id
    	name    = "http_app"
    	content = "${cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id}.cfargotunnel.com"
    	type    = "CNAME"
    	ttl     = 1
    	proxied = true
    }
    
    # 用已发布的应用配置隧道以进行无客户端访问。
    resource "cloudflare_zero_trust_tunnel_cloudflared_config" "gcp_tunnel_config" {
    	tunnel_id  = cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id
    	account_id = var.cloudflare_account_id
    	config     = {
    		ingress 	= [
    			{
    				hostname = "http_app.${var.cloudflare_zone}"
    				service  = "http://httpbin:80"
    			},
    			{
    				service  = "http_status:404"
    			}
    		]
    	}
    }
    
    # (可选)通过隧道路由 GCP 实例的内部 IP,以便使用 WARP 进行私有网络访问。
    resource "cloudflare_zero_trust_tunnel_cloudflared_route" "example_tunnel_route" {
    account_id         = var.cloudflare_account_id
    tunnel_id          = cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id
    network            = google_compute_instance.http_server.network_interface.0.network_ip
    comment            = "Example tunnel route"
    }
    
    # 创建一个可复用的 Access 策略。
    resource "cloudflare_zero_trust_access_policy" "allow_emails" {
    	account_id   = var.cloudflare_account_id
    	name         = "Allow email addresses"
    	decision     = "allow"
    	include      = [
    		{
    			email = {
    				email = var.cloudflare_email
    			}
    		},
    		{
    			email_domain = {
    				domain = "@example.com"
    			}
    		}
    	]
    }
    
    # 创建一个 Access 应用程序以控制谁可以连接到公共主机名。
    resource "cloudflare_zero_trust_access_application" "http_app" {
    	account_id       = var.cloudflare_account_id
    	type             = "self_hosted"
    	name             = "Access application for http_app.${var.cloudflare_zone}"
    	domain           = "http_app.${var.cloudflare_zone}"
    	policies = [
    		{
    			id = cloudflare_zero_trust_access_policy.allow_emails.id
    			precedence = 1
    		}
    	]
    }
    # 为隧道生成一个 32 字节的机密(secret)。
    resource "random_bytes" "tunnel_secret" {
    	byte_length = 32
    }
    
    # 为 GCP VM 创建一个新的远程管理隧道。
    resource "cloudflare_zero_trust_tunnel_cloudflared" "gcp_tunnel" {
    	account_id = var.cloudflare_account_id
    	name       = "Terraform GCP tunnel"
    	secret     = random_bytes.tunnel_secret.base64
    }
    
    # 创建将 http_app.${var.cloudflare_zone} 路由到该隧道的 CNAME 记录。
    resource "cloudflare_record" "http_app" {
    	zone_id = var.cloudflare_zone_id
    	name    = "http_app"
    	content   = "${cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.cname}"
    	type    = "CNAME"
    	proxied = true
    }
    
    # 用已发布的应用配置隧道以进行无客户端访问。
    resource "cloudflare_zero_trust_tunnel_cloudflared_config" "gcp_tunnel_config" {
    	tunnel_id = cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id
    	account_id = var.cloudflare_account_id
    	config {
    		ingress_rule {
    			hostname = "${cloudflare_record.http_app.hostname}"
    			service  = "http://httpbin:80"
    		}
    		ingress_rule {
    			service  = "http_status:404"
    		}
    	}
    }
    
    # (可选)通过隧道路由 GCP 实例的内部 IP,以便使用 WARP 进行私有网络访问。
    resource "cloudflare_zero_trust_tunnel_route" "example_tunnel_route" {
    account_id         = var.cloudflare_account_id
    tunnel_id          = cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.id
    network            = google_compute_instance.http_server.network_interface.0.network_ip
    comment            = "Example tunnel route"
    }
    
    # 创建一个 Access 应用程序以控制谁可以连接到公共主机名。
    resource "cloudflare_zero_trust_access_application" "http_app" {
    	account_id          = var.cloudflare_account_id
    	name             = "Access application for http_app.${var.cloudflare_zone}"
    	domain           = "http_app.${var.cloudflare_zone}"
    }
    
    # 为 Access 应用程序创建一个(旧版)Access 策略。
    resource "cloudflare_zero_trust_access_policy" "allow_emails" {
    	application_id = cloudflare_zero_trust_access_application.http_app.id
    	account_id        = var.cloudflare_account_id
    	name           = "Example policy for http_app.${var.cloudflare_zone}"
    	precedence     = "1"
    	decision       = "allow"
    	include {
    		email = [var.cloudflare_email]
    	}
    }

要了解有关这些资源的更多信息,请参阅 Cloudflare provider 文档

配置 GCP 资源

以下配置定义了 GCP 虚拟机(VM)的规格,并配置了在引导时运行的启动脚本。

  1. 在您的配置目录中,创建一个 .tf 文件:

    touch GCP-config.tf
  2. 将以下内容添加到 GCP-config.tf 中:

    # 服务器将使用的操作系统
    data "google_compute_image" "image" {
    	family  = "ubuntu-2204-lts"
    	project = "ubuntu-os-cloud"
    }
    
    # GCP 实例资源
    resource "google_compute_instance" "http_server" {
    	name         = "test"
    	machine_type = var.machine_type
    	zone         = var.zone
    	tags         = []
    	boot_disk {
    		initialize_params {
    			image = data.google_compute_image.image.self_link
    		}
    	}
    
    	network_interface {
    		network = "default"
    		access_config {
    			// 临时 IP
    		}
    	}
    	// 可选配置,以使实例成为临时实例
    /*  scheduling {
    		preemptible       = true
    		automatic_restart = false
    	} */
    
    	// 将隧道令牌传递给 GCP 服务器,以便服务器可以在启动时安装并运行隧道。
    	metadata_startup_script = templatefile("./install-tunnel.tftpl",
    		{
    			tunnel_token = data.cloudflare_zero_trust_tunnel_cloudflared_token.gcp_tunnel_token.token
    		})
    }
    # 服务器将使用的操作系统
    data "google_compute_image" "image" {
    	family  = "ubuntu-2204-lts"
    	project = "ubuntu-os-cloud"
    }
    
    # GCP 实例资源
    resource "google_compute_instance" "http_server" {
    	name         = "test"
    	machine_type = var.machine_type
    	zone         = var.zone
    	tags         = []
    	boot_disk {
    		initialize_params {
    			image = data.google_compute_image.image.self_link
    		}
    	}
    
    	network_interface {
    		network = "default"
    		access_config {
    			// 临时 IP
    		}
    	}
    	// 可选配置,以使实例成为临时实例
    /*  scheduling {
    		preemptible       = true
    		automatic_restart = false
    	} */
    
    	// 将隧道令牌传递给 GCP 服务器,以便服务器可以在启动时安装并运行隧道。
    	metadata_startup_script = templatefile("./install-tunnel.tftpl",
    		{
    			tunnel_token = cloudflare_zero_trust_tunnel_cloudflared.gcp_tunnel.tunnel_token
    		})
    }

创建启动脚本

以下脚本将安装 cloudflared 并作为服务运行该隧道。此示例还安装了一个轻量级 HTTP 应用程序,您可以使用它来测试连接。

  1. 在您的配置目录中,创建一个 Terraform 模板文件:

    touch install-tunnel.tftpl
  2. 在文本编辑器中打开该文件并复制并粘贴以下 bash 脚本:

    # 安装 Cloudflare Tunnel 和 Docker 资源的脚本
    
    # Docker 配置
    cd /tmp
    sudo apt-get install software-properties-common
    # 检索此操作系统的 docker 软件源
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
    sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
    # 更新操作系统并安装 docker
    sudo apt update -y && sudo apt upgrade -y
    sudo apt install docker docker-compose -y
    # 添加 HTTPBin 应用程序并在 localhost:8080 上运行它。
    cat > /tmp/docker-compose.yml << "EOF"
    version: '3'
    services:
      httpbin:
        image: kennethreitz/httpbin
        restart: always
        container_name: httpbin
        ports:
          - 8080:80
    
      cloudflared:
        image: cloudflare/cloudflared:latest
        restart: always
        container_name: cloudflared
        command: tunnel run --token ${tunnel_token}
    EOF
    cd /tmp
    sudo docker-compose up -d

6. 部署 Terraform

要部署配置文件:

  1. 初始化您的配置目录:

    terraform init
  2. 预览将要创建的所有内容:

    terraform plan
  3. 应用该配置:

    terraform apply

GCP 实例和 Tunnel 可能需要几分钟时间才能上线。您可以在 Cloudflare 仪表板Networks(网络) > Tunnels(隧道) 下查看您的新 Tunnel,在 Zero Trust > Access controls(访问控制) > **Applications(应用程序)**下查看您的 Access 应用程序和策略。新的 DNS 记录可在 DNS 记录页面上找到。

7. 测试连接

  1. Networks(网络) > Tunnels(隧道) 中,验证您的 Tunnel 是否处于活动状态。

  2. Access controls(访问控制) > Applications(应用程序) 中,验证您的 Access 策略是否允许您的 Cloudflare 电子邮件。

  3. 从任何设备打开浏览器,转到 http_app.<CLOUDFLARE_ZONE>(例如,http_app.example.com)。

    如果您最近没有登录,您将看到 Access 登录页面。

  4. 使用您的 Cloudflare 电子邮件登录。

    您应该会看到 HTTPBin 主页。

这篇文档对您有帮助吗?