DevOps Series Ansible Deployment of Nginx with SSL

0
12619

This is the 12th article in the DevOps series. It is a tutorial on installing Nginx with SSL. Nginx is a high performance Web server and can be used as a load balancer.

Nginx is a Web server written in C by Igor Sysoev. It can be used as a load balancer, reverse proxy and HTTP cache server. Nginx was designed to handle over 10,000 client connections and has support for TLS (transport layer security) and SSL (secure sockets layer). It requires a very low memory footprint and is IPv6-compatible. Nginx can also be used as a mail server proxy. It was first released in 2004 under a BSD-like licence.

The OpenSSL project provides a free and open source software security library that implements the SSL and TLS protocols. This library is used by applications to secure communication between machines in a computer network. The library is written in C and Assembly, and uses a dual-licence — Apache License 1.0 and a four-clause BSD licence. The library implements support for a number of ciphers and cryptographic functions. It was first released in 1998 and is widely used in Internet Web servers.

An Ubuntu 16.04.1 LTS guest virtual machine (VM) instance using KVM/QEMU is chosen to install Nginx.

$ cat /etc/lsb-release

DISTRIB_ID=Ubuntu

DISTRIB_RELEASE=16.04

DISTRIB_CODENAME=xenial

DISTRIB_DESCRIPTION=”Ubuntu 16.04.1 LTS”

The default installation on the guest VM does not come with Python2, and hence you need to install this on the guest machine, manually, as shown below:

$ sudo apt-get update

$ sudo apt-get install python-minimal

The host system is a Parabola GNU/Linux-libre x86_64 system, and Ansible is installed on the host system using the distribution package manager. The version of Ansible used is 2.4.2.0 as indicated below:

$ ansible --version

ansible 2.4.2.0

config file = /etc/ansible/ansible.cfg

configured module search path = [u’/home/shakthi/.ansible/plugins/modules’, u’/usr/share/ansible/plugins/modules’]

ansible python module location = /usr/lib/python2.7/site-packages/ansible

executable location = /bin/ansible

python version = 2.7.14 (default, Sep 20 2017, 01:25:59) [GCC 7.2.0]

You should add an entry to the /etc/hosts file for the guest Ubuntu VM, as follows:

192.168.122.244 ubuntu
Figure 1: Nginx home page

On the host system, let’s create a project directory structure to store the Ansible playbooks, inventory and configuration files, as follows:

ansible/inventory/kvm/

/playbooks/configuration/

/playbooks/admin/

/files/

An ‘inventory’ file is created inside the inventory/kvm folder that contains the following:

ubuntu ansible_host=192.168.122.244 ansible_connection=ssh ansible_user=ubuntu ansible_password=password

You should now be able to issue commands to the guest OS, using Ansible. For example:

$ ansible -i inventory/kvm/inventory ubuntu -m ping

ubuntu | SUCCESS => {

“changed”: false,

“ping”: “pong”

}

Installing Nginx

The Nginx software package in Ubuntu can be installed on the guest machine. The APT package repository is first updated before installing the Nginx Web server. The Uncomplicated Firewall (UFW) is then used to enable both HTTP and HTTPS access on the guest OS. The Web server is then started, and the playbook waits for the server to listen on port 80. The Ansible playbook is provided below for reference:

---

- name: Install nginx

hosts: ubuntu

become: yes

become_method: sudo

gather_facts: true

tags: [nginx]

tasks:

- name: Update the software package repository

apt:

update_cache: yes

- name: Install nginx

package:

name: “{{ item }}”

state: latest

with_items:

- nginx

- name: Allow Nginx Full

ufw:

rule: allow

name: Nginx Full

state: enabled

- name: Allow Nginx Full

ufw:

rule: allow

name: OpenSSH

state: enabled

- name: Start nginx

service:

name: nginx

state: started

- wait_for:

port: 80

The above playbook can be invoked as follows:

$ ansible-playbook -i inventory/kvm/inventory playbooks/configuration/nginx-ssl.yml --tags nginx -K
Figure 2: SSL certificate

The -K option prompts for the sudo password of the Ubuntu user. You can append multiple -v to the end of the playbook invocation to get a more verbose output.

