INTEGRITY Документация

Terraform

Terraform это программный инструмент для управления инфраструктурой как кодом, который позволяет развёртывать сервисы разных провайдеров с помощью единого синтаксиса конфигурации. При создании файла конфигурации Terraform вы описываете конечное состояние конфигурации, а не пошаговую процедуру его достижения. Это позволяет легко развёртывать, изменять и управлять своими туннелями наряду с остальной инфраструктурой.

В этом руководстве вы используете Terraform, чтобы развернуть:

Предварительные требования

Чтобы выполнить следующую процедуру, вам потребуется:

1. Установите Terraform

См. Руководство по установке Terraform для вашей операционной системы.

2. Установите gcloud CLI

  1. Установите gcloud CLI чтобы Terraform мог взаимодействовать с вашей учётной записью GCP.

  2. Аутентифицируйтесь через CLI, выполнив команду:

    gcloud auth application-default login

3. Создайте токен Cloudflare API

Создание API-токена чтобы Terraform мог взаимодействовать с вашей учётной записью Cloudflare. Как минимум токен должен включать следующие разрешения:

Type Элемент Разрешение
Аккаунт Cloudflare Tunnel Изменить
Аккаунт Access: приложения и политики Изменить
Zone DNS Изменить

4. Создайте каталог конфигурации

Terraform работает через рабочий каталог, содержащий файлы конфигурации. Вы можете хранить конфигурацию в нескольких файлах или в одном: Terraform обработает все файлы конфигурации в каталоге так, как если бы они были одним документом.

  1. Создайте папку для конфигурации Terraform:

    mkdir cloudflare-tf
  2. Перейдите в каталог:

    cd cloudflare-tf

5. Создайте файлы конфигурации Terraform

Определите входные переменные

Указанные ниже переменные будут переданы в конфигурацию GCP и Cloudflare.

  1. В своём каталоге конфигурации создайте .tf файле:

    touch variables.tf
  2. Откройте файл в текстовом редакторе и скопируйте туда следующее:

    # GCP variables
    variable "gcp_project_id" {
      description = "Google Cloud Platform (GCP) project ID"
      type        = string
    }
    
    variable "zone" {
      description = "Geographical zone for the GCP VM instance"
      type        = string
    }
    
    variable "machine_type" {
      description = "Machine type for the GCP VM instance"
      type        = string
    }
    
    # Cloudflare variables
    variable "cloudflare_zone" {
      description = "Domain used to expose the GCP VM instance to the Internet"
      type        = string
    }
    
    variable "cloudflare_zone_id" {
      description = "Zone ID for your domain"
      type        = string
    }
    
    variable "cloudflare_account_id" {
      description = "Account ID for your Cloudflare account"
      type        = string
      sensitive   = true
    }
    
    variable "cloudflare_email" {
      description = "Email address for your Cloudflare account"
      type        = string
      sensitive   = true
    }
    
    variable "cloudflare_token" {
      description = "Cloudflare API token"
      type        = string
      sensitive   = true
    }

Назначить значения переменным

  1. В своём каталоге конфигурации создайте .tfvars файле:

    touch terraform.tfvars

    Terraform автоматически использует эти переменные, если файл называется terraform.tfvars, иначе файл переменных нужно будет передавать вручную.

  2. Добавьте следующие переменные в terraform.tfvars. Обязательно замените в примере значения на свои.

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

Настройте провайдеры Terraform

