Skip to content

Commit

Permalink
ara_frontend_nginx: add support for https/ssl
Browse files Browse the repository at this point in the history
  • Loading branch information
dmsimard committed Aug 17, 2021
1 parent a196494 commit c19b047
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 5 deletions.
13 changes: 13 additions & 0 deletions roles/ara_api/defaults/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ ara_api_frontend_server: null
# Use this variable if you need to have your own custom nginx or apache configuration.
ara_api_frontend_vhost: null

# Enable https on port 443 in addition to http on port 80
# Enabling SSL requires specifying the path to your key and certificate via
# ara_api_frontend_ssl_key and ara_api_frontend_ssl_cert
ara_api_frontend_ssl: false

# Path to an existing SSL private key provisioned outside of this role
# ex: /etc/letsencrypt/live/ara.example.org/privkey.pem
ara_api_frontend_ssl_key: null

# Path to an existing SSL certificate provisioned outside of this role
# ex: /etc/letsencrypt/live/ara.example.org/fullchain.pem
ara_api_frontend_ssl_cert: null

# The WSGI server for running ARA's API server
# - null [default]: No persistent WSGI application server will be set up. Only the offline API client will work.
# - gunicorn: gunicorn will be installed and set up to run the API as a systemd service.
Expand Down
4 changes: 3 additions & 1 deletion roles/ara_frontend_nginx/tasks/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,10 @@
when: ara_api_external_auth

- name: Set up the ARA API nginx vhost
vars:
_default_vhost: "{{ ara_api_frontend_ssl | ternary('ara-api-ssl.conf.j2', 'ara-api.conf.j2') }}"
template:
src: "{{ ara_api_frontend_vhost | default('ara-api.conf.j2', True) }}"
src: "{{ ara_api_frontend_vhost | default( _default_vhost, True) }}"
dest: "{{ ara_nginx_config_path }}/ara-api.conf"
notify:
- restart nginx
Expand Down
55 changes: 55 additions & 0 deletions roles/ara_frontend_nginx/templates/ara-api-ssl.conf.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# {{ ansible_managed }}

upstream ara_api {
# fail_timeout=0 means we always retry an upstream even if it failed
# to return a good HTTP response
server {{ ara_api_wsgi_bind }} fail_timeout=0;
}

server {
listen 80;
server_name {{ ara_api_fqdn }};
return 301 https://{{ ara_api_fqdn }}$request_uri;
}

server {
listen 443;
server_name {{ ara_api_fqdn }};
{% if ara_api_external_auth -%}
auth_basic "Privileged access";
# A .htpasswd file
auth_basic_user_file {{ ara_api_external_auth_file }};
{% endif %}

client_max_body_size {{ ara_nginx_client_max_body_size }};
access_log /var/log/nginx/{{ ara_api_fqdn }}_access.log;
error_log /var/log/nginx/{{ ara_api_fqdn }}_error.log;

ssl on;
ssl_certificate {{ ara_api_frontend_ssl_cert }};
ssl_certificate_key {{ ara_api_frontend_ssl_key }};
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers EECDH+AESGCM:EDH+AESGCM;

location /static {
expires 7d;
add_header Cache-Control "public";
}

# Everything, including static files, is served by the backend
location ~ {
# checks if the file exists, if not found proxy to app
try_files $uri @proxy_to_app;
}

location @proxy_to_app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;

proxy_redirect off;
proxy_pass http://ara_api;
}
}
2 changes: 2 additions & 0 deletions roles/ara_frontend_nginx/templates/ara-api.conf.j2
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# {{ ansible_managed }}

upstream ara_api {
# fail_timeout=0 means we always retry an upstream even if it failed
# to return a good HTTP response
Expand Down
1 change: 1 addition & 0 deletions tests/test_tasks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
[ara]
api_client = {{ ara_api_client | default('offline') }}
api_server = {{ ara_api_server | default('http://127.0.0.1:8000') }}
api_insecure = {{ ara_api_insecure | default('false') }}
{% if ara_api_username is defined and ara_api_username %}
api_username = {{ ara_api_username }}
{% endif %}
Expand Down
11 changes: 8 additions & 3 deletions tests/vars/nginx_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,22 @@ ara_api_secure_logging: false
ara_api_configure_cron: true

# Set up gunicorn with nginx in front for external authentication
# TODO: add ssl support https://github.com/ansible-community/ara-collection/issues/37
ara_api_fqdn: 127.0.0.1
ara_api_fqdn: ara.example.org
ara_api_allowed_hosts:
- "{{ ara_api_fqdn }}"

ara_api_wsgi_server: gunicorn
ara_api_frontend_server: nginx
# Test with SSL enabled (self-signed)
ara_api_frontend_ssl: true
ara_api_frontend_ssl_key: /etc/ssl/ara.example.org/privkey.pem
ara_api_frontend_ssl_cert: /etc/ssl/ara.example.org/cert.pem
# TODO: Make the callback trust the certificate
ara_api_insecure: 'true'

# Configuration to the callback plugin
ara_api_client: http
ara_api_server: http://127.0.0.1
ara_api_server: https://ara.example.org
ara_api_username: ara
ara_api_password: hunter2

Expand Down
79 changes: 78 additions & 1 deletion tests/with_nginx.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,88 @@
# Copyright (c) 2021 The ARA Records Ansible authors
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

- name: Deploy and test ARA API with nginx and authentication enabled
- name: Deploy and test ARA API with nginx, authentication and SSL enabled
hosts: ara-api-server
gather_facts: yes
vars_files:
- vars/nginx_tests.yaml
pre_tasks:
- become: true
block:
- name: "Set {{ ara_api_fqdn }} to 127.0.0.1"
lineinfile:
path: /etc/hosts
line: "127.0.0.1 {{ ara_api_fqdn }}"

- name: Ensure directory for SSL certificate and private key exists
file:
path: "{{ item }}"
state: directory
recurse: yes
loop:
- "{{ ara_api_frontend_ssl_key | dirname }}"
- "{{ ara_api_frontend_ssl_cert | dirname }}"

- name: Set path to selfsigned openssl configuration
set_fact:
_selfsigned_conf: "{{ ara_api_frontend_ssl_key | dirname }}/selfsigned.conf"

# Generate a self-signed certificate over shell
# community.crypto requires python cryptography to be installed (on the remote note)
- name: Template an openssl configuration file
copy:
dest: "{{ _selfsigned_conf }}"
content: |
[ req ]
default_bits = 2048
default_keyfile = {{ ara_api_frontend_ssl_key }}
default_md = sha256
prompt = no
distinguished_name = distinguished_name
x509_extensions = v3_ca
[ v3_ca ]
basicConstraints = CA:TRUE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer:always
[ distinguished_name ]
commonName = {{ ara_api_fqdn }}
countryName = CA
stateOrProvinceName = QC
localityName = Montreal
organizationName = ara
organizationalUnitName = integration-tests
- name: Generate private key and self-signed certificate
command: >-
openssl req
-x509
-config {{ _selfsigned_conf }}
-newkey rsa:2048
-keyform PEM
-out {{ ara_api_frontend_ssl_cert }}
-outform PEM
-days 3650
-nodes
# https://docs.ansible.com/ansible/latest/collections/community/crypto/docsite/guide_selfsigned.html
# TODO: Use community.crypto
# - name: Create certificate signing request (CSR) for self-signed certificate
# community.crypto.openssl_csr_pipe:
# privatekey_path: "{{ ara_api_frontend_ssl_key }}"
# common_name: "{{ ara_api_fqdn }}"
# organization_name: ara_integration_tests
# register: _csr

# TODO: Has moved to community.crypto
# - name: Create self-signed certificate from CSR
# community.crypto.x509_certificate:
# path: "{{ ara_api_frontend_ssl_cert }}"
# csr_content: "{{ _csr.csr }}"
# privatekey_path: "{{ ara_api_frontend_ssl_key }}"
# provider: selfsigned

tasks:
- name: Set up the API with the ara_api Ansible role
include_role:
Expand Down

0 comments on commit c19b047

Please sign in to comment.