Начало работы с Terraform и Cloudflare (часть 1 из 2)

Начало работы с Terraform и Cloudflare (часть 1 из 2)

by moiseevrus

Как менеджер по продукту в Cloudflare, я провожу довольно много времени, разговаривая с клиентами. Одна из самых частых тем, о которых меня спрашивают, — управление конфигурацией. Разработчики хотят знать, как написать код для управления своей конфигурацией Cloudflare, не взаимодействуя напрямую с нашими API или пользовательским интерфейсом.

Следуя передовым методам разработки программного обеспечения, они хотят хранить конфигурацию в своем собственном репозитории исходного кода (будь то GitHub или где-либо еще), внедрить процесс управления изменениями, включающий проверку кода, и иметь возможность отслеживать версии и историю своих конфигураций с течением времени. Кроме того, им нужна возможность быстро и легко откатывать изменения при необходимости.

Когда я впервые поговорил с нашими инженерами об этих требованиях, они дали мне лучший ответ, который только мог ожидать менеджер по продукту: уже существует инструмент с открытым исходным кодом, который делает все это (и многое другое), с сильным сообществом и плагином. система для загрузки — она называется Terraform .

Эта запись в блоге посвящена началу работы с Terraform с Cloudflare и новой версией 1.0 нашего поставщика Terraform. «Поставщик» — это просто плагин, который знает, как взаимодействовать с определенным набором API — в данном случае с Cloudflare, но есть также провайдеры, доступные для AWS, Azure, Google Cloud, Kubernetes, VMware и многих других сервисов . Сегодняшний выпуск расширяет возможности нашего существующего провайдера, который ранее поддерживал только записи DNS, за счет поддержки настроек зоны, ограничения скорости, балансировки нагрузки и правил страницы.

Contents

До и после Терраформ

Прежде чем мы перейдем к некоторым реальным примерам использования Terraform с Cloudflare, вот набор диаграмм, иллюстрирующих смену парадигмы.

До Terraform вам нужно было научиться использовать интерфейсы конфигурации или API-интерфейсы каждого облачного и пограничного поставщика, например, Google Cloud и Cloudflare ниже. Кроме того, ваша способность хранить свою конфигурацию в вашей собственной системе управления исходным кодом зависит от механизмов экспорта конфигурации конкретного поставщика (которые могут существовать, а могут и не существовать).

С Terraform вы можете хранить и контролировать свою конфигурацию в GitHub (или в выбранной вами системе управления исходным кодом). Как только вы изучите синтаксис конфигурации Terraform, вам не нужно будет изучать, как использовать пользовательские интерфейсы или API-интерфейсы поставщиков — вы просто говорите Terraform, что вам нужно, а он вычисляет все остальное.

Установка Терраформ

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

$ brew install terraform 
==> Downloading https://homebrew.bintray.com/bottles/terraform-0.11.7.sierra.bottle.tar.gz ######################################################################## 100.0% 
==> Pouring terraform-0.11.7.sierra.bottle.tar.gz 
🍺 /usr/local/Cellar/terraform/0.11.7: 6 files, 80.2MB $ terraform version 
Terraform v0.11.7

Если вам интересно узнать, как использовать конкретный ресурс или технику Terraform, щелкните одну из следующих якорных ссылок:

  • Установка Терраформ
  • Привет, мир!
  • Отслеживание истории изменений
  • Применение настроек зоны
  • Управление ограничениями скорости
  • Ресурс балансировки нагрузки (следующий пост!)
  • Ресурс правил страницы (следующий пост!)
  • Проверка и откат конфигурации (следующий пост!)
  • Импорт существующего состояния и конфигурации (следующий пост!)

Привет, мир!

Теперь, когда Terraform установлен, пришло время начать его использовать. Предположим, у вас есть веб-сервер для вашего домена, доступный на 203.0.113.10. Вы только что зарегистрировали свой домен example.comв Cloudflare и хотите управлять всем с помощью Terraform.

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

$ cat > cloudflare.tf <<'EOF' 
provider "cloudflare" { 
  email = "you@example.com" 
  token = "your-api-key" 
} variable "domain" { 
  default = "example.com" 
} resource "cloudflare_record" "www" { 
  domain = "${var.domain}" 
  name = "www" 
  value = "203.0.113.10" 
  type = "A" 
  proxied = true 
} 
EOF

