Setup a landing page on Hugo: ssh, fail2ban, nginx, certbot

You want a landing page or blog. You can use Wordpress but I think it is too mush for this. The best choice will be being Hugo - a static site generator written in Go. It is optimized for speed, easy use and configurability. Hugo takes a directory with content and templates and renders them into a full HTML website which hosted by nginx. Hugo makes use of markdown files with front matter for meta data.

So in series of articles we setup a landing page with a full template based on little virtual machine (5$) in Digital Ocean. Also, we will use Ubuntu, Docker and Nginx.

Setup will separate to three stages:

  1. Basic server setup and some security improvements:
    • ssh - changing default pot, disabling password authentication and generate public / secret key;
    • fail2ban - for ban hackers and scriptkiddies;
    • nginx - the open source reverse proxy for basic load balancing and HTTPS termination;
    • Digital Ocean firewall - disable unused ports otherwise ssh port, 80 (http) and 443 (https).
  2. Set up the Docker and Portainer:
    • docker - is a software platform for quickly building, debugging and deploying applications using containers;
    • portainer - useful admin panel for docker control.
  3. Set up hugo an gitlab ci:
    • building setup with gitlab ci and setup landing page template;
    • githook for automatic update your site after your changes.

Lets start!

Basic server setup and some security improvements

This stage based on fact that you already have a virtual machine.

Create user with sudo access

Use the adduser command to add a new user to your system:

adduser username

Use the usermod command to add the user to the sudo group:

usermod -aG sudo username

By default, on Ubuntu, members of the sudo group have sudo privileges.

Check sudo:

sudo su

SSH setup

WARNING! All ssh changes test in a separate session without closing current. Otherwise you can’t revert your changes without recreating a virtual machine.

Base config file of SSH-server placed in /etc/ssh/sshd_config. You can change it only as superuser. After changes you must restart ssh service for applying changes:

sudo service ssh restart

Disable login as root

By default, root access is denied by password (by key - you can) - the PermitRootLogin option is set to without-password4). But, provided that by default in Ubuntu the user added during the installation of the system has the ability to solve all administrative tasks through sudo, creating the ability to root access to the system via ssh - it seems unreasonable (even with key authentication). It is recommended to completely disable it. this option, or apply it only in forced-commands-only mode. You can disable root access this way:

PermitRootLogin no

Password authentication

Password-enabled authentication by default is practically the most primitive way of authorization in sshd. On the one hand, this simplifies the configuration and connection of new users (it is enough for the user to know their system login / password), on the other hand, you can always pick up a password, and users often neglect to create complex and long passwords. Special bots constantly scan ssh servers available from the Internet and try to log in to them by sorting logins / passwords from their database. Using password authentication is strongly discouraged. You can disable it like this:

PasswordAuthentication no

Ssh key authentication

The most preferred method of authorization is authentication based on SSH2 RSA keys. With this method, the user generates a key pair on his side, of which one is secret and the other public. The public key is copied to the server and used to verify the identity of the user. For more details about creating a key pair and how to place it on the server, see the description of the SSH-client. You can enable authentication with a public key in the following way:

PubkeyAuthentication yes

The port can be set as an absolute value for all interfaces using the Port directive, or as a specific value for each interface using the ListenAddress directive. For example, the ListenAddress directive:

Port 30433

Setup ssh client

On the client machine, we generate the key:

ssh-keygen -t rsa

We receive a prompt to enter a password to protect the key file (it turns out to be useful when the file falls into the wrong hands). If we are going to run scripts via SSH, we leave it blank. Transmit the public key to the server with the command:

ssh-copy-id -i ~/.ssh/ user@server

Okay, we can be login.

But our ssh server running on custom port. So use next command:

ssh-copy-id -i ~/.ssh/ "-p port user@server"

Fail2ban setup

Ensure your system is up to date, install fail2ban

apt-get update && apt-get upgrade -y
apt-get install fail2ban
ufw allow ssh
ufw enable

Fail2ban reads .conf configuration files first, then .local files override any settings. Because of this, all changes to the configuration are generally done in .local files, leaving the .conf files untouched.

fail2ban.conf contains the default configuration profile. The default settings will give you a reasonable working setup. If you want to make any changes, it’s best to do it in a separate file, fail2ban.local, which overrides fail2ban.conf. Rename a copy fail2ban.conf to fail2ban.local.

cp /etc/fail2ban/fail2ban.conf /etc/fail2ban/fail2ban.local

From here, you can opt to edit the definitions in fail2ban.local to match your desired configuration. The values that can be changed are:

  • loglevel: The level of detail that Fail2ban’s logs provide can be set to 1 (error), 2 (warn), 3 (info), or 4 (debug).
  • logtarget: Logs actions into a specific file. The default value of /var/log/fail2ban.log puts all logging into the defined file. Alternately, you can change the value to:
    • STDOUT: output any data
    • STDERR: output any errors
    • SYSLOG: message-based logging
    • FILE: output to a file
  • socket: The location of the socket file.
  • pidfile: The location of the PID file.

The jail.conf file will enable Fail2ban for SSH by default for Debian and Ubuntu, but not CentOS. All other protocols and configurations (HTTP, FTP, etc.) are commented out. If you want to change this, create a jail.local for editing:

cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

If using CentOS or Fedora you will need to change the backend option in jail.local from auto to systemd. This is not necessary on Debian 8 or Ubuntu 16.04, even though both use systemd as well.


