Changeset - d26fe0368a4b
[Not reviewed]
0 5 0
Branko Majic (branko) - 9 years ago 2015-05-29 19:09:58
branko@majic.rs
MAR-5: Updated php and wsgi website roles to include set-up website-specific TLS configuration for Nginx (including separate TLS key/certificate if necessary).
5 files changed with 67 insertions and 7 deletions:
0 comments (0 inline, 0 general)
docs/rolereference.rst
Show inline comments
 
@@ -907,24 +907,25 @@ in quite generic way, allowing the integrator to write his/her own logic for
 
deploying the necessary PHP applications, while still reusing a common base and
 
reducing the workload.
 

	
 
The role implements the following:
 

	
 
* Creates a dedicated user/group for running the PHP scripts.
 
* Creates a base directory where the website-specific code and data should be
 
  stored at.
 
* Adds nginx to website's group, so nginx could read the necessary files.
 
* Adds website administrator to website's group, so administrator could manage
 
  the code and data.
 
* Installs additional packages required for running the role (as configured).
 
* Deploys the HTTPS TLS private key and certificate (for website vhost).
 
* Configures PHP FPM and nginx to serve the website.
 

	
 
The role is implemented with the following layout/logic in mind:
 

	
 
* Website users are named after the ``FQDN`` (fully qualified domain name) of
 
  website, in format of ``web-ESCAPEDFQDN``, where ``ESCAPEDFQDN`` is equal to
 
  ``FQDN`` where dots have been replaced by underscores (for example,
 
  ``web-cloud_example_com``).
 
* All websites reside within a dedicated sub-directory in ``/var/www``. The
 
  sub-directory name is equal to the ``FQDN`` used for accessing the
 
  website. Owner of the directory is set to be the application administrator,
 
  while group is set to be the website group. Additionally, ``SGID`` bit is set
 
@@ -956,24 +957,32 @@ Parameters
 

	
 
**fqdn** (string, mandatory)
 
  Fully-qualified domain name where the website is reachable. This value is used
 
  for calculating the user/group name for dedicated website user, as well as
 
  home directory of the website user (where data/code should be stored at).
 

	
 
**index** (string, optional)
 
  Space-separated list of files which should be treated as index files by the
 
  web server. The web server will attempt opening these index files, in
 
  succession, until the first match, or until it runs out of matches, when a
 
  client requests an URI pointing to directory. Default is ``index.php``.
 

	
 
**https_tls_certificate** (string, mandatory)
 
  Path to file on Ansible host that contains the X.509 certificate used for TLS
 
  for HTTPS service. The file will be copied to directory ``/etc/ssl/certs/``.
 

	
 
**https_tls_key** (string, mandatory)
 
  Path to file on Ansible host that contains the private key used for TLS for
 
  HTTPS service. The file will be copied to directory ``/etc/ssl/private/``.
 

	
 
**php_file_regex** (string, optional)
 
  Regular expression used for determining which file should be interepted via
 
  PHP. Default is ``\.php$``.
 

	
 
**php_rewrite_urls** (list, optional)
 
  A list of rewrite rules that are applied to incoming requests. These rewrite
 
  rules are specifically targetted at prettying-up the URLs used by the PHP
 
  scripts. Each element of the list should be a string value compatible with the
 
  format of ``nginx`` option ``rewrite``. The keyword ``rewrite`` itself should
 
  be omitted, as well as trailing semi-colon (``;``).
 

	
 
**rewrites** (list, optional)
 
@@ -1011,33 +1020,36 @@ running *ownCloud* and *The Bug Genie* applications):
 
        - ^/\.well-known/caldav /remote.php/caldav/ redirect
 
        - ^/apps/calendar/caldav\.php /remote.php/caldav/
 
        - ^/apps/contacts/carddav\.php /remote.php/carddav/
 
        - ^/remote/(.*) /remote.php
 
      deny_files_regex:
 
        - ^(\.|autotest|occ|issue|indie|db_|console|build/|tests/|config/|lib/|3rdparty/|templates/).*
 
      packages:
 
        # For ownCloud
 
        - php5-gd
 
        - php5-json
 
        - php5-mysql
 
        - php5-curl
 
      https_tls_key: "{{ inventory_dir }}/tls/cloud.example.com_https.key"
 
      https_tls_certificate: "{{ inventory_dir }}/tls/cloud.example.com_https.pem"
 
    - role: php_website
 
      admin: admin
 
      deny_files_regex:
 
        - ^\..*
 
      php_rewrite_urls:
 
        - ^(.*) /index.php?url=$1
 
      fqdn: tbg.example.com
 
      uid: 2007
 

	
 
      https_tls_key: "{{ inventory_dir }}/tls/tbg.example.com_https.key"
 
      https_tls_certificate: "{{ inventory_dir }}/tls/tbg.example.com_https.pem"
 

	
 

	
 
WSGI Website
 
------------
 

	
 
The ``wsgi_website`` role can be used for setting-up a website powered by Python
 
on destination machine. The website needs to use the WSGI specification for
 
making the Python web application(s) available.
 

	
 
This role is normally not supposed to be used directly, but should instead serve
 
as the basis for writing website-specific roles. Therefore the role is written
 
in quite generic way, allowing the integrator to write his/her own logic for
 
@@ -1049,24 +1061,25 @@ The role implements the following:
 
* Creates a dedicated user/group for running the WSGI application.
 
* Creates a base directory where the website-specific code and data should be
 
  stored at.
 
* Adds nginx to website's group, so nginx could read the necessary files.
 
* Adds website administrator to website's group, so administrator could manage
 
  the code and data.
 
* Installs additional packages required for running the role (as configured).
 
* Sets-up a dedicated Python virtual environment for website.
 
* Install Gunicorn in Python virtual environment.
 
* Installs additional packages required for running the role in Python virtual
 
  environment (as configured).
 
* Configures systemd to run the website code (using Gunicorn)
 
* Deploys the HTTPS TLS private key and certificate (for website vhost).
 
* Configures nginx to serve the website (static files served directly, requests
 
  passed on to Gunicorn).
 

	
 
The role is implemented with the following layout/logic in mind:
 

	
 
* Website users are named after the ``FQDN`` (fully qualified domain name) of
 
  website, in format of ``web-ESCAPEDFQDN``, where ``ESCAPEDFQDN`` is equal to
 
  ``FQDN`` where dots have been replaced by underscores (for example,
 
  ``web-wiki_example_com``).
 
* All websites reside within a dedicated sub-directory in ``/var/www``. The
 
  sub-directory name is equal to the ``FQDN`` used for accessing the
 
  website. Owner of the directory is set to be the application administrator,
 
@@ -1094,24 +1107,32 @@ Parameters
 
~~~~~~~~~~
 

	
 
**admin** (string, mandatory)
 
  Name of the operating system user in charge of maintaining the website. This
 
  user is capable of making modifications to website configuration anda data
 
  stored within the website directory.
 

	
 
**fqdn** (string, mandatory)
 
  Fully-qualified domain name where the website is reachable. This value is used
 
  for calculating the user/group name for dedicated website user, as well as
 
  home directory of the website user (where data/code should be stored at).
 

	
 
**https_tls_certificate** (string, mandatory)
 
  Path to file on Ansible host that contains the X.509 certificate used for TLS
 
  for HTTPS service. The file will be copied to directory ``/etc/ssl/certs/``.
 

	
 
**https_tls_key** (string, mandatory)
 
  Path to file on Ansible host that contains the private key used for TLS for
 
  HTTPS service. The file will be copied to directory ``/etc/ssl/private/``.
 

	
 
**packages** (list, optional)
 
  A list of additional packages to install for this particular WSGI
 
  website. This is usually going to be development libraries for building Python
 
  packages.
 

	
 
**rewrites** (list, optional)
 
  A list of rewrite rules that are applied to incoming requests. Each element of
 
  the list should be a string value compatible with the format of ``nginx``
 
  option ``rewrite``. The keyword ``rewrite`` itself should be omitted, as well
 
  as trailing semi-colon (``;``).
 

	
 
**static_locations** (list, optional)
 
@@ -1138,24 +1159,24 @@ Parameters
 
  ``use_paste`` option is enabled, the value should be equal to filename of the
 
  Python Paste ini file, located in the ``code`` sub-directory.
 

	
 

	
 
Examples
 
~~~~~~~~
 

	
 
Here is an example configuration for setting-up a (base) WSGI website (for
 
running a bare Django project):
 

	
 
.. code-block:: yaml
 

	
 
    ---
 

	
 
    - role: wsgi_website
 
      admin: admin
 
      fqdn: django.example.com
 
      static_locations:
 
        - /static
 
        - /media
 
      uid: 2004
 
      virtualenv_packages:
 
        - django
 
      wsgi_application: django_example_com.wsgi:application
 
      https_tls_key: "{{ inventory_dir }}/tls/wsgi.example.com_https.key"
 
      https_tls_certificate: "{{ inventory_dir }}/tls/wsgi.example.com_https.pem"
roles/php_website/tasks/main.yml
Show inline comments
 
@@ -23,23 +23,35 @@
 
- name: Add admin to website group
 
  user: name="{{ admin }}" groups="{{ user }}" append="yes"
 

	
 
- name: Install extra packages for website
 
  apt: name="{{ item }}" state=installed
 
  with_items: packages
 

	
 
- name: Deploy PHP FPM configuration file for website
 
  template: src="fpm_site.conf.j2" dest="/etc/php5/fpm/pool.d/{{ fqdn }}.conf" validate="php5-fpm -t -y %s"
 
  notify:
 
    - Restart php5-fpm
 

	
 
- name: Deploy nginx TLS private key for website
 
  copy: dest="/etc/ssl/private/{{ https_tls_key | basename }}" src="{{ https_tls_key }}"
 
        mode=640 owner=root group=root
 
  notify:
 
    - Restart nginx
 

	
 
- name: Deploy nginx TLS certificate for website
 
  copy: dest="/etc/ssl/certs/{{ https_tls_certificate | basename }}" src="{{ https_tls_certificate }}"
 
        mode=644 owner=root group=root
 
  notify:
 
    - Restart nginx
 

	
 
- name: Deploy nginx configuration file for website
 
  template: src="nginx_site.j2" dest="/etc/nginx/sites-available/{{ fqdn }}"
 
            owner=root group=root mode=640 validate="/usr/local/bin/nginx_verify_site.sh -n '{{ fqdn }}' %s"
 
  notify:
 
    - Restart nginx
 

	
 
- name: Enable website
 
  file: src="/etc/nginx/sites-available/{{ fqdn }}" dest="/etc/nginx/sites-enabled/{{ fqdn }}"
 
        state=link
 
  notify:
 
    - Restart nginx
roles/php_website/templates/nginx_site.j2
Show inline comments
 
server {
 
    # Base settings.
 
    listen 80;
 
    root {{ home }}/htdocs/;
 
    index {{ index }};
 
    server_name {{ fqdn }};
 

	
 
    # HTTP (plaintext) configuration.
 
    listen 80;
 

	
 
    # HTTPS (TLS) configuration.
 
    listen 443 ssl;
 
    listen [::]:443 ssl;
 
    ssl_certificate_key /etc/ssl/private/{{ https_tls_key | basename }};
 
    ssl_certificate /etc/ssl/certs/{{ https_tls_certificate | basename }};
 

	
 
    {% if rewrites -%}
 
    # Generic URL rewrites.
 
    {% for rewrite in rewrites -%}
 
    rewrite {{ rewrite }};
 
    {% endfor -%}
 
    {% endif %}
 

	
 
    {% if deny_files_regex -%}
 
    # Deny access to user-specified files.
 
    {% for regex in deny_files_regex -%}
 
    location ~ {{ regex }} {
 
        deny all;
roles/wsgi_website/tasks/main.yml
Show inline comments
 
@@ -72,23 +72,35 @@
 
            owner=root group=root mode=644
 
  notify:
 
    - Reload systemd
 
    - "Restart website {{ fqdn }}"
 

	
 
- name: Enable the website service
 
  service: name="{{ fqdn }}" enabled=yes state=started
 

	
 
- name: Create directory where static files can be served from
 
  file: path="{{ home }}/htdocs/" state=directory
 
        owner="{{ admin }}" group="{{ user }}" mode="2750"
 

	
 
- name: Deploy nginx TLS private key for website
 
  copy: dest="/etc/ssl/private/{{ https_tls_key | basename }}" src="{{ https_tls_key }}"
 
        mode=640 owner=root group=root
 
  notify:
 
    - Restart nginx
 

	
 
- name: Deploy nginx TLS certificate for website
 
  copy: dest="/etc/ssl/certs/{{ https_tls_certificate | basename }}" src="{{ https_tls_certificate }}"
 
        mode=644 owner=root group=root
 
  notify:
 
    - Restart nginx
 

	
 
- name: Deploy nginx configuration file for website
 
  template: src="nginx_site.j2" dest="/etc/nginx/sites-available/{{ fqdn }}"
 
            owner=root group=root mode=640 validate="/usr/local/bin/nginx_verify_site.sh -n '{{ fqdn }}' %s"
 
  notify:
 
    - Restart nginx
 

	
 
- name: Enable nginx website
 
  file: src="/etc/nginx/sites-available/{{ fqdn }}" dest="/etc/nginx/sites-enabled/{{ fqdn }}"
 
        state=link
 
  notify:
 
    - Restart nginx
roles/wsgi_website/templates/nginx_site.j2
Show inline comments
 
server {
 
    listen 80;
 

	
 
    # Base settings.
 
    root {{ home }}/htdocs/;
 

	
 
    server_name {{ fqdn }};
 

	
 
    # HTTP (plaintext) configuration.
 
    listen 80;
 

	
 
    # HTTPS (TLS) configuration.
 
    listen 443 ssl;
 
    listen [::]:443 ssl;
 
    ssl_certificate_key /etc/ssl/private/{{ https_tls_key | basename }};
 
    ssl_certificate /etc/ssl/certs/{{ https_tls_certificate | basename }};
 

	
 
    {% if rewrites -%}
 
    # Site rewrites.
 
    {% for rewrite in rewrites -%}
 
    rewrite {{ rewrite }};
 
    {% endfor -%}
 
    {% endif %}
 

	
 
    {% if static_locations -%}
 
    # Static locations
 
    {% for location in static_locations -%}
 
    location {{ location }} {
 
        try_files $uri $uri/ =404;
0 comments (0 inline, 0 general)