Теперь, когда вы создали базовую конфигурацию в HCL, давайте инициализируем Terraform и попросим применить конфигурацию к Cloudflare. HCL расшифровывается как HashiCorp Configuration Lanaguage и назван в честь создателя Terraform.

$ terraform init Initializing provider plugins... 
- Checking for available provider plugins on https://releases.hashicorp.com... 
- Downloading plugin for provider "cloudflare" (1.0.0)... The following providers do not have any version constraints in configuration, so the latest version was installed. To prevent automatic upgrades to new major versions that may contain breaking changes, it is recommended to add version = "..." constraints to the corresponding provider blocks in configuration, with the constraint strings suggested below. * provider.cloudflare: version = "~> 1.0" Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary. When you run terraform init, any plugins required, such as the Cloudflare Terraform provider, are automatically downloaded and saved locally to a .terraform directory: $ find .terraform/ 
.terraform/ 
.terraform//plugins 
.terraform//plugins/darwin_amd64 .terraform//plugins/darwin_amd64/lock.json .terraform//plugins/darwin_amd64/terraform-provider-cloudflare_v1.0.0_x4

Установив поставщика Cloudflare, давайте попросим Terraform показать изменения, которые он планирует внести в вашу учетную запись Cloudflare, чтобы вы могли подтвердить, что он соответствует запланированной вами конфигурации:

$ terraform plan 
Refreshing Terraform state in-memory prior to plan... 
The refreshed state will be used to calculate this plan, but will not be persisted to local or remote state storage. ------------------------------------------------------------------------ An execution plan has been generated and is shown below. 
Resource actions are indicated with the following symbols: 
  + create Terraform will perform the following actions:   + cloudflare_record.www 
      id:          <computed> 
      created_on:  <computed> 
      domain:      "example.com" 
      hostname:    <computed> 
      metadata.%:  <computed> 
      modified_on: <computed> 
      name:        "www" 
      proxiable:   <computed> 
      proxied:     "true" 
      ttl:         <computed> 
      type:        "A" 
      value:       "203.0.113.10" 
      zone_id:     <computed> Plan: 1 to add, 0 to change, 0 to destroy. ------------------------------------------------------------------------Note: You didn't specify an "-out" parameter to save this plan, so Terraform can't guarantee that exactly these actions will be performed if "terraform apply" is subsequently run.

Как видно из приведенного выше «плана выполнения», Terraform собирается создать новую запись DNS в соответствии с запросом. Значения, которые вы явно указали, отображаются, например, значение записи— A— 203.0.113.10в то время как значения, полученные на основе других вызовов API, например, при поиске zone_id или возвращенные после создания объекта, отображаются как <computed>.

Команда плана важна, так как она позволяет предварительно проверить точность изменений, прежде чем их вносить. Как только вы освоитесь с планом выполнения, пришло время применить его:

$ terraform apply --auto-approve 
cloudflare_record.www: Creating... 
  created_on:  "" => "<computed>" 
  domain:      "" => "example.com" 
  hostname:    "" => "<computed>" 
  metadata.%:  "" => "<computed>" 
  modified_on: "" => "<computed>" 
  name:        "" => "www" 
  proxiable:   "" => "<computed>" 
  proxied:     "" => "true" 
  ttl:         "" => "<computed>" 
  type:        "" => "A" 
  value:       "" => "203.0.113.10" 
  zone_id:     "" => "<computed>" 
cloudflare_record.www: Creation complete after 1s (ID: c38d3103767284e7cd14d5dad3ab8668) Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Обратите внимание, что я указал –auto-approve в командной строке для более краткого вывода; без этого флага Terraform покажет вам вывод, terraform planа затем запросит подтверждение перед его применением.

Вернувшись в панель мониторинга Cloudflare и выбрав вкладку DNS, я вижу запись, созданную Terraform:

Если вы хотите увидеть полные результаты, возвращаемые вызовом API (включая значения по умолчанию, которые вы не указали, но позволили вычислить Terraform), вы можете запустить terraform show:

$ terraform show 
cloudflare_record.www: 
  id = c38d3103767284e7cd14d5dad3ab8668 
  created_on = 2018-04-08T00:37:33.76321Z 
  data.% = 0 
  domain = example.com 
  hostname = www.example.com 
  metadata.% = 2 
  metadata.auto_added = false 
  metadata.managed_by_apps = false 
  modified_on = 2018-04-08T00:37:33.76321Z 
  name = www 
  priority = 0 
  proxiable = true 
  proxied = true 
  ttl = 1 
  type = A 
  value = 203.0.113.10 
  zone_id = e2e6391340be87a3726f91fc4148b122$ curl https://www.example.com
