Простая выписка сертификата для dns через terraform
В этом посте выпишем сертификат для dns имени, посмотрим как его продлевать, и все это без использования виртуальных машин
Сертификаты - слово, которое почти наверняка вызывает боль у инженера. Где и как их взять? Как настроить под них систему? А где хранить? Как продлевать? Попробуем выстроить универсальный подход работы с сертификатами в случае, когда практически нет инфраструктуры (или вовсе отсутствует), но хочется начать с ними работать без особых сложностей.
Что будем делать?
Сертификат будем получать у Let’s Encrypt по ACME протоколу. Идея следующая:
- Клиент (в нашем случае сценарий, запущенный через terraform) создает аккаунт и генерирует запрос на сертификат.
- Let’s Encrypt проверяет, что вы контролируете домен. Есть разные проверки, мы создадим специальную dns-запись для домена
- После успешной проверки Let’s Encrypt автоматически выдает сертификат, мы сохраним его в стейт локально. При желании возможна настройка сохранения стейта в удаленное хранилище
- Проверим, что сертификат продляется по истечению заданного периода
Подготовка к работе
Для воспроизведения работы с сертификатом понадобится сделать небольшую подготовку.
Настройка окружения
Нам понадобятся
Создание домена
Если нет своего домена, можно завести прям в облаке. После регистрации заходите в панель управления доменами и заводите домен в любой из зон:
- .tw1.su
- .tw1.ru
- .webtm.ru
- .twc1.net
Для примера выпишем сертификат для example-test.tw1.su
Создание токена для terraform
Для работы через тераформ создайте свой токен в разделе API и Terraform. Сохраните ваш timeweb токен в переменную окружения TF_VAR_timeweb_token
Создание клиента
Итак, пришло время время автоматизации. Создайте файл dns.tf, всю дальнейшую работу будем вести в нем. Для начала опишем провайдеры.
Настройка провайдеров
Нам понадобятся:
- Провайдер от timeweb для взаимодействия с облаком
- Провайдер для создания tls ключей для создания acme регистрации и для выписки сертификата
- Acme провайдер для прохождения проверки и получения сертификата
Также убедитесь, что ваш timeweb токен сохранен в TF_VAR_timeweb_token
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
terraform {
required_providers {
twc = {
source = "tf.timeweb.cloud/timeweb-cloud/timeweb-cloud"
version = "1.6.6"
}
tls = {
source = "hashicorp/tls"
version = "4.1.0"
}
acme = {
source = "vancluever/acme"
version = "2.38.0"
}
}
}
variable "timeweb_token" {
description = "Timeweb cloud token, more info https://github.com/timeweb-cloud/terraform-provider-timeweb-cloud/tree/main"
type = string
sensitive = true
}
provider "twc" {
token = var.timeweb_token
}
provider "acme" {
server_url = "https://acme-staging-v02.api.letsencrypt.org/directory"
}
Обратите внимание, что в провайдере acme мы используем стейджовый сервер:
1
https://acme-staging-v02.api.letsencrypt.org/directory
В то время как адрес основного сервера:
1
https://acme-v02.api.letsencrypt.org/directory
В чем разница между серверами?
- Различные rate limits у prod и stage. Например, перевыписка сертификата для тех же доменных имен на prod возможна 5 раз в неделю, а на stage 30000 раз в неделю
- Браузеры и ОС не доверяют по умолчанию сертификатам со stage сервера, хотя по-прежденему можно добавлять эти сертификаты в доверенные вручную. С prod сервера доверие настроено по умолчанию.
Таким образом, для тестов пользуемся stage. Далее для боевых задач адрес нужно поменять на prod-овый.
Регистрируемся в let’s encrypt
Для регистрации нам потребуется RSA ключ и собственно сам запрос регистрации
1
2
3
4
5
6
7
8
9
resource "tls_private_key" "acme_account" {
algorithm = "RSA"
rsa_bits = 4096
}
resource "acme_registration" "this" {
email_address = "example@gmail.com"
account_key_pem = tls_private_key.acme_account.private_key_pem
}
Получаем сертификат для домена
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
resource "tls_private_key" "this" {
algorithm = "RSA"
rsa_bits = 2048
}
resource "tls_cert_request" "this" {
private_key_pem = tls_private_key.this.private_key_pem
dns_names = ["example-test.tw1.su", "*.example-test.tw1.su"]
subject {
common_name = "example-test.tw1.su"
}
}
resource "acme_certificate" "this" {
account_key_pem = acme_registration.this.account_key_pem
certificate_request_pem = tls_cert_request.this.cert_request_pem
min_days_remaining = 30
dns_challenge {
provider = "timewebcloud"
config = {
TIMEWEBCLOUD_AUTH_TOKEN = var.timeweb_token
}
}
}
output "acme_certificate_this" {
value = acme_certificate.this
sensitive = true
}
Что здесь происходит
- Сначала выписываем новый приватный RSA ключ
- Далее создаем CSR (Certificate Signing Request). По сути это описание, какой сертификат мы хотим получить. Так мы сами контролируем, какие поля попадут в сертифкат. И что важнее, мы нигде не светим приватный ключ от сертификата
- И наконец ресурс acme_certificate, который непосредственно отправляет запрос и получает подписанный сертификат от let’s encrypt.
Теперь, чтобы получить сертификат, просто выполните
1
tofu apply
Чтобы посмотреть, какой сертификат пришел, выполните
1
tofu output acme_certificate_this
Сертификат будет лежать в certificate_pem
Полностью готовый манифест можно скачать по ссылке
Контроль времени жизни сертификата
Сейчас в ресурсе acme_certificate задан параметр min_days_remaining = 30. Это значит, что за 30 дней до истечения сертификата при выполении tofu apply будет перевыписываться сертификат. Как проверить, что это работает? Сделайте значение min_days_remaining = 90, так сертификат будет перевыписываться всякий раз, когда будет проходить apply. Перевыписку можно автоматизировать. Самый простой способ, выполнять tofu apply -auto-approve по расписанию.
Заключение
Использование terraform (или opentofu) совместно с ACME позволяет позволяет полностью автоматизировать работу с tls сертификатами. В результате выпуск и продление сертификатов превращаются в декларативно описанный сценарий, который можно по расписанию запускать в любой CI/CD системе. Получение сертификата возможно до создания какой-либо реальной инфраструктуры, что дает возможность с нуля начать пользоваться зашифрованными соединениями. Такой подход:
- экономит время
- повышает безопасность
- снижает риски ошибок
- масштабируется под различные цели и задачи
С таким подходом создание инфраструктуры с нуля в облаке становится немного легче.
