Changeset - 3af07319e2f3
[Not reviewed]
0 7 1
Branko Majic (branko) - 9 years ago 2016-06-14 17:28:35
branko@majic.rs
MAR-59: Removed ability to specify admin user for php_website and wsgi_website roles. Updated testsite and usage instructions. Updated role reference.
8 files changed with 65 insertions and 52 deletions:
0 comments (0 inline, 0 general)
docs/rolereference.rst
Show inline comments
 
@@ -1170,12 +1170,13 @@ 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 dedicated administrator user for maintaining the website.
 
* 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).
 
@@ -1185,12 +1186,16 @@ The role implements the following:
 
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``).
 
* Administrator users are named after the ``FQDN`` (fully qualified domain name)
 
  of website, in format of ``admin-ESCAPEDFQDN``, where ``ESCAPEDFQDN`` is equal
 
  to ``FQDN`` where dots have been replaced by underscores (for example,
 
  ``admin-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
 
  on the directory. This allows admin, with correct umask, to create necessary
 
  files and directories that should be readable (and eventually writeable) by
 
@@ -1226,16 +1231,15 @@ Parameters
 
  **comment** (string, mandatory)
 
    Comment describing the configuration option.
 

	
 
  **value** (string, mandatory)
 
    Configuration option.
 

	
 
**admin** (string, optional, ``web-{{ fqdn | replace('.', '_') }}``)
 
  Name of the operating system user in charge of maintaining the website. This
 
  user is capable of making modifications to website configuration and data
 
  stored within the website directory.
 
**admin_uid** (integer, optional, ``whatever OS picks``)
 
  UID of the dedicated website administrator user. The user will be member of
 
  website group.
 

	
 
**deny_files_regex** (list, optional, ``[]``)
 
  List of regular expressions for matching files/locations to which the web
 
  server should deny access. This is useful to block access to any sensitive
 
  files that should not be served directly by the web server. The format must be
 
  compatible with regular expressions used by ``nginx`` for ``location ~``
 
@@ -1298,13 +1302,12 @@ running *ownCloud* and *The Bug Genie* applications):
 

	
 
.. code-block:: yaml
 

	
 
    - role: php_website
 
      fqdn: cloud.example.com
 
      uid: 2001
 
      admin: admin
 
      php_file_regex: \.php($|/)
 
      rewrites:
 
        - ^/\.well-known/host-meta /public.php?service=host-meta
 
        - ^/\.well-known/host-meta\.json /public.php?service=host-meta-json
 
        - ^/\.well-known/carddav /remote.php/carddav/ redirect
 
        - ^/\.well-known/caldav /remote.php/caldav/ redirect
 
@@ -1324,13 +1327,12 @@ running *ownCloud* and *The Bug Genie* applications):
 
      additional_nginx_config:
 
        - comment: Use custom page for forbidden files.
 
          value: error_page 403 /core/templates/403.php;
 
        - comment: Use custom page for non-existing locations/files.
 
          value: error_page 404 /core/templates/404.php;
 
    - role: php_website
 
      admin: admin
 
      deny_files_regex:
 
        - ^\..*
 
      php_rewrite_urls:
 
        - ^(.*) /index.php?url=$1
 
      fqdn: tbg.example.com
 
      uid: 2007
 
@@ -1351,12 +1353,13 @@ in quite generic way, allowing the integrator to write his/her own logic for
 
deploying the necessary Python applications/packages, while still reusing a
 
common base and reducing the workload.
 

	
 
The role implements the following:
 

	
 
* Creates a dedicated user/group for running the WSGI application.
 
* Creates a dedicated administrator user for maintaining the website.
 
* 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).
 
@@ -1374,26 +1377,29 @@ The role implements the following:
 
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``).
 
* Administrator users are named after the ``FQDN`` (fully qualified domain name)
 
  of website, in format of ``admin-ESCAPEDFQDN``, where ``ESCAPEDFQDN`` is equal
 
  to ``FQDN`` where dots have been replaced by underscores (for example,
 
  ``admin-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
 
  on the directory. This allows admin, with correct umask, to create necessary
 
  files and directories that should be readable (and eventually writeable) by
 
  the website user (running the WSGI application) without having to become root.
 
* All files placed in the website directory should be either created there
 
  directly, or copied to the directory in order to make sure the ``SGID`` gets
 
  honored. **Do not move the files, the permissions will not be set correctly.**
 
* Within the website directory, Python virtual environment can be found within
 
  the ``virtualenv`` sub-directory. The virtual environment is also symlinked to
 
  website admin's ``~/.virtualenvs/`` directory for easier access (and
 
  auto-completion with virtualenvwrapper).
 
  the ``virtualenv`` sub-directory. Switching to administrator user via login
 
  shell will automatically activate the virtual environment.
 