Hello, this is 203.0.113.10!

Отслеживание истории изменений

На terraform applyшаге выше вы создали и применили базовую конфигурацию Cloudflare. Terraform смогла применить эту конфигурацию к вашей учетной записи, поскольку вы указали свой адрес электронной почты и токен API в верхней части файла cloudflare.

$ head -n4 cloudflare.tf
provider “cloudflare” {
email = “you@example.com”
token = “your-api-key”
}

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

В качестве хорошей практики безопасности нам необходимо удалить ваши учетные данные Cloudflare из всего, что будет зафиксировано в репозитории. Поставщик Cloudflare Terraform поддерживает чтение этих значений из переменных среды CLOUDFLARE_EMAILи CLOUDFLARE_TOKEN, поэтому все, что нам нужно сделать, это:

$ sed -ie 's/^.*email =.*$/ # email pulled from $CLOUDFLARE_EMAIL/' cloudflare.tf 
$ sed -ie 's/^.*token =.*$/ # token pulled from $CLOUDFLARE_TOKEN/' cloudflare.tf $ head -n4 cloudflare.tf 
provider "cloudflare" { 
  # email pulled from $CLOUDFLARE_EMAIL 
  # token pulled from $CLOUDFLARE_TOKEN 
} $ export CLOUDFLARE_EMAIL=you@example.com 
$ export CLOUDFLARE_TOKEN=your-api-key

Обратите внимание, что вам нужно оставить пустое определение провайдера в файле, чтобы Terraform знал об установке плагина Cloudflare.

После выполнения вышеуказанного шага рекомендуется убедиться, что вы все еще можете пройти аутентификацию в Cloudflare. Запустив, terraform planмы можем заставить Terraform получить текущее состояние (для чего требуется действительный адрес электронной почты и ключ API):

$ terraform plan 
Refreshing Terraform state in-memory prior to plan... 
The refreshed state will be used to calculate this plan, but will not be persisted to local or remote state storage. cloudflare_record.www: Refreshing state... (ID: c38d3102767284e7ca14d5dad3ab8b69) ------------------------------------------------------------------------ No changes. Infrastructure is up-to-date. This means that Terraform did not detect any differences between your configuration and real physical resources that exist. As a result, no actions need to be performed.
  1. Сохраните свою конфигурацию в GitHub
    Теперь, когда учетные данные удалены, пришло время инициализировать репозиторий git с вашей конфигурацией Cloudflare, а затем отправить ее на GitHub.

Сначала мы создадим репозиторий GitHub для хранения конфигурации. Это можно сделать через пользовательский интерфейс GitHub или с помощью простого вызова API:

$ export GITHUB_USER=your-github-user 
$ export GITHUB_TOKEN=your-github-token $ export GITHUB_URL=$(curl -sSXPOST https://api.github.com/user/repos?access_token=$GITHUB_TOKEN -H 'Content-Type: application/json' \ 
-d '{"name": "cf-config", "private":"true"}' 2>/dev/null | jq -r .ssh_url) $ echo $GITHUB_URL 
git@github.com:$GITHUB_USER/cf-config.git


Теперь мы инициализируем репозиторий git и сделаем нашу первую фиксацию:
$ git init 
Initialized empty Git repository in $HOME/cf-config/.git/ $ git remote add origin $GITHUB_URL 
$ git add cloudflare.tf $ git commit -m "Step 2 - Initial commit with webserver definition." 
[master (root-commit) 5acea17] Step 2 - Initial commit with webserver definition. 
 1 file changed, 16 insertions(+) 
 create mode 100644 cloudflare.tf

Внимательный читатель мог заметить, что мы не фиксировали ни .terraformкаталог, ни файл terraform.tfstate. Первый не был зафиксирован, потому что этот репозиторий может использоваться на другой архитектуре, а плагины, содержащиеся в этом каталоге, созданы для системы, на которой запущен terraform init. Последний не был зафиксирован, так как i) он может в конечном итоге содержать конфиденциальные строки и ii) это не лучший способ синхронизировать состояние, как HashiCorp [объясняет].

