Kamal gives us an option to connect with a non-root user, but how can we create it?
Creating a non-root user
If you use cloud VM providers like Hetzner or Digital Ocean, your virtual private server with likely come with root
only. And Kamal unfortunately cannot create additional users automatically.
To change root
to our own user, we can take advantage of the SSH key of the root
user and use it to set up a new user. We can do that with a provisioning tool, script, or issuing the following commands after logging in to the server:
$ ssh root@public-ip
As a first step, we create the user itself with a home directory where we’ll install the SSH keys:
useradd --create-home username
usermod -s /bin/bash username
We also select a default shell (here bash
) which will make the future connections more comfortable.
Then we can create the ~/.ssh/authorized_keys
file and move the already existing root
key (installed by our cloud provider). If we do that using the just created user (using su
) we’ll get the right ownership:
su - username -c 'mkdir -p ~/.ssh'
su - username -c 'touch ~/.ssh/authorized_keys'
cat /root/.ssh/authorized_keys >> /home/username/.ssh/authorized_keys
chmod 700 /home/username/.ssh
chmod 600 /home/username/.ssh/authorized_keys
At the end we are finishing it up by setting the expected access rights (only read and write for this user).
Finally, we need to ensure that we can still make all the administrative tasks on the servers. So we make the user to be able get root privileges with sudo
and make him part of docker
group:
echo 'username ALL=(ALL:ALL) NOPASSWD: ALL' | tee /etc/sudoers.d/username
chmod 0440 /etc/sudoers.d/username
visudo -c -f /etc/sudoers.d/username
usermod -aG docker username
Note that this configuration really make the user as powerful as root
. You could also directly limit the actions in the /etc/sudoers.d/username
file for finer control.
The steps mentioned can be used in Ubuntu 22 and 24, and likely many other systems.
Kamal config
Changing the SSH user in Kamal simple. Providing the user
under the ssh
setting will make Kamal always connect with this user from now on:
# config/deploy.yml
ssh:
user: username
Implications
While changing the user is simple, it still comes from some implications. Since Kamal doesn’t know anything about issuing commands with sudo
, Kamal won’t be able to cleanly remove directories that don’t have write access for this specific users.
This can happen when creating a PostgreSQL directory which ends up using its own user as the owner:
sudo ls /home/username/app-postgres -all
drwx------ 19 999 username 4096 Oct 11 14:06 data
Now it isn’t a real problem per-se, but kamal remove
will now fail trying to delete the directory.
To fix issues like this we need to create these kind of directories with write and execute bits for the user beforehand. Adding them for the group here would make this work.
Disabling root
Once the configuration works as expected, we might want to disable the root
login altogether.
To do that we need to change the PermitRootLogin
SSH daemon configuration and restart the service:
sed -i 's@PasswordAuthentication yes@PasswordAuthentication no@g' /etc/ssh/sshd_config;
sed -i 's@PermitRootLogin yes@PermitRootLogin no@g' /etc/ssh/sshd_config;
systemctl restart ssh
I am also disabling password authentication while I am at it. We should use only SSH keys.