Get Rewarded! We will reward you with up to €50 credit on your account for every tutorial that you write and we publish!

Basic Cloud Config

profile picture
Author
Carsten
Published
2019-06-21
Time to read
6 minutes reading time

Introduction

An additional feature during creation of a Hetzner cloud server (CX11 and above) is user data. This allows the execution of a cloud-init configuration for the newly created server.

After following this tutorial, you will be able to adjust the setup of new cloud servers with custom configuration.

Parts of this configuration are based on Securing the SSH service.

Step 1 - Insert first line

The cloud-init config uses YAML ("YAML Ain't Markup Language") as markup language.

The file must start with a comment on the first line so that the code is interpreted as cloud-config by the server:

#cloud-config

Step 2 - Create user

After the initial comment, let us start by creating a new admin user with sudo privileges and pre-configured SSH key.

How to create a SSH key pair?

users:
  - name: holu
    groups: users, admin
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    ssh_authorized_keys:
      - <public_ssh_key>

Step 3 - Update packages

The first thing we do is an update of the package lists and installed packages. A fresh copy of Linux may have outdated packages with critical security vulnerabilities.

In this step, you could install necessary packages for your projects too.

If packages are to be installed or upgraded, cloud-init will update the package lists beforehand.

packages:
  - fail2ban
  - ufw
package_update: true
package_upgrade: true

Step 4 – Run commands

cloud-init allows us to run CLI (Command Line Interface) commands right after the server has been created.

Start the procedure by typing runcmd: on a new line.

Step 4.1 – Configure fail2ban

Each command is written to a new line, preceded by a hyphen.

To secure the SSH network protocol a bit, we use fail2ban against brute force attacks. By default, it will ban attackers for 10 minutes – after 5 failed login attempts within 10 minutes.

More about this step.

  - printf "[sshd]\nenabled = true\nbanaction = iptables-multiport" > /etc/fail2ban/jail.local
  - systemctl enable fail2ban

Step 4.2 – Enable ufw

Uncomplicated Firewall (ufw) is used to lock down the system and allow only used services.

Later you will need to allow services or ports (like 80/443) for any applications that require them.

  - ufw allow OpenSSH
  - ufw enable

Step 4.3 – Harden SSH

The last sequence of commands is dedicated to securing SSH.

We use the command sed to customize parts of the sshd_config file.

Changes made:

  - sed -i -e '/^\(#\|\)PermitRootLogin/s/^.*$/PermitRootLogin no/' /etc/ssh/sshd_config
  - sed -i -e '/^\(#\|\)PasswordAuthentication/s/^.*$/PasswordAuthentication no/' /etc/ssh/sshd_config
  - sed -i -e '/^\(#\|\)KbdInteractiveAuthentication/s/^.*$/KbdInteractiveAuthentication no/' /etc/ssh/sshd_config
  - sed -i -e '/^\(#\|\)ChallengeResponseAuthentication/s/^.*$/ChallengeResponseAuthentication no/' /etc/ssh/sshd_config
  - sed -i -e '/^\(#\|\)MaxAuthTries/s/^.*$/MaxAuthTries 2/' /etc/ssh/sshd_config
  - sed -i -e '/^\(#\|\)AllowTcpForwarding/s/^.*$/AllowTcpForwarding no/' /etc/ssh/sshd_config
  - sed -i -e '/^\(#\|\)X11Forwarding/s/^.*$/X11Forwarding no/' /etc/ssh/sshd_config
  - sed -i -e '/^\(#\|\)AllowAgentForwarding/s/^.*$/AllowAgentForwarding no/' /etc/ssh/sshd_config
  - sed -i -e '/^\(#\|\)AuthorizedKeysFile/s/^.*$/AuthorizedKeysFile .ssh\/authorized_keys/' /etc/ssh/sshd_config
  - sed -i '$a AllowUsers holu' /etc/ssh/sshd_config

"ChallengeResponseAuthentication" is a deprecated alias for "KbdInteractiveAuthentication". The configuration above includes both options since the commands will only change options that are actually in the file anyway. The rest will be skipped.

Step 4.4 - Reboot

We have to execute one last command: reboot

Why? Three birds with one stone:

  1. After package updates, there might be a reboot necessary for patches to work properly.
  2. fail2ban needs a restart to perform the enabled SSH protection.
  3. The changed configuration of SSH will be applied after a restart too.