Чтобы git не беспокоил нас об этих файлах, давайте добавим их в новый .gitignoreфайл, зафиксируем его и отправим все на GitHub:

$ cat > .gitignore <<'EOF' 
.terraform/ 
terraform.tfstate* 
EOF $ git add .gitignore $ git commit -m "Step 2 - Ignore terraform plugin directory and state file." 
[master 494c6d6] Step 2 - Ignore terraform plugin directory and state file. 
 1 file changed, 2 insertions(+) 
 create mode 100644 .gitignore $ git push 
Counting objects: 6, done. 
Delta compression using up to 8 threads. 
Compressing objects: 100% (4/4), done. 
Writing objects: 100% (6/6), 762 bytes | 0 bytes/s, done. 
Total 6 (delta 0), reused 0 (delta 0) 
To git@github.com:$GITHUB_USER/cf-config.git 
 * [new branch] master -> master

Применение настроек зоны

Теперь, когда у вас есть базовый веб-сайт, проксированный через Cloudflare, пришло время использовать Terraform для настройки некоторых дополнительных параметров в вашей зоне. Ниже мы настроим некоторые дополнительные параметры HTTPS, а затем отправим обновленную конфигурацию на GitHub для последующего использования.

Мы будем использовать новую ветку git для изменений, а затем объединим ее с основной перед применением. В команде вы можете использовать этот шаг как возможность для других просмотреть ваше изменение перед его слиянием и развертыванием. Или вы можете интегрировать Terraform в свою систему CI/CD для автоматического выполнения тестов с использованием другого домена Cloudflare.

Здесь мы изменяем конфигурацию Terraform, чтобы включить следующие параметры: TLS 1.3 , Always Use HTTPS , Strict SSL mode и Cloudflare WAF . Для строгого режима требуется действующий сертификат SSL в вашем источнике, поэтому обязательно используйте Cloudflare Origin CA для его создания.

$ git checkout -b step3-https 
Switched to a new branch 'step3-https' $ cat >> cloudflare.tf <<'EOF' resource "cloudflare_zone_settings_override" "example-com-settings" { 
  name = "${var.domain}"   settings { 
  tls_1_3 = "on" 
  automatic_https_rewrites = "on" 
  ssl = "strict" 
  waf = "on" 
 } 
} 
EOF

Давайте посмотрим, что предлагает Terraform, прежде чем применять его. Мы фильтруем terraform planвыходные данные, чтобы игнорировать те значения, которые будут «вычислены» — в данном случае настройки, которые будут оставлены со значениями по умолчанию. Для краткости с этого момента мы будем опускать некоторые посторонние выходные данные Terraform; чтобы увидеть вывод точно так же, как и при запуске, см. полный учебник .

Refreshing Terraform state in-memory prior to plan... 
The refreshed state will be used to calculate this plan, but will not be persisted to local or remote state storage.cloudflare_record.www: Refreshing state... (ID: c38d3103767284e7cd14d5dad3ab8668) ------------------------------------------------------------------------ An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: 
  + create Terraform will perform the following actions:   + cloudflare_zone_settings_override.example-com-settings 
      name:                                   "example.com" 
      settings.#:                             "1" 
      settings.0.automatic_https_rewrites:    "on" 
      settings.0.ssl:                         "strict" 
      settings.0.tls_1_3:                     "on" 
      settings.0.waf:                         "on" Plan: 1 to add, 0 to change, 0 to destroy.

Предложенные изменения выглядят хорошо, поэтому мы объединим их в master, а затем применим с помощью terraform apply. При работе в команде вам может потребоваться запрашивать запросы на вытягивание и использовать эту возможность для независимой проверки любых предлагаемых изменений конфигурации.

$ git add cloudflare.tf 
$ git commit -m "Step 3 - Enable TLS 1.3, Always Use HTTPS, and SSL Strict mode." [step3-https d540600] Step 3 - Enable TLS 1.3, Always Use HTTPS, and SSL Strict mode. 
 1 file changed, 11 insertions(+) $ git checkout master 
Switched to branch 'master' $ git merge step3-https 
Updating d26f40b..d540600 
Fast-forward 
 cloudflare.tf | 11 +++++++++++ 
 1 file changed, 11 insertions(+) $ git push 
Counting objects: 3, done. 
Delta compression using up to 8 threads. 
Compressing objects: 100% (3/3), done. 
Writing objects: 100% (3/3), 501 bytes | 0 bytes/s, done. 
Total 3 (delta 1), reused 0 (delta 0) 
remote: Resolving deltas: 100% (1/1), completed with 1 local object. 
To git@github.com:$GITHUB_USER/cf-config.git 
   d26f40b..d540600 master -> master

Прежде чем применять изменения, давайте посмотрим, сможем ли мы подключиться с помощью TLS 1.3. Подсказка: у нас не должно получиться с настройками по умолчанию. Если вы хотите пройти этот тест, вам нужно [скомпилировать curl для BoringSSL].

$ curl -v --tlsv1.3 https://www.upinatoms.com 2>&1 | grep "SSL connection\|error" 
* error:1000042e:SSL routines:OPENSSL_internal:TLSV1_ALERT_PROTOCOL_VERSION 
curl: (35) error:1000042e:SSL routines:OPENSSL_internal:TLSV1_ALERT_PROTOCOL_VERSION

Как показано выше, мы получаем сообщение об ошибке, поскольку TLS 1.3 еще не включен в вашей зоне. Давайте включим его, запустив terraform apply и попробуем еще раз:

$ terraform apply --auto-approve 
cloudflare_record.www: Refreshing state... (ID: c38d3103767284e7cd14d5dad3ab8668) 
cloudflare_zone_settings_override.example-com-settings: Creating...   
  initial_settings.#:                     "" => "<computed>" 
  initial_settings_read_at:               "" => "<computed>" 
  name:                                   "" => "example.com" 
  readonly_settings.#:                    "" => "<computed>" 
  settings.#:                             "" => "1" 
  settings.0.advanced_ddos:               "" => "<computed>"   
  settings.0.always_online:               "" => "<computed>" 
  settings.0.always_use_https:            "" => "<computed>" 
  settings.0.automatic_https_rewrites:    "" => "on" 
  settings.0.brotli:                      "" => "<computed>" 
  settings.0.browser_cache_ttl:           "" => "<computed>" 
  settings.0.browser_check:               "" => "<computed>" 
  settings.0.cache_level:                 "" => "<computed>" 
  settings.0.challenge_ttl:               "" => "<computed>" 
  settings.0.cname_flattening:            "" => "<computed>" 
  settings.0.development_mode:            "" => "<computed>" 
  settings.0.edge_cache_ttl:              "" => "<computed>" 
  settings.0.email_obfuscation:           "" => "<computed>" 
  settings.0.hotlink_protection:          "" => "<computed>" 
  settings.0.http2:                       "" => "<computed>" 
  settings.0.ip_geolocation:              "" => "<computed>" 
  settings.0.ipv6:                        "" => "<computed>" 
  settings.0.max_upload:                  "" => "<computed>" 
  settings.0.minify.#:                    "" => "<computed>" 
  settings.0.mirage:                      "" => "<computed>" 
  settings.0.mobile_redirect.#:           "" => "<computed>"  
  settings.0.opportunistic_encryption:    "" => "<computed>" 
  settings.0.origin_error_page_pass_thru: "" => "<computed>" 
  settings.0.polish:                      "" => "<computed>" 
  settings.0.prefetch_preload:            "" => "<computed>" 
  settings.0.privacy_pass:                "" => "<computed>" 
  settings.0.pseudo_ipv4:                 "" => "<computed>" 
  settings.0.response_buffering:          "" => "<computed>" 
  settings.0.rocket_loader:               "" => "<computed>" 
  settings.0.security_header.#:           "" => "<computed>" 
  settings.0.security_level:              "" => "<computed>" 
  settings.0.server_side_exclude:         "" => "<computed>" 
  settings.0.sha1_support:                "" => "<computed>" 
  settings.0.sort_query_string_for_cache: "" => "<computed>" 
  settings.0.ssl:                         "" => "strict" 
  settings.0.tls_1_2_only:                "" => "<computed>" 
  settings.0.tls_1_3:                     "" => "on" 
  settings.0.tls_client_auth:             "" => "<computed>"  
  settings.0.true_client_ip_header:       "" => "<computed>" 
  settings.0.waf:                         "" => "on" 
  settings.0.webp:                        "" => "<computed>" 
  settings.0.websockets:                  "" => "<computed>" 
  zone_status:                            "" => "<computed>" 
  zone_type:                              "" => "<computed>" cloudflare_zone_settings_override.example-com-settings: Creation complete after 2s (ID: e2e6491340be87a3726f91fc4148b125) Apply complete! Resources: 1 added, 0 changed, 0 destroyed.



