Black Friday 2025 – NPI EA (cat = Baeldung on Linux)
announcement - icon

Yes, we're now running our Black Friday Sale. All Access and Pro are 33% off until 2nd December, 2025:

>> EXPLORE ACCESS NOW

Baeldung Pro – Linux – NPI EA (cat = Baeldung on Linux)
announcement - icon

Learn through the super-clean Baeldung Pro experience:

>> Membership and Baeldung Pro.

No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.

Partner – Orkes – NPI EA (tag=Kubernetes)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

1. Overview

cloud-init is an open-source tool that helps us automate the initial setup of cloud servers. It’s like a script that runs when a new server starts up. It configures things like network settings, users, and security.

One of the most important things cloud-init can do is manage SSH keys, which are essential for secure remote access.

In this tutorial, we’ll learn how to use cloud-init to set up SSH keys on our Linux servers. We’ll cover both user SSH keys (for remote access) and host SSH keys (to verify server identity).

2. Understanding SSH Keys and cloud-init

To grasp how cloud-init handles SSH keys, let’s first take a closer look at SSH keys themselves. SSH keys are pairs of cryptographic keys used for authentication, essentially replacing the need for passwords. We place the public key on the server, while the private key securely resides on our local machine.

Furthermore, when we log into the server via SSH, we use keys to establish a secure connection. This verifies our identity without transmitting passwords over the network.

Now, let’s see how cloud-init fits into the picture. cloud-init enables us to automate the initial setup of our cloud servers. Among its many capabilities is configuring SSH keys during the startup process.

cloud-init configuration files, usually written in YAML format, provide specific sections to specify both user and host keys.

Hence, understanding these fundamentals about SSH keys and cloud-init’s role in managing them sets the stage for efficiently configuring secure remote access to our Linux instances.

3. Setting up SSH Authorized Keys

In a Linux environment, setting up SSH authorized keys with cloud-init is essential for securing remote access to our instances. SSH key-based authentication is more secure than password-based authentication. This is because the former reduces the risk of brute-force attacks.

Let’s walk through the process of adding SSH authorized keys using cloud-init.

3.1. Adding Authorized Keys

Authorized keys are the public keys that allow access to specific user accounts on our server. SSH authorized keys are stored in the ~/.ssh/authorized_keys file for each user.

Moreover, to configure these keys with cloud-init, we need to define the ssh_authorized_keys section within the users block of our cloud-init configuration file:

#cloud-config
users:
  - name: myuser
    groups: sudo
    shell: /bin/bash
    ssh_authorized_keys:
      - ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvZ4sFxzmLCYdEm...

Here, we configure a user named myuser. We also add the user to the sudo group (granting administrative privileges). Then, we set the default shell to /bin/bash, and most importantly, provide the user’s public SSH key under ssh_authorized_keys.

However, we have to ensure the public key is in a single line, without any line breaks or extra spaces. Moreover, the key type (such as ssh-rsa) and the key must be properly formatted.

3.2. Adding Multiple SSH Keys

Sometimes, we may need to authorize multiple SSH keys for a single user. This is common in environments where multiple administrators need access or a backup key is required.

We can list multiple keys under the ssh_authorized_keys field:

#cloud-config
users:
  - name: myuser
    groups: sudo
    shell: /bin/bash
    ssh_authorized_keys:
      - ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvZ4sFxzmLCYdEm...
      - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB6D64fi...

In this case, myuser now has two authorized keys. The system will add each key to the ~/.ssh/authorized_keys file of the user. The user can now log in using either key.

Consequently, this can help provide access for multiple administrators or backup keys in case one is lost.

4. Troubleshooting Common Issues

Even with cloud-init’s assistance, setting up SSH authorized keys can sometimes lead to issues. Let’s consider some common issues and possible solutions.

4.1. Incorrect Key Format

We have to always ensure that our public keys are formatted correctly. They should start with the key type (for example, ssh-rsa or ssh-ed25519) and be on a single continuous line without any extra spaces. An incorrect format can cause authentication failures.

4.2. File and Directory Permissions

The .ssh directory and the authorized_keys file within it need specific permissions. On a Linux machine, the .ssh directory should have file permissions set to 700 (read, write, and execute for the owner only). Moreover, the authorized_keys file should have file permissions set to 600 (read and write for the owner only).

We can set these permissions using the chmod command:

$ chmod 700 ~/.ssh
$ chmod 600 ~/.ssh/authorized_keys

4.3. User Creation Issues

We need to ensure the user specified in the cloud-init configuration exists or will be created during the instance initialization. If the user doesn’t exist, cloud-init won’t be able to add the key correctly. This could lead to SSH access issues.

4.4. SSH Service Issues

If SSH isn’t working as expected, there might be issues with the SSH service itself. We need to ensure that the SSH service is running correctly. We can check the status of the service and restart it using the systemctl utility:

$ sudo systemctl status sshd
$ sudo systemctl restart sshd

4.5. Log File Review

If there’s still more to troubleshoot, checking the logs can be quite helpful. We can inspect the SSH log file (usually at /var/log/auth.log or /var/log/secure) and the cloud-init logs (/var/log/cloud-init.log) for any errors or warnings that can shed light on the problem.

By following these tips and using cloud-init effectively, we can streamline the process of setting up SSH authorized keys.

5. Managing Host SSH Keys

Managing SSH host keys is a critical aspect of maintaining secure server connections in a Linux environment. These keys are used to verify the server’s identity to the client during the SSH handshake process. This ensures that the client connects to the legitimate server and not an impostor.

5.1. The Role of Host SSH Keys in Server Identity

Host SSH keys act as the server’s unique fingerprint, establishing a trusted connection between the client and the server.

During the initial SSH connection, the server presents its host key to the client. The client stores this key for subsequent connections to verify the server’s identity.

However, if the server’s host key changes unexpectedly, it could indicate a potential security breach, like a man-in-the-middle attack. This prompts the client to issue a warning.

5.2. Default Behavior of cloud-init and Potential Issues

By default, cloud-init generates new SSH host keys during the first boot of a Linux instance. The server uses these keys stored in the /etc/ssh/ directory for all SSH connections.

Although this default behavior enhances security in dynamic cloud environments where instances are frequently created and destroyed, it can lead to issues in scenarios where server identity needs to remain consistent.

For example, if we re-image a server and connect to it via SSH, we encounter a warning indicating that the remote host identification has changed:

$ ssh <ip_address>
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!

Someone could be eavesdropping on you right now (man-in-the-middle attack)!

It is also possible that a host key has just been changed.

The fingerprint for the RSA key sent by the remote host is

xx:xx:xx.

Please contact your system administrator.

Add correct host key in /home/hostname/.ssh/known_hosts to get rid of this message.

Offending RSA key in /var/lib/sss/pubconf/known_hosts:4

RSA host key for user has changed and you have requested strict checking.

Host key verification failed.

This warning occurs because the host keys generated by cloud-init do not match those stored on the client from previous connections.

5.3. Controlling Key Regeneration With ssh_deletekeys

To prevent cloud-init from regenerating SSH host keys on each boot, we can use the ssh_deletekeys option in our cloud-init configuration.

By setting ssh_deletekeys: false, we ensure the existing host keys are retained. This prevents unnecessary warnings about host key changes. Additionally, the approach is particularly useful for physical devices or scenarios where maintaining a consistent server identity is crucial.

Now, let’s see how we can disable key regeneration in our cloud-init configuration:

#cloud-config
users:
  - name: myuser
    groups: sudo
    shell: /bin/bash
    ssh_authorized_keys:
      - ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvZ4sFxzmLCYdEm...
      - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB6D64fi...
ssh_deletekeys: false

By simply placing the ssh_deletekeys: false at the top level in our cloud-init file, we can prevent the deletion of existing host keys and ensure a stable server identity.

5.4. Specifying Host Keys Using cloud-init

There are scenarios where we want to provide our own host keys instead of letting cloud-init generate them. For instance, we may need to work with predefined keys or adhere to specific security requirements.

In such cases, cloud-init enables us to provide the host keys directly in our configuration. We can do this using the ssh_keys section:

#cloud-config
...
ssh_keys:
  rsa_private: |
    -----BEGIN OPENSSH PRIVATE KEY-----
    ...
    -----END OPENSSH PRIVATE KEY-----
  rsa_public: ssh-rsa AAAAB3NzaC1yc2EAAAADAQAB...
  
ssh_deletekeys: false 

In this example, we provide the private and public parts of an RSA key pair. We enclose the private key within a | symbol. This enables us to use multi-line string formatting in YAML.

Furthermore, handling private keys requires extra caution. We must securely store our cloud-init configuration file and ensure that the communication channel between our cloud provider and the server is encrypted.

6. Conclusion

In this article, we’ve explored how to use cloud-init for secure SSH key management in Linux instances. Additionally, we covered setting up authorized keys for users and managing host keys, including preventing key regeneration.

Therefore, using cloud-init’s capabilities, we can automate SSH key configuration. This enhances security and streamlines access control during instance initialization.