Tworzenie tokenów konta Cloudflare dla CI, VM i administratora

Tokeny konta Cloudflare zarządzane przez OpenTofu.

Jeden wspólny token CF dla VM, CI/CD i admina jest łatwy na początku, ale szybko zaczyna być to frustrujące, mało stabilne rozwiązanie. Dodatkowo tworzenie tokenów przez UI szybko robi się irytujące.

Rozwiązaniem jest używanie odpowiedniego typu tokenu do zasobu oraz tworzenie go w IaC, z pomocą agentów AI. Token „USER” wydaje się na początku szybkim wyjściem. Warto jednak zainwestować raz i stworzyć token konta dla OpenTofu, a potem generować nim dedykowane tokeny per usługa: CI/CD, VM, admin.

Tokeny API należące do konta rozwiązują problem właściciela. Są service principals należącymi do konta, a nie do pracownika, który je utworzył. Dla trwałej automatyzacji warto utworzyć osobny token dla każdego obciążenia:

Tożsamość Cel Typowy zakres
VM OpenTofu zarządzające infrastrukturą Cloudflare Uprawnienia wymagane przez zadeklarowane zasoby konta i stref
CI/CD Wrangler wdrażający Workery Workers Scripts i tylko powiązane funkcje używane przez pipeline
Laptop administratora Kontrolowana administracja lokalna Jawna lista administracyjnych uprawnień konta i stref

Poprawny bootstrap

Tworzenie tokenów konta wymaga roli Super Administrator na koncie docelowym. Utworzenie przez API wymaga nadrzędnego poświadczenia z Account API Tokens Write albo Global API Key Super Administratora użytego do pierwszego bootstrapu. Ten jeden token nadrzędny tworzysz w panelu, resztę robi już OpenTofu.

User API Tokens Write nie wystarcza. To uprawnienie zarządza tokenami należącymi do użytkownika przez inne API.

Provider uwierzytelniamy przez środowisko, nigdy przez wersjonowany HCL:

export CLOUDFLARE_API_TOKEN='token-nadrzedny-z-account-api-tokens-write'

Przy Global API Key trzeba usunąć CLOUDFLARE_API_TOKEN i ustawić CLOUDFLARE_EMAIL oraz CLOUDFLARE_API_KEY.

Właściwy zasób tokenu konta

Provider v5 ma dwa podobnie nazwane zasoby:

  • cloudflare_api_token tworzy token użytkownika, zwykle z prefiksem cfut_;
  • cloudflare_account_token tworzy token konta, zwykle z prefiksem cfat_.

Prefiks jest najszybszym sposobem rozpoznania, co trzymasz w zmiennej:

Poświadczenie Format Kiedy używać
Global API Key cfk_ + 40 znaków + suma kontrolna tylko pierwszy bootstrap
Token API użytkownika cfut_ + 40 znaków + suma kontrolna osobiste skrypty
Token API konta cfat_ + 40 znaków + suma kontrolna VM, CI/CD, admin

Tokeny sprzed 2026 roku nie mają prefiksu i nadal działają. Nowe wartości są skanowalne, więc GitHub potrafi wykryć wyciek i zgłosić go do Cloudflare. Szczegóły opisują formaty tokenów.

GUID-y grup uprawnień różnią się między tymi API. Nie należy kopiować identyfikatora Workers Scripts z przykładu tokenu użytkownika. Aktualne grupy pobieramy według dokładnej nazwy i zakresu:

data "cloudflare_account_api_token_permission_groups_list" "available" {
  account_id = var.account_id
  max_items  = 1000
}

locals {
  wanted = {
    workers_scripts = {
      name  = "Workers Scripts Write"
      scope = "com.cloudflare.api.account"
    }
    workers_routes = {
      name  = "Workers Routes Write"
      scope = "com.cloudflare.api.account.zone"
    }
  }

  permission_ids = {
    for key, wanted in local.wanted : key => one([
      for available in data.cloudflare_account_api_token_permission_groups_list.available.result : available.id
      if available.name == wanted.name && contains(available.scopes, wanted.scope)
    ])
  }
}

Zakres jest istotny, ponieważ Cloudflare ma grupy konta i stref o zbliżonych nazwach.

Osobne polityki konta i stref

Skrócony przykład tworzy token CI. Tokeny VM i laptopa korzystają z tego samego wzorca, ale mają szersze, jawne listy uprawnień.

resource "cloudflare_account_token" "cicd" {
  account_id = var.account_id
  name       = "Deploy Workers (gitlab-ci)"

  policies = [{
    effect = "allow"
    permission_groups = [{
      id = local.permission_ids.workers_scripts
    }]
    resources = jsonencode({
      "com.cloudflare.api.account.${var.account_id}" = "*"
    })
    }, {
    effect = "allow"
    permission_groups = [{
      id = local.permission_ids.workers_routes
    }]
    resources = jsonencode({
      "com.cloudflare.api.account.${var.account_id}" = {
        "com.cloudflare.api.account.zone.*" = "*"
      }
    })
  }]
}

Token konta wymaga takiego zagnieżdżenia stref. Główny wildcard com.cloudflare.api.account.zone.*, spotykany w przykładach tokenów użytkownika, jest odrzucany przez API tokenów konta.

CI nie powinno dostać praw do DNS, R2, Access ani zarządzania tokenami, jeśli jego polecenia ich nie używają. Listy dla VM i laptopa budujemy z zasobów zarządzanych przez ich rooty OpenTofu, a nie z szablonu „wszystkie uprawnienia”.

Rotacja przy każdym apply

Wbudowany terraform_data wymusza wymianę bez dodatkowego providera czasu lub losowości:

resource "terraform_data" "rotation" {
  triggers_replace = timestamp()
}

resource "cloudflare_account_token" "cicd" {
  # account_id, name i policies pominięte

  lifecycle {
    create_before_destroy = true
    replace_triggered_by  = [terraform_data.rotation]
  }
}

Każdy apply tworzy nowy token przed unieważnieniem poprzedniego. Dystrybucja musi nastąpić od razu: konsumenci powinni dostać nowe wartości przed następnym apply. Przy rzadszej rotacji timestamp() można zastąpić ręcznie zwiększaną wersją.

Współdzielone runnery GitLab nie powinny mieć warunku IP, ponieważ ich adresy wyjściowe się zmieniają. VM lub laptop można ograniczyć, jeśli zawsze korzysta ze statycznego adresu albo CIDR sieci VPN.

Jak odebrać token po apply

Wartość tokenu widać tylko przez output, a każdy output jest oznaczony jako sensitive:

output "cicd_token" {
  value     = cloudflare_account_token.this["cicd"].value
  sensitive = true
}

Dlatego samo tofu output pokaże <sensitive>. Wartość wyciągasz flagą -raw:

tofu init && tofu apply
tofu output -raw cicd_token

Moduł zwraca trzy nazwy: cicd_token, vm_token i laptop_token.

Wartość CI zapisujemy jako maskowaną i chronioną zmienną CLOUDFLARE_API_TOKEN. Tokeny VM i laptopa trafiają do osobnych magazynów sekretów. sensitive = true ukrywa tylko wydruk CLI — sekrety nadal siedzą w stanie. Stan trzeba chronić i zachować, aby kolejne apply mogły unieważniać poprzednie tokeny.

Źródła