Теперь мы можем попробовать ту же команду, что и выше, и убедиться, что она удалась.
 Нииис, TLS 1.3!
$ curl -v --tlsv1.3 https://www.example.com 2>&1 | grep "SSL connection\|error" 
* SSL connection using TLSv1.3 / AEAD-AES128-GCM-SHA256

Управление ограничениями скорости

Прежде чем продолжить, убедитесь, что для вашей учетной записи включено ограничение скорости. Если вы пользуетесь тарифным планом Enterprise, вам следует попросить об этом своего менеджера по работе с клиентами; в противном случае вы можете подписаться на ограничение скорости в панели инструментов Cloudflare.

Поскольку настройки нашей зоны заблокированы, а наш сайт начинает привлекать больше внимания, к сожалению, он начал привлекать некоторых из менее щепетильных персонажей в Интернете. Журналы доступа к нашему серверу показывают попытки взлома нашей страницы входа в систему по адресу https://www.example.com/loginДавайте посмотрим, что мы можем сделать с продуктом ограничения скорости Cloudflare , чтобы положить конец этим усилиям.

После создания новой ветки указываем правило ограничения скорости:

$ git checkout -b step4-ratelimit 
Switched to a new branch 'step4-ratelimit' $ cat >> cloudflare.cf <<'EOF' 
resource "cloudflare_rate_limit" "login-limit" { 
  zone = "${var.domain}"   threshold = 5 
  period = 60 
  match { 
    request { 
      url_pattern = "${var.domain}/login" 
      schemes = ["HTTP", "HTTPS"] 
      methods = ["POST"] 
    } 
    response { 
      statuses = [401, 403] 
      origin_traffic = true 
    } 
  } 
  action { 
    mode = "simulate"  
    timeout = 300 
    response { 
       content_type = "text/plain" 
       body = "You have failed to login 5 times in a 60 second period and will be blocked from attempting to login again for the next 5 minutes." 
    } 
  } 
  disabled = false 
  description = "Block failed login attempts (5 in 1 min) for 5 minutes." 
} 
EOF

Это правило немного сложнее, чем правило настроек зоны, поэтому давайте разберем его:

00: resource "cloudflare_rate_limit" "login-limit" { 
01: zone = "${var.domain}" 
02: 
03: threshold = 5 
04: period = 60

Порог представляет собой целое число, указывающее, сколько раз событие (определяемое блоком соответствия ниже) должно быть обнаружено за период до того, как правило начнет действовать. Период измеряется в секундах, поэтому в приведенном выше правиле говорится, что нужно действовать, если спичка загорится 5 раз за 60 секунд.


05: match { 
06: request { 
07: url_pattern = "${var.domain}/login" 
08: схемы = ["HTTP", "HTTPS"] 
09: методы = ["POST"] 
10: } 
11: ответ { 
12: статусы = [401, 403] 
13: } 
14: }

Блок match сообщает Edge Cloudflare, на что обращать внимание, т. е. HTTP или HTTPS POST-запросы к https://www.example.com/login. Кроме того, мы ограничиваем соответствие HTTP 401/Unauthorizedили 403/Forbiddenкодами ответов, возвращаемыми из источника.

15: action { 
16: mode = "simulate" 
17: timeout = 300 
18: response { 
19: content_type = "text/plain" 
20: body = "Вы не смогли войти в систему 5 раз за 60-секундный период и будете заблокированы от попыток входа в систему в течение следующих 5 минут». 
21: } 
22: } 
23: disabled = false 
24: description = "Блокировать неудачные попытки входа (5 за 1 мин) на 5 минут." 
25: }

После сопоставления трафика мы устанавливаем действие для нашего края. При тестировании рекомендуется установить режим имитации и просмотра журналов, прежде чем предпринимать принудительные действия (см. ниже). Поле тайм-аута здесь указывает, что мы хотим применить это действие в течение 300 секунд (5 минут), а блок ответа указывает, что должно быть отправлено обратно вызывающей стороне, которая отключила ограничение скорости.

Как обычно, мы смотрим на предложенный план, прежде чем вносить какие-либо изменения:

