Fish script and Ansible playbook for automating a fresh install of an Ubuntu server. Tested on 14.04 and 14.10 on Digital Ocean.
Firstly, install Ansible. I found some nice instructions here.
When booting up a server, please indicate that you wish to connect by public key, and supply one. The rest of the instructions will assume that you can already authenticate by public key.
Else, you can use ssh-copy-id
to copy a generated public key to the server. This can be slightly complicated/annoying (making sure that your permissions to your .ssh
folder and keys are restricted):
ssh-keygen -t rsa -f "key filename"
ssh-copy-id -i "~/.ssh/[key filename]" [user]@[server]
Google for more information.
Moving on, the Ansible playbook requires you to set some options before it can be run.
After booting up your servers, paste your server names/IPs into hosts.ini
. Start each server on a new line (and of course delete my lorem ipsum servers).
The rest of the options below are found in bootstrap.yml
.
Default: utopic
Can be changed: yes
Specify the codename of your Ubuntu release. This will be required for the script to download the appropriate unattended-packages
repo.
Default: deploy
Can be changed: yes, recommended
Specify the username of your deploy user.
Default: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
Can be changed: yes, recommended
Specifies the location of the public key you want to install on the server for authentication.
Default: No default
Can be changed: MUST CHANGE (read below)
Firstly, Ansible requires passwords to be hashed, so let's generate some on our local machine.
Ubuntu's crypt function allows us to specify which encryption algorithm to use. In this instance, we will choose SHA-512. See documentation on crypt() here.
On Mac OS X, its crypt function only supports DES, which is insecure, so download the passlib library from pip or easy_install:
pip install passlib
Then, run the following Python script in Terminal. Using Python's getpass()
function, it will prompt you for the password you want to set, then generate a SHA-512 hash using a randomly-generated salt, with 5000 rounds. 5000 rounds was chosen specifically to match with Linux's crypt()
implementation, which appears to use 5000 rounds as a default.
echo "from passlib.hash import sha512_crypt; import getpass; print (sha512_crypt.encrypt(getpass.getpass(), rounds=5000))" | python -
The script should print out something that resembles the following:
$6$[random_string_of_characters_which_is_your_salt]$[hashed_output]
Note: If you're using SHA-256, it will start with $5
instead.
Repeat the above twice to generate passwords for both root and your deploy user, then copy and paste it into the appropriate fields in the Ansible playbook. Remember to store your passwords securely (1Password, or write it down on a post-it and stick it on the side of your CPU (just kidding)).
Default: [vim,fail2ban]
Can be changed: yes
Specify the name of packages you want to install from apt-get
.
Default: 22
Can be changed: yes, recommended
Specify the new SSH port of your server.
Default:
ssh:
port: {{ new_ssh_port }}
protocol: tcp
Can be changed: yes
Specify the ports/protocol you want to allow in your firewall (ufw). If you're setting up a web server, after setup you can open up port 80
(and any other ports you need) by running the following command:
sudo ufw allow 80/tcp
After this is done, you can log onto your server with the following command:
ssh [your_deploy_username]@[server] -p [your_ssh_port] -i [your_key_location]
Note: If your SSH port is 22 (unchanged), the -p [your_ssh_port]
portion can be omitted. Likewise, if your key location is unchanged, the -i [your_key_location]
can be omitted.
This is a weird thing, but it seems that you need to run bash manually after logging in for the first time. Just type bash
in the command, and you should see your current username@server prepended to your $
prompt. Also, the server may make some noise about needing to configure your locale and/or needing to reboot. Proceed accordingly.
To save some keystrokes, you can configure a config
file in your .ssh
folder:
-
If it doesn't already exist, create a file in your
.ssh
folder calledconfig
(note the absence of any file extension). -
Append the following to the file:
Host [convenient_shortcut] HostName [server] Port [your_ssh_port] User [your_deploy_username] IdentityFile [your_key_location]
Now, from here on, you can simply SSH in by typing the command ssh [convenient_shortcut]
.
You can go on to configure fail2ban's options to your liking, for example setting up a mail server to email regular intrusion attempts to you. Google for more information.
If the Ansible playbook fails at any point, feel free to tweak the options or do some other form of troubleshooting and run it again. The script is idempotent (can be run over and over) as long as it doesn't reach the last step of disallowing root SSH access. If you wish to configure the Ansible playbook, make sure that disallowing root SSH access remains the last step.
if the script is able to run ssh-keyscan
successfully but fails when Ansible is trying to SSH in, try running just the Ansible command with verbose output:
ansible-playbook -i hosts.ini bootstrap.yml --user root -vvvv
If it's nagging at you about your permissions being too open, chmod
your folder and keys accordingly. I suggest 600
permissions for your keys and either 700
or 755
for your .ssh
folder.
chmod 600 id_rsa.pub
Otherwise, drop me a note and I'll see if I can help.
- Create a bash version of the script
- Variants of this playbook that set up Mosh and/or fish
- Test on EC2
- Integrate with DO and EC2 APIs