Организация защищенного доступа к контенту в Cloud CDN с помощью Terraform
Чтобы настроить защищенный доступ к контенту в Cloud CDN:
- Подготовьте облако к работе.
- Делегируйте домен сервису Cloud DNS.
- Создайте инфраструктуру.
- Опубликуйте сайт на веб-сервере.
- Проверьте работу защищенного доступа к файлам.
Если созданные ресурсы вам больше не нужны, удалите их.
Подготовьте облако к работе
Зарегистрируйтесь в Yandex Cloud и создайте платежный аккаунт:
- Перейдите в консоль управления
, затем войдите в Yandex Cloud или зарегистрируйтесь. - На странице Yandex Cloud Billing
убедитесь, что у вас подключен платежный аккаунт, и он находится в статусеACTIVE
илиTRIAL_ACTIVE
. Если платежного аккаунта нет, создайте его и привяжите к нему облако.
Если у вас есть активный платежный аккаунт, вы можете создать или выбрать каталог, в котором будет работать ваша инфраструктура, на странице облака
Подробнее об облаках и каталогах.
Необходимые платные ресурсы
В стоимость поддержки инфраструктуры для организации защищенного доступа к контенту в Cloud CDN входят:
- плата за использование публичного IP-адреса (см. тарифы Yandex Virtual Private Cloud);
- плата за вычислительные ресурсы и диски ВМ (см. тарифы Yandex Compute Cloud);
- плата за использование публичной DNS-зоны и публичные DNS-запросы (см. тарифы Yandex Cloud DNS);
- плата за хранение данных в Object Storage, операции с ними и исходящий трафик (см. тарифы Object Storage);
- плата за исходящий трафик с CDN-серверов (см. тарифы Cloud CDN).
Делегируйте домен сервису Cloud DNS
Делегируйте ваш домен сервису Cloud DNS. Для этого в личном кабинете вашего регистратора домена укажите в настройках домена адреса DNS-серверов ns1.yandexcloud.net
и ns2.yandexcloud.net
.
Создайте инфраструктуру
Terraform
Terraform распространяется под лицензией Business Source License
Подробную информацию о ресурсах провайдера смотрите в документации на сайте Terraform
Для создания инфраструктуры c помощью Terraform:
-
Установите Terraform, получите данные для аутентификации и укажите источник для установки провайдера Yandex Cloud (раздел Настройте провайдер, шаг 1).
-
Подготовьте файлы с описанием инфраструктуры:
Готовая конфигурацияВручную-
Клонируйте репозиторий с конфигурационными файлами.
git clone https://github.com/yandex-cloud-examples/yc-cdn-protected-access
-
Перейдите в директорию с репозиторием. В ней должны появиться файлы:
yc-cdn-secure-token.tf
— конфигурация создаваемой инфраструктуры.yc-cdn-secure-token.auto.tfvars
— файл c пользовательскими данными.
-
Подготовьте файлы для загрузки в бакет:
-
Сохраните любое изображение в формате JPEG в файл
content.jpg
. -
Создайте файл
index.html
:<html> <body> </body> </html>
-
-
Создайте папку для конфигурационных файлов.
-
Создайте в папке:
-
Конфигурационный файл
yc-cdn-secure-token.tf
:yc-cdn-secure-token.tf
# Объявление переменных variable "folder_id" { type = string } variable "domain_name" { type = string } variable "subdomain_name" { type = string } variable "bucket_name" { type = string } variable "cdn_cname" { type = string } variable "secure_key" { type = string } variable "ssh_key_path" { type = string } variable "index_file_path" { type = string } variable "content_file_path" { type = string } locals { sa_name = "my-service-account" network_name = "webserver-network" subnet_name = "webserver-subnet-ru-central1-b" sg_name = "webserver-sg" vm_name = "mywebserver" domain_zone_name = "my-domain-zone" cert_name = "mymanagedcert" origin_gp_name = "my-origin-group" } # Настройка провайдера terraform { required_providers { yandex = { source = "yandex-cloud/yandex" } } required_version = ">= 0.13" } provider "yandex" { folder_id = var.folder_id } # Создание сервисного аккаунта resource "yandex_iam_service_account" "ig-sa" { name = local.sa_name } # Назначение роли сервисному аккаунту resource "yandex_resourcemanager_folder_iam_member" "storage-editor" { folder_id = var.folder_id role = "storage.editor" member = "serviceAccount:${yandex_iam_service_account.ig-sa.id}" } # Создание статического ключа доступа для СА resource "yandex_iam_service_account_static_access_key" "sa-static-key" { service_account_id = "${yandex_iam_service_account.ig-sa.id}" } # Создание сети resource "yandex_vpc_network" "webserver-network" { name = local.network_name } # Создание подсети resource "yandex_vpc_subnet" "webserver-subnet-b" { name = local.subnet_name zone = "ru-central1-b" network_id = "${yandex_vpc_network.webserver-network.id}" v4_cidr_blocks = ["192.168.1.0/24"] } # Создание группы безопасности resource "yandex_vpc_security_group" "webserver-sg" { name = local.sg_name network_id = "${yandex_vpc_network.webserver-network.id}" ingress { protocol = "TCP" description = "http" v4_cidr_blocks = ["0.0.0.0/0"] port = 80 } ingress { protocol = "TCP" description = "https" v4_cidr_blocks = ["0.0.0.0/0"] port = 443 } ingress { protocol = "TCP" description = "ssh" v4_cidr_blocks = ["0.0.0.0/0"] port = 22 } egress { protocol = "ANY" description = "any" v4_cidr_blocks = ["0.0.0.0/0"] from_port = 0 to_port = 65535 } } # Создание загрузочного диска для ВМ resource "yandex_compute_disk" "boot-disk" { type = "network-ssd" zone = "ru-central1-b" size = "20" image_id = "fd8jtn9i7e9ha5q25niu" } # Создание ВМ resource "yandex_compute_instance" "mywebserver" { name = local.vm_name platform_id = "standard-v2" zone = "ru-central1-b" resources { cores = "2" memory = "2" } boot_disk { disk_id = yandex_compute_disk.boot-disk.id } network_interface { subnet_id = "${yandex_vpc_subnet.webserver-subnet-b.id}" nat = true security_group_ids = ["${yandex_vpc_security_group.webserver-sg.id}"] } metadata = { user-data = "#cloud-config\nusers:\n - name: yc-user\n groups: sudo\n shell: /bin/bash\n sudo: 'ALL=(ALL) NOPASSWD:ALL'\n ssh-authorized-keys:\n - ${file("${var.ssh_key_path}")}" } } # Создание публичной DNS-зоны resource "yandex_dns_zone" "my-domain-zone" { name = local.domain_zone_name zone = "${var.domain_name}." public = true } # Создание ресурсной A-записи для веб-сервера resource "yandex_dns_recordset" "rsА1" { zone_id = yandex_dns_zone.my-domain-zone.id name = "${yandex_dns_zone.my-domain-zone.zone}" type = "A" ttl = 600 data = ["${yandex_compute_instance.mywebserver.network_interface.0.nat_ip_address}"] } # Добавление сертификата Let's Encrypt resource "yandex_cm_certificate" "le-certificate" { name = local.cert_name domains = [var.domain_name,"${var.subdomain_name}.${var.domain_name}"] managed { challenge_type = "DNS_CNAME" challenge_count = 2 } } # Создание CNAME-записей для валидации доменов при выпуске сертификата resource "yandex_dns_recordset" "validation-record" { count = yandex_cm_certificate.le-certificate.managed[0].challenge_count zone_id = yandex_dns_zone.my-domain-zone.id name = yandex_cm_certificate.le-certificate.challenges[count.index].dns_name type = yandex_cm_certificate.le-certificate.challenges[count.index].dns_type ttl = 600 data = [yandex_cm_certificate.le-certificate.challenges[count.index].dns_value] } # Ожидание валидации доменов и выпуска сертификата Let's Encrypt data "yandex_cm_certificate" "example-com" { depends_on = [yandex_dns_recordset.validation-record] certificate_id = yandex_cm_certificate.le-certificate.id wait_validation = true } # Создание бакета resource "yandex_storage_bucket" "cdn-source" { access_key = yandex_iam_service_account_static_access_key.sa-static-key.access_key secret_key = yandex_iam_service_account_static_access_key.sa-static-key.secret_key bucket = var.bucket_name max_size = 1073741824 anonymous_access_flags { read = true list = true } website { index_document = "index.html" } } # Загрузка в бакет главной страницы веб-сайта resource "yandex_storage_object" "index-object" { access_key = yandex_iam_service_account_static_access_key.sa-static-key.access_key secret_key = yandex_iam_service_account_static_access_key.sa-static-key.secret_key bucket = "${yandex_storage_bucket.cdn-source.bucket}" key = var.index_file_path source = var.index_file_path } # Загрузка в бакет файла с тестовым контентом resource "yandex_storage_object" "content-object" { access_key = yandex_iam_service_account_static_access_key.sa-static-key.access_key secret_key = yandex_iam_service_account_static_access_key.sa-static-key.secret_key bucket = "${yandex_storage_bucket.cdn-source.bucket}" key = var.content_file_path source = var.content_file_path } # Создание группы источников CDN resource "yandex_cdn_origin_group" "my-origin-group" { name = local.origin_gp_name origin { source = "${var.bucket_name}.website.yandexcloud.net" } } # Создание CDN-ресурса resource "yandex_cdn_resource" "my-resource" { cname = "${var.subdomain_name}.${var.domain_name}" active = true origin_protocol = "match" origin_group_id = "${yandex_cdn_origin_group.my-origin-group.id}" ssl_certificate { type = "certificate_manager" certificate_manager_id = "${data.yandex_cm_certificate.example-com.id}" } options { custom_host_header = "${var.bucket_name}.website.yandexcloud.net" secure_key = "${var.secure_key}" enable_ip_url_signing = true } } # Создание CNAME-записи для CDN-ресурса resource "yandex_dns_recordset" "cdn-cname" { zone_id = yandex_dns_zone.my-domain-zone.id name = "${yandex_cdn_resource.my-resource.cname}." type = "CNAME" ttl = 600 data = [var.cdn_cname] }
-
Файл c пользовательскими данными
yc-cdn-secure-token.auto.tfvars
:yc-cdn-secure-token.auto.tfvars
folder_id = "<идентификатор_каталога>" ssh_key_path = "<путь_к_файлу_с_публичным_SSH-ключом>" index_file_path = "<имя_файла_с_главной_страницей_сайта>" content_file_path = "<имя_файла_с_контентом_для_загрузки_в_бакет>" domain_name = "<имя_домена>" subdomain_name = "<префикс_поддомена_для_CDN-ресурса>" bucket_name = "<имя_бакета>" cdn_cname = "<значение_доменного_имени_CDN_провайдера>" secure_key = "<секретный_ключ>"
-
Более подробную информацию о параметрах используемых ресурсов в Terraform см. в документации провайдера:
- Сервисный аккаунт — yandex_iam_service_account
. - Роль сервисному аккаунту — yandex_resourcemanager_folder_iam_member
. - Статический ключ доступа — yandex_iam_service_account_static_access_key
. - Сеть — yandex_vpc_network
. - Подсеть — yandex_vpc_subnet
. - Группа безопасности — yandex_vpc_security_group
. - Диск виртуальной машины — yandex_compute_disk
. - Виртуальная машина — yandex_compute_instance
. - DNS-зона — yandex_dns_zone
. - Ресурсная запись DNS — yandex_dns_recordset
. - TLS-Сертификат — yandex_cm_certificate
. - Бакет — yandex_storage_bucket
. - Объект — yandex_storage_object
. - Группа источников — yandex_cdn_origin_group
. - CDN-ресурс — yandex_cdn_resource
.
-
-
В файле
yc-cdn-secure-token.auto.tfvars
задайте пользовательские параметры:folder_id
— идентификатор каталога.ssh_key_path
— путь к файлу с открытым SSH-ключом для аутентификации пользователя на ВМ. Подробнее см. Создание пары ключей SSH.index_file_path
— путь к файлу с главной страницей сайта.content_file_path
— путь к файлу с контентом для загрузки в бакет.domain_name
— имя вашего домена, напримерexample.com
.subdomain_name
— префикс поддомена для CDN-ресурса, напримерcdn
.bucket_name
— имя бакета в соответствии с правилами именования.cdn_cname
— значение доменного имени Cloud CDN провайдера для CDN-ресурсов каталога.secure_key
— секретный ключ. Произвольная строка длиной от 6 до 32 символов. Необходим, чтобы ограничить доступ к ресурсу с помощью защищенных токенов.
-
Создайте ресурсы:
-
В терминале перейдите в папку, где вы отредактировали конфигурационный файл.
-
Проверьте корректность конфигурационного файла с помощью команды:
terraform validate
Если конфигурация является корректной, появится сообщение:
Success! The configuration is valid.
-
Выполните команду:
terraform plan
В терминале будет выведен список ресурсов с параметрами. На этом этапе изменения не будут внесены. Если в конфигурации есть ошибки, Terraform на них укажет.
-
Примените изменения конфигурации:
terraform apply
-
Подтвердите изменения: введите в терминале слово
yes
и нажмите Enter.
-
Чтобы новые настройки существующего ресурса применились к CDN-серверам, может потребоваться до 15 минут. После этого рекомендуется очистить кеш ресурса.
Контент на созданном CDN-ресурсе будет доступен только по подписанным ссылкам.
Опубликуйте сайт на веб-сервере
Далее вы создадите и опубликуете на вашем веб-сервере сайт, который будет генерировать подписанные ссылки на контент, расположенный на защищенном CDN-ресурсе. Чтобы обеспечить безопасность передаваемых данных, вы скопируете на веб-сервер созданный ранее TLS-сертификат и включите SSL-шифрование.
Выгрузите сертификат из Certificate Manager
Чтобы использовать созданный в Certificate Manager TLS-сертификат в конфигурации вашего веб-сервера, выгрузите цепочку сертификатов и закрытый ключ в текущую директорию:
-
Узнайте идентификатор созданного ранее TLS-сертификата:
yc certificate-manager certificate list
Результат:
+----------------------+---------------+-----------------------------+---------------------+---------+--------+ | ID | NAME | DOMAINS | NOT AFTER | TYPE | STATUS | +----------------------+---------------+-----------------------------+---------------------+---------+--------+ | fpq90lobsh0l******** | mymanagedcert | cdn.example.com,example.com | 2024-03-22 16:42:53 | MANAGED | ISSUED | +----------------------+---------------+-----------------------------+---------------------+---------+--------+
Подробнее о команде
yc certificate-manager certificate list
читайте в справочнике CLI. -
Выгрузите ключ и сертификат, указав полученный на предыдущем шаге идентификатор:
yc certificate-manager certificate content \ --id <идентификатор_сертификата> \ --chain ./certificate_full_chain.pem \ --key ./private_key.pem
Подробнее о команде
yc certificate-manager certificate content
читайте в справочнике CLI.
Настройте веб-сервер
-
Скопируйте на ВМ с веб-сервером полученные сертификаты и закрытый ключ:
scp ./certificate_full_chain.pem yc-user@<IP-адрес_ВМ>:certificate_full_chain.pem \ && scp ./private_key.pem yc-user@<IP-адрес_ВМ>:private_key.pem
Где
<IP-адрес_ВМ>
— публичный IP-адрес созданной ранее ВМ с веб-сервером.Узнать IP-адрес ВМ можно в консоли управления
на странице ВМ в блоке Сеть или с помощью команды CLIyc compute instance get mywebserver
.При первом подключении к ВМ появится предупреждение о неизвестном хосте:
The authenticity of host '51.250.**.*** (51.250.**.***)' can't be established. ED25519 key fingerprint is SHA256:PpcKdcT09gjU045pkEIwIU8lAXXLpwJ6bKC********. This key is not known by any other names Are you sure you want to continue connecting (yes/no/[fingerprint])?
Введите в терминале слово
yes
и нажмите Enter. -
Подключитесь к ВМ с веб-сервером:
ssh yc-user@<IP-адрес_ВМ>
-
Создайте директорию для сертификата и перенесите в нее скопированные файлы:
sudo mkdir /etc/ssl-certificates sudo mv certificate_full_chain.pem /etc/ssl-certificates/ sudo mv private_key.pem /etc/ssl-certificates/
-
Создайте директорию для файлов вашего сайта и предоставьте необходимые права на нее пользователю
www-data
:sudo mkdir -p /var/www/<имя_домена>/public_html sudo chown www-data:www-data /var/www/<имя_домена>/public_html
Где
<имя_домена>
— доменное имя вашего сайта, напримерexample.com
. -
Настройте виртуальный хост для вашего сайта:
-
Создайте файл конфигурации виртуального хоста:
sudo nano /etc/apache2/sites-available/mywebsite.conf
-
Поместите в созданный файл следующую конфигурацию:
<VirtualHost *:443> ServerName <имя_домена> ServerAdmin webmaster@localhost DocumentRoot /var/www/<имя_домена>/public_html ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined SSLEngine on SSLCertificateFile /etc/ssl-certificates/certificate_full_chain.pem SSLCertificateChainFile /etc/ssl-certificates/certificate_full_chain.pem SSLCertificateKeyFile /etc/ssl-certificates/private_key.pem </VirtualHost>
Где
<имя_домена>
— доменное имя вашего сайта, напримерexample.com
. -
Активируйте созданный виртуальный хост:
sudo a2ensite mywebsite
Результат:
Enabling site mywebsite. To activate the new configuration, you need to run: systemctl reload apache2
-
Включите для веб-сервера поддержку
ssl
:sudo a2enmod ssl
Результат:
Considering dependency setenvif for ssl: Module setenvif already enabled Considering dependency mime for ssl: Module mime already enabled Considering dependency socache_shmcb for ssl: Enabling module socache_shmcb. Enabling module ssl. See /usr/share/doc/apache2/README.Debian.gz on how to configure SSL and create self-signed certificates. To activate the new configuration, you need to run: systemctl restart apache2
-
Перезапустите веб-сервер:
sudo systemctl reload apache2
-
Создайте сайт
-
Создайте файл главной страницы сайта:
sudo nano /var/www/<имя_домена>/public_html/index.php
Где
<имя_домена>
— доменное имя вашего сайта, напримерexample.com
. -
Поместите в созданный файл
index.php
следующий код:<!DOCTYPE html> <html> <head> <title>Secure token generator website</title> <meta charset="utf-8" /> </head> <body> <h2>Генератор защищенных ссылок</h2> <p>Ниже сгенерирована подписанная ссылка на защищенный CDN-ресурс. Срок жизни ссылки — 5 минут. Контент по ссылке доступен лишь тому пользователю, для которого сайт эту ссылку сгенерировал (проверяется по IP-адресу).</p> <br> <?php $secret = '<секретный_ключ>'; $ip = trim(getUserIpAddr()); $domain_name = '<доменное_имя>'; $path = '<ключ_объекта>'; $expires = time() + 300; $link = "$expires$path$ip $secret"; $md5 = md5($link, true); $md5 = base64_encode($md5); $md5 = strtr($md5, '+/', '-_'); $md5 = str_replace('=', '', $md5); $url = '<a href="https://'.$domain_name.$path.'?md5='.$md5.'&expires='.$expires.'" target="_blank">Подписанная ссылка на файл</a>'; echo "<p>Ваш IP-адрес: <b>".$ip."</b></p><p>Если вы используете VPN, ссылка может не сработать. Для корректной работы генератора подписанных ссылок отключите VPN.</p>"; echo "<br><br>"; echo $url; function getUserIpAddr() { if (!empty($_SERVER['HTTP_CLIENT_IP'])) { $addr = $_SERVER['HTTP_CLIENT_IP']; } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $addr = $_SERVER['HTTP_X_FORWARDED_FOR']; } else { $addr = $_SERVER['REMOTE_ADDR']; } return $addr; } ?> </body> </html>
Где:
$secret
— созданный при настройке CDN-ресурса секретный ключ.$domain_name
— доменное имя созданного CDN-ресурса. Например:cdn.example.com
.$path
— ключ объекта в бакете-источнике, например/content.jpg
. Обязательно со знаком/
.
Сайт будет генерировать подписанную ссылку для доступа к этому объекту через CDN-ресурс.
Проверьте работу защищенного доступа к файлам
Чтобы проверить работу генератора подписанных ссылок на защищенный CDN-ресурс:
-
Откройте в браузере созданный сайт, например
https://example.com
. -
Пройдите по сгенерированной ссылке.
Если все работает правильно, у вас должно открыться изображение на защищенном CDN-ресурсе.
Примечание
Подключенный VPN может мешать правильной работе генератора подписанных ссылок. Для корректной работы сайта отключите VPN.
-
Откройте полученную ссылку на другом устройстве, выходящем в интернет с другого IP-адреса. Например, на смартфоне.
Доступ к контенту окажется запрещен.
-
Попробуйте открыть полученную ссылку на исходном устройстве по истечении пяти минут.
Доступ к контенту окажется запрещен.
Вы настроили защищенный доступ к контенту.
При формировании ссылок также можно задать определенный доверенный IP-адрес, например IP-адрес компании, который используется в корпоративной сети для доступа к интернету. Так вы сможете запретить доступ к вашему контенту за пределами сетевой инфраструктуры вашей компании.
Как удалить созданные ресурсы
Чтобы перестать платить за созданные ресурсы:
-
Откройте конфигурационный файл
yc-cdn-secure-token.tf
и удалите описание создаваемой инфраструктуры из файла. -
Примените изменения:
-
В терминале перейдите в папку, где вы отредактировали конфигурационный файл.
-
Проверьте корректность конфигурационного файла с помощью команды:
terraform validate
Если конфигурация является корректной, появится сообщение:
Success! The configuration is valid.
-
Выполните команду:
terraform plan
В терминале будет выведен список ресурсов с параметрами. На этом этапе изменения не будут внесены. Если в конфигурации есть ошибки, Terraform на них укажет.
-
Примените изменения конфигурации:
terraform apply
-
Подтвердите изменения: введите в терминале слово
yes
и нажмите Enter.
-