If you open a browser on the host system with the URL http://192.168.122.244, you should see the default Nginx home page as shown in Figure 1.

Generating SSL certificates

The required SSL certificates need to be created using Ansible. The OpenSSL and Python-openssl packages are installed after updating the APT software repository in Ubuntu. An OpenSSL private key is generated in the /etc/ssl/private/ansible.com.pem file. The /etc/ssl/csr directory is created before generating the OpenSSL certificate signing request (CSR) with the required certificate parameters in the /etc/ssl/csr/www.ansible.com.csr file. The actual self-signed certificate is then generated as shown in the following playbook:

- name: Create SSL certificates

hosts: ubuntu

become: yes

become_method: sudo

gather_facts: true

tags: [ssl]

tasks:

- name: Update the software package repository

apt:

update_cache: yes

- name: Install openssl

package:

name: “{{ item }}”

state: latest

with_items:

- openssl

- python-openssl

- name: Generate an OpenSSL private key

openssl_privatekey:

path: /etc/ssl/private/ansible.com.pem

- name: Create directory

file:

path: /etc/ssl/csr

state: directory

mode: 0755

- name: Generate an OpenSSL Certificate Signing Request

openssl_csr:

path: /etc/ssl/csr/www.ansible.com.csr

privatekey_path: /etc/ssl/private/ansible.com.pem

country_name: IN

organization_name: Ansible

email_address: author@shakthimaan.com

common_name: www.ansible.com

- name: Generate a self signed certificate

openssl_certificate:

path: /etc/ssl/certs/nginx-selfsigned.crt

privatekey_path: /etc/ssl/private/ansible.com.pem

csr_path: /etc/ssl/csr/www.ansible.com.csr

provider: selfsigned

The above playbook can be run as follows:

$ ansible-playbook -i inventory/kvm/inventory playbooks/configuration/nginx-ssl.yml --tags ssl -K

Configuring Nginx for SSL

The final step is to configure Nginx to use SSL. A self-signed.conf file is created in the /etc/nginx/snippets folder that contains the following:

ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;

ssl_certificate_key /etc/ssl/private/ansible.com.pem;

The SSL parameter configurations are stored in the /etc/nginx/snippets/ssl-params.conf file as shown below:

# from https://cipherli.st/

# and https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

ssl_prefer_server_ciphers on;

ssl_ciphers “EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH”;

ssl_ecdh_curve secp384r1;

ssl_session_cache shared:SSL:10m;

ssl_session_tickets off;

ssl_stapling on;

ssl_stapling_verify on;

resolver 8.8.8.8 8.8.4.4 valid=300s;

resolver_timeout 5s;

# Disable preloading HSTS for now. You can use the commented out header line that includes

# the “preload” directive if you understand the implications.

#add_header Strict-Transport-Security “max-age=63072000; includeSubdomains; preload”;

add_header Strict-Transport-Security “max-age=63072000; includeSubdomains”;

add_header X-Frame-Options DENY;

add_header X-Content-Type-Options nosniff;
Figure 3: Nginx HTTPS home page

The Nginx Web server configuration for this host (google.com, for example) is then created in the /etc/nginx/sites-enabled folder with the following contents:

server {

listen 80;

root /var/www/html;

index index.nginx-debian.html;

server_name google.com www.google.com;

}

server {

listen 443 ssl http2 default_server;

include snippets/self-signed.conf;

include snippets/ssl-params.conf;

}

The Ansible playbook for configuring Nginx with SSL is as follows:

- name: Setup nginx with SSL

hosts: ubuntu

become: yes

become_method: sudo

gather_facts: true

tags: [https]

tasks:

- copy:

src: ../../files/self-signed.conf

dest: /etc/nginx/snippets/self-signed.conf

owner: root

group: root

mode: 0644

- copy:

src: ../../files/ssl-params.conf

dest: /etc/nginx/snippets/ssl-params.conf

owner: root

group: root

mode: 0644

- copy:

src: ../../files/google.com

dest: /etc/nginx/sites-enabled/google.com

owner: root

group: root

mode: 0644

- name: Restart nginx

service:

name: nginx

state: restarted

- wait_for:

port: 443

The above playbook can be executed as follows:

$ ansible-playbook -i inventory/kvm/inventory playbooks/configuration/nginx-ssl.yml --tags https -K

You can now open https://192.168.122.244 in a browser on the host system, and view the self-signed certificate as shown in Figure 2.

After accepting the certificate, you will be able to see the default Nginx home page as shown in Figure 3.

You can also use the curl command to view the home page from the command line, as follows:

$ curl https://192.168.122.244 -k

<!DOCTYPE html>

<html>

<head>

<title>Welcome to nginx!</title>

<style>

body {

width: 35em;

margin: 0 auto;

font-family: Tahoma, Verdana, Arial, sans-serif;

}

</style>

</head>

<body>

<h1>Welcome to nginx!</h1>

<p>If you see this page, the nginx web server is successfully installed and

working. Further configuration is required.</p>

<p>For online documentation and support please refer to

<a href=”http://nginx.org/”>nginx.org</a>.<br/>

Commercial support is available at

<a href=”http://nginx.com/”>nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>

</body>

</html>

Validation

You can run a number of validation checks on the SSL certificate, periodically, to ascertain that it still holds good and meets your requirements. A few examples of sanity checks that you can perform on the certificate are shown below for reference:

- name: Validate SSL certificate

hosts: ubuntu

become: yes

become_method: sudo

gather_facts: true

tags: [validate]

tasks:

- name: Certificate matches with the private key

openssl_certificate:

path: /etc/ssl/certs/nginx-selfsigned.crt

privatekey_path: /etc/ssl/private/ansible.com.pem

provider: assertonly

- name: Certificate can be used for digital signatures

openssl_certificate:

path: /etc/ssl/certs/nginx-selfsigned.crt

provider: assertonly

key_usage:

- digitalSignature

key_usage_strict: true

- name: Certificate uses a recent signature algorithm (no SHA1, MD5 or DSA)

openssl_certificate:

path: /etc/ssl/certs/nginx-selfsigned.crt

provider: assertonly

signature_algorithms:

- sha224WithRSAEncryption

- sha256WithRSAEncryption

- sha384WithRSAEncryption

- sha512WithRSAEncryption

- sha224WithECDSAEncryption

- sha256WithECDSAEncryption

- sha384WithECDSAEncryption

- sha512WithECDSAEncryption

- name: Certificate matches the domain

openssl_certificate:

path: /etc/ssl/certs/nginx-selfsigned.crt

provider: assertonly

subject_alt_name:

- DNS:www.ansible.com

- name: Certificate is valid for another month (30 days) from now

openssl_certificate:

path: /etc/ssl/certs/nginx-selfsigned.crt

provider: assertonly

valid_in: 2592000

You can invoke the above validation checks in the playbook using the following command:

$ ansible-playbook -i inventory/kvm/inventory playbooks/configuration/nginx-ssl.yml --tags validate -K

Uninstalling

An uninstall playbook is provided in the playbooks/admin/uninstall-nginx.yml file to stop the Nginx Web server, disable access to the port in the firewall, and to remove the software from the guest VM:

---

- name: Uninstall Nginx

hosts: ubuntu

become: yes

become_method: sudo

gather_facts: true

tags: [server]

tasks:

- name: Stop the web server

service:

name: nginx

state: stopped

- name: Disable Nginx Full

ufw:

rule: deny

name: Nginx Full

state: enabled

- name: Uninstall apache2

package:

name: “{{ item }}”

state: absent

with_items:

- nginx

The above playbook can be run as follows:

$ ansible-playbook -i inventory/kvm/inventory playbooks/admin/uninstall-nginx.yml -K

You can verify the firewall status using the ufw command, as shown below:

$ sudo ufw status

Status: active

To                     Action              From

--                     ------              ----

Nginx Full             DENY              Anywhere

OpenSSH                ALLOW             Anywhere

Nginx Full (v6)        DENY             Anywhere (v6)

OpenSSH (v6)           ALLOW            Anywhere (v6)

Please refer to the Nginx documentation website (https://nginx.org/en/docs/) for more information.

LEAVE A REPLY

Please enter your comment!
Please enter your name here