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
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
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;
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.