$ terraform plan 
... 
Terraform will perform the following actions:    + cloudflare_rate_limit.login-limit 
       id: <computed> 
       action.#: "1" 
       action.0.mode: "simulate" 
       action.0.response.#: "1" 
       action.0.response.0.body: "You have failed to login 5 times in a 60 second period and will be blocked from attempting to login again for the next 5 minutes." 
       action.0.response.0.content_type: "text/plain" 
       action.0.timeout: "300" description: "Block failed login attempts (5 in 1 min) for 5 minutes." disabled: "false" match.#: "1" 
      match.0.request.#: "1" match.0.request.0.methods.#: "1" 
      match.0.request.0.methods.1012961568: "POST" 
      match.0.request.0.schemes.#: "2" 
      match.0.request.0.schemes.2328579708: "HTTP" 
      match.0.request.0.schemes.2534674783: "HTTPS" 
      match.0.request.0.url_pattern: "www.example.com/login" 
      match.0.response.#: "1" match.0.response.0.origin_traffic: "true" 
      match.0.response.0.statuses.#: "2"   
      match.0.response.0.statuses.1057413486: "403" 
      match.0.response.0.statuses.221297644: "401" 
      period: "60" 
      threshold: "5" 
      zone: "example.com" 
      zone_id: <computed> Plan: 1 to add, 0 to change, 0 to destroy.
План выглядит хорошо, так что давайте продолжим, объединим его и применим.
$ git add cloudflare.tf 
$ git commit -m "Step 4 - Add rate limiting rule to protect /login." 
[step4-ratelimit 0f7e499] Step 4 - Add rate limiting rule to protect /login. 
 1 file changed, 28 insertions(+) $ git checkout master 
Switched to branch 'master' $ git merge step4-ratelimit 
Updating 321c2bd..0f7e499 
Fast-forward 
 cloudflare.tf | 28 ++++++++++++++++++++++++++++  
 1 file changed, 28 insertions(+) 
$ terraform apply --auto-approve cloudflare_record.www: Refreshing state... (ID: c38d3103767284e7cd14d5dad3ab8668) 
cloudflare_zone_settings_override.example-com-settings: Refreshing state... (ID: e2e6491340be87a3726f91fc4148b125) cloudflare_rate_limit.login-limit: Creating... 
  action.#: "" => "1" 
  action.0.mode: "" => "simulate" 
  action.0.response.#: "" => "1" 
  action.0.response.0.body: "" => "You have failed to login 5 times in a 60 second period and will be blocked from attempting to login again for the next 5 minutes." 
  action.0.response.0.content_type: "" => "text/plain" 
  action.0.timeout: "" => "300" 
  description: "" => "Block failed login attempts (5 in 1 min) for 5 minutes." 
  disabled: "" => "false" 
  match.#: "" => "1" 
  match.0.request.#: "" => "1" 
  match.0.request.0.methods.#: "" => "1" 
  match.0.request.0.methods.1012961568: "" => "POST" 
  match.0.request.0.schemes.#: "" => "2" 
  match.0.request.0.schemes.2328579708: "" => "HTTP" 
  match.0.request.0.schemes.2534674783: "" => "HTTPS" 
  match.0.request.0.url_pattern: "" => "www.example.com/login" 
  match.0.response.#: "" => "1" match.0.response.0.origin_traffic: "" => "true" 
  match.0.response.0.statuses.#: "" => "2" 
  match.0.response.0.statuses.1057413486: "" => "403" 
  match.0.response.0.statuses.221297644: "" => "401" 
  period: "" => "60" 
  threshold: "" => "5" 
  zone: "" => "example.com" 
  zone_id: "" => "<computed>" 
cloudflare_rate_limit.login-limit: Creation complete after 1s (ID: 8d518c5d6e63406a9466d83cb8675bb6) Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Обратите внимание: если вы еще не приобрели ограничение скорости, вы увидите следующую ошибку при попытке применить новое правило:

Error: Error applying plan: 1 error(s) occurred: * cloudflare_rate_limit.login-limit: 1 error(s) occurred: * cloudflare_rate_limit.login-limit: error creating rate limit for zone: error from makeRequest: HTTP status 400: content "{\n \"result\": null,\n \"success\": false,\n \"errors\": [\n {\n \"code\": 10021,\n \"message\": \"ratelimit.api.not_entitled.account\"\n }\n ],\n \"messages\": []\n}\n"

После подтверждения того, что правило срабатывает, как и планировалось, в журналах (но еще не применяется), пришло время переключиться с имитации на запрет:

