How was this site deployed?

May 16, 2020, 3:47 p.m.   Gavin   Programming  

Welcome to the Deployment Zone

On this very first blog post I'm going to cover how this site was deployed.

The journey we're going on starts after the creation of a Ubuntu 18.04 LTS DigitalOcean Droplet with Docker preinstalled and the purchasing of a Namecheap domain. Both of these processes are simple and self-explanatory. From there I will cover everything from setting up the domain to configuring best practice precautions for the server.

Below you can find the Github README for the setup instructions of the Docker Stack itself -

Linking Domain to Server

The first step is to configure Namecheap with the DigitalOcean nameservers. It's really quite easy.

At the Namecheap account dashboard,

  • Go to Domain - then Manage
  • Under Nameservers - select Custom DNS - enter the DO nameservers

See this official DigitalOcean article for more details and some pretty screenshots of the process.

Configuring DNS Records

Now that the domain is aware of the proper nameservers, managing DNS records through DigitalOcean is possible.

At the DigitalOcean project Dashboard,

  • Go to Networking - then Domains
  • Under Create new record are tabs for each record type - Create the following records, leave the default TTL
    • A Record
      "HOSTNAME"= @
      "WILL DIRECT TO"= DropletHere
    • CNAME Record
      "HOSTNAME"= www
      "IS AN ALIAS OF"= DomainNameHere
    • CAA Record
      "HOSTNAME"= @
      "TAG"= issue

The CAA Record is just for being in compliance with best practice and getting a good score on SSL Labs.

See this official DigitalOcean article and this Official Digital article, for more details and some more nice screenshots of the process.

Now let's do some server work.

Creating the Docker Swarm

Docker is already installed on this droplet by default so that process is not covered here. All that's needed is one command executed by root.

docker swarm init --advertise-addr <ip address of droplet> --listen-addr <ip address of droplet>

Creating users and groups

For this system two new users are required since the goal is to have a user for remote login only and a docker user for running the application.

There are two things to note here in this example.

  • Since I wanted to create remote backups off-site I required that the docker user and remote user share a group for file transfer.
  • The command group below specifies a uid and gid for each user. This is only necessary for the docker user since the Docker images being deployed possess the same uid/gid of the user running the stack.

The remote user here has been named as ruser and the docker user as duser.
As root the following is executed.

#add docker user
groupadd -g <gid here> duser
useradd -g duser -u <uid here> -s /bin/bash duser
mkhomedir_helper duser
passwd duser
#add to the docker group in order to run docker commands
usermod -aG docker duser
id duser
#test out the user
su - duser

#add remote user
groupadd -g <gid here> ruser
useradd -g ruser -u <uid here> -s /bin/bash ruser
mkhomedir_helper ruser
passwd ruser
id ruser
#test out the user
su - ruser

#add file-transfer group
groupadd file-transfer
usermod -aG file-transfer duser
usermod -g file-transfer ruser
usermod -aG ruser ruser

Adding SSH keys to the remote user

During the creation process of the droplet. The SSH keys option was chosen for the root user login. This means the client key can be copied from root's home.

As root the following is executed.

#copy the key file to ruser
mkdir /home/ruser/.ssh
cp /root/.ssh/authorized_keys /home/ruser/.ssh/authorized_keys
chown -R ruser:ruser /home/ruser/.ssh

#back on your workstation test if ssh works.
ssh -p 22 ruser@<Server IP here>

Configuring UFW and Changing the Default SSH Port

Choose a port to change for SSH. Execute the following commands as root.

#open firewall ports
#the ssh port is configured as limit to prevent brute force login attempts
ufw allow 80
ufw allow 443
ufw limit <new ssh port here>/tcp

#change ssh port by editing the "Port 22" line of this file
vi /etc/ssh/sshd_config

#restart the ssh service
systemctl restart sshd
#check to see if your new port has been binded to the service
netstat -tulpn | grep <new ssh port here>

#in case of a mistake, open another client session to test the new ssh port login
ssh -p <new ssh port here> root@<Server IP here>

#if the login above works, remove port 22 from the ufw rules
ufw status numbered
ufw delete <place the rule number here>

#check ufw status
ufw status verbose

#finally change the named port for ssh
#find 22 in the file below
vi /etc/services

Configure sudo Access for the Remote User and Disable Root User Remote Login

Run the following commands as root.

#add remote user to sudo group
usermod -aG sudo ruser

#confirm the remote user has access
su - ruser
ls -la /root
sudo su - root

