Yandex Cloud
Search
Contact UsGet started
  • Blog
  • Pricing
  • Documentation
  • All Services
  • System Status
    • Featured
    • Infrastructure & Network
    • Data Platform
    • Containers
    • Developer tools
    • Serverless
    • Security
    • Monitoring & Resources
    • ML & AI
    • Business tools
  • All Solutions
    • By industry
    • By use case
    • Economics and Pricing
    • Security
    • Technical Support
    • Customer Stories
    • Gateway to Russia
    • Cloud for Startups
    • Education and Science
  • Blog
  • Pricing
  • Documentation
Yandex project
© 2025 Yandex.Cloud LLC
Yandex Cloud CDN
  • Getting started
    • All tutorials
    • Publishing game updates
    • Editing website images using Thumbor
    • Integrating an L7 load balancer with CDN and Object Storage
    • Blue-green and canary deployment of service versions
      • Overview
      • Management console, CLI, and API
      • Terraform
    • Migrating to Yandex Cloud CDN from a third-party CDN provider
  • Access management
  • Pricing policy
  • Terraform reference
  • Monitoring metrics
  • Audit Trails events
  • Release notes
  • Troubleshooting

In this article:

  • Prepare your cloud
  • Required paid resources
  • Delegate your domain to Cloud DNS
  • Create an infrastructure
  • Publish the webiste on the web server
  • Download the certificate from Certificate Manager
  • Configure the web server
  • Create a website
  • Test secure access to files
  • How to delete the resources you created
  1. Tutorials
  2. Providing secure access to content
  3. Terraform

Providing secure access to content in Cloud CDN through Terraform

Written by
Yandex Cloud
Updated at May 7, 2025
  • Prepare your cloud
    • Required paid resources
  • Delegate your domain to Cloud DNS
  • Create an infrastructure
  • Publish the webiste on the web server
    • Download the certificate from Certificate Manager
    • Configure the web server
    • Create a website
  • Test secure access to files
  • How to delete the resources you created

To configure secure access to content in Cloud CDN:

  1. Prepare your cloud.
  2. Delegate your domain to Cloud DNS.
  3. Create an infrastructure.
  4. Publish the webiste on the web server.
  5. Test secure access to files.

If you no longer need the resources you created, delete them.

Prepare your cloud

Sign up in Yandex Cloud and create a billing account:

  1. Navigate to the management console and log in to Yandex Cloud or register a new account.
  2. On the Yandex Cloud Billing page, make sure you have a billing account linked and it has the ACTIVE or TRIAL_ACTIVE status. If you do not have a billing account, create one and link a cloud to it.

If you have an active billing account, you can navigate to the cloud page to create or select a folder for your infrastructure to operate in.

Learn more about clouds and folders.

Required paid resources

The cost of infrastructure support for setting up secure access to Cloud CDN content includes:

  • Fee for using a public IP address (see Yandex Virtual Private Cloud pricing).
  • Fee for VM computing resources and disks (see Yandex Compute Cloud pricing).
  • Fee for using a public DNS zone and public DNS requests (see Yandex Cloud DNS pricing).
  • Fee for data storage in Object Storage, operations with data, and outgoing traffic (see Object Storage pricing).
  • Fee for outgoing traffic from CDN servers (see Cloud CDN pricing).

Delegate your domain to Cloud DNS

Delegate your domain to Cloud DNS. To do this, in your domain registrar's account, specify the addresses of these DNS servers in your domain settings: ns1.yandexcloud.net and ns2.yandexcloud.net.

Create an infrastructure

With Terraform, you can quickly create a cloud infrastructure in Yandex Cloud and manage it using configuration files. These files store the infrastructure description written in HashiCorp Configuration Language (HCL). If you change the configuration files, Terraform automatically detects which part of your configuration is already deployed, and what should be added or removed.

Terraform is distributed under the Business Source License. The Yandex Cloud provider for Terraform is distributed under the MPL-2.0 license.

For more information about the provider resources, see the documentation on the Terraform website or mirror website.

