Ansible Playbook designed for a server-rendered React app using Nginx, PM2, and Certbot via Let's Encrypt.
Tested with OS: Ubuntu 16.04 LTS (64-bit)
Tested with Cloud Providers: Digital Ocean
- Replace
example.com
in theproduction
playbook with your IP address or domain name. If using a domain, make sure your domain points to your server's IP address using an A record. - Run
. bin/production/setup_production.sh
- Visit your IP address to view the sample site.
A quick way to get started is using the test application at https://github.com/YPCrumble/server-rendered-react-app.
The main settings to change are in the group_vars/production/vars.yml and group_vars/production/vault.yml files, where you can configure the location of your Git project, the project name, and the application name which will be used throughout the Ansible configuration. Make sure to change the sudo user password (default for convenient testing is password
).
If your app needs additional system packages installed, you can add them in roles/web/tasks/install_additional_packages.yml
.
NOTE: Do not run the Security role without understanding what it does. Improper configuration could lock you out of your machine.
Security role tasks
The security module performs several basic server hardening tasks. Inspired by this blog post:
- Updates apt
- Performs
aptitude safe-upgrade
- Adds a user specified by the
server_user
variable, found inroles/base/defaults/main.yml
- Adds authorized key for the new user
- Installs sudo and adds the new user to sudoers with the password specified by the
server_user_password
variable found inroles/security/defaults/main.yml
- Installs and configures various security packages:
- Unattended upgrades
- Uncomplicated Firewall
- Fail2ban (NOTE: Fail2ban is disabled by default as it is the most likely to lock you out of your server. Handle with care!)
- Restricts connection to the server to SSH and http(s) ports
- Limits su access to the sudo group
- Disallows password authentication (be careful!)
- Disallows root SSH access (you will only SSH to your machine as your new user and use a password for
sudo
access) - Restricts SSH access to the new user specified by the
server_user
variable - Deletes the
root
password
Security role configuration
- Change the
server_user
fromroot
to something else inroles/base/defaults/main.yml
- Change the sudo password in
group_vars/production/vault.yml
- Change variables in
./roles/security/vars/
per your desired configuration
Running the Security role
- The security role can be run by running
security.yml
via:
ansible-playbook -i production security.yml
NOTE: to enable the Security module you can use the steps above prior to following the steps below.
For example:
# production
[webservers]
example.com nginx_use_letsencrypt=true
[production:children]
webservers
ansible-playbook -i production site.yml -K
ansible-playbook -i production site.yml -K --tags=deploy
- Make sure ssh-agent is running on your local machine (run
ssh-agent
from the command line) - Test that your ssh key is ready for forwarding via
ssh-add -L
; if not, add your key (ssh-add yourkey
) - Create an entry in your local .ssh/config file:
Host example.com User example # Make sure to set this to your application user. IdentityFile ~/.ssh/id_rsa ForwardAgent Yes IdentitiesOnly yes
- Make sure you're using the SSH connection to your repo (not the HTTPS connection)
- Ensure that your domain (e.g.,
example.com
) points to your server's IP address - Refer to the Github tutorial for help debugging SSH agent forwarding
By default, the playbook won't create a swap file. To create/enable swap, simply change the values in roles/base/defaults/main.yml.
You can also override these values in the main playbook, for example:
---
...
roles:
- { role: base, create_swap_file: yes, swap_file_size_kb: 1024 }
- web
This will create and mount a 1GB swap. Note that block size is 1024, so the size of the swap file will be 1024 x swap_file_size_kb
.
A certbot
role has been added to automatically install the certbot
client and generate a Let's Encrypt SSL certificate.
Requirements:
- A DNS "A" or "CNAME" record must exist for the host to issue the certificate to.
- The
--standalone
option is being used, so port 80 or 443 must not be in use (the playbook will automatically check if Nginx is installed and will stop and start the service automatically).
In roles/nginx/defaults.main.yml
, you're going to want to override the nginx_use_letsencrypt
variable and set it to yes/true to reference the Let's Encrypt certificate and key in the Nginx template.
In roles/certbot/defaults/main.yml
, you may want to override the certbot_admin_email
variable.
A cron job to automatically renew the certificate will run daily. Note that if a certificate is due for renewal (expiring in less than 30 days), Nginx will be stopped before the certificate can be renewed and then started again once renewal is finished. Otherwise, nothing will happen so it's safe to leave it running daily.
- Ansible - Getting Started
- Ansible - Best Practices
- How to deploy encrypted copies of your SSL keys and other files with Ansible and OpenSSL
- Using SSH agent forwarding - GitHub developer guide
Contributions are welcome!
Thanks to the following repositories for code and inspiration: