Use Cloud-Init to Automatically Configure and Secure Your Servers

Traducciones al Español
Estamos traduciendo nuestros guías y tutoriales al Español. Es posible que usted esté viendo una traducción generada automáticamente. Estamos trabajando con traductores profesionales para verificar las traducciones de nuestro sitio web. Este proyecto es un trabajo en curso.
Create a Linode account to try this guide with a $ credit.
This credit will be applied to any valid services used during your first  days.

By using Akamai’s Metadata service, you can automatically configure your new Compute Instances through cloud-init. This guide walks you through building a cloud-config file (for use with cloud-init) and deploying a Compute Instance using that configuration. It covers a series of recommended options for initializing and securing a Compute Instance. These configurations parallel the steps in our guide on Setting Up and Securing a Compute Instance. Toward the end of the guide, you can see the Complete Cloud-Config File as well as steps for how to Deploy an Instance with User Data.

What is cloud-init?

Cloud-init is an industry standard method for automating cloud instance initialization, with support across distributions and platforms. Cloud-init manages initialization using a combination of instance metadata and configuration scripts (user data) to automate the process of setting up a new server.

Akamai’s Metadata service provides an API for cloud-init to consume, offering the relevant instance and user data to initialize your server. When your new instance spins up, cloud-init starts running locally, accesses the metadata, and automatically configures your system accordingly.

Metadata Availability
Akamai’s Metadata service is now available in select data centers. Use Metadata to automate system configuration by adding directives or scripts when deploying Compute Instances. This user data can then be consumed by cloud-init, an industry standard system initialization tool, or accessed directly using the Metadata API. For instructions on using the Metadata service and for a list of supported regions and distributions, reference our documentation.

Create a Cloud-Config File

Cloud-init consumes instance metadata from Akamai’s Metadata server. This gives cloud-init relevant information about the Compute Instance. From there, cloud-init derives specific initialization steps from supplied user data, which comes in the form of cloud-config scripts.

Cloud-config scripts use declarative YAML formatting to define configuration and other steps cloud-init needs to initialize the new instance. Learn more about cloud-config scripts in our guide on Using Cloud-Config Files to Configure a Server.

When creating an Akamai Compute Instance, you can add the cloud-config via the Cloud Manager, Linode CLI, or Linode API when deploying the instance. Refer to our Metadata guide for details on when and how to add the cloud-config user data.

To start, create an initial cloud-config file to design your desired server initialization. The examples that follow name the file cloud-config.yaml. All cloud-config files begin with the following line:

File: cloud-config.yaml
1
#cloud-config

From there, you need to fill out the cloud-config with specific options matching your needs for the server. Follow the steps within this guide to build your file or skip to end of the guide to see the a completed Cloud-Config file.

Update Your System

Cloud-config includes a module for handling system updates using two keys: package_update and package_upgrade. Including both of these with values of true ensures that package updates and upgrades are run as part of the initialization.

File: cloud-config.yaml
1
2
package_update: true
package_upgrade: true

The section on how to Install Any Additional Required Software provides further resources for working with packages in cloud-config.

Set the Hostname and Timezone

Cloud-config has a range of options for setting up server details. This section applies two of those options, providing your server with recommended details by setting up the timezone and hostname.

The timezone key provides a straightforward method for setting your server’s timezone. It takes any valid timezone for your system as an argument. Typically, you can find these using timedatectl list-timezones or by navigating the paths in /usr/share/zoneinfo.

File: cloud-config.yaml
1
timezone: 'US/Central'

The hostname key conveniently assigns a hostname for your instance. In this example, the cloud-init initializes the server with the hostname examplehost:

File: cloud-config.yaml
1
hostname: examplehost
Note

Cloud-config’s hostname key does not modify the /etc/hosts file by default. That lets you take control over the server’s hostname by the usual route of modifying the /etc/hosts file after initialization.

You can, alternatively, have cloud-init fully manage the /etc/hosts file by setting the manage_etc_hosts key to true. On each boot, cloud-init ensures that the contents of the /etc/hosts matches the contents of /etc/cloud/templates/hosts.tmpl.

Learn more in cloud-init’s module reference for Update Etc Hosts.

Add a Limited User Account

Your instance should have at least one limited user account to prevent remote root access and the associated security risks. Cloud-config’s users key can define one or more new users for your system, including features like sudo access.