To create an infrastructure using Terraform:

  1. Install Terraform, get the authentication credentials, and specify the source for installing the Yandex Cloud provider (see Configure a provider, step 1).

  2. Prepare files with the infrastructure description:

    Ready-made configuration
    Manually
    1. Clone the repository with configuration files.

      git clone https://github.com/yandex-cloud-examples/yc-cdn-protected-access
      
    2. Go to the directory with the repository. Make sure it contains the following files:

      • yc-cdn-secure-token.tf: New infrastructure configuration.
      • yc-cdn-secure-token.auto.tfvars: User data file.
    1. Prepare files for uploading to the bucket:

      1. Save any image in JPEG format to the content.jpg file.

      2. Create a file named index.html:

        <html>
            <body>
            </body>
        </html>
        
    2. Create a folder for configuration files.

    3. In the folder, create:

      1. yc-cdn-secure-token.tf configuration file

        yc-cdn-secure-token.tf
        # Declaring variables
        
        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"
        }
        
        # Configuring a provider 
        
        terraform {
          required_providers {
            yandex = {
              source = "yandex-cloud/yandex"
            }
          }
          required_version = ">= 0.13"
        }
        
        provider "yandex" {
          folder_id = var.folder_id
        }
        
        # Creating a service account
        
        resource "yandex_iam_service_account" "ig-sa" {
          name = local.sa_name
        }
        
        # Assigning roles to a service account
        
        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}"
        }
        
        # Creating a static access key for CA
        
        resource "yandex_iam_service_account_static_access_key" "sa-static-key" {
          service_account_id = "${yandex_iam_service_account.ig-sa.id}"
        }
        
        # Create network
        
        resource "yandex_vpc_network" "webserver-network" {
          name = local.network_name
        }
        
        # Create subnet
        
        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"]
        }
        
        # Creating a security group
        
        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
          }
        }
        
        # Creating a boot disk for the VM
        
        resource "yandex_compute_disk" "boot-disk" {
          type     = "network-ssd"
          zone     = "ru-central1-b"
          size     = "20"
          image_id = "fd8jtn9i7e9ha5q25niu"
        }
        
        # Creating a VM instance
        
        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}")}"
          }
        }
        
        # Creating a public DNS zone
        
        resource "yandex_dns_zone" "my-domain-zone" {
          name    = local.domain_zone_name
          zone    = "${var.domain_name}."
          public  = true
        }
        
        # Creating a type A resource record for the web server
        
        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}"]
        }
        
        # Adding a Let's Encrypt certificate
        
        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
          }
        }
        
        # Creating CNAME records for domain validation when issuing a certificate
        
        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]
        }
        
        # Waiting for domain validation and the issue of a Let's Encrypt certificate
        
        data "yandex_cm_certificate" "example-com" {
          depends_on      = [yandex_dns_recordset.validation-record]
          certificate_id  = yandex_cm_certificate.le-certificate.id
          wait_validation = true
        }
        
        # Creating a bucket
        
        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"
          }
        }
        
        # Uploading the website home page to the bucket
        
        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
        }
        
        # Uploading a test file to the bucket
        
        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
        }
        
        # Creating a CDN origin group
        
        resource "yandex_cdn_origin_group" "my-origin-group" {
          name = local.origin_gp_name
          origin {
            source = "${var.bucket_name}.website.yandexcloud.net"
          }
        }
        
        # Creating a CDN resource
        
        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
          }
        }
        
        # Creating a CNAME record for the CDN resource
        
        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]
        }
        
      2. The yc-cdn-secure-token.auto.tfvars user data file:

        yc-cdn-secure-token.auto.tfvars
        folder_id         = "<folder_ID>"
        ssh_key_path      = "<path_to_public_SSH_key_file>"
        index_file_path   = "<name_of_website_homepage_file>"
        content_file_path = "<name_of_file_with_content_to_upload_to_bucket>"
        domain_name       = "<domain_name>"
        subdomain_name    = "<CDN_resource_subdomain_prefix>"
        bucket_name       = "<bucket_name>"
        cdn_cname         = "<CDN_provider_domain_name_value>"
        secure_key        = "<secret_key>"
        

    For more information about the parameters of resources used in Terraform, see the provider documentation:

    • Service account: yandex_iam_service_account.
    • Service account role: yandex_resourcemanager_folder_iam_member.
    • Static access key: yandex_iam_service_account_static_access_key.
    • Network: yandex_vpc_network.
    • Subnets: yandex_vpc_subnet.
    • Security group: yandex_vpc_security_group.
    • VM disk: yandex_compute_disk.
    • VM instance: yandex_compute_instance.
    • DNS zone: yandex_dns_zone.
    • DNS resource record: yandex_dns_recordset.
    • TLS certificate: yandex_cm_certificate.
    • Bucket: yandex_storage_bucket.
    • Object: yandex_storage_object.
    • Origin group: yandex_cdn_origin_group.
    • CDN resource: yandex_cdn_resource.
  3. In the yc-cdn-secure-token.auto.tfvars file, set the following user-defined properties:

    • folder_id: Folder ID.
    • ssh_key_path: Path to the file with a public SSH key to authenticate the user on the VM. For more information, see Creating an SSH key pair.
    • index_file_path: Path to the website homepage file.
    • content_file_path: Path to the file with content for upload to the bucket.
    • domain_name: Your domain name, e.g., example.com.
    • subdomain_name: Prefix of subdomain for the CDN resource, e.g., cdn.
    • bucket_name: Bucket name consistent with the naming conventions.
    • cdn_cname: Domain name of the Cloud CDN provider for the folder's CDN resources.
    • secure_key: Secret key that is a string of 6 to 32 characters. It is required to restrict access to a resource using secure tokens.
  4. Create resources:

    1. In the terminal, change to the folder where you edited the configuration file.

    2. Make sure the configuration file is correct using the command:

      terraform validate
      

      If the configuration is correct, the following message is returned:

      Success! The configuration is valid.
      
    3. Run the command:

      terraform plan
      

      The terminal will display a list of resources with parameters. No changes are made at this step. If the configuration contains errors, Terraform will point them out.

    4. Apply the configuration changes:

      terraform apply
      
    5. Confirm the changes: type yes in the terminal and press Enter.