Step 5 – Create the server

Open the Cloud Console, select your project and create a server. In the "Cloud config" text box, enter the configuration you just created. You can view the complete configuration at the bottom of this page.

Reference an external cloud-init script

Instead of specifying the entire configuration at server creation, you can also link to an external configuration file. This can make sense if you need to use the same configuration for several servers, or when you create a server via a curl command.

Save your file and copy the link, e.g. https://URLtoCode/config.yaml.

  • Cloud Console

    Open the Cloud Console, select your project and create a server. In the "Cloud config" text box, enter:

    #include
    https://URLtoCode/config.yaml
  • Curl command

    Change the server properties as you need them. For user_data, add #include followed by a break and the URL to your configuration file, e.g. #include\nhttps://URLtoCode/config.yaml.

    curl \
    	-X POST \
    	-H "Authorization: Bearer $API_TOKEN" \
    	-H "Content-Type: application/json" \
    	-d '{"image":"ubuntu-22.04","location":"nbg1","name":"my-server","server_type":"cx11","user_data":"#include\nhttps://URLtoCode/config.yaml"}' \
      	'https://api.hetzner.cloud/v1/servers'

Now boot your secured-by-default cloud server.

After the server is created, go to Graphs using the Cloud Console and wait until CPU usage shrinks to zero. All commands have been executed and the server has rebooted. You can now login over SSH with the created user account.

Conclusion

Many words for 21 lines of code, huh?

I hope you learned a lot! For example:

  • Build a cloud-init config from scratch.
  • Create users with sudo and other properties.
  • Run commands during first server boot.
  • Utilize sed to edit parts of a file.

Now go ahead and build something awesome!

The complete configuration:

#cloud-config
users:
  - name: holu
    groups: users, admin
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    ssh_authorized_keys:
      - <public_ssh_key>
packages:
  - fail2ban
  - ufw
package_update: true
package_upgrade: true
runcmd:
  - printf "[sshd]\nenabled = true\nbanaction = iptables-multiport" > /etc/fail2ban/jail.local
  - systemctl enable fail2ban
  - ufw allow OpenSSH
  - ufw enable
  - sed -i -e '/^\(#\|\)PermitRootLogin/s/^.*$/PermitRootLogin no/' /etc/ssh/sshd_config
  - sed -i -e '/^\(#\|\)PasswordAuthentication/s/^.*$/PasswordAuthentication no/' /etc/ssh/sshd_config
  - sed -i -e '/^\(#\|\)KbdInteractiveAuthentication/s/^.*$/KbdInteractiveAuthentication no/' /etc/ssh/sshd_config
  - sed -i -e '/^\(#\|\)ChallengeResponseAuthentication/s/^.*$/ChallengeResponseAuthentication no/' /etc/ssh/sshd_config
  - sed -i -e '/^\(#\|\)MaxAuthTries/s/^.*$/MaxAuthTries 2/' /etc/ssh/sshd_config
  - sed -i -e '/^\(#\|\)AllowTcpForwarding/s/^.*$/AllowTcpForwarding no/' /etc/ssh/sshd_config
  - sed -i -e '/^\(#\|\)X11Forwarding/s/^.*$/X11Forwarding no/' /etc/ssh/sshd_config
  - sed -i -e '/^\(#\|\)AllowAgentForwarding/s/^.*$/AllowAgentForwarding no/' /etc/ssh/sshd_config
  - sed -i -e '/^\(#\|\)AuthorizedKeysFile/s/^.*$/AuthorizedKeysFile .ssh\/authorized_keys/' /etc/ssh/sshd_config
  - sed -i '$a AllowUsers holu' /etc/ssh/sshd_config
  - reboot

See more about cloud-configs here

License: MIT
Want to contribute?

Get Rewarded: Get up to €50 in credit! Be a part of the community and contribute. Do it for the money. Do it for the bragging rights. And do it to teach others!

Report Issue
Try Hetzner Cloud

Get €20/$20 free credit!

Valid until: 31 December 2024 Valid for: 3 months and only for new customers
Get started
Want to contribute?

Get Rewarded: Get up to €50 credit on your account for every tutorial you write and we publish!

Find out more