diff --git a/docs/rolereference.rst b/docs/rolereference.rst index 42f68c3185b87f066d40230f4e657143f7dc97cf..a052cd6e44a6ab30ad892ffd134aeec468ce31d4 100644 --- a/docs/rolereference.rst +++ b/docs/rolereference.rst @@ -846,8 +846,8 @@ Web Server The ``web_server`` role can be used for setting-up a web server on destination machine. -The role is supposed very lightweight, providing a basis for deployment of web -applications. +The role is supposed to be very lightweight, providing a basis for deployment of +web applications. The role implements the following: @@ -882,7 +882,7 @@ Parameters Examples ~~~~~~~~ -Here is an example configuration for setting-up XMPP server using Prosody: +Here is an example configuration for setting-up web server: .. code-block:: yaml @@ -893,3 +893,123 @@ Here is an example configuration for setting-up XMPP server using Prosody: web_default_title: "Welcome to Example Inc." web_default_message: "You are attempting to access the web server using a wrong name or an IP address. Please check your URL." + + +PHP Website +-------- + +The ``php_website`` role can be used for setting-up a website powered by PHP on +destination machine. + +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 +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). +* 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 + 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 PHP scripts) 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, nginx/php5-fpm will expect to find the relevant + files within the htdocs sub-directory (this can be symlink too). +* nginx communicates with PHP FPM over a dedicated Unix socket for each website. + + +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. + +**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 ~`` + syntax. + +**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). + +**php_rewrite_url** (string, optional) + If implementing some form of clean URL schema, this parameter can be used for + defining how the clean URLs should be mapped onto actual PHP scripts. When + specifying this parameter, one special variable is available - ``$suburi`` + (which is the URI requested by HTTP client, usually in clean URL form). This + is in addition to any other variables provided out of the box by ``nginx`` + (like ``$args`` and such). + +**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 (``;``). + +**packages** (list, optional) + A list of additional packages to install for this particular PHP + appliction. This is usually going to be different PHP extensions. + +**uid** (integer, mandatory) + UID/GID (they are set-up to be the same) of the dedicated website + user/group. + + +Examples +~~~~~~~~ + +Here is an example configuration for setting-up a (base) PHP website (for running +``ownCloud`` application): + +.. code-block:: yaml + + --- + + - role: php_website + fqdn: cloud.example.com + uid: 2001 + admin: admin + php_rewrite_url: /index.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 + - ^/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 diff --git a/roles/php_website/defaults/main.yml b/roles/php_website/defaults/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..10a4e5926fda3faf945f1de8ac1c7e091a104c40 --- /dev/null +++ b/roles/php_website/defaults/main.yml @@ -0,0 +1,6 @@ +--- + +deny_files_regex: [] +packages: [] +php_rewrite_url: "" +rewrites: [] diff --git a/roles/php_website/tasks/main.yml b/roles/php_website/tasks/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..6c2ab307365e5da5b25a6a4b5aa9a145d48d3772 --- /dev/null +++ b/roles/php_website/tasks/main.yml @@ -0,0 +1,45 @@ +--- + +- set_fact: + user: "web-{{ fqdn | replace('.', '_') }}" + home: "/var/www/{{ fqdn }}" + +- name: Create PHP website group + group: name="{{ user }}" gid="{{ uid }}" 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 }}" group="{{ user }}" + system=yes createhome=no state=present + +- name: Add nginx user to site group + user: name="www-data" groups="{{ user }}" append="yes" + notify: + - Restart nginx + +- name: Add admin to site group + user: name="{{ admin }}" groups="{{ user }}" append="yes" + +- name: Install extra packages for site + apt: name="{{ item }}" state=installed + with_items: packages + +- name: Deploy PHP FPM configuration file for site + 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 configuration file for site + template: src="nginx_site.j2" dest="/etc/nginx/sites-available/{{ fqdn }}" + owner=root group=root mode=640 + notify: + - Restart nginx + +- name: Enable site + file: src="/etc/nginx/sites-available/{{ fqdn }}" dest="/etc/nginx/sites-enabled/{{ fqdn }}" + state=link + notify: + - Restart nginx diff --git a/roles/php_website/templates/fpm_site.conf.j2 b/roles/php_website/templates/fpm_site.conf.j2 new file mode 100644 index 0000000000000000000000000000000000000000..6f73cdf2d75517fdbcebcfbc21b657e0d90740fb --- /dev/null +++ b/roles/php_website/templates/fpm_site.conf.j2 @@ -0,0 +1,28 @@ +; Start a new named pool. +[{{ fqdn }}] + +; Set the user and group that should execute the scripts. +user = {{ user }} +group = {{ user }} + +; Listen on a dedicated UNIX socket. +listen = /var/run/php5-fpm/{{ fqdn }}.sock + +; Set-up UNIX socket permissions (allow web server to connect). +listen.owner = www-data +listen.group = www-data +listen.mode = 0660 + +; Configure how processes are managed and how many are launched. +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 + +; Chdir to this directory at the start. +chdir = / + +; Redirect worker stdout/stder into main error log. This will also allow Nginx +; to log errors in site-specific log file. +catch_workers_output = yes diff --git a/roles/php_website/templates/nginx_site.j2 b/roles/php_website/templates/nginx_site.j2 new file mode 100644 index 0000000000000000000000000000000000000000..543f5ce09396f483d3989083368e1cb4c57df88c --- /dev/null +++ b/roles/php_website/templates/nginx_site.j2 @@ -0,0 +1,45 @@ +server { + listen 80; + + root {{ home }}/htdocs/; + + index index.php; + + server_name {{ fqdn }}; + + # Site rewrites. + {% for rewrite in rewrites -%} + rewrite {{ rewrite }}; + {% endfor %} + + # Interpret PHP files via FastCGI. + location ~ \.php($|/) { + include snippets/fastcgi-php.conf; + fastcgi_pass unix:/var/run/php5-fpm/{{ fqdn }}.sock; + } + + # Deny access to all hidden files (this will prevent access to + # .htaccess too). + location ~ /\. { + deny all; + } + + {% for regex in deny_files_regex -%} + # Deny access to user-specified files. + location ~ {{ regex }} { + deny all; + } + {% endfor %} + + {% if php_rewrite_url -%} + # Serve the remaining files directly or rewrite request for PHP processing + # (clean URLs). + location ~ /(.*) { + set $suburi $1; + try_files $uri $uri/ {{ php_rewrite_url }}; + } + {% endif %} + + access_log /var/log/nginx/{{ fqdn }}-access.log; + error_log /var/log/nginx/{{ fqdn }}-error.log; +}