$ git checkout step4-ratelimit 
$ sed -i.bak -e 's/simulate/ban/' cloudflare.tf $ git diff 
diff --git a/cloudflare.tf b/cloudflare.tf 
index ed5157c..9f25a0c 100644 
--- a/cloudflare.tf 
+++ b/cloudflare.tf 
@@ -42,7 +42,7 @@ resource "cloudflare_rate_limit" "login-limit" { 
     } 
   } 
   action { 
- mode = "simulate" 
+ mode = "ban" 
  timeout = 300 
  response { 
    content_type = "text/plain" $ terraform plan 
Refreshing Terraform state in-memory prior to plan... 
The refreshed state will be used to calculate this plan, but will not be persisted to local or remote state storage. cloudflare_zone_settings_override.example-com-settings: Refreshing state... (ID: e2e6491340be87a3726f91fc4148b126) cloudflare_rate_limit.login-limit: Refreshing state... (ID: 8d518c5d6e63406a9466d83cb8675bb6) 
cloudflare_record.www: Refreshing state... (ID: c38d3103767284e7cd14d5dad3ab8669) ------------------------------------------------------------------------ An execution plan has been generated and is shown below. 
Resource actions are indicated with the following symbols: 
  ~ update in-place Terraform will perform the following actions:   ~ cloudflare_rate_limit.login-limit 
      action.0.mode: "simulate" => "ban" Plan: 0 to add, 1 to change, 0 to destroy.
$ git add cloudflare.tf $ git commit -m "Step 4 - Update /login rate limit rule from 'simulate' to 'ban'." [step4-ratelimit e1c38cf] Step 4 - Update /login rate limit rule from 'simulate' to 'ban'. 1 file changed, 1 insertion(+), 1 deletion(-) $ git checkout master && git merge step4-ratelimit && git push 
Switched to branch 'master' 
Updating 0f7e499..e1c38cf 
Fast-forward 
  cloudflare.tf | 2 +- 
  1 file changed, 
  1 insertion(+), 1 deletion(-) 
Counting objects: 3, done. 
Delta compression using up to 8 threads. 
Compressing objects: 100% (3/3), done. 
Writing objects: 100% (3/3), 361 bytes | 0 bytes/s, done. 
Total 3 (delta 1), reused 0 (delta 0) 
remote: Resolving deltas: 100% (1/1), completed with 1 local object. 
To git@github.com:$GITHUB_USER/cf-config.git 
    0f7e499..e1c38cf master -> master$ terraform apply --auto-approve 
cloudflare_rate_limit.login-limit: Refreshing state... (ID: 8d518c5d6e63406a9466d83cb8675bb6) 
cloudflare_record.www: Refreshing state... (ID: c38d3103767284e7cd14d5dad3ab8669) 
cloudflare_zone_settings_override.example-com-settings: Refreshing state... (ID: e2e6491340be87a3726f91fc4148b126) cloudflare_rate_limit.login-limit: Modifying... (ID: 8d518c5d6e63406a9466d83cb8675bb6) 
  action.0.mode: "simulate" => "ban" cloudflare_rate_limit.login-limit: Modifications complete after 0s (ID: 8d518c5d6e63406a9466d83cb8675bb6) Apply complete! Resources: 0 added, 1 changed, 0 destroyed.

Этот шаг необязателен, но это хороший способ продемонстрировать, что правило работает должным образом (обратите внимание на окончательный 429ответ):

$ for i in {1..6}; do curl -XPOST -d '{"username": "foo", "password": "bar"}' -vso /dev/null https://www.example.com/login 2>&1 | grep "
< HTTP"; sleep 1; done 
< HTTP/1.1 401 Unauthorized 
< HTTP/1.1 401 Unauthorized 
< HTTP/1.1 401 Unauthorized 
< HTTP/1.1 401 Unauthorized 
< HTTP/1.1 401 Unauthorized 
< HTTP/1.1 429 Too Many Requests

Подведение итогов

Вот и все на сегодня! Следите за обновлениями на следующей неделе, чтобы увидеть вторую часть этого поста , в которой мы продолжим тур по следующим ресурсам и методам:

  • Ресурс балансировки нагрузки
  • Ресурс правил страницы
  • Просмотр и откат изменений
  • Импорт существующего состояния и конфигурации

You may also like

Leave a Comment