It may take up to 15 minutes for the new settings of the existing resource to apply to CDN servers. After that, we recommend purging the resource cache.

The content on the new CDN resource will be accessible only via signed links.

Publish the webiste on the web server

Next, you will create and publish on your web server a website that will generate signed links to content hosted on the secure CDN resource. For data transfer security, you will copy the previously created TLS certificate to the same web server and enable SSL encryption.

Download the certificate from Certificate Manager

To use the TLS certificate created in Certificate Manager in your web server configuration, download the certificate chain and private key to the current directory:

Yandex Cloud CLI
  1. Learn the ID of the previously created TLS certificate:

    yc certificate-manager certificate list
    

    Result:

    +----------------------+---------------+-----------------------------+---------------------+---------+--------+
    |          ID          |     NAME      |           DOMAINS           |      NOT AFTER      |  TYPE   | STATUS |
    +----------------------+---------------+-----------------------------+---------------------+---------+--------+
    | fpq90lobsh0l******** | mymanagedcert | cdn.example.com,example.com | 2024-03-22 16:42:53 | MANAGED | ISSUED |
    +----------------------+---------------+-----------------------------+---------------------+---------+--------+
    

    For more information about the yc certificate-manager certificate list command, see the CLI reference.

  2. Download the key and certificate by specifying the ID you got in the previous step:

    yc certificate-manager certificate content \
      --id <certificate_ID> \
      --chain ./certificate_full_chain.pem \
      --key ./private_key.pem
    

    For more information about the yc certificate-manager certificate content command, see the CLI reference.

Configure the web server

  1. Copy the certificates and private key thus obtained to the VM hosting the web server:

    scp ./certificate_full_chain.pem yc-user@<VM_IP_address>:certificate_full_chain.pem \
      && scp ./private_key.pem yc-user@<VM_IP_address>:private_key.pem
    

    Where <VM_IP_address> is the public IP address of the previously created VM with a web server.

    You can find the IP address of your VM in the management console on the VM page under Network or using the yc compute instance get mywebserver CLI command.

    If this is your first time connecting to the VM, you will see an unknown host warning:

    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])?
    

    Type yes in the terminal and press Enter.

  2. Connect to the VM with the web server:

    ssh yc-user@<VM_IP_address>
    
  3. Create a directory for the certificate and move the copied files there:

    sudo mkdir /etc/ssl-certificates
    sudo mv certificate_full_chain.pem /etc/ssl-certificates/
    sudo mv private_key.pem /etc/ssl-certificates/
    
  4. Create a directory for your website files and grant the required permissions for it to the www-data user:

    sudo mkdir -p /var/www/<domain_name>/public_html
    sudo chown www-data:www-data /var/www/<domain_name>/public_html
    

    Where <domain_name> is the domain name of your website, e.g., example.com.

  5. Configure a virtual host for your website:

    1. Create a virtual host configuration file:

      sudo nano /etc/apache2/sites-available/mywebsite.conf
      
    2. Add the following configuration into the file:

      <VirtualHost *:443>
      ServerName <domain_name>
      ServerAdmin webmaster@localhost
      DocumentRoot /var/www/<domain_name>/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>
      

      Where <domain_name> is the domain name of your website, e.g., example.com.

    3. Activate the virtual host you created:

      sudo a2ensite mywebsite
      

      Result:

      Enabling site mywebsite.
      To activate the new configuration, you need to run:
        systemctl reload apache2
      
    4. Enable ssl for the web server:

      sudo a2enmod ssl
      

      Result:

      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
      
    5. Restart the web server:

      sudo systemctl reload apache2
      

Create a website

  1. Create the home page file for the website:

    sudo nano /var/www/<domain_name>/public_html/index.php
    

    Where <domain_name> is the domain name of your website, e.g., example.com.

  2. Add the following code into the index.php file you created:

    <!DOCTYPE html>
    <html>
    <head>
      <title>Secure token generator website</title>
      <meta charset="utf-8" />
    </head>
    <body>
    
      <h2>Secure link generator</h2>
      <p>Below, a signed link to the secure CDN resource has been generated. The link is valid for five minutes. The content at this link is available only to the user the link was generated for by the website (verified by IP address).</p>
      <br>
    
      <?php
    
        $secret = '<secret_key>';
        $ip = trim(getUserIpAddr());
        $domain_name = '<domain_name>';
        $path = '<object_key>';
        $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">Signed link to file</a>';
    
        echo "<p>Your IP address: <b>".$ip."</b></p><p>If you are using a VPN, you link may not work. For the signed link generator to work properly, disable your 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>
    

    Where:

    • $secret: Secret key created when configuring the CDN resource.
    • $domain_name: Domain name of the created CDN resource, e.g., cdn.example.com.
    • $path: Key of the object in the source bucket, e.g., /content.jpg. It must contain /.
      The website will generate a signed link to access this object via the CDN resource.

Test secure access to files

To test the generator of signed links to the secure CDN resource:

  1. In your browser, go to the website you created, e.g., https://example.com.

  2. Click the link that was generated.

    If everything works as it should, you will see the image hosted on the secure CDN resource.

    Note

    An active VPN may interfere with the signed link generator's operation. For the website to work correctly, disable your VPN.

  3. Open the generated link on another device that uses another IP address to access the internet, e.g., a smartphone.

    Access to content will be denied.

  4. Try opening the link on the first device after the five-minute timeout expires.

    Access to content will be denied.

You have configured secure access to your content.

When generating links, you can also specify a trusted IP address, e.g., the one used for internet access in your corporate network. Thus you will restrict access to your content from outside your company’s network infrastructure.

How to delete the resources you created

To stop paying for the resources you created:

  1. Open the yc-cdn-secure-token.tf configuration file and delete the description of the new infrastructure from it.

  2. Apply the changes:

    1. In the terminal, change to the folder where you edited the configuration file.

    2. Make sure the configuration file is correct using the command:

      terraform validate
      

      If the configuration is correct, the following message is returned:

      Success! The configuration is valid.
      
    3. Run the command:

      terraform plan
      

      The terminal will display a list of resources with parameters. No changes are made at this step. If the configuration contains errors, Terraform will point them out.

    4. Apply the configuration changes:

      terraform apply
      
    5. Confirm the changes: type yes in the terminal and press Enter.

See also

  • Providing secure access to content in Cloud CDN using the management console, CLI, or API

Was the article helpful?

Previous
Management console, CLI, and API
Next
Migrating to Yandex Cloud CDN from a third-party CDN provider
Yandex project
© 2025 Yandex.Cloud LLC