מדריך קלאודפלייר בעברית

תיעוד 177

Terraform

Terraform ↗︎ הוא כלי תשתית כקוד שמאפשר להגדיר ולנהל את המנהרות שלך לצד תשתית אחרת. המדריך הזה פורס:

  • מכונה וירטואלית ב-GCP שמריצה שרת אינטרנט
  • מנהרת Cloudflare שהופכת את השרת לזמין דרך האינטרנט
  • (אופציונלי) מדיניות Cloudflare Access שמגדירה מי יכול להתחבר

#דרישות מוקדמות

#1. התקנת Terraform

עיין במדריך ההתקנה של Terraform ↗︎ עבור מערכת ההפעלה שלך.

#2. התקנת ה-CLI של gcloud

  1. התקן את ה-CLI של gcloud ↗︎ כדי ש-Terraform יוכל לתקשר עם חשבון ה-GCP שלך.

  2. אמת מול ה-CLI על ידי הרצה:

    gcloud auth application-default login

#3. יצירת אסימון API של Cloudflare

צור אסימון API כדי ש-Terraform יוכל לתקשר עם חשבון Cloudflare שלך. לכל הפחות, האסימון צריך לכלול את ההרשאות הבאות:

סוגפריטהרשאה
AccountCloudflare TunnelEdit
AccountAccess: Apps and PoliciesEdit
ZoneDNSEdit

#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.tfvars במערכת בקרת הגרסאות שלך. לדוגמה, אם בקרת הגרסאות שלך היא git, הוסף את terraform.tfvars לקובץ .gitignore.

#הגדרת ספקי 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 ↗︎ תחת Networking > Tunnels.

#הסרת משאבי Terraform

אם צריך לבטל את התצורה, הרץ terraform destroy כדי למחוק את כל מה שנוצר באמצעות Terraform. גם terraform apply וגם terraform destroy מבקשים קלט מהמשתמש לפני החלת השינויים. כדי להריץ בלי לדרוש קלט מהמשתמש, אפשר להוסיף את הדגל -auto-approve לפקודה.

#7. בדיקת החיבור

  1. בלוח הבקרה של Cloudflare ↗︎, עבור אל Networking > Tunnels וודא שהמנהרה שלך במצב Active.

  2. (אופציונלי) אם הגדרת Access, עבור אל Security > Access > Applications וודא שכתובת הדוא"ל שלך ב-Cloudflare מותרת על ידי מדיניות ה-Access.

  3. מכל מכשיר, פתח דפדפן ועבור אל http_app.<CLOUDFLARE_ZONE> (לדוגמה, http_app.example.com).

    אם הגדרת Access, תוצג דף ההתחברות של Access. התחבר עם כתובת הדוא"ל שלך ב-Cloudflare.

  4. אמור להופיע דף הבית של HTTPBin, וזה מאשר שהמנהרה מנתבת תעבורה כראוי.