#disable password remote access
#make only the remote user have remote access
vi /etc/ssh/sshd_config
  #Ensure these settings are present
  ChallengeResponseAuthentication no
  PasswordAuthentication no
  X11Forwarding no
  AllowUsers ruser
  PermitRootLogin no
  #setting this is not entirely necessary
  #fun fact - it removes the DigitalOcean login message
  usePAM no

#restart the ssh service  
systemctl restart sshd

#test the new ssh port login
#only ruser will work
client$ ssh -p <new ssh port here> root@<Server IP here>
client$ ssh -p <new ssh port here> duser@<Server IP here>
client$ ssh -p <new ssh port here> ruser@<Server IP here>

#no need for the public key on the root user anymore
rm /root/.ssh/authorized_keys

Tweak History and sudo Settings

To have a better tracking over all commands run on the server and to have a history for sudo commands and remove unneeded permissions with sudo, apply the following settings.

#add history settings to each user
vi ~/.bashrc
  HISTTIMEFORMAT="%d/%m/%y %T "

#this must be done as root
#since I prefer vi over nano, visudo was set for vi
export EDITOR=vi
#edit /etc/sudoers
  Defaults  requiretty
  Defaults  use_pty
  Defaults  log_input, log_output
  Defaults  log_host, log_year, logfile="/var/log/sudo.log"

Performing Updates and Installing fail2ban.

Run the following commands as root.

#update the system
apt-get update && apt-get upgrade -y

#install fail2ban
apt-get install fail2ban

#change the ssh port to match the custom port
vi /etc/fail2ban/jail.local

#check the status of fail2ban 
fail2ban-client status 
vi /var/log/auth.log

Configuring the Server for Unattended Upgrades

Run the following commands as root.

#install packages
apt-get install unattended-upgrades apt-listchanges
dpkg-reconfigure --priority=low unattended-upgrades

#set intervals
vi /etc/apt/apt.conf.d/20auto-upgrades
  APT::Periodic::Update-Package-Lists "1";
  APT::Periodic::Unattended-Upgrade "1";
  APT::Periodic::Download-Upgradeable-Packages "1";
  APT::Periodic::AutocleanInterval "7";

#other settings
#// is a comment, below only enables security updates
vi /etc/apt/apt.conf.d/50unattended-upgrades
  Unattended-Upgrade::Automatic-Reboot "true";
  Unattended-Upgrade::Automatic-Reboot-Time "02:00";
  Unattended-Upgrade::Allowed-Origins {
  //       "${distro_id}:${distro_codename}-updates";
  //      "${distro_id}:${distro_codename}-proposed";
  //      "${distro_id}:${distro_codename}-backports";

#test and upgrade without running it
unattended-upgrade -v -d --dry-run

#run an actual upgrade
unattended-upgrade -v -d

#log file location
vi /var/log/unattended-upgrades/unattended-upgrades.log

Configuring the Network Firewall

Now that the server is setup. The DigitalOcean Network firewall can be configured. This is advised because unwanted access is prevented before reaching the server.

As many firewalls as desired can be configured in the DigitalOcean Networking dashboard, so identifiers can be used to mark droplets with what firewalls should apply to the droplet.

Adding identifiers

At the DigitalOcean project Dashboard,

  • Go to Droplets - then find the docker droplet's Tags column
  • Add two tags - frontend and management

Creating the firewalls

At the DigitalOcean project Dashboard,

  • Go to Networking - then Firewalls
  • Go to Create Firewall
    • Name it frontend-fw
    • Delete the default Inbound rule for type SSH
    • Add two new Inbound rules for type HTTP and HTTPS
    • At the bottom under Apply to Droplets - add frontend
  • Go to Create Firewall
    • Name it management-fw
    • Delete the default Inbound rule for type SSH
    • Add a new rule with the custom SSH port with the Source set to the remote workstation IPs needed for SSH access
    • At the bottom under Apply to Droplets - add management

As an optional last step, the local server firewall (ufw) can be disabled if managing future rules in both the network and local firewalls is unwanted. Regardless the status of the changes applied can be checked from another server or workstation. See below.

#optional step
systemctl stop ufw
systemctl disable ufw

#to check if the rules are in effect,
#issue the following command from somewhere outside your server.
nmap -Pn <IP-Address-Here>

See this official DigitalOcean article for more details on setting up one of their network firewalls.

That's all for this post.

Thanks for reading to the end and hopefully this little tutorial was of some use.

devops docker wagtail Namecheap DigitalOcean deployment