תיעוד 177
Terraform
Terraform ↗︎ הוא כלי תשתית כקוד שמאפשר להגדיר ולנהל את המנהרות שלך לצד תשתית אחרת. המדריך הזה פורס:
- מכונה וירטואלית ב-GCP שמריצה שרת אינטרנט
- מנהרת Cloudflare שהופכת את השרת לזמין דרך האינטרנט
- (אופציונלי) מדיניות Cloudflare Access שמגדירה מי יכול להתחבר
#דרישות מוקדמות
#1. התקנת Terraform
עיין במדריך ההתקנה של Terraform ↗︎ עבור מערכת ההפעלה שלך.
#2. התקנת ה-CLI של gcloud
התקן את ה-CLI של gcloud ↗︎ כדי ש-Terraform יוכל לתקשר עם חשבון ה-GCP שלך.
אמת מול ה-CLI על ידי הרצה:
gcloud auth application-default login
#3. יצירת אסימון API של Cloudflare
צור אסימון API כדי ש-Terraform יוכל לתקשר עם חשבון Cloudflare שלך. לכל הפחות, האסימון צריך לכלול את ההרשאות הבאות:
| סוג | פריט | הרשאה |
|---|---|---|
| Account | Cloudflare Tunnel | Edit |
| Account | Access: Apps and Policies | Edit |
| Zone | DNS | Edit |
#4. יצירת ספריית תצורה
Terraform פועל דרך ספריית עבודה שמכילה קובצי תצורה. אפשר לאחסן את התצורה בכמה קבצים או בקובץ אחד בלבד. Terraform יעריך את כל קובצי התצורה בספרייה כאילו היו במסמך אחד.
צור תיקייה לתצורת Terraform:
mkdir cloudflare-tfעבור לספרייה:
cd cloudflare-tf
#5. יצירת קובצי תצורת Terraform
#הגדרת משתני קלט
המשתנים הבאים יועברו לתצורת GCP ו-Cloudflare שלך.
בספריית התצורה, צור קובץ
.tf:touch variables.tfפתח את הקובץ בעורך טקסט והעתק והדבק את התוכן הבא:
# 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 }
#הקצאת ערכים למשתנים
בספריית התצורה, צור קובץ
.tfvars:touch terraform.tfvarsTerraform ישתמש במשתנים האלה אוטומטית אם הקובץ נקרא
terraform.tfvars, אחרת יהיה צורך להעביר את קובץ המשתנים ידנית.הוסף את המשתנים הבאים לקובץ
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
יהיה עליך להצהיר על הספקים ↗︎ שמשמשים להקצאת התשתית.
בספריית התצורה, צור קובץ
.tf:touch providers.tfהוסף את הספקים הבאים לקובץ
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 שלך.
בספריית התצורה, צור קובץ
.tf:touch Cloudflare-config.tfהוסף את המשאבים הבאים לקובץ
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 ומגדירה סקריפט הפעלה שירוץ בעת האתחול.
בספריית התצורה, צור קובץ
.tf:touch GCP-config.tfהוסף את התוכן הבא לקובץ
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 קל משקל שאפשר להשתמש בו כדי לבדוק קישוריות.
בספריית התצורה, צור קובץ תבנית של Terraform:
touch install-tunnel.tftplפתח את הקובץ בעורך טקסט והעתק והדבק את סקריפט ה-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
כדי לפרוס את קובצי התצורה:
אתחל את ספריית התצורה:
terraform initהצג תצוגה מקדימה של כל מה שייווצר:
terraform planהחל את התצורה:
terraform apply
ייתכן שיחלפו כמה דקות עד שמכונת ה-GCP והמנהרה יעלו לאוויר. אפשר לראות את המנהרה החדשה בלוח הבקרה של Cloudflare ↗︎ תחת Networking > Tunnels.
#הסרת משאבי Terraform
אם צריך לבטל את התצורה, הרץ terraform destroy כדי למחוק את כל מה שנוצר באמצעות Terraform. גם terraform apply וגם terraform destroy מבקשים קלט מהמשתמש לפני החלת השינויים. כדי להריץ בלי לדרוש קלט מהמשתמש, אפשר להוסיף את הדגל -auto-approve לפקודה.
#7. בדיקת החיבור
בלוח הבקרה של Cloudflare ↗︎, עבור אל Networking > Tunnels וודא שהמנהרה שלך במצב Active.
(אופציונלי) אם הגדרת Access, עבור אל Security > Access > Applications וודא שכתובת הדוא"ל שלך ב-Cloudflare מותרת על ידי מדיניות ה-Access.
מכל מכשיר, פתח דפדפן ועבור אל
http_app.<CLOUDFLARE_ZONE>(לדוגמה,http_app.example.com).אם הגדרת Access, תוצג דף ההתחברות של Access. התחבר עם כתובת הדוא"ל שלך ב-Cloudflare.
אמור להופיע דף הבית של HTTPBin, וזה מאשר שהמנהרה מנתבת תעבורה כראוי.