Вам нужно будет объявить поставщики используется для развёртывания инфраструктуры.

  1. В своём каталоге конфигурации создайте .tf файле:

    touch providers.tf
  2. Добавьте следующих поставщиков в providers.tf. random поставщик используется для генерации секрета туннеля.

    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.

  1. В своём каталоге конфигурации создайте .tf файле:

    touch Cloudflare-config.tf
  2. Добавьте следующие ресурсы в Cloudflare-config.tf:

    
    # Creates a new remotely-managed tunnel for the GCP VM.
    resource "cloudflare_zero_trust_tunnel_cloudflared" "gcp_tunnel" {
    	account_id    = var.cloudflare_account_id
    	name          = "Terraform GCP tunnel"
    	config_src    = "cloudflare"
    }
    
    # Reads the token used to run the tunnel on the server.
    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
    }
    
    # Creates the CNAME record that routes http_app.${var.cloudflare_zone} to the tunnel.
    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
    }
    
    # Configures tunnel with a published application for clientless access.
    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"
    			}
    		]
    	}
    }
    
    # (Optional) Routes internal IP of GCP instance through the tunnel for private network access using 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"
    }
    
    # Creates a reusable Access policy.
    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"
    			}
    		}
    	]
    }
    
    # Creates an Access application to control who can connect to the public hostname.
    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
    		}
    	]
    }
    # Generates a 32-byte secret for the tunnel.
    resource "random_bytes" "tunnel_secret" {
    	byte_length = 32
    }
    
    # Creates a new remotely-managed tunnel for the 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
    }
    
    # Creates the CNAME record that routes http_app.${var.cloudflare_zone} to the tunnel.
    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
    }
    
    # Configures tunnel with a published application for clientless access.
    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"
    		}
    	}
    }
    
    # (Optional) Route internal IP of GCP instance through the tunnel for private network access using 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"
    }
    
    # Creates an Access application to control who can connect to the public hostname.
    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}"
    }
    
    # Creates a (legacy) Access policy for the Access application.
    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.

Настройте ресурсы GCP

Следующая конфигурация задаёт характеристики виртуальной машины GCP и настраивает сценарий запуска, который выполняется при загрузке.

  1. В своём каталоге конфигурации создайте .tf файле:

    touch GCP-config.tf
  2. Добавьте следующее содержимое в GCP-config.tf:

    # OS the server will use
    data "google_compute_image" "image" {
    	family  = "ubuntu-2204-lts"
    	project = "ubuntu-os-cloud"
    }
    
    # GCP Instance resource
    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 {
    			//Ephemeral IP
    		}
    	}
    	// Optional config to make instance ephemeral
    /*  scheduling {
    		preemptible       = true
    		automatic_restart = false
    	} */
    
    	// Pass the tunnel token to the GCP server so that the server can install and run the tunnel upon startup.
    	metadata_startup_script = templatefile("./install-tunnel.tftpl",
    		{
    			tunnel_token = data.cloudflare_zero_trust_tunnel_cloudflared_token.gcp_tunnel_token.token
    		})
    }
    # OS the server will use
    data "google_compute_image" "image" {
    	family  = "ubuntu-2204-lts"
    	project = "ubuntu-os-cloud"
    }
    
    # GCP Instance resource
    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 {
    			//Ephemeral IP
    		}
    	}
    	// Optional config to make instance ephemeral
    /*  scheduling {
    		preemptible       = true
    		automatic_restart = false
    	} */
    
    	// Pass the tunnel token to the GCP server so that the server can install and run the tunnel upon startup.
    	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-скрипт:

    # Script to install Cloudflare Tunnel and Docker resources
    
    # Docker configuration
    cd /tmp
    sudo apt-get install software-properties-common
    # Retrieving the docker repository for this OS
    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"
    # The OS is updated and docker is installed
    sudo apt update -y && sudo apt upgrade -y
    sudo apt install docker docker-compose -y
    # Add the HTTPBin application and run it on 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 и туннеля может занять несколько минут. Просмотреть новый туннель можно в Панель управления Cloudflare в разделе Сеть > Tunnels, а также приложение и политику Access в разделе Zero Trust > Контроль доступа > Приложения. Новые DNS-записи доступны на DNS-записи страница.

7. Проверьте подключение

  1. В Сеть > Tunnels, убедитесь, что ваш туннель активен.

  2. В Контроль доступа > Приложения, убедитесь, что ваш адрес электронной почты Cloudflare разрешён политикой Access.

  3. С любого устройства откройте браузер и перейдите по адресу http_app.<CLOUDFLARE_ZONE> (например, http_app.example.com).

    Если вы недавно не выполняли вход, откроется страница входа Access.

  4. Войдите с помощью своей электронной почты Cloudflare.

    Вы должны увидеть HTTPBin домашняя страница.