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.
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.
- 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:
- Deactivate the root login
- Enable user for SSH
- Deactivate password authentication
- Automatic disconnection in case of incorrect login
- Deactivate unused functions
- 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:
- After package updates, there might be a reboot necessary for patches to work properly.
- fail2ban needs a restart to perform the enabled SSH protection.
- 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