* Within the website directory, nginx will expect to find the static files
 
  within the ``htdocs`` sub-directory (this can be symlink too). Locations/aliases
 
  can be configured for static file serving.
 
* Within the website directory, systemd service will expect to find the website
 
  code within the ``code`` sub-directory (this can be symlink too).
 
* nginx communicates with WSGI server over a dedicated Unix socket for each
 
@@ -1423,16 +1429,15 @@ Parameters
 
  **comment** (string, mandatory)
 
    Comment describing the configuration option.
 

	
 
  **value** (string, mandatory)
 
    Configuration option.
 

	
 
**admin** (string, optional, ``web-{{ fqdn | replace('.', '_') }}``)
 
  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.
 
**admin_uid** (integer, optional, ``whatever OS picks``)
 
  UID of the dedicated website administrator user. The user will be member of
 
  website group.
 

	
 
**enforce_https** (boolean, optional, ``True``)
 
  Specify if HTTPS should be enforced for the website or not. If enforced,
 
  clients connecting via plaintext will be redirected to HTTPS.
 

	
 
**fqdn** (string, mandatory)
 
@@ -1500,13 +1505,12 @@ 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:
docs/usage.rst
Show inline comments
 
@@ -1210,17 +1210,19 @@ To make the example a bit simpler, no parameters will be introducd for this role
 
Before we start, here is a couple of useful pointers regarding the
 
``php_website`` role we'll be using for the PHP part:
 

	
 
* The role is designed to execute every application via dedicated user and
 
  group. The user/group name is automatically derived from the FQDN of website,
 
  for example ``web-tbg_example_com``.
 
* An administrative user is created as well, and this user should be used when
 
  running maintenance and installation commands. Similar to application user,
 
  the name is also derived from the FQDN of website, for example
 
  ``admin-tbg_example_com``. Administrative user does not have a dedicated
 
  group, and instead belongs to same group as the application user.
 
* PHP applications are executed via FastCGI, using the ``php5-fpm`` package.
 
* Static content (non-PHP) is served directly by *Nginx*.
 
* For administrative purposes you can use a separate user that you have created
 
  before (for example via the ``common`` role). This user will get added to
 
  application's group.
 
* Each web application gets distinct sub-directory under ``/var/www``, named
 
  after the FQDN. All sub-directories created under there are created with
 
  ``2750`` permissions, with ownership set to admin user, and group set to the
 
  application's group. In other words, all directories will have ``SGID`` bit
 
  set-up, allowing you to create files/directories that will have their group
 
  automatically set to the group of the parent directory.
 
@@ -1254,17 +1256,12 @@ Before we start, here is a couple of useful pointers regarding the
 
      ---
 

	
 
      dependencies:
 
         # Ok, so this role helps us set-up Nginx virtual host for serving our
 
         # app.
 
         - role: php_website
 
           # We shall let our admin account be able to read/write files
 
           # belonging to the application. This allows us to use regular user
 
           # (admin in this case) without sudo for managing and accessing
 
           # application files.
 
           admin: admin
 
           # Our virtual host will for PHP website will respond to this name.
 
           fqdn: tbg.example.com
 
           # Some additional packages are required in order to deploy and use TBG.
 
           packages:
 
              - php5-gd
 
              - php5-curl
 
