This document explains the SSH key behavior specific to Hetzner Cloud deployments and provides guidance for users who want stricter security.
When deploying to Hetzner Cloud, the deployer configures SSH access through two independent mechanisms:
| Mechanism | User | When Applied | Purpose |
|---|---|---|---|
OpenTofu hcloud_ssh_key |
root |
Server creation (before boot) | Emergency/debug access |
cloud-init ssh_authorized_keys |
torrust |
First boot | Normal application access |
Result: Both root and torrust users have SSH access after deployment.
The primary reason is debugging capability. If cloud-init fails, the server would be completely inaccessible without root SSH access.
- YAML syntax errors in cloud-init configuration
- Network issues during package installation
- User creation failures in cloud-init
- Script execution errors in cloud-init
- Timeouts during cloud-init execution
With root access, you can:
# SSH as root to diagnose
ssh -i ~/.ssh/your_key root@<server-ip>
# Check cloud-init status
cloud-init status --wait
# View cloud-init logs
cat /var/log/cloud-init-output.log
journalctl -u cloud-init- Elevated privileges: Root has unrestricted system access
- Larger attack surface: Compromised SSH key grants full system control
- Principle of least privilege: Violated by default
- Application runs as non-root: The
torrustuser runs the tracker - Passwordless sudo:
torrustcan escalate when needed - Same SSH key: No additional key exposure (both mechanisms use the same key)
For production deployments where you want stricter security, you can disable root SSH access after verifying the deployment succeeded.
The simplest approach - removes the SSH key from root's configuration:
ssh torrust@<server-ip> "sudo rm /root/.ssh/authorized_keys"Effect: Root can no longer SSH in. You can still access via torrust user with sudo.
Modifies the SSH daemon to reject root logins entirely:
ssh torrust@<server-ip> "sudo sed -i 's/#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config && sudo systemctl restart sshd"Effect: SSH daemon rejects all root login attempts, regardless of authentication method.
Removes the key from Hetzner's account-level registry:
- Go to Hetzner Cloud Console
- Navigate to Security → SSH Keys
- Find the key named
torrust-tracker-vm-<environment>-ssh-key - Click Delete
Effect: Key is removed from your Hetzner account. Does NOT affect existing servers - only prevents the key from being used for future server creation.
After disabling root access, verify the change:
# This should fail (connection refused or permission denied)
ssh -i ~/.ssh/your_key root@<server-ip>
# This should still work
ssh -i ~/.ssh/your_key torrust@<server-ip>| Provider | Root SSH Access | Reason |
|---|---|---|
| Hetzner | ✅ Enabled by default | Debugging capability for cloud-init failures |
| LXD | ❌ Not applicable | lxc exec provides direct console access without SSH |
The LXD provider doesn't need this pattern because:
- LXD runs locally on your machine
lxc exec <instance> -- bashgives direct console access- No SSH required for debugging
Keep root access enabled. The debugging capability is valuable when iterating on configurations.
Consider disabling root access after successful deployment:
- Verify deployment completed successfully
- Test that you can SSH as
torrustuser - Apply one of the disable options above
- Verify root access is blocked