For example, below is the typical configuration for initializing a limited user with sudo access. The setup also includes the default user, which is recommended.

File: cloud-config.yaml
1
2
3
4
5
6
7
8
users:
  - default
  - name: example-user
    groups:
      - sudo
    sudo:
      - ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash

However, the example is incomplete, since the user does not have a password or SSH key. You can create a password using the passwd option, but this is not recommended. Instead, you should set up an SSH key for the user, as shown in the next section.

For more on creating and managing users with cloud-init, refer to our guide Use Cloud-Init to Manage Users on New Servers. The guide includes more information on setting up user passwords, should you need to.

Add an SSH Key to Your Limited User Account

Rather than using a password for access, the more secure approach is setting up your limited user, or users, with SSH key authentication. If you do not yet have an SSH key pair, get one by following the relevant section of our guide on how to Use SSH Public Key Authentication.

Once you have an SSH key pair, you can add an SSH public key to a user defined in the cloud-config with the ssh_authorized_keys option. The option accepts a list of SSH public keys to authorize for access to this user.

File: cloud-config.yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
users:
  - default
  - name: example-user
    groups:
      - sudo
    sudo:
      - ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    ssh_authorized_keys:
      - <SSH_PUBLIC_KEY>

Harden SSH

To increase the security of SSH connections into your Compute Instance, you should generally disable password authentication and root logins via SSH. This way, access is restricted to limited users and connections authenticated by SSH key pairs.

By default, the cloud-config users setup assumes lock_passwd: true, automatically disabling password authentication. You can learn more about user setup and managing such features in our guide Use Cloud-Init to Manage Users on New Servers.

To disable root logins, you need to modify the SSH configuration file. Cloud-config does not have a direct option for this, but you can use its versatile runcmd key to automate the necessary commands. Learn more about the runcmd option in our guide Use Cloud-Init to Run Commands and Bash Scripts on First Boot.

The example below removes any existing PermitRootLogin configuration and adds a new configuration disabling PermitRootLogin. The last command restarts the sshd service for the changes to take effect.

File: cloud-config.yaml
1
2
3
4
runcmd:
  - sed -i '/PermitRootLogin/d' /etc/ssh/sshd_config
  - echo "PermitRootLogin no" >> /etc/ssh/sshd_config
  - systemctl restart sshd
Note

The example above assumes your system uses systemctl to manage the SSH service. While that is the case with the latest versions of the most popular distributions, it is not the case for all distributions. You may need to modify the commands above depending on how your system manages the SSH service.

For instance, systems like CentOS 6, Debian 7, and Ubuntu 14.04 use service instead of systemctl. So you would need to replace the systemctl command above with the following:

service sshd restart

Install Any Additional Required Software

With cloud-config’s packages key, you can automate software installation and management as part of server initialization. For thorough coverage of cloud-init’s package management features, and examples of how to use it, see our guide Use Cloud-Init to Install and Update Software on New Servers.

As a basic illustration, the snippet below shows how to install a set of software during instance initialization. The example installs software for a LEMP web stack (NGINX, MySQL, and PHP) a popular setup for web applications. You can learn more about LEMP stacks in our guide on how to Install a LEMP Stack.

File: cloud-config.yaml
1
2
3
4
packages:
  - mysql-server
  - nginx
  - php
Note
Software packages have different names depending on the distribution you are using. Reference your distribution’s package repository to identify the package names for the software you wish to install.

Complete Cloud-Config File

What follows is a complete example cloud-config file, summarizing all of the initialization options covered in this tutorial. You can use this as a basis for initializing your own Compute Instances. For example, remove the packages section and customize the limited user and server details, and you have a script following our Setting Up and Securing a Compute Instance guide. Feel free to add other features to fine-tune the instance to your needs.

File: cloud-config.yaml
 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
33
#cloud-config

# Configure a limited user
users:
  - default
  - name: example-user
    groups:
      - sudo
    sudo:
      - ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    ssh_authorized_keys:
      - "SSH_PUBLIC_KEY"

# Perform System Updates
package_update: true
package_upgrade: true

# Configure server details
timezone: 'US/Central'
hostname: examplehost

