An administrative task that I've done countless number of times is spinning up new servers to host applications like a web dashboard or a trading engine. At uSwitch my colleagues use Puppet and other smart devops tools that I have no idea about to automate our infrastructure. For my own work I just need an easy tool to run some commands over ssh. I chose to use Fabric to automate this repetitive but necessary task of hardening a fresh Ubuntu server. Now that I have this set of Fabric tasks, after I create a new instance, I can just run a single Fabric task and the server would be configured properly for use.
The choice for Fabric is because I've been using Fabric for some time already to perform simple deployment tasks. Then I stumbled on an open-source project that actually configures an Ubuntu server using Fabric. Much of these scripts are based on that source. Automating these tasks saves me a lot of time and ensures consistency of configurations. Perhaps when the need arises, I should look into other tools like Vagrant too.
Here are some basic tasks that I perform on a new Ubuntu 12.04 server and the corresponding Fabric task script.
Create an administrator account
from fabric.api import *
from fabric.contrib.files import append
from fabric.contrib.files import sed
from fabric.contrib.files import exists
from fabric.operations import prompt
def create_admin_account(admin, default_password=None):
"""Create an account for an admin to use to access the server."""
env.user = "root"
opts = dict(
admin=admin,
default_password=default_password or env.get('default_password') or 'secret',
)
# create user
sudo('egrep %(admin)s /etc/passwd || adduser %(admin)s --disabled-password --gecos ""' % opts)
# add public key for SSH access
if not exists('/home/%(admin)s/.ssh' % opts):
sudo('mkdir /home/%(admin)s/.ssh' % opts)
opts['pub'] = prompt("Paste %(admin)s's public key: " % opts)
sudo("echo '%(pub)s' > /home/%(admin)s/.ssh/authorized_keys" % opts)
# allow this user in sshd_config
append("/etc/ssh/sshd_config", 'AllowUsers %(admin)s@*' % opts, use_sudo=True)
# allow sudo for maintenance user by adding it to 'sudo' group
sudo('gpasswd -a %(admin)s sudo' % opts)
# set default password for initial login
sudo('echo "%(admin)s:%(default_password)s" | chpasswd' % opts)
harden ssh server
def harden_sshd():
"""Security harden sshd."""
# Disable password authentication
sed('/etc/ssh/sshd_config',
'#PasswordAuthentication yes',
'PasswordAuthentication no',
use_sudo=True)
# Deny root login
sed('/etc/ssh/sshd_config',
'PermitRootLogin yes',
'PermitRootLogin no',
use_sudo=True)
sudo("restart ssh")
setup firewall
def install_ufw(rules=None):
"""Install and configure Uncomplicated Firewall."""
sudo('apt-get update')
sudo('apt-get -yq install ufw')
configure_ufw(rules)
def configure_ufw(rules=None):
"""Configure Uncomplicated Firewall."""
# reset rules so we start from scratch
sudo('ufw --force reset')
rules = rules or env.rules or err("env.rules must be set")
for rule in rules:
sudo(rule)
# re-enable firewall and print rules
sudo('ufw --force enable')
sudo('ufw status verbose')
time synchronisation daemon
def set_system_time(timezone=None):
"""Set timezone and install ``ntp`` to keep time accurate."""
opts = dict(
timezone=timezone or env.get('timezone') or '/usr/share/zoneinfo/UTC',
)
# set timezone
sudo('cp %(timezone)s /etc/localtime' % opts)
# install NTP
sudo('apt-get -yq install ntp')
enable unattended upgrades
def install_unattended_upgrades(email=None):
"""Configure Ubuntu to automatically install security updates."""
opts = dict(
email=email or env.get('email') or err('env.email must be set'),
)
sudo('apt-get -yq install unattended-upgrades')
sed('/etc/apt/apt.conf.d/50unattended-upgrades',
'//Unattended-Upgrade::Mail "root@localhost";',
'Unattended-Upgrade::Mail "%(email)s";' % opts,
use_sudo=True)
sed('/etc/apt/apt.conf.d/20auto-upgrades',
'APT::Periodic::Update-Package-Lists "0";',
'APT::Periodic::Update-Package-Lists "1";',
use_sudo=True)
append('/etc/apt/apt.conf.d/20auto-upgrades',
'APT::Periodic::Unattended-Upgrade "1";',
use_sudo=True)