# "backend" specifies the backend used to get files modification.
# Available options are "pyinotify", "gamin", "polling", "systemd" and "auto".
# This option can be overridden in each jail as well.
backend = systemd

No jails are enabled by default in CentOS 7. For example, to enable the SSH daemon jail, uncomment the following lines in jail.local:


enabled = true

Whitelist IPPermalink To ignore specific IPs, add them to the ignoreip line. By default, this command will not ban the localhost. If you work from a single IP address often, it may be beneficial to add it to the ignore list:


# "ignoreip" can be an IP address, a CIDR mask or a DNS host. Fail2ban will not
# ban a host which matches an address in this list. Several addresses can be
# defined using space separator.
ignoreip =

If you wish to whitelist IPs only for certain jails, this can be done with the fail2ban-client command. Replace JAIL with the name of your jail, and with the IP you wish to whitelist.

fail2ban-client set JAIL addignoreip

Ban Time and Retry AmountPermalink

Set bantime, findtime, and maxretry to define the circumstances and the length of time of a ban:


# "bantime" is the number of seconds that a host is banned.
bantime  = 600

# A host is banned if it has generated "maxretry" during the last "findtime"
# seconds.
findtime = 600
maxretry = 3
  • bantime: The length of time in seconds for which an IP is banned. If set to a negative number, the ban will be permanent. The default value of 600 is set to ban an IP for a 10-minute duration.
  • findtime: The length of time between login attempts before a ban is set. For example, if Fail2ban is set to ban an IP after five (5) failed log-in attempts, those 5 attempts must occur within the set 10-minute findtime limit. The findtime value should be a set number of seconds.
  • maxretry: How many attempts can be made to access the server from a single IP before a ban is imposed. The default is set to 3.

Nginx + HTTPS

You should install nginx:

sudo apt install nginx
sudo ufw allow 'Nginx Full'

So, we can check your site: http:/// Next, we setup Let’s Encrypt certbot for get cert and setup https.

Installing Certbot

Certbot is in very active development, so the Certbot packages provided by Ubuntu tend to be outdated. However, the Certbot developers maintain a Ubuntu software repository with up-to-date versions, so we’ll use that repository instead.

sudo add-apt-repository ppa:certbot/certbot

Then, update the package list to pick up the new repository’s package information.

sudo apt-get update

And finally, install Certbot’s Nginx package with apt-get.

sudo apt-get install python-certbot-nginx

Setting up Nginx

Certbot can automatically configure SSL for Nginx, but it needs to be able to find the correct server block in your config. It does this by looking for a server_name directive that matches the domain you’re requesting a certificate for.

If you’re starting out with a fresh Nginx install, you can update the default config file. Open it with nano or your favorite text editor.

sudo nano /etc/nginx/sites-available/default

Find the existing server_name line and replace the underscore, _, with your domain name:



Save the file and quit your editor.

Then, verify the syntax of your configuration edits.

sudo nginx -t

If you get any errors, reopen the file and check for typos, then test it again.

Once your configuration’s syntax is correct, reload Nginx to load the new configuration.

sudo nginx -s reload

Certbot will now be able to find the correct server block and update it.

Obtaining an SSL Certificate

Certbot provides a variety of ways to obtain SSL certificates, through various plugins. The Nginx plugin will take care of reconfiguring Nginx and reloading the config whenever necessary:

sudo certbot --nginx -d -d

This runs certbot with the –nginx plugin, using -d to specify the names we’d like the certificate to be valid for.

If this is your first time running certbot, you will be prompted to enter an email address and agree to the terms of service. After doing so, certbot will communicate with the Let’s Encrypt server, then run a challenge to verify that you control the domain you’re requesting a certificate for.

If that’s successful, certbot will ask how you’d like to configure your HTTPS settings. So follow the steps from certbot.

At the end of install certificate process certbot will wrap up with a message telling you the process was successful and where your certificates are stored:


 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/ Your cert will
   expire on 2017-10-23. To obtain a new or tweaked version of this
   certificate in the future, simply run certbot again with the
   "certonly" option. To non-interactively renew *all* of your
   certificates, run "certbot renew"
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:
   Donating to EFF:          
Your certificates are downloaded, installed, and loaded. Try reloading your website using https:// and notice your browser’s security indicator. It should indicate that the site is properly secured, usually with a green lock icon. If you test your server using the SSL Labs Server Test, it will get an A grade.

Let’s finish by testing the renewal process.

Testing Certbot Auto-Renewal

Let’s Encrypt’s certificates are only valid for ninety days. This is to encourage users to automate their certificate renewal process. The certbot package we installed takes care of this for us by running ‘certbot renew’ twice a day via a systemd timer. On non-systemd distributions this functionality is provided by a script placed in /etc/cron.d. This task runs twice a day and will renew any certificate that’s within thirty days of expiration.

To test the renewal process, you can do a dry run with certbot:

sudo certbot renew --dry-run

If you see no errors, you’re all set. When necessary, Certbot will renew your certificates and reload Nginx to pick up the changes. If the automated renewal process ever fails, Let’s Encrypt will send a message to the email you specified, warning you when your certificate is about to expire.

Setup Digital ocean firewall

In your client account choose networking -> firewalls -> create new firewall. When you should add 3 rules:

  • HTTP rule for port 80.
  • HTTPS rule for port 443.
  • Custom rule with TCP traffic for port 30433 (custom ssh port).

So check ssh and https on your site.

Next post » Set up the Docker and Portainer