Each Ubuntu VPS that I run and maintain is a “pet” in the sense of pets vs. cattle1. They’re pets in the sense that each one is critical and each one has a different role, and that’s OK because – thanks to Docker (for my web VPS) and Mail-in-a-Box (for my email etc. VPS) – enough of the configuration for each is automated that I don’t have to worry massively about the time outlay of recovering from unexpected failures.
However, when I do recover or migrate one of my VPSen, there are some common bits of setup I always do. Creating a non-root user that has passwordless sudo. Ensuring root logins are disabled. Things like that.
For a while now I’ve been using
cloud-config user-data (when recovering/migrating EC2 VPSen) to make the default non-root user that is created have the same name as the non-root user on my local machine. This just saves me having to maintain a different username in my local SSH config.
To do this I refer to https://alestic.com/2014/01/ec2-change-username/ and then enter this as my
#cloud-config system_info: default_user: name: david
And that’s it. The EC2 Ubuntu images are otherwise configured just as I like them; root logins are disabled by default and the default user has passwordless sudo.
Things are less ideal with Linode and Digital Ocean.
Until yesterday Linode was the only other VPS provider I’ve used in recent years for critical systems. They don’t support
cloud-config. Nodes are just created with a root login. I can’t actually remember whether they allow an SSH public key to be passed in as authorized, or whether the only option is to receive/set a root password somehow. But anyway – there’s no
cloud-config. So I always had to create my non-root user and reconfigure SSH and sudo manually.
For a number of small reasons – the above being one of them – I’d been meaning to move my Linode VPSen over to Digital Ocean for a while now. Then, earlier this week, I saw https://blog.linode.com/2016/01/05/security-notification-and-linode-manager-password-reset/ (which I first encountered via Hacker News: https://news.ycombinator.com/item?id=10845170). I finally made the move yesterday.
Digital Ocean does support
cloud-config, but trying the same config that I use with EC2 had no effect. This must be because Digital Ocean’s default
cloud-config does not define a default non-root user – and overriding the name of a user that isn’t being created is a no-op (if you create a Droplet without any
cloud-config your Droplet is just created with a root login – no “ubuntu” user like on EC2).
After a bit of trial and error I came up with the following
user-data config to get me the headstart I was looking for with setting up new Droplets:
#cloud-config timezone: Etc/UTC users: - name: david shell: /bin/bash gecos: Ubuntu groups: [adm, audio, cdrom, dialout, floppy, video, plugdev, dip, netdev, sudo] sudo: ['ALL=(ALL) NOPASSWD:ALL'] ssh-authorized-keys: - ssh-rsa 0xDEADBEAF...replace.me...0xDEADBEAF== david@ash disable_root: true
The Droplets I created are located in Digital Ocean’s London datacenter. Despite this I found that my freshly created Droplets had their timezone set to an East Coast US timezone. Hence the timezone override to
The other consequence of Digital Ocean not defining a default non-root user is that root itself does have logins enabled by default (worse still, it’s via a generated password if you don’t check the box on the Droplet creation page that adds your SSH public key as authorized). I found that checking that box and setting
disable_root: true gets me the EC2-like behaviour that I desire.
There are other things I do both on Digital Ocean and EC2 like enabling
unattended-upgrades etc. that I haven’t bothered automating because it’s not been worth it yet. Creating and reconfiguring user logins, plus SSH and sudo access is slightly more involved and easier to get wrong than
dpkg-reconfigure unattended-upgrades though, hence the above being worth it for me.
This post is equal parts a note to my future self and equal parts a note to anyone else looking for EC2-like behaviour on Digital Ocean; my searching yesterday didn’t find a ready-to-go answer, so I pieced this together from the
cloud-config examples and docs. I hope it’s useful.