@@ -1325,67 +1322,67 @@ Before we start, here is a couple of useful pointers regarding the
 

	
 
      - name: Download the TBG archive
 
        get_url: url=https://github.com/thebuggenie/thebuggenie/archive/v{{ tbg_version }}.tar.gz
 
                 dest="/var/www/tbg.example.com/thebuggenie-{{ tbg_version }}.tar.gz"
 
                 sha256sum=0fd0a680ba281adc97d5d2c720e63b995225c99716a36eca6a198b8a5ebf8057
 
        become: yes
 
        become_user: admin
 
        become_user: admin-tbg_example_com
 

	
 
      - name: Download Composer
 
        get_url: url=https://getcomposer.org/download/1.0.0-alpha10/composer.phar
 
                 dest="/usr/local/bin/composer"
 
                 sha256sum=9f2c7d0364bc743bcde9cfe1fe84749e5ac38c46d47cf42966ce499135fd4628
 
                 owner=root group=root mode=755
 

	
 
      - name: Unpack TBG
 
        unarchive: src="/var/www/tbg.example.com/thebuggenie-{{ tbg_version }}.tar.gz"
 
                   dest="/var/www/tbg.example.com/" copy=no
 
                   creates="/var/www/tbg.example.com/thebuggenie-{{ tbg_version }}"
 
        become: yes
 
        become_user: admin
 
        become_user: admin-tbg_example_com
 

	
 
      - name: Create TBG cache directory
 
        file: path="/var/www/tbg.example.com/thebuggenie-{{ tbg_version }}/cache" state=directory mode=2770
 
        become: yes
 
        become_user: admin
 
        become_user: admin-tbg_example_com
 

	
 
      - name: Set-up the necessary write permissions for TBG directories
 
        file: path="{{ item }}" mode=g+w
 
        with_items:
 
           - /var/www/tbg.example.com/thebuggenie-{{ tbg_version }}/
 
           - /var/www/tbg.example.com/thebuggenie-{{ tbg_version }}/public/
 
           - /var/www/tbg.example.com/thebuggenie-{{ tbg_version }}/core/config/
 

	
 
      - name: Create symbolic link to TBG application
 
        file: src="/var/www/tbg.example.com/thebuggenie-{{ tbg_version }}/public"
 
              path="/var/www/tbg.example.com/htdocs"
 
              state=link
 
              owner="admin" group="web-tbg_example_com" mode=2750
 
              owner="admin-tbg_example_com" group="web-tbg_example_com" mode=2750
 
        become: yes
 
        become_user: admin
 
        become_user: admin-tbg_example_com
 

	
 
      - name: Install TBG dependencies
 
        composer: command=install working_dir="/var/www/tbg.example.com/thebuggenie-{{ tbg_version }}"
 
        become: yes
 
        become_user: admin
 
        become_user: admin-tbg_example_com
 

	
 
      - name: Deploy database configuration file for TBG
 
        copy: src="b2db.yml" dest="/var/www/tbg.example.com/thebuggenie-{{ tbg_version }}/core/config/b2db.yml"
 
              mode=640 owner=admin group=web-tbg_example_com
 
              mode=640 owner=admin-tbg_example_com group=web-tbg_example_com
 

	
 
      - name: Deploy expect script for installing TBG
 
        copy: src="tbg_expect_install" dest="/var/www/tbg.example.com/tbg_expect_install" mode=750
 
        become: yes
 
        become_user: admin
 
        become_user: admin-tbg_example_com
 

	
 
      - name: Run TBG installer via expect script
 
        command: /var/www/tbg.example.com/tbg_expect_install
 
                 chdir=/var/www/tbg.example.com/thebuggenie-{{ tbg_version }} 
 
                 creates=/var/www/tbg.example.com/thebuggenie-{{ tbg_version }}/installed
 
        become: yes
 
        become_user: admin
 
        become_user: admin-tbg_example_com
 

	
 
5. Set-up the files that are deployed by our role.
 

	
 
   :file:`~/mysite/roles/tbg/files/b2db.yml`
 
   ::
 

	
 
@@ -1449,20 +1446,22 @@ Most of the notes on how a ``php_website`` role is deployed also stand for the
 
``wsgi_website`` role, but we will reiterate and clarify them a bit just to be
 
on the safe side:
 

	
 
* The role is designed to execute every application via dedicated user and
 
  group. The user/group name is automatically derived from the FQDN of website,
 
  for example ``web-wiki_example_com``.
 
* An administrative user is created as well, and this user should be used when
 
  running maintenance and installation commands. Similar to application user,
 
  the name is also derived from the FQDN of website, for example
 
  ``admin-wiki_example_com``. Administrative user does not have a dedicated
 
  group, and instead belongs to same group as the application user. As
 
  convenience, whenever you switch to this user the Python virtual environment
 
  will be automatically activated for you.
 
* WSGI applications are executed via *Gunicorn*. The WSGI server listens on a
 
  Unix socket, making the socket accessible by *Nginx*.
 
* Static content is served directly by *Nginx*.
 
* For administrative purposes you can use a separate user that you have created
 
  before (for example via the ``common`` role). This user will get added to
 
  application's group. The administrator will also be able to switch to website
 
  virtual environment using the ``workon`` command from ``virtualenv-wrapper``
 
  package.
 
* Each web application gets distinct sub-directory under ``/var/www``, named
 
  after the FQDN. All sub-directories created under there are created with
 
  ``2750`` permissions, with ownership set to admin user, and group set to the
 
  application's group. In other words, all directories will have ``SGID`` bit
 
  set-up, allowing you to create files/directories that will have their group
 
  automatically set to the group of the parent directory.
 
