diff --git a/defaults/main.yml b/defaults/main.yml
index c7837fc..79371a1 100644
--- a/defaults/main.yml
+++ b/defaults/main.yml
@@ -1,2 +1,80 @@
---
-# defaults file for .
+
+nginx_apt_package: nginx-full
+
+#
+# Nginx shared variables
+#
+nginx_root: "/var/www"
+nginx_log_dir: '/var/log/nginx'
+nginx_ssl_dir: '/etc/nginx/ssl'
+nginx_resolver:
+ hosts: ['208.67.222.222', '208.67.220.220'] # OpenDNS
+ valid: '300'
+ timeout: '5'
+nginx_error_log_level: 'warn' # http://nginx.org/en/docs/ngx_core_module.html#error_log
+nginx_dh_length: 2048
+
+
+#
+# Load upstream
+#
+
+# PHP
+nginx_php: false
+nginx_php_method: "unix"
+nginx_php_unix_sockets:
+ - "/var/run/php5-fpm.sock"
+nginx_php_tcp_sockets:
+ - host: "127.0.0.1"
+ port: "15000"
+ weight: "1"
+ max_fails: "5"
+ fail_timeout: "10s"
+
+#
+# Nginx configuration
+#
+nginx_user: 'www-data'
+nginx_worker_processes: '{{ ansible_processor_vcpus }}'
+nginx_pid: '/run/nginx.pid'
+
+nginx_events:
+ worker_connections: '512'
+ multi_accept: 'on'
+ use: 'epoll'
+
+#
+# Nginx HTTP
+#
+nginx_http:
+ access_log: 'off'
+ error_log: 'off'
+ client_body_buffer_size: '1M'
+ client_header_buffer_size: '1M'
+ client_max_body_size: '10M'
+ large_client_header_buffers: '8 8k'
+ client_body_timeout: '60'
+ client_header_timeout: '60'
+ keepalive_timeout: '30 30'
+ send_timeout: '120'
+ ignore_invalid_headers: 'on'
+ keepalive_requests: '100'
+ recursive_error_pages: 'on'
+ sendfile: 'on'
+ server_name_in_redirect: 'off'
+ server_tokens: 'off'
+ tcp_nodelay: 'on'
+ tcp_nopush: 'on'
+ reset_timedout_connection: 'on'
+ gzip: 'on'
+ gzip_buffers: '16 8k'
+ gzip_comp_level: '9'
+ gzip_http_version: '1.0'
+ gzip_min_length: '0'
+ gzip_types: 'text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml'
+ gzip_vary: 'on'
+ gzip_disable: '"msie6"'
+# etag: 'off'
+
+nginx_vhosts: []
diff --git a/files/index.html b/files/index.html
new file mode 100644
index 0000000..ab31912
--- /dev/null
+++ b/files/index.html
@@ -0,0 +1 @@
+
HTML works
diff --git a/files/index.php b/files/index.php
new file mode 100644
index 0000000..22e08b8
--- /dev/null
+++ b/files/index.php
@@ -0,0 +1,3 @@
+PHP works!";
diff --git a/handlers/main.yml b/handlers/main.yml
index 050cdd1..b3cf511 100644
--- a/handlers/main.yml
+++ b/handlers/main.yml
@@ -1,2 +1,6 @@
---
-# handlers file for .
+- name: restart nginx
+ action: service name=nginx state=restarted enabled=yes
+
+- name: reload nginx
+ action: service name=nginx state=reloaded enabled=yes
diff --git a/tasks/main.yml b/tasks/main.yml
index ace551a..45cc97c 100644
--- a/tasks/main.yml
+++ b/tasks/main.yml
@@ -1,2 +1,35 @@
---
-# tasks file for .
+
+- name: APT | Install nginx
+ apt: pkg={{ nginx_apt_package }} state=latest update_cache=yes cache_valid_time=3600
+
+- name: TEMPLATE | Deploy nginx.conf
+ template: src=etc/nginx/nginx.conf.j2 dest=/etc/nginx/nginx.conf validate= "nginx -t"
+ notify: restart nginx
+
+- name: FILE | Create /etc/nginx/helpers
+ file: dest=/etc/nginx/helpers owner=root mode=0755 state=directory
+
+- name: FILE | Create /etc/nginx/ssl
+ file: dest=/etc/nginx/ssl owner=root mode=0755 state=directory
+
+#- name: COMMAND | Creates DH file
+# command: openssl dhparam -out {{ nginx_dh_path }} {{ nginx_dh_length }}
+# args:
+# creates: "{{ nginx_dh_path }}"
+
+- name: TEMPLATE | Deploy all helpers
+ template: src={{ item }} dest=/etc/nginx/helpers/{{ item | basename | regex_replace('\.j2$','') }}
+ with_fileglob: '../templates/etc/nginx/helpers/*.j2'
+ notify: reload nginx
+
+- name: INCLUDE | PHP configuration
+ include: php.yml
+ when: nginx_php
+
+- name: INCLUDE | Vhosts configuration
+ include: vhost.yml
+
+# TODO:
+# - Python
+# - Ruby (SHIT!)
diff --git a/tasks/php.yml b/tasks/php.yml
new file mode 100644
index 0000000..4bc91fe
--- /dev/null
+++ b/tasks/php.yml
@@ -0,0 +1,10 @@
+---
+
+- name: INCLUDE_VARS | Include PHP defaults vars from PHP role if unspecified
+ include_vars: ../../php/defaults/main.yml
+ when: php_fpm_socket is undefined
+
+- name: TEMPLATE | Deploy PHP upstream (UNIX sockets) to Nginx
+ template: src=etc/nginx/upstream/php.conf.j2 dest=/etc/nginx/conf.d/php.conf
+ notify: reload nginx
+
diff --git a/tasks/vhost.yml b/tasks/vhost.yml
new file mode 100644
index 0000000..86093b0
--- /dev/null
+++ b/tasks/vhost.yml
@@ -0,0 +1,49 @@
+---
+
+- name: FILE | Create root folders (foreach nginx_vhosts)
+ file: path={{ nginx_root }}/{{ item.name }} state=directory recurse=yes owner=www-data group=www-data mode=0755
+ file: path={{ nginx_root }}/{{ item.name }}/public state=directory recurse=yes owner=www-data group=www-data mode=0755
+ with_items: nginx_vhosts
+
+- name: TEMPLATE | Create vhosts
+ template: src=etc/nginx/sites-available/{{ item.template }}.j2 dest=/etc/nginx/sites-available/{{ item.name }}
+ with_items: nginx_vhosts
+ notify: reload nginx
+
+- name: COMMAND | Get sites available
+ command: ls -1 /etc/nginx/sites-available
+ register: old_vhosts
+ changed_when: false
+ ignore_errors: true
+
+- name: Delete unmanaged vhosts
+ file: path=/etc/nginx/sites-enabled/{{ item }} state=absent
+ file: path=/etc/nginx/sites-available/{{ item }} state=absent
+ with_items: old_vhosts.stdout_lines
+ when: item not in nginx_vhosts|map(attribute='name') and item != 'default'
+
+#- name: COPY | Add index.html / index.php
+# copy: src={{ item }} dest={{ nginx_root }}/{{ item.name }}/public/{{ item }} owner=www-data group=www-data mode=0666
+# with_fileglob: "*"
+
+- name: FILE | Enable vhosts (symlink to sites-enabled)
+ file: src=/etc/nginx/sites-available/{{ item.name }} dest=/etc/nginx/sites-enabled/{{ item.name }} state=link
+ with_items: nginx_vhosts
+ notify: reload nginx
+
+- name: FILE | Create ssl dir per vhost (if needed)
+ file: dest=/etc/nginx/ssl/{{ item.name }} owner=root mode=0750 state=directory
+ with_items: nginx_vhosts
+ when: item.ssl.use is defined and item.ssl.use
+
+# TODO...
+#- name: COPY | Deploy SSL keys if needed
+# copy: src=keys/{{ item.name }}/{{ item.name }}.crt dest=/etc/nginx/ssl/{{ item.name }} mode=660
+# copy: src=keys/{{ item.name }}/{{ item.name }}.key dest=/etc/nginx/ssl/{{ item.name }} mode=660
+# with_items: nginx_vhosts
+# when: item.ssl.use and not generatekey
+
+# TODO:
+# - deploy defaults files (index.html/index.php) allready in files/
+# - work with role "ssl_autosign"
+
diff --git a/templates/etc/nginx/helpers/no-ht.j2 b/templates/etc/nginx/helpers/no-ht.j2
new file mode 100644
index 0000000..d262d38
--- /dev/null
+++ b/templates/etc/nginx/helpers/no-ht.j2
@@ -0,0 +1,10 @@
+#
+# {{ ansible_managed }}
+#
+
+location ~ /\.ht {
+ deny all;
+}
+
+
+# vim:filetype=nginx
diff --git a/templates/etc/nginx/helpers/php.j2 b/templates/etc/nginx/helpers/php.j2
new file mode 100644
index 0000000..addb5db
--- /dev/null
+++ b/templates/etc/nginx/helpers/php.j2
@@ -0,0 +1,19 @@
+#
+# {{ ansible_managed }}
+#
+
+location ~ \.php$ {
+ fastcgi_pass php;
+ fastcgi_index index.php;
+ fastcgi_intercept_errors on;
+ include fastcgi_params;
+
+ # TODO...
+ # Newrelic custom header: https://docs.newrelic.com/docs/apm/other-features/request-queueing/request-queue-server-configuration-examples
+ #fastcgi_param HTTP_X_REQUEST_START "t=${msec}";
+ # Newrelic custom PHP appname: https://docs.newrelic.com/docs/agents/php-agent/configuration/php-directory-ini-settings#perdir-nginx
+ #fastcgi_param PHP_VALUE "newrelic.appname=${host}";
+}
+
+
+# vim:filetype=nginx
diff --git a/templates/etc/nginx/helpers/ssl-legacy.j2 b/templates/etc/nginx/helpers/ssl-legacy.j2
new file mode 100644
index 0000000..7464285
--- /dev/null
+++ b/templates/etc/nginx/helpers/ssl-legacy.j2
@@ -0,0 +1,18 @@
+#
+# {{ ansible_managed }}
+#
+
+ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
+ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
+ssl_prefer_server_ciphers on;
+ssl_session_cache shared:SSL:10m;
+add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
+add_header X-Frame-Options DENY;
+add_header X-Content-Type-Options nosniff;
+ssl_stapling on;
+ssl_stapling_verify on;
+resolver {{ nginx_resolver.hosts | default(['208.67.222.222', '208.67.220.220']) | join(' ') }} valid={{ nginx_resolver.valid}}s;
+resolver_timeout {{ nginx_resolver.timeout }}s;
+
+
+# vim:filetype=nginx
diff --git a/templates/etc/nginx/helpers/ssl-strong.j2 b/templates/etc/nginx/helpers/ssl-strong.j2
new file mode 100644
index 0000000..1e5a5c4
--- /dev/null
+++ b/templates/etc/nginx/helpers/ssl-strong.j2
@@ -0,0 +1,18 @@
+#
+# {{ ansible_managed }}
+#
+
+ssl_ciphers "AES256+EECDH:AES256+EDH";
+ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
+ssl_prefer_server_ciphers on;
+ssl_session_cache shared:SSL:10m;
+add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
+add_header X-Frame-Options DENY;
+add_header X-Content-Type-Options nosniff;
+ssl_stapling on;
+ssl_stapling_verify on;
+resolver {{ nginx_resolver.hosts | default(['208.67.222.222', '208.67.220.220']) | join(' ') }} valid={{ nginx_resolver.valid}}s;
+resolver_timeout {{ nginx_resolver.timeout }}s;
+
+
+# vim:filetype=nginx
diff --git a/templates/etc/nginx/helpers/static-expires.j2 b/templates/etc/nginx/helpers/static-expires.j2
new file mode 100644
index 0000000..f553b32
--- /dev/null
+++ b/templates/etc/nginx/helpers/static-expires.j2
@@ -0,0 +1,10 @@
+#
+# {{ ansible_managed }}
+#
+
+location ~* \.(txt|js|css|png|jpg|jpeg|gif|ico|svg)$ {
+ expires 30d;
+ log_not_found off;
+}
+
+# vim:filetype=nginx
diff --git a/templates/etc/nginx/nginx.conf.j2 b/templates/etc/nginx/nginx.conf.j2
new file mode 100644
index 0000000..50bc444
--- /dev/null
+++ b/templates/etc/nginx/nginx.conf.j2
@@ -0,0 +1,30 @@
+#
+# {{ ansible_managed }}
+#
+
+user {{ nginx_user }};
+worker_processes {{ nginx_worker_processes }};
+pid {{ nginx_pid }};
+
+events {
+{% for key, value in nginx_events.iteritems() %}
+{{ "\t%-30s %s" | format(key, value) }};
+{% endfor %}
+}
+
+http {
+ types_hash_max_size 2048;
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ # From Ansible
+{% for key, value in nginx_http.iteritems() %}
+{{ "\t%-30s %s" | format(key, value) }};
+{% endfor %}
+ # /From Ansible
+
+ include /etc/nginx/conf.d/*.conf;
+ include /etc/nginx/sites-enabled/*;
+}
+
+# vim:filetype=nginx
diff --git a/templates/etc/nginx/sites-available/COMMON.j2 b/templates/etc/nginx/sites-available/COMMON.j2
new file mode 100644
index 0000000..b3e565e
--- /dev/null
+++ b/templates/etc/nginx/sites-available/COMMON.j2
@@ -0,0 +1,26 @@
+{% if item.ssl is defined and item.ssl.use is defined and item.ssl.use %}
+ listen {{ item.listen | default('443') }};
+# server_name {{ item.name | join(' ')}}; # TODO: gérer plusieurs vhosts
+ server_name {{ item.name }};
+ ssl on;
+ ssl_certificate {{ nginx_ssl_dir }}/{{ item.name }}/{{ item.name }}.crt;
+ ssl_certificate_key {{ nginx_ssl_dir }}/{{ item.name }}/{{ item.name }}.key;
+ include /etc/nginx/helpers/ssl-{{ item.ssl.template | default('strong') }};
+{% else %}
+ listen {{ item.listen | default('80') }};
+# server_name {{ item.name | join(' ')}}; # TODO: gérer plusieurs vhosts
+ server_name {{ item.name }};
+{% endif %}
+
+ root {{ nginx_root }}/{{ item.name }}/public;
+
+{% if item.use_access_log is defined and item.use_access_log %}
+ access_log {{ nginx_log_dir }}/{{ item.name }}_access.log combined;
+{% else %}
+ access_log off;
+{% endif %}
+{% if item.use_error_log is defined and item.use_error_log %}
+ error_log {{ nginx_log_dir }}/{{ item.name }}_error.log {{ nginx_error_log_level }};
+{% else %}
+ error_log off;
+{% endif %}
diff --git a/templates/etc/nginx/sites-available/REDIRECT.j2 b/templates/etc/nginx/sites-available/REDIRECT.j2
new file mode 100644
index 0000000..57dbb4d
--- /dev/null
+++ b/templates/etc/nginx/sites-available/REDIRECT.j2
@@ -0,0 +1,6 @@
+{% if item.redirect_server_name is defined %}
+server {
+ server_name {{ item.redirect_server_name | join(' ') }};
+ return 301 {% if item.ssl.use %}https{% else %}http{% endif %}://{{ item.server_name[0] }}$request_uri;
+}
+{% endif %}
diff --git a/templates/etc/nginx/sites-available/simple-php.j2 b/templates/etc/nginx/sites-available/simple-php.j2
new file mode 100644
index 0000000..d98af9f
--- /dev/null
+++ b/templates/etc/nginx/sites-available/simple-php.j2
@@ -0,0 +1,19 @@
+#
+# {{ ansible_managed }}
+#
+
+server {
+{% include 'COMMON.j2' %}
+
+ index index.htm index.html index.php;
+ location / {
+ try_files $uri $uri/ /index.php;
+ }
+ include /etc/nginx/helpers/php;
+ include /etc/nginx/helpers/no-ht;
+ include /etc/nginx/helpers/static-expires;
+}
+
+{% include 'REDIRECT.j2' %}
+
+# vim:filetype=nginx
diff --git a/templates/etc/nginx/sites-available/static.j2 b/templates/etc/nginx/sites-available/static.j2
new file mode 100644
index 0000000..e449c5d
--- /dev/null
+++ b/templates/etc/nginx/sites-available/static.j2
@@ -0,0 +1,19 @@
+#
+# {{ ansible_managed }}
+#
+
+server {
+{% include 'COMMON.j2' %}
+
+ index index.htm index.html;
+
+ location / {
+ try_files $uri $uri/ =404;
+ }
+ include /etc/nginx/helpers/static-expires;
+ include /etc/nginx/helpers/no-ht;
+}
+
+{% include 'REDIRECT.j2' %}
+
+# vim:filetype=nginx
diff --git a/templates/etc/nginx/sites-available/wordpress.j2 b/templates/etc/nginx/sites-available/wordpress.j2
new file mode 100644
index 0000000..3929170
--- /dev/null
+++ b/templates/etc/nginx/sites-available/wordpress.j2
@@ -0,0 +1,20 @@
+#
+# {{ ansible_managed }}
+#
+
+server {
+{% include 'COMMON.j2' %}
+
+ index index.htm index.html index.php;
+
+ location / {
+ try_files $uri $uri/ /index.php?q=$uri&$args;
+ }
+ include /etc/nginx/helpers/static-expires;
+ include /etc/nginx/helpers/no-ht;
+ include /etc/nginx/helpers/php;
+}
+
+{% include 'REDIRECT.j2' %}
+
+# vim:filetype=nginx
diff --git a/templates/etc/nginx/upstream/php.conf.j2 b/templates/etc/nginx/upstream/php.conf.j2
new file mode 100644
index 0000000..7f7a85c
--- /dev/null
+++ b/templates/etc/nginx/upstream/php.conf.j2
@@ -0,0 +1,26 @@
+#
+# {{ ansible_managed }}
+#
+
+{% if nginx_php_method == 'unix' %}
+upstream php {
+ {% for item in php_fpm_unix_sockets %}
+ server unix:{{ item }};
+ {% endfor %}
+}
+{% elif nginx_php_method == 'tcp' %}
+upstream php {
+ {% if nginx_cluster_php_method is defined %}
+ {{ nginx_cluster_php_method }};
+ {% endif %}
+ {% for item in nginx_cluster_servers %}
+ server {{ item.host }}:{{ item.port }} weight={{ item.weight | default('100') }} max_fails={{ item.max_fails | default('5') }} fail_timeout={{ item.fail_timeout | default('10s') }};
+ {% endfor %}
+}
+{% else %}
+#
+# Bad configuration...
+#
+{% endif%}
+
+# vim:filetype=nginx
diff --git a/templates/etc/nginx/upstream/php_tcp.j2 b/templates/etc/nginx/upstream/php_tcp.j2
new file mode 100644
index 0000000..c31ab21
--- /dev/null
+++ b/templates/etc/nginx/upstream/php_tcp.j2
@@ -0,0 +1,14 @@
+#
+# {{ ansible_managed }}
+#
+
+upstream php {
+ {% if nginx_cluster_php_method is defined %}
+ {{ nginx_cluster_php_method }};
+ {% endif %}
+ {% for item in nginx_cluster_servers %}
+ server {{ item.host }}:{{ item.port }} weight={{ item.weight | default('100') }} max_fails={{ item.max_fails | default('5') }} fail_timeout={{ item.fail_timeout | default('10s') }};
+ {% endfor %}
+}
+
+# vim:filetype=nginx
diff --git a/tests/test.yml b/tests/test.yml
index a8a2bc0..82ef063 100644
--- a/tests/test.yml
+++ b/tests/test.yml
@@ -1,5 +1,13 @@
---
- hosts: all
+ vars:
+ nginx_vhosts:
+ - name: 'test.local'
+ template: 'static'
+ ssl:
+ use: false
+ generatekey: false
+ template: 'strong'
roles:
- ../../
diff --git a/vars/main.yml b/vars/main.yml
index a38c5fb..9d92874 100644
--- a/vars/main.yml
+++ b/vars/main.yml
@@ -1,2 +1 @@
----
-# vars file for .
+nginx_dh_path: /etc/nginx/ssl/dhparams.pem