# Harden SSH access
runcmd:
  - sed -i '/PermitRootLogin/d' /etc/ssh/sshd_config
  - echo "PermitRootLogin no" >> /etc/ssh/sshd_config
  - systemctl restart sshd

# Install additional software packages
packages:
  - nginx
  - mysql-server
  - php

Deploy an Instance with User Data

There are three paths to deploy a new Compute Instance using your cloud-config initialization script. These options are summarized below, with links to relevant deployment guides. For more coverage of cloud-init deployments, review our Overview of the Metadata Service guide.

  • Cloud Manager: You can conveniently configure and deploy new instances using a web browser. Follow along with our Create a Compute Instance guide to deploy a new instance. The guide includes a section on how to Add User Data, showing where you can input your cloud-config content.

  • Linode CLI: The command-line tool provides a command for creating a new Compute Instance, and that command can take a --metdata.user_data option with your cloud-config script. To learn more about using the Linode CLI, see our guides Getting Started with the Linode CLI and Linode CLI Commands for Compute Instances.

    What follows is an example set of commands to deploy an instance from a cloud-config file. The example assumes you have set up the Linode CLI tool and that your cloud-config is stored as cloud-config.yaml in the current directory. Review the guide linked above for more on the other options used in this example command.

    The CLI command requires that the cloud-config be encoded as a base64 string, which you can make using the cat cloud-config.yaml | base64 -w 0 command. The first command below does this and assigns the value to a shell variable for convenience.

    cloudconfigvar="$(cat cloud-config.yaml | base64 -w 0)"
    
    linode-cli linodes create \
      --label cloud-init-example \
      --region us-iad \
      --type g6-nanode-1 \
      --image linode/ubuntu22.04 \
      --root_pass EXAMPLE_ROOT_PASSWORD \
      --metadata.user_data "$cloudconfigvar"
  • Linode API: Within the instances/ endpoint of the API, you have access to a metadata.user_data option for inputting a cloud-config. Using this, you can initialize a new Compute Instance in a convenient POST request. Learn more about the Linode API in our documentation on the Linode API and the Linode Instances API documentation.

    With versatility being one of its main advantages, there are numerous ways to use the Linode API to deploy a server. The steps below show a simple approach using just the command line. This example is easily adaptable for other contexts as well.

    1. Create a payload file. This is a convenient approach for a POST request’s data, as it makes the data easier to craft and reuse. You can create the file using your preferred text editor, but this example uses a > command to do so.

      cat > cloud-init-example-deployment.json <<'EOF'
      {
        "label": "cloud-init-example",
        "region": "us-iad",
        "type": "g6-nanode-1",
        "image": "linode/ubuntu22.04",
        "root_pass": "EXAMPLE_ROOT_PASSWORD",
        "metadata": {
          "user_data": "<INSERT_CLOUD_CONFIG>"
        }
      }
      EOF
    2. Insert the cloud-config as metadata.user_data. The API, like the CLI, requires that the cloud-config be encoded as a base64 string. Below, that is accomplished using the command cat cloud-config.yaml | base64 -w 0, and the value is assigned to a shell variable for convenience.

      cloudconfigvar="$(cat cloud-config.yaml | base64 -w 0)"
      sed -i "s,<INSERT_CLOUD_CONFIG>,$cloudconfigvar," cloud-init-example-deployment.json

      Alternatively, you can copy the base64 string, open the file, and paste the string into the user_data field.

    3. Make the POST request to the instances API endpoint. Include your Linode API personal access token (covered in the guide linked above) as an Authorization: Bearer header.

      curl -H "Content-Type: application/json" \
        -H "Authorization: Bearer API_ACCESS_TOKEN" \
        -X POST \
        -d @cloud-init-example-deployment.json \
        https://api.linode.com/v4/linode/instances

This page was originally published on


Your Feedback Is Important

Let us know if this guide was helpful to you.


Join the conversation.
Read other comments or post your own below. Comments must be respectful, constructive, and relevant to the topic of the guide. Do not post external links or advertisements. Before posting, consider if your comment would be better addressed by contacting our Support team or asking on our Community Site.
The Disqus commenting system for Linode Docs requires the acceptance of Functional Cookies, which allow us to analyze site usage so we can measure and improve performance. To view and create comments for this article, please update your Cookie Preferences on this website and refresh this web page. Please note: You must have JavaScript enabled in your browser.