@@ -1496,13 +1495,12 @@ on the safe side:
 
   ::
 

	
 
      ---
 

	
 
      dependencies:
 
         - role: wsgi_website
 
           admin: admin
 
           fqdn: wiki.example.com
 
           # In many cases you need to have some development packages available
 
           # in order to build Python packages installed via pip
 
           packages:
 
              - build-essential
 
              - python-dev
 
@@ -1567,21 +1565,21 @@ on the safe side:
 
   ::
 

	
 
      ---
 

	
 
      - name: Create Django project directory
 
        file: dest="/var/www/wiki.example.com/code" state=directory
 
              owner=admin group=web-wiki_example_com
 
              owner=admin-wiki_example_com group=web-wiki_example_com
 
              mode=2750
 

	
 
      - name: Start Django project for the Wiki website
 
        command: /var/www/wiki.example.com/virtualenv/bin/exec django-admin.py startproject wiki_example_com /var/www/wiki.example.com/code
 
                 chdir=/var/www/wiki.example.com
 
                 creates=/var/www/wiki.example.com/code/wiki_example_com
 
        become: yes
 
        become_user: admin
 
        become_user: admin-wiki_example_com
 

	
 
      - name: Deploy settings for wiki website
 
        copy: src="{{ item }}" dest="/var/www/wiki.example.com/code/wiki_example_com/{{ item }}"
 
              mode=640 owner=admin group=web-wiki_example_com
 
        with_items:
 
           - settings.py
 
@@ -1591,27 +1589,27 @@ on the safe side:
 

	
 
      - name: Deploy project database and deploy static files
 
        django_manage: command="{{ item }}"
 
                       app_path="/var/www/wiki.example.com/code/"
 
                       virtualenv="/var/www/wiki.example.com/virtualenv/"
 
        become: yes
 
        become_user: admin
 
        become_user: admin-wiki_example_com
 
        with_items:
 
           - syncdb
 
           - migrate
 
           - collectstatic
 

	
 
      - name: Deploy the superadmin creation script
 
        copy: src="create_superadmin.py" dest="/var/www/wiki.example.com/code/create_superadmin.py"
 
              owner=admin group=web-wiki_example_com mode=750
 
              owner=admin-wiki_example_com group=web-wiki_example_com mode=750
 

	
 
      - name: Create initial superuser
 
        command: /var/www/wiki.example.com/virtualenv/bin/exec ./create_superadmin.py
 
                 chdir=/var/www/wiki.example.com/code/
 
        become: yes
 
        become_user: admin
 
        become_user: admin-wiki_example_com
 
        register: wiki_superuser
 
        changed_when: wiki_superuser.stdout == "Created superuser."
 

	
 
5. There is a couple of files that we are deploying through the above
 
   tasks. Let's create them as well.
 

	
roles/php_website/defaults/main.yml
Show inline comments
 
@@ -5,9 +5,8 @@ deny_files_regex: []
 
enforce_https: True
 
index: index.php
 
packages: []
 
php_file_regex: \.php$
 
php_rewrite_urls: []
 
rewrites: []
 
admin: "web-{{ fqdn | replace('.', '_') }}"
 
https_tls_certificate: "{{ lookup('file', tls_certificate_dir + '/' + fqdn + '_https.pem') }}"
 
https_tls_key: "{{ lookup('file', tls_private_key_dir + '/' + fqdn + '_https.key') }}"
roles/php_website/tasks/main.yml
Show inline comments
 
---
 

	
 
- name: Calculate username and home
 
  set_fact:
 
    admin: "admin-{{ fqdn | replace('.', '_') }}"
 
    user: "web-{{ fqdn | replace('.', '_') }}"
 
    home: "/var/www/{{ fqdn }}"
 

	
 
- name: Create PHP website group
 
  group: name="{{ user }}" gid="{{ uid | default(omit) }}" state=present
 

	
 
- name: Create PHP website admin user
 
  user: name="{{ admin }}" uid="{{ admin_uid | default(omit) }}" group="{{ user }}"
 
        shell=/bin/bash createhome=no home="{{ home }}" state=present
 

	
 
- name: Create home directory for the user (avoid populating with skeleton)
 
  file: path="{{ home }}" state=directory
 
        owner="{{ admin }}" group="{{ user }}" mode=2750
 

	
 
- name: Create PHP website user
 
  user: name="{{ user }}" uid="{{ uid | default(omit) }}" group="{{ user }}"
roles/wsgi_website/files/bashrc
Show inline comments
 
new file 100644
 
# Activate the virtual environment.
 
. "$HOME/virtualenv/bin/activate"
 
\ No newline at end of file
roles/wsgi_website/tasks/main.yml
Show inline comments
 
---
 

	
 
- set_fact:
 
    admin: "admin-{{ fqdn | replace('.', '_') }}"
 
    user: "web-{{ fqdn | replace('.', '_') }}"
 
    home: "/var/www/{{ fqdn }}"
 

	
 
- name: Create WSGI website group
 
  group: name="{{ user }}" gid="{{ uid | default(omit) }}" state=present
 

	
 
- name: Create WSGI website admin user
 
  user: name="{{ admin }}" uid="{{ admin_uid | default(omit) }}" group="{{ user }}"
 
        shell=/bin/bash createhome=no home="{{ home }}" state=present
 

	
 
- name: Create home directory for the user (avoid populating with skeleton)
 
  file: path="{{ home }}" state=directory
 
        owner="{{ admin }}" group="{{ user }}" mode=2750
 

	
 
- name: Create WSGI website user
 
  user: name="{{ user }}" uid="{{ uid | default(omit) }}" group="{{ user }}"
 
@@ -36,24 +41,23 @@
 
  command: /usr/bin/virtualenv --prompt "({{ fqdn }})" "{{ home }}/virtualenv" creates="{{ home }}/virtualenv/bin/activate"
 

	
 
- name: Configure project directory for the Python virtual environment
 
  template: src="venv_project.j2" dest="{{ home }}/virtualenv/.project"
 
            owner="{{ admin }}" group="{{ user }}" mode="640"
 

	
 
- name: Create directory where virtualenvs will be symlinked to
 
  become_user: "{{ admin }}"
 
  file: path="~/.virtualenvs" state=directory mode=750
 

	
 
- name: Create convenience symlink for Python virtual environment wrapper utility
 
  become_user: "{{ admin }}"
 
  file: src="{{ home }}/virtualenv" dest="~/.virtualenvs/{{ fqdn }}" state=link
 

	
 
- name: Deploy virtualenv wrapper
 
  template: src="venv_exec.j2" dest="{{ home }}/virtualenv/bin/exec"
 
            owner="{{ admin }}" group="{{ user }}" mode="750"
 

	
 
- name: Deploy minimalistic bashrc for auto-activating the virtual environment
 
  copy: src="bashrc" dest="{{ item }}"
 
        owner="root" group="{{ user }}" mode="640"
 
  with_items:
 
    - "{{ home }}/.bashrc"
 
    - "{{ home }}/.profile"
 

	
 
- name: Install futures package for use with Gunicorn thread workers
 
  become_user: "{{ admin }}"
 
  pip: name=futures version="{{ futures_version }}" state=present virtualenv="{{ home }}/virtualenv"
 

	
 
- name: Install Gunicorn in Python virtual environment
 
  become_user: "{{ admin }}"
testsite/playbooks/roles/phpinfo/meta/main.yml
Show inline comments
 
---
 

	
 
dependencies:
 
  - role: php_website
 
    admin: admin
 
    fqdn: phpinfo.{{ testsite_domain }}
 
    php_rewrite_urls:
 
      - ^(.*) /index.php
 
    admin_uid: 3000
 
    uid: 2000
 
    enforce_https: False
 
    https_tls_key: "{{ lookup('file', inventory_dir + '/tls/phpinfo.' + testsite_domain + '_https.key') }}"
 
    https_tls_certificate: "{{ lookup('file', inventory_dir + '/tls/phpinfo.' + testsite_domain + '_https.pem') }}"
 
  - role: database
 
    db_name: phpinfo_{{ testsite_domain_underscores }}
testsite/playbooks/roles/wsgihello/meta/main.yml
Show inline comments
 
---
 

	
 
dependencies:
 
  - role: wsgi_website
 
    admin: admin
 
    fqdn: wsgi.{{ testsite_domain }}
 
    admin_uid: 3001
 
    uid: 2001
 
    wsgi_application: wsgi:application
 
    static_locations:
 
      - /static/
 
    https_tls_key: "{{ lookup('file', inventory_dir + '/tls/wsgi.' + testsite_domain + '_https.key') }}"
 
    https_tls_certificate: "{{ lookup('file', inventory_dir + '/tls/wsgi.' + testsite_domain + '_https.pem') }}"
0 comments (0 inline, 0 general)