├── .gitignore ├── .travis.yml ├── CHANGES.rst ├── COPYRIGHT ├── LICENSE ├── README.md ├── defaults └── main.yml ├── docs ├── acme-support.rst ├── changelog.rst ├── copyright.rst ├── defaults-detailed.rst ├── getting-started.rst ├── includes │ ├── all.rst │ └── role.rst ├── index.rst ├── introduction.rst └── playbooks │ └── nginx.yml ├── handlers └── main.yml ├── meta ├── ansigenome.yml └── main.yml ├── tasks ├── main.yml ├── nginx │ ├── post_main.yml │ └── pre_main.yml ├── nginx_configs.yml ├── nginx_htpasswd.yml ├── nginx_servers.yml └── passenger_config.yml └── templates ├── etc ├── ansible │ └── facts.d │ │ └── nginx.fact.j2 ├── nginx │ ├── conf.d │ │ ├── custom.conf.j2 │ │ ├── map.conf.j2 │ │ ├── upstream_default.conf.j2 │ │ ├── upstream_php.conf.j2 │ │ ├── upstream_php5.conf.j2 │ │ └── upstream_rails.conf.j2 │ ├── nginx.conf.j2 │ ├── sites-available │ │ ├── custom.conf.j2 │ │ ├── debops__tpl_macros.j2 │ │ ├── default.conf.j2 │ │ ├── php.conf.j2 │ │ ├── php5.conf.j2 │ │ ├── proxy.conf.j2 │ │ └── rails.conf.j2 │ └── snippets │ │ └── acme-challenge.conf.j2 ├── pki │ └── hooks │ │ └── nginx.j2 └── sudoers.d │ └── nginx_webadmins.j2 └── srv └── www └── sites └── welcome └── public ├── index.html.j2 └── normalize.css /.gitignore: -------------------------------------------------------------------------------- 1 | ## For quick testing. 2 | ## Generated by https://github.com/ypid/ypid-ansible-common/blob/master/bin/sphinx-debops-role-build 3 | docs/Makefile 4 | docs/_build/ 5 | docs/conf.py 6 | docs/defaults.rst 7 | docs/includes/global.rst 8 | docs/_templates/page.html 9 | docs/_templates/.gitkeep 10 | docs/_static/custom.css 11 | docs/_static/.gitkeep 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | language: 'python' 4 | python: '2.7' 5 | 6 | virtualenv: 7 | system_site_packages: true 8 | 9 | env: 10 | - TEST_NGINX_FLAVOR="full" 11 | - TEST_NGINX_FLAVOR="passenger" 12 | 13 | before_install: True 14 | install: True 15 | 16 | script: 17 | - 'git clone --depth 1 https://github.com/nickjj/rolespec' 18 | - 'cd rolespec ; bin/rolespec -r https://github.com/debops/test-suite' 19 | 20 | notifications: 21 | webhooks: 22 | - 'https://galaxy.ansible.com/api/v1/notifications/' 23 | -------------------------------------------------------------------------------- /CHANGES.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | .. include:: includes/all.rst 5 | 6 | **debops.nginx** 7 | 8 | This project adheres to `Semantic Versioning `__ 9 | and `human-readable changelog `__. 10 | 11 | The current role maintainer_ is drybjed_. 12 | 13 | 14 | `debops.nginx master`_ - unreleased 15 | ----------------------------------- 16 | 17 | .. _debops.nginx master: https://github.com/debops/ansible-nginx/compare/v0.2.1...master 18 | 19 | 20 | `debops.nginx v0.2.1`_ - 2017-10-18 21 | ----------------------------------- 22 | 23 | .. _debops.nginx v0.2.1: https://github.com/debops/ansible-nginx/compare/v0.2.0...v0.2.1 24 | 25 | Added 26 | ~~~~~ 27 | 28 | - Support to disable :envvar:`nginx_acme_domain`. [ypid_] 29 | 30 | - Support simple maintenance mode using a static HTML page in the document root 31 | directory. [drybjed_] 32 | 33 | - Add :envvar:`nginx_extra_options` variable which can be used to add 34 | :command:`nginx` configuration outside of the ``http`` block in the 35 | :file:`/etc/nginx/nginx.conf` configuration file. [drybjed_] 36 | 37 | - Add a default :command:`nginx` map used to upgrade HTTP connections to 38 | WebSockets, usually used in reverse proxy configuration. [drybjed_] 39 | 40 | Changed 41 | ~~~~~~~ 42 | 43 | - ``item.frame_option`` and ``item.content_type_options`` can now be set to the 44 | special value ``{{ omit }}`` to omit their corresponding HTTP headers in 45 | nginx servers. [ypid_] 46 | 47 | Fixed 48 | ~~~~~ 49 | 50 | - Fixed the usage of :envvar:`nginx_default_ssl_verify_client` when 51 | :envvar:`nginx_ocsp` is set to ``False``. 52 | :envvar:`nginx_default_ssl_verify_client` does not depend on :envvar:`nginx_ocsp`. [ypid_] 53 | 54 | - CSP for welcome server did not end with a semicolon resulting in an invalid CSP with 55 | :envvar:`nginx__http_csp_append` set. [ypid_] 56 | 57 | 58 | `debops.nginx v0.2.0`_ - 2017-04-15 59 | ----------------------------------- 60 | 61 | .. _debops.nginx v0.2.0: https://github.com/debops/ansible-nginx/compare/v0.1.9...v0.2.0 62 | 63 | Added 64 | ~~~~~ 65 | 66 | - ``item.welcome_force`` which when set to ``True`` will ensure that the 67 | welcome page is up-to-date. Note that setting this to ``True`` will not allow 68 | idempotent operation. [ypid_] 69 | 70 | - Add/Set the default `Referrer Policy`_ to ``same-origin`` and made it 71 | configurable via :ref:`http_referrer_policy `. 72 | 73 | Also set the `Referrer Policy`_ in the welcome page as HTML meta option as 74 | some website checkers like https://webbkoll.dataskydd.net/en seem to not get 75 | the HTTP header option yet. 76 | 77 | Note that ``no-referrer`` was originally used in an unreleased version of the 78 | role but this seemed to cause issues with certain applications so it was 79 | changed to ``same-origin`` by default. ``no-referrer`` can still be used when 80 | you know it does not break anything. [ypid_, drybjed_, scibi_] 81 | 82 | Changed 83 | ~~~~~~~ 84 | 85 | - Standardize names of sites folders (`site-default` renamed to `sites-default`). [thiagotalma_] 86 | 87 | - During configuration, role will check the list of names of a given :program:`nginx` 88 | server against the list of configured PKI realms to see if any name is 89 | a subdomain of domain with an existing PKI realm. If such PKI realm is found, 90 | it will be used for this server instead of the default one. [drybjed_] 91 | 92 | - The URL scheme for the welcome page now defaults to ``HTTPS``. It can be 93 | configured as needed using the ``item.welcome_url_scheme`` option. [ypid_] 94 | 95 | - Update to the latest recommenced set of ciphers suites from 96 | https://bettercrypto.org/. [ypid_] 97 | 98 | - Rework the welcome page. Update to HTML5, make status configurable, define 99 | `Content Security Policy`, fix warnings and one templating error for 100 | ``nginx_tpl_welcome_title``. [ypid_] 101 | 102 | - Increase Ansible min version to ``2.1.5``. Everything below is deprecated 103 | anyway and has vulnerabilities so you don’t want to use that anymore. [ypid_] 104 | 105 | - Change :envvar:`nginx_hsts_preload` from ``True`` to ``False`` by default. 106 | Setting this value to ``True`` alone does not achieve anything and can 107 | actually cause problems if you are not prepared. 108 | Thus it is disabled by default. 109 | If you are ready for the future of HTTPS and TLS only, you are encouraged to 110 | enable it! [ypid_] 111 | 112 | - Redesign Content Security Policy support of the role. 113 | ``item.csp_policy`` has been renamed to ``item.csp`` and the original 114 | ``item.csp`` is now called ``item.csp_enabled``. 115 | It is now also possible to set a global ``report-uri`` for all CSPs. 116 | The role will assert that it is being used with the redesigned interface and 117 | will fail if it is not. You will need to update your role/playbook/inventory. 118 | Refer to :ref:`nginx__ref_servers_http_security_headers`. [ypid_] 119 | 120 | Deprecated 121 | ~~~~~~~~~~ 122 | 123 | - Deprecated the ``item.when`` and ``item.delete`` options. Use ``item.state`` instead. [ypid_] 124 | 125 | - Deprecated the ``php5`` server type in favor to :ref:`nginx__ref_servers_php`. [ypid_] 126 | 127 | Removed 128 | ~~~~~~~ 129 | 130 | - Remove the ``debops_nginx`` Ansible inventory group. Make sure you hosts 131 | are in ``debops_service_nginx``. [ypid_] 132 | 133 | - Remove the Ansible role dependencies. Make sure that role dependencies get 134 | executed in the playbook and get the depend variables of the role passed. 135 | ``nginx_dependencies`` has no effect anymore and can be removed from your 136 | inventory if you used it. [ypid_] 137 | 138 | Fixed 139 | ~~~~~ 140 | 141 | - Usage of an empty list for the ``name`` option of :envvar:`nginx__servers` items as documented. [ypid_] 142 | 143 | - Fixed :ref:`xss_protection ` which 144 | unintentionally determined if the 145 | :ref:`robots_tag ` header was set for a given 146 | Nginx server. [ypid_] 147 | 148 | - Make sure that the default HTTP server is selected even when ``item.listen`` 149 | parameter is not specified in any of the server configuration entries. 150 | [drybjed_] 151 | 152 | - Make sure that the configuration is generated correctly when HSTS preload is 153 | disabled. [drybjed_] 154 | 155 | 156 | `debops.nginx v0.1.9`_ - 2016-07-19 157 | ----------------------------------- 158 | 159 | .. _debops.nginx v0.1.9: https://github.com/debops/ansible-nginx/compare/v0.1.8...v0.1.9 160 | 161 | Changed 162 | ~~~~~~~ 163 | 164 | - Make sure that lists of IP addresses are always defined. [thiagotalma_] 165 | 166 | Security 167 | ~~~~~~~~ 168 | 169 | - Mitigate `HTTPOXY `_ attack on PHP applications 170 | server-side. [drybjed_] 171 | 172 | 173 | `debops.nginx v0.1.8`_ - 2016-07-13 174 | ----------------------------------- 175 | 176 | .. _debops.nginx v0.1.8: https://github.com/debops/ansible-nginx/compare/v0.1.7...v0.1.8 177 | 178 | Added 179 | ~~~~~ 180 | 181 | - Add new PHP upstream and PHP server templates. [drybjed_] 182 | 183 | - Support custom log directory path for nginx servers, specified by 184 | ``item.log_path`` parameter. [drybjed_] 185 | 186 | Changed 187 | ~~~~~~~ 188 | 189 | - Use the new debops.php_ fact to detect PHP version. [drybjed_] 190 | 191 | - Update Changelog format, add more documentation. [drybjed_] 192 | 193 | - Move the configuration of other roles to new namespaced variables, so 194 | playbooks can begin the switch. Old-style variables still work. [drybjed_] 195 | 196 | - Reorganize support for different :program:`nginx` flavors to use YAML dictionary 197 | maps for APT key ids and APT repositories. Full GPG key ids are used to 198 | download APT repository keys. [drybjed_] 199 | 200 | - Update tasks that manage :program:`nginx` servers, upstreams, maps and custom 201 | configuration. These lists now support the ``item.state`` parameter to 202 | control when configuration files should be present or absent. [drybjed_] 203 | 204 | - The ``item.enabled`` parameter in servers, maps, upstreams is now optional 205 | and if not specified, results in ``True``. [drybjed_] 206 | 207 | - Passwords used by ``htpasswd`` will now be hashes using ``sha512_crypt`` 208 | scheme. The default HTTP Basic Auth configuration variable is renamed from 209 | ``nginx_htpasswd_default`` to :envvar:`nginx__http_auth_htpasswd`. New 210 | :envvar:`nginx__dependent_htpasswd` list can be used by other roles to create 211 | ``htpasswd`` files as needed. [drybjed_] 212 | 213 | - You can now specify single server name in ``item.name`` parameter as a string 214 | instead of using a list notation. Lists are still supported. [drybjed_] 215 | 216 | - Direct output of ``service nginx reload`` in the :program:`nginx` PKI hook script to 217 | :file:`/dev/null`. This should stop annoying emails from :program:`cron` each time 218 | :program:`nginx` service is reloaded after certificate changes. [drybjed_] 219 | 220 | - Move variables from :file:`vars/main.yml` to :file:`defaults/main.yml` to allow 221 | modification. [drybjed_] 222 | 223 | Deprecated 224 | ~~~~~~~~~~ 225 | 226 | - Some of the default variables are deprecated in this version. Below you can 227 | find a list with their replacements. The old variable names will still be 228 | recognized for some time. [drybjed_] 229 | 230 | +------------------------------------------+--------------------------------------------+ 231 | | Deprecated variable | New variable | 232 | +==========================================+============================================+ 233 | | ``nginx_apt_preferences_dependent_list`` | ``nginx__apt_preferences__dependent_list`` | 234 | +------------------------------------------+--------------------------------------------+ 235 | | ``nginx_ferm_dependent_rules`` | ``nginx__ferm__dependent_rules`` | 236 | +------------------------------------------+--------------------------------------------+ 237 | | ``nginx_maps`` | ``nginx__maps`` | 238 | +------------------------------------------+--------------------------------------------+ 239 | | ``nginx_default_maps`` | ``nginx__default_maps`` | 240 | +------------------------------------------+--------------------------------------------+ 241 | | ``nginx_dependent_maps`` | ``nginx__dependent_maps`` | 242 | +------------------------------------------+--------------------------------------------+ 243 | | ``nginx_upstreams`` | ``nginx__upstreams`` | 244 | +------------------------------------------+--------------------------------------------+ 245 | | ``nginx_default_upstreams`` | ``nginx__default_upstreams`` | 246 | +------------------------------------------+--------------------------------------------+ 247 | | ``nginx_dependent_upstreams`` | ``nginx__dependent_upstreams`` | 248 | +------------------------------------------+--------------------------------------------+ 249 | | ``nginx_custom_config`` | ``nginx__custom_config`` | 250 | +------------------------------------------+--------------------------------------------+ 251 | | ``nginx_servers`` | ``nginx__servers`` | 252 | +------------------------------------------+--------------------------------------------+ 253 | | ``nginx_default_servers`` | ``nginx__default_servers`` | 254 | +------------------------------------------+--------------------------------------------+ 255 | | ``nginx_internal_servers`` | ``nginx__internal_servers`` | 256 | +------------------------------------------+--------------------------------------------+ 257 | | ``nginx_dependent_servers`` | ``nginx__dependent_servers`` | 258 | +------------------------------------------+--------------------------------------------+ 259 | | ``nginx_htpasswd`` | ``nginx__htpasswd`` | 260 | +------------------------------------------+--------------------------------------------+ 261 | 262 | - The ``php5`` server and upstream templates are deprecated in favor of 263 | ``php`` server and upstream templates. [drybjed_] 264 | 265 | Removed 266 | ~~~~~~~ 267 | 268 | - Remove the ``item.locked`` parameter from :program:`nginx` server configuration 269 | parameters. [drybjed_] 270 | 271 | 272 | `debops.nginx v0.1.7`_ - 2016-06-14 273 | ----------------------------------- 274 | 275 | .. _debops.nginx v0.1.7: https://github.com/debops/ansible-nginx/compare/v0.1.6...v0.1.7 276 | 277 | Added 278 | ~~~~~ 279 | 280 | - Added :envvar:`nginx__deploy_state` to allow to specify the desired state this role 281 | should achieve. State ``absent`` is not fully implemented yet. [ypid_] 282 | 283 | - Expose Nginx version via Ansible facts as ``ansible_local.nginx.version`` so 284 | that it can be used outside of this role. 285 | Check :file:`templates/etc/nginx/sites-available/default.conf.j2` 286 | for an example usage. [ypid_] 287 | 288 | Changed 289 | ~~~~~~~ 290 | 291 | - Changed the default value of the ``X-XSS-Protection`` HTTP header field from 292 | ``1`` (enabled), to ``1; mode=block`` (enabled and block rendering) for 293 | increased security and made the global default configurable via 294 | :envvar:`nginx__http_xss_protection`. Note that the ``mode=block`` might create 295 | `a vulnerability in old versions of Internet Explorer 296 | `. 297 | Additionally, changed the header field from ``X-Xss-Protection`` to the more 298 | common one ``X-XSS-Protection`` (``XSS`` in all upper case). [ypid_] 299 | 300 | - Make the ``X-Robots-Tag`` HTTP header field configurable via 301 | ``item.robots_tag`` and :envvar:`nginx__http_robots_tag`. [ypid_] 302 | 303 | - Make the ``X-Permitted-Cross-Domain-Policies`` HTTP header field configurable 304 | via ``item.permitted_cross_domain_policies`` and 305 | :envvar:`nginx__http_permitted_cross_domain_policies`. [ypid_] 306 | 307 | Fixed 308 | ~~~~~ 309 | 310 | - Fixed Ansible check mode. Check mode did fail when the role was trying to 311 | symlink a non-existing file. [ypid_] 312 | 313 | 314 | `debops.nginx v0.1.6`_ - 2016-03-07 315 | ----------------------------------- 316 | 317 | .. _debops.nginx v0.1.6: https://github.com/debops/ansible-nginx/compare/v0.1.5...v0.1.6 318 | 319 | Added 320 | ~~~~~ 321 | 322 | - Add support for defining error pages in a list, with better control over 323 | their configuration. [drybjed_] 324 | 325 | Changed 326 | ~~~~~~~ 327 | 328 | - Do not create welcome pages automatically if creation of webroot directories 329 | is disabled. [drybjed_] 330 | 331 | - Make sure that :file:`/var/lib/nginx/` directory exists. [pedroluislopez_] 332 | 333 | - Ensure that list of site referers is unique. [drybjed_] 334 | 335 | - Use an absolute path in the :program:`nginx` PKI hook for ``service`` command, since 336 | it's outside of the default ``$PATH`` defined by :program:`cron`. [drybjed_] 337 | 338 | 339 | `debops.nginx v0.1.5`_ - 2016-02-07 340 | ----------------------------------- 341 | 342 | .. _debops.nginx v0.1.5: https://github.com/debops/ansible-nginx/compare/v0.1.4...v0.1.5 343 | 344 | Added 345 | ~~~~~ 346 | 347 | - Create a proof-of-concept "solo" version of the role, that does not include 348 | additional Ansible role dependencies. [drybjed_] 349 | 350 | - Add default ``localhost`` nginx server. It has disabled HTTPS support and can 351 | be used by other applications to get the nginx status page locally. [drybjed_] 352 | 353 | - Add support for getting the client IP address from a custom header, when 354 | :program:`nginx` is used behind a proxy server. [drybjed_] 355 | 356 | - Add a way to control if ``debops.nginx`` role automatically adds 357 | ``ipv6only=false`` to the configuration to support dual-stack IPv4/IPv6 358 | connections. This was the default, now it can be disabled so that users can 359 | control the listening ports themselves. [drybjed_] 360 | 361 | - Add support for ``HTTP/2`` deprecating ``SPDY`` in :program:`nginx` 1.9.5. 362 | [MatthewMi11er] 363 | 364 | - Add support for Automated Certificate Management Environment (ACME) 365 | challenges. [drybjed_] 366 | 367 | - Provide a clean and simple welcome page which is displayed by default if 368 | specified server does not exist. The welcome page will be generated only if 369 | ``index.html`` is not present in the webroot directory. [drybjed_] 370 | 371 | - Add a hook script in :file:`/etc/pki/hooks/` directory. When certificates used by 372 | :program:`nginx` are changed, it will reload the webserver to enable them. [drybjed_] 373 | 374 | Changed 375 | ~~~~~~~ 376 | 377 | - Switch from using Diffie-Hellman parameters generated by debops.pki_ role 378 | to DH parameters managed by debops.dhparam_ role. [drybjed_] 379 | 380 | - Most of the file paths used by :program:`nginx` are now configurable using default 381 | variables. This allows to run :program:`nginx` on an unprivileged account. 382 | 383 | ``nginx_root_www_path`` variable has been renamed to ``nginx_www``. [drybjed_] 384 | 385 | - Allow configuration of default ``listen`` and ``listen_ssl`` directives using 386 | default variables. [drybjed_] 387 | 388 | - Move configuration of ``debops.nginx`` role dependencies to default 389 | variables. It can be used to configure firewall and APT preferences using 390 | Ansible playbooks instead of hardcoding the dependencies in the role itself. 391 | 392 | Existing role dependencies are still used, and will be removed once all 393 | involved application playbooks which depend on ``debops.nginx`` are updated. 394 | [drybjed_] 395 | 396 | - Update ``localhost`` server to also accept connections on loopback IP 397 | addresses, so that check plugins like :command:`check_mk` can work correctly. [ypid_] 398 | 399 | - Wrap the default HTTP redirect configuration in ``location / {}`` section. 400 | This allows addition of other location sections as necessary without breaking 401 | the page. [drybjed_] 402 | 403 | - Support ``item.options`` YAML text block in nginx upstreams. [drybjed_] 404 | 405 | - Move the ``root`` parameter to its own macro block and use it separately in 406 | HTTP and HTTPS server configuration section. This is needed for the HTTP 407 | configuration to serve files from a sane directory. [drybjed_] 408 | 409 | - Don't print ``root`` option in the :program:`nginx` server configuration if it's set 410 | as ``False`` (shouldn't be used, but it is checked just in case). [drybjed_] 411 | 412 | - Make sure that ``root`` and ACME configuration is not added two times when 413 | HTTP listen configuration is disabled. [drybjed_] 414 | 415 | - Clean up default variables related to debops.pki_ role, add variables that 416 | configure client CA and trusted CA for OCSP stapling in :file:`default.conf` 417 | template. [drybjed_] 418 | 419 | - Update OCSP stapling support. Two new default variables are added to better 420 | control OCSP configuration. 421 | 422 | The ``debops.nginx`` role will now use the trusted certificate chain from 423 | debops.pki_ by default. The caveat is, if at least a Root CA certificate 424 | is not provided in the debops.pki_ realm, :program:`nginx` configuration will be 425 | invalid and restarting the webserver will fail. Right now you can avoid this 426 | by setting ``nginx_ocsp_verify`` variable to ``False`` if needed, there's 427 | also per-vhost ``item.ocsp_verify`` equivalent. 428 | 429 | The internal debops.pki_ certificates should work out of the box. 430 | [drybjed_] 431 | 432 | - Support autodetection of PKI realms. 433 | 434 | The ``debops.nginx`` role will check if any of the server names for a given 435 | vhost have corresponding PKI realms. If a corresponding realm is found, its 436 | certificates will be used for that server, unless overridden by 437 | ``item.pki_realm`` parameter. If a corresponding realm is not found, that 438 | vhost will use the default PKI realm. [drybjed_] 439 | 440 | - Support `HSTS preloading `_ in :program:`nginx` 441 | server configuration. [drybjed_] 442 | 443 | - Reorganize server, upstream and map default variables. 444 | 445 | The ``nginx_servers`` variable has been split into 446 | 447 | - ``nginx_default_servers`` (default welcome page of the server); 448 | - ``nginx_internal_servers`` (``localhost`` and ``acme`` servers); 449 | - ``nginx_dependent_servers`` (webservers managed by other roles); 450 | 451 | Similar split has been done with ``nginx_upstreams`` and ``nginx_maps`` 452 | variables. The order of the variables is designed so that if you configure an 453 | :program:`nginx` website in the ``nginx_servers`` list (the same as up until now), 454 | the first one on the list will be marked as default, easily overriding the 455 | welcome page defined in ``nginx_default_servers``. 456 | 457 | The ``nginx_server_default`` dictionary variable has been renamed to 458 | ``nginx_server_welcome`` and now defines the default welcome page. You might 459 | need to update the Ansible inventory. 460 | 461 | The ``nginx_upstream_php5`` dictionary variable has been renamed to 462 | ``nginx_upstream_php5_www_data`` to be more specific. It defines an upstream 463 | for the default ``www-data`` PHP5 pool used by various services packaged in 464 | Debian. You might need to update the Ansible inventory. [drybjed_] 465 | 466 | - The default "welcome page" :program:`nginx` server will use the ``welcome`` server 467 | name, so that role users can use empty name (``[]``) parameter in Ansible 468 | inventory without the configuration being constantly overwritten in an 469 | idempotency loop. The welcome page automatically gets its own web root 470 | directory :file:`/srv/www/sites/welcome/public/`, and shouldn't conflict with the 471 | default root. 472 | 473 | This shouldn't affect the effect of ``default_server`` option. The 474 | ``welcome`` "hostname" most likely won't ever be present in the DNS and 475 | nothing should directly point to it. [drybjed_] 476 | 477 | - Create the specified :program:`nginx` maps and upstreams even when ``nginx_maps`` 478 | and ``nginx_upstreams`` lists are empty. [drybjed_] 479 | 480 | Removed 481 | ~~~~~~~ 482 | 483 | - Remove the "solo" version of the role, a different concept will be created in 484 | its place. [drybjed_] 485 | 486 | - Remove ``item.pki`` in favor of ``item.ssl`` in the nginx site configuration. 487 | [patrickheeney_] 488 | 489 | - Remove ``nginx_default_root`` variable. A default root directory is managed 490 | dynamically in the :file:`default.conf` server template. [drybjed_] 491 | 492 | Fixed 493 | ~~~~~ 494 | 495 | - Fix https site detection when using debops.nginx as a dependency. 496 | [patrickheeney_] 497 | 498 | - Fix bare variables due to deprecation. [drybjed_] 499 | 500 | 501 | `debops.nginx v0.1.4`_ - 2015-09-24 502 | ----------------------------------- 503 | 504 | .. _debops.nginx v0.1.4: https://github.com/debops/ansible-nginx/compare/v0.1.3...v0.1.4 505 | 506 | Added 507 | ~~~~~ 508 | 509 | - Add an option to set ``client_max_body_size`` globally for entire nginx 510 | server, by setting ``nginx_http_client_max_body_size`` variable in Ansible 511 | inventory. [drybjed_] 512 | 513 | - Add DebOps pre-tasks and post-tasks hooks. [drybjed_] 514 | 515 | - Add an option to set custom index files in nginx configuration. [drybjed_] 516 | 517 | - Add ``item.redirect_to`` key which lets you redirect connection from all 518 | server names listed in ``item.name`` to a specific server name (inverse 519 | ``item.redirect_from``). [drybjed_] 520 | 521 | - Add support for :program:`nginx` package from upstream (https://nginx.org/), thanks 522 | to Pedro Luis López Sánchez. [drybjed_] 523 | 524 | - Add ``proxy`` nginx server template. [drybjed_] 525 | 526 | - Add ``item.ssl_crt``, ``item.ssl_key``, and ``item.ssl_dhparam`` to override 527 | pki nginx configuration per site. [patrickheeney_] 528 | 529 | - Added ``enabled`` to entries in ``item.location_list``. [scibi_] 530 | 531 | Changed 532 | ~~~~~~~ 533 | 534 | - Allow to override ``nginx_passenger_root`` and ``nginx_passenger_ruby`` 535 | variables using Ansible inventory variables. [drybjed_] 536 | 537 | - Make sure that lists of IP addresses used in the templates are unique, this 538 | is required to eliminate duplicate IPv6 addresses in case of VLAN use. 539 | [drybjed_] 540 | 541 | - Move most of the http options from :file:`/etc/nginx/nginx.conf` template to 542 | ``nginx_http_options`` YAML text block for easy modification if necessary. 543 | [drybjed_] 544 | 545 | - By default access to hidden files is blocked in :program:`nginx` servers, 546 | ``item.deny_hidden`` key allows you to disable that. [drybjed_] 547 | 548 | - Filter out ``link-local`` IPv6 addresses from list of addresses that can 549 | access the :file:`/nginx_status` page. [drybjed_] 550 | 551 | - Change how list of nameservers is gathered from :file:`/etc/resolv.conf` to fix 552 | an issue with ``sed`` in shell command. [drybjed_] 553 | 554 | - Use ``fastcgi_params`` instead of :file:`fastcgi.conf` as the FastCGI parameters 555 | file when ``nginx.org`` flavor is installed, because it is not provided by 556 | the non-Debian packages. On ``passenger`` and ``nginx.org`` flavors, missing 557 | ``SCRIPT_FILENAME`` parameter will be added directly in nginx server 558 | configuration. [drybjed_] 559 | 560 | - Update ``userdir`` support to be more configurable. [drybjed_] 561 | 562 | - Use all available nameservers as OCSP resolvers instead of just the first 563 | one. User can also override the list of OCSP resolvers if needed. [drybjed_] 564 | 565 | - Rearrange parts of the configuration templates and add more Jinja blocks to 566 | be able to remove ``index`` and ``root`` directives programmatically. 567 | [drybjed_] 568 | 569 | Fixed 570 | ~~~~~ 571 | 572 | - Fix an issue where :program:`nginx` used SSL configuration when support for it was 573 | disabled in debops.pki_ (or it was not present). [drybjed_] 574 | 575 | 576 | `debops.nginx v0.1.3`_ - 2015-03-27 577 | ----------------------------------- 578 | 579 | .. _debops.nginx v0.1.3: https://github.com/debops/ansible-nginx/compare/v0.1.2...v0.1.3 580 | 581 | Added 582 | ~~~~~ 583 | 584 | - Add support for custom configuration templates using text blocks. [drybjed_] 585 | 586 | Changed 587 | ~~~~~~~ 588 | 589 | - Be more explicit while getting the list of nameservers from 590 | :file:`/etc/resolv.conf` [drybjed_] 591 | 592 | 593 | `debops.nginx v0.1.2`_ - 2015-03-13 594 | ----------------------------------- 595 | 596 | .. _debops.nginx v0.1.2: https://github.com/debops/ansible-nginx/compare/v0.1.1...v0.1.2 597 | 598 | Added 599 | ~~~~~ 600 | 601 | - Add a way to redirect HTTP site to HTTPS conditionally, with configuration 602 | being set in a separate file. [drybjed_] 603 | 604 | Changed 605 | ~~~~~~~ 606 | 607 | - Switch to older version of :file:`/etc/nginx/fastcgi_params` when Phusion 608 | Passenger is enabled, because Passenger packages do not provide 609 | :file:`/etc/nginx/fastcgi.conf` configuration file at the moment. [drybjed_] 610 | 611 | 612 | `debops.nginx v0.1.1`_ - 2015-03-12 613 | ----------------------------------- 614 | 615 | .. _debops.nginx v0.1.1: https://github.com/debops/ansible-nginx/compare/v0.1.0...v0.1.1 616 | 617 | Added 618 | ~~~~~ 619 | 620 | - Add support for `Phusion Passenger`_ nginx flavor, using external APT 621 | packages. [rchady, drybjed_] 622 | 623 | Changed 624 | ~~~~~~~ 625 | 626 | - Automatically enable or disable SSL support in :program:`nginx` depending on the 627 | presence or absence of debops.pki_ local Ansible facts. [drybjed_] 628 | 629 | .. _Phusion Passenger: https://www.phusionpassenger.com/ 630 | 631 | 632 | debops.nginx v0.1.0 - 2015-02-11 633 | -------------------------------- 634 | 635 | Added 636 | ~~~~~ 637 | 638 | - First release, add CHANGES.rst [drybjed_] 639 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | debops.nginx - Manage nginx webserver using Ansible 2 | 3 | Copyright (C) 2014-2017 Maciej Delmanowski 4 | Copyright (C) 2015-2017 Robin Schneider 5 | Copyright (C) 2014-2017 DebOps https://debops.org/ 6 | 7 | This Ansible role is part of DebOps. 8 | 9 | DebOps is free software; you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License version 3, as 11 | published by the Free Software Foundation. 12 | 13 | DebOps is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with DebOps. If not, see https://www.gnu.org/licenses/. 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | 676 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## [![DebOps](https://debops.org/images/debops-small.png)](https://debops.org) nginx 2 | 3 | 5 | 6 | [![Travis CI](https://img.shields.io/travis/debops/ansible-nginx.svg?style=flat)](https://travis-ci.org/debops/ansible-nginx) 7 | [![test-suite](https://img.shields.io/badge/test--suite-ansible--nginx-blue.svg?style=flat)](https://github.com/debops/test-suite/tree/master/ansible-nginx/) 8 | [![Ansible Galaxy](https://img.shields.io/badge/galaxy-debops.nginx-660198.svg?style=flat)](https://galaxy.ansible.com/debops/nginx) 9 | 10 | 11 | The [nginx](https://nginx.org/) is a fast and light webserver with extensible 12 | configuration. 13 | 14 | The `debops.nginx` role can be used to install and manage `nginx` configuration 15 | for multiple websites at the same time. The server is configured using 16 | inventory variables. This role can also be used as a dependency of another role 17 | to configure a webserver for that role using dependency variables. 18 | 19 | ### Installation 20 | 21 | This role requires at least Ansible `v2.1.5`. To install it, run: 22 | 23 | ```Shell 24 | ansible-galaxy install debops.nginx 25 | ``` 26 | 27 | ### Documentation 28 | 29 | More information about `debops.nginx` can be found in the 30 | [official debops.nginx documentation](https://docs.debops.org/en/latest/ansible/roles/ansible-nginx/docs/). 31 | 32 | 33 | ### Role dependencies 34 | 35 | - `debops.secret` 36 | - `debops.apt_preferences` 37 | - `debops.ferm` 38 | 39 | ### Are you using this as a standalone role without DebOps? 40 | 41 | You may need to include missing roles from the [DebOps common 42 | playbook](https://github.com/debops/debops-playbooks/blob/master/playbooks/common.yml) 43 | into your playbook. 44 | 45 | [Try DebOps now](https://debops.org/) for a complete solution to run your Debian-based infrastructure. 46 | 47 | 48 | 49 | 50 | 51 | ### Authors and license 52 | 53 | - [Maciej Delmanowski](https://docs.debops.org/en/latest/debops-keyring/docs/entities.html#debops-keyring-entity-drybjed) (maintainer) | [e-mail](mailto:drybjed@gmail.com) | [Twitter](https://twitter.com/drybjed) | [GitHub](https://github.com/drybjed) 54 | - [Robin Schneider](https://docs.debops.org/en/latest/debops-keyring/docs/entities.html#debops-keyring-entity-ypid) | [e-mail](mailto:ypid@riseup.net) | [GitHub](https://github.com/ypid) 55 | 56 | License: [GPL-3.0](https://tldrlegal.com/license/gnu-general-public-license-v3-%28gpl-3%29) 57 | 58 | *** 59 | 60 | This role is part of [DebOps](https://debops.org/). README generated by [ansigenome](https://github.com/nickjj/ansigenome/). 61 | -------------------------------------------------------------------------------- /docs/acme-support.rst: -------------------------------------------------------------------------------- 1 | Automated Certificate Management Environment (ACME) 2 | =================================================== 3 | 4 | ACME is an initiative designed by Internet Security Research Group for the 5 | `Let's Encrypt `_ service. It can be used to 6 | provision trusted X.509 certificates in a fully automated way. 7 | 8 | One of the challenges to prove control over a domain to an ACME CA server is 9 | called ``http-01`` , which uses a well-known path on the client web server to 10 | serve files which can then be validated by the CA server. This should be 11 | sufficient to prove that a given domain is controlled by the entity that 12 | requests the certificate. 13 | 14 | The ``debops.nginx`` Ansible role includes support for the ``http-01`` challenge. 15 | They are enabled by default for all server configurations and can be used to 16 | prove control over specified domains using a ACME client. 17 | 18 | Ansible local facts 19 | ------------------- 20 | 21 | The following ACME related Ansible local facts are exposed by the role: 22 | 23 | .. code-block:: none 24 | 25 | ansible_local.nginx.acme 26 | ansible_local.nginx.acme_root 27 | ansible_local.nginx.acme_server 28 | ansible_local.nginx.acme_domain 29 | 30 | How ACME support works 31 | ---------------------- 32 | 33 | By default, all servers that have enabled ACME support, will answer queries 34 | on URL: 35 | 36 | .. code-block:: none 37 | 38 | http:///.well-known/acme-challenge/xxxxxxxxxxxxxxxx 39 | 40 | These queries will be answered over HTTP. Files will be served from the 41 | particular server ``root`` directory, for example: 42 | 43 | .. code-block:: none 44 | 45 | /srv/www/sites//public/.well-known/acme-challenge/ 46 | 47 | If the challenge file is not found at the server location, :program:`nginx` will 48 | switch the request to the "global" server ``root`` directory, by default: 49 | 50 | .. code-block:: none 51 | 52 | /srv/www/sites/acme/public/.well-known/acme-challenge/ 53 | 54 | This directory can be configured in the ``debops.nginx`` default variables, and 55 | is not managed by the role itself. Other Ansible roles are expected to create 56 | it and secure it using UNIX permissions as necessary. 57 | 58 | If the requested file is not found on the "global" server ``root`` directory, 59 | the ACME challenge will be redirected over the same protocol (HTTP or HTTPS) to 60 | a different host on configured domain, by default: 61 | 62 | .. code-block:: none 63 | 64 | $scheme://acme.{{ nginx_acme_domain }}$request_uri?redirect=yes 65 | 66 | The redirected host should provide a configured webserver to respond to the 67 | ACME challenges. A default server is provided in the ``debops.nginx`` 68 | configuration and can be enabled on a given host (see below). The additional 69 | parameter ``redirect=yes`` is used by the :program:`nginx` server to detect and 70 | terminate redirect loops. 71 | 72 | Manual nginx configuration 73 | -------------------------- 74 | 75 | The above steps are configured in a separate file on the webserver host: 76 | 77 | .. code-block:: none 78 | 79 | /etc/nginx/snippets/acme-challenge.conf 80 | 81 | To enable a given :program:`nginx` server to respond to ACME challenges, all you 82 | need to do is to include that file in the ``server {}`` section, for example: 83 | 84 | .. code-block:: nginx 85 | 86 | server { 87 | listen [::]:80 88 | 89 | server_name example.org; 90 | 91 | root /srv/www/sites/example.org/public; 92 | 93 | include snippets/acme-challenge.conf; 94 | 95 | location / { 96 | try_files $uri $uri/ /index.html =404; 97 | } 98 | } 99 | 100 | Above configuration should be sufficient to satisfy local or remote ACME 101 | challenges. Similar configuration can be done on HTTPS server to achieve the 102 | same results. 103 | -------------------------------------------------------------------------------- /docs/changelog.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../CHANGES.rst 2 | -------------------------------------------------------------------------------- /docs/copyright.rst: -------------------------------------------------------------------------------- 1 | Copyright 2 | ========= 3 | 4 | .. literalinclude:: ../COPYRIGHT 5 | -------------------------------------------------------------------------------- /docs/defaults-detailed.rst: -------------------------------------------------------------------------------- 1 | .. _nginx__ref_default_variable_details: 2 | 3 | Default variable details 4 | ======================== 5 | 6 | .. include:: includes/all.rst 7 | 8 | Some of ``debops.nginx`` default variables have more extensive configuration 9 | than simple strings or lists, here you can find documentation and examples for 10 | them. 11 | 12 | .. This section is intended to be similar to the docs in debops.apache. If you 13 | modify this section, consider also checking debops.apache if the same also 14 | applies there, please. 15 | 16 | .. contents:: 17 | :local: 18 | :depth: 2 19 | 20 | 21 | .. _nginx__ref_servers: 22 | 23 | nginx__servers 24 | -------------- 25 | 26 | Common role options 27 | ~~~~~~~~~~~~~~~~~~~ 28 | 29 | ``name`` 30 | Required, string or list of strings. 31 | Domain names for the `Nginx server_name option documentation`_. 32 | 33 | The first element is used to create the name of the nginx configuration 34 | file and must be a normal domain name, other elements can include 35 | wildcards and regexp matches. 36 | 37 | The list can also be empty (but needs to be defined) in which case the 38 | configuration it is included in will be named :file:`default`. 39 | 40 | ``filename`` 41 | Optional, string. 42 | Alternative name of the nginx configuration file under the 43 | :file:`/etc/nginx/sites-available/` directory. The suffix :file:`.conf` will be 44 | added automatically. This can be used to distinguish different server 45 | configurations for the same ``item.name``. For example separate 46 | configuration for HTTP and HTTPS. 47 | 48 | ``enabled`` 49 | Optional, boolean. Defaults to ``True``. 50 | Specifies if the configuration should be enabled by creating a symlink in 51 | :file:`/etc/nginx/sites-enabled/`. 52 | 53 | ``state`` 54 | Optional, string. Defaults to ``present``. 55 | Whether the Nginx server should be ``present`` or ``absent``. 56 | 57 | ``when`` 58 | Deprecated, optional, boolean. Use ``state: 'present'`` instead. 59 | 60 | ``delete`` 61 | Deprecated, optional, boolean. Use ``state: 'absent'`` instead. 62 | 63 | ``by_role`` 64 | Optional, string. Name of a Ansible role in the format ``ROLE_OWNER.ROLE_NAME`` which is 65 | responsible for the server configuration. 66 | 67 | ``type`` 68 | Optional. Specify name of the template to use to generate nginx server 69 | configuration. Templates can extend other templates. 70 | 71 | ``owner`` 72 | Optional, string. If specified, nginx will configure the server root to 73 | :file:`/srv/www/<``owner>/sites//public/`, if not specified, 74 | nginx will configure server root to 75 | :file:`/srv/www/sites/<``name[0]/public/`. 76 | 77 | 78 | .. _nginx__ref_servers_common_webserver_options: 79 | 80 | Common webserver options 81 | ~~~~~~~~~~~~~~~~~~~~~~~~ 82 | 83 | ``index`` 84 | Optional, string or boolean (``False``). 85 | Space separated list of index filenames. 86 | The directive will be omitted if set to ``False``. 87 | 88 | ``root`` 89 | Optional, string. 90 | Absolute path to server root to use for this server configuration. 91 | Defaults to :file:`/srv/www/<``name[0]>/public/`. 92 | See also ``owner`` parameter. 93 | The directive will be omitted if set to ``False``. 94 | 95 | ``try_files`` 96 | Optional, string. Defaults to ``nginx_default_try_files``. 97 | Checks for the existence of files in order, and returns the 98 | first file that is found for location /. 99 | Refer to the `Nginx try_files directive` for details. 100 | 101 | ``keepalive`` 102 | Optional, integer. Defaults to ``nginx_default_keepalive_timeout``. 103 | Set custom KeepAlive timeout for this server, in seconds. 104 | 105 | ``deny_hidden`` 106 | Optional, boolean. Defaults to ``True``. 107 | If ``True`` deny access to all hidden files. 108 | 109 | ``favicon`` 110 | Optional, boolean. Defaults to ``True``. 111 | Ignore :file:`/favicon.ico` requests in server logs to reduce noise if 112 | ``True``. 113 | 114 | ``listen`` 115 | Optional, list of strings/integers or boolean (``False``). 116 | Defaults to ``nginx_listen_port``. 117 | List of ports, IP addresses or sockets this server configuration should 118 | listen on for HTTP connections. 119 | 120 | ``listen_ssl`` 121 | Optional, list of strings/integers or boolean (``False``). 122 | Defaults to ``nginx_listen_ssl_port``. 123 | List of ports, IP addresses or sockets this server configuration should 124 | listen on for HTTPS connections. 125 | 126 | ``include_files_begin`` 127 | Optional, list of strings. 128 | List of files that will be included at the beginning of the server 129 | configuration using `include`. 130 | 131 | ``include_files_end`` 132 | Optional, list of strings. 133 | List of files that will be included at the end of the server 134 | configuration using `include`. 135 | 136 | 137 | Redirects 138 | ~~~~~~~~~ 139 | 140 | ``redirect`` 141 | Optional, string. 142 | Redirect incoming requests on the HTTP port to the given URL. 143 | FIXME: Rename to redirect_http 144 | 145 | ``redirect_ssl`` 146 | Optional, string. 147 | Redirect incoming requests on the HTTPS port to the given URL. 148 | FIXME: Rename to redirect_https 149 | 150 | ``redirect_code`` 151 | Optional, string. Specify HTTP code used in the redirect response, by default 152 | 307 Temporary Redirect. 153 | FIXME: Rename to redirect_http_code 154 | 155 | ``redirect_code_ssl`` 156 | Optional, string. Specify HTTP code used in the redirect response from HTTP to 157 | HTTPS, by default 301 Moved Permanently. 158 | FIXME: Rename to redirect_https_code 159 | 160 | ``redirect_from`` 161 | Optional, list of strings or boolean. 162 | Create a separate `Nginx server block documentation`_ which will automatically redirect 163 | requests from specified list of server names (or all but the first name in 164 | the ``name`` list if ``redirect_from`` is set to ``True``) to the first 165 | server name specified in the ``name`` list. 166 | 167 | ``redirect_to`` 168 | Optional, string. Create separate `Nginx server block documentation`_ which redirects all 169 | requests on servers specified in the ``name`` list to the server 170 | specified in ``redirect_to``. The specified server name will be used as 171 | the only name in subsequent HTTP and HTTPS configuration. 172 | 173 | ``redirect_to_ssl`` 174 | Optional, boolean. Defaults to ``True`` 175 | If ``True``, redirect connection from HTTP to the HTTPS version of the site. 176 | Set to ``False`` to allow to serve the website via HTTP and HTTPS and don't 177 | redirect HTTP to HTTPS. 178 | FIXME: Rename to redirect_to_https 179 | 180 | 181 | HTTPS and TLS 182 | ~~~~~~~~~~~~~ 183 | 184 | ``acme`` 185 | Optional, boolean. Defaults to ``nginx_acme``. 186 | Enable or disable support for Automated Certificate Management Environment 187 | challenge queries for this server. 188 | 189 | ``ssl`` 190 | Optional, boolean. Defaults to ``nginx_pki``. 191 | Enable or disable HTTPS for this server configuration. 192 | FIXME: Rename to https_enabled 193 | 194 | ``ssl_crt`` 195 | Optional, string. Absolute path to a custom X.509 certificate to use. If not 196 | supplied, a certificate managed by debops.pki_ will be used. 197 | FIXME: Rename to tls_cert 198 | 199 | ``ssl_key`` 200 | Optional, string. Absolute path to custom private key to use. If not 201 | supplied ``pki_key`` will be used instead. 202 | FIXME: Rename to tls_key 203 | 204 | ``ssl_ca`` 205 | Optional, string. Specifies the absolute path to the client CA certificate 206 | used to authenticate clients. If not specified, ``pki_ca`` will be used 207 | instead. 208 | 209 | ``ssl_trusted`` 210 | Optional, string. Specifies the absolute path to the intermediate+root CA server 211 | certificates which will be used for OCSP stapling verification. If not 212 | specified, the value of ``pki_trusted`` will be used instead. 213 | 214 | ``ssl_dhparam`` 215 | Optional, string. Absolute path to custom DHE parameters to use. If not supplied, 216 | ``nginx_ssl_dhparam`` will be used instead. 217 | FIXME: Rename to tls_dhparam_file 218 | 219 | ``ssl_ciphers`` 220 | Optional, strings. Defaults to ``nginx_default_ssl_ciphers``. 221 | Name of the list of preferred server ciphers defined in ``nginx_ssl_ciphers`` to use. 222 | 223 | ``ssl_curve`` 224 | Optional, string. Defaults to ``nginx_default_ssl_curve``. 225 | ECC curve enabled for this server. 226 | 227 | ``ssl_verify_client`` 228 | Optional, boolean. Requests the client certificate and verifies it if the 229 | certificate is present. 230 | 231 | ``ssl_client_certificate`` 232 | Optional, string. Specifies a file with trusted CA certificates in the PEM 233 | format used to verify client certificates. 234 | 235 | ``ssl_crl`` 236 | Optional. Specifies a file with revoked certificates (CRL) in PEM 237 | format used to verify client certificates. 238 | 239 | ``pki_realm`` 240 | Optional, string. Overwrites the default PKI realm used by nginx for this 241 | server configuration. See the debops.pki_ role for more information, as well 242 | as the :file:`/etc/pki/realms` directory on remote hosts for a list of 243 | available realms. 244 | 245 | ``pki_crt`` 246 | Optional, string. Path to custom X.509 certificate to use, relative to the 247 | currently enabled PKI realm. 248 | 249 | ``pki_key`` 250 | Optional, string. Path to custom private key to use, relative to the 251 | currently enabled PKI realm. 252 | 253 | ``pki_ca`` 254 | Optional, string. Path to custom client CA certificate to use for client 255 | authentication, relative the to currently enabled PKI realm. 256 | 257 | ``pki_trusted`` 258 | Optional, string. Path to custom intermediate+root CA certificate to use for 259 | OCSP stapling verification, relative to currently enabled PKI realm. 260 | 261 | ``ocsp`` 262 | Optional, boolean. Defaults to ``nginx_ocsp``. 263 | Enable or disable OCSP stapling for a given server. 264 | FIXME: Rename to ocsp_stapling_enabled 265 | 266 | ``ocsp_verify`` 267 | Optional, boolean. Defaults to ``nginx_ocsp_verify`` 268 | Enable or disable OCSP stapling verification for a given server. An 269 | intermediate+root CA certificate is required for this. 270 | FIXME: Rename to ocsp_stapling_verify 271 | 272 | ``hsts_enabled`` 273 | Optional, boolean. Defaults to ``True``. If this is set to ``True`` and HTTPS 274 | is enabled for this item, the `HTTP Strict Transport Security`_ header is set 275 | in the server's responses. If this is set to ``False``, the header will not 276 | be set in the server's responses. 277 | 278 | ``hsts_preload`` 279 | Optional, boolean. Defaults to ``nginx_hsts_preload``. 280 | Add a "preload" parameter to the HSTS header which can be used with the 281 | https://hstspreload.appspot.com/ site to configure HSTS preloading for a 282 | given website. 283 | 284 | 285 | User authentication 286 | ~~~~~~~~~~~~~~~~~~~ 287 | 288 | ``auth_basic`` 289 | Optional, boolean. Enable HTTP Basic Authentication for this server. 290 | 291 | ``auth_basic_realm`` 292 | Optional. String which will be displayed to the user in the HTTP Basic Auth 293 | dialog box. 294 | Defaults to ``nginx_default_auth_basic_realm``. 295 | 296 | ``auth_basic_name`` 297 | Optional, string. Required with ``auth_basic``. Specifies the name of the 298 | htpasswd file used for this server authentication. htpasswd files are 299 | stored in :file:`/etc/nginx/private/` directory. 300 | 301 | You can use ``auth_basic_filename`` and specify the full path to the 302 | htpasswd file to use; file needs to be readable by nginx system user. 303 | 304 | 305 | Locations 306 | ~~~~~~~~~ 307 | 308 | ``location`` 309 | Optional. Dict of location sections to include in this server configuration, 310 | in YAML text block format (semicolons at end of each configuration line 311 | required). Each key defines a string used as "location" option, values are 312 | strings or text blocks to be included inside each location section. 313 | Examples: 314 | 315 | .. code-block:: yaml 316 | :linenos: 317 | 318 | location: 319 | '/': 'try_files $uri $uri/ /index.html =404;' 320 | 321 | '~ ^/doc$': | 322 | alias /usr/share/doc; 323 | autoindex on; 324 | 325 | ``location_allow`` 326 | Optional. Dict which adds "allow" entries to each location section defined 327 | above from a list. Each location needs to have a corresponding entry in 328 | ``location`` dict. If ``item.location_deny`` is not defined, 'deny all;' is 329 | added at the end. Examples: 330 | 331 | .. code-block:: yaml 332 | :linenos: 333 | 334 | location_allow: 335 | '~ ^/doc$': [ '127.0.0.1', '::1' ] 336 | 337 | ``location_deny`` 338 | Optional. Dict which adds "deny" entries to each location section 339 | defined above from a list. Each location needs to have corresponding 340 | entry in ``location`` dict. Examples: 341 | 342 | .. code-block:: yaml 343 | :linenos: 344 | 345 | location_deny: 346 | '/': [ '192.168.0.1/24' ] 347 | '~ ^/doc$': [ 'all' ] 348 | 349 | ``location_referers`` 350 | Optional. Dict with lists of valid referers accepted for a given 351 | location, all other referers will be blocked by nginx. Each location 352 | needs to have corresponding entry in ``location`` dict. Examples: 353 | 354 | .. code-block:: yaml 355 | :linenos: 356 | 357 | location_referers: 358 | '/': [ '{{ ansible_fqdn }}', 'www.{{ ansible_fqdn }}', '*.{{ ansible_domain }}' ] 359 | 360 | ``location_list`` 361 | Optional, list of dicts. This is an alternative syntax of ``location_*`` 362 | entries; instead of using text blocks directly, it uses dict keys and values 363 | to configure each location, which allows for greater control and nesting. 364 | List of known keys and their descriptions: 365 | 366 | ``pattern`` 367 | Location string pattern, for example: ``/`` or ``~ ^/doc$`` or ``gitlab`` 368 | 369 | ``pattern_prefix`` 370 | String prepended to the location pattern, for example: ``@`` which will 371 | create the named location ``@gitlab`` 372 | 373 | ``enabled`` 374 | Boolean value specifying if the location should be included in 375 | configuration, defaults to ``True``. 376 | 377 | ``referers`` 378 | List of allowed valid referer strings. 379 | 380 | .. code-block:: yaml 381 | :linenos: 382 | 383 | referers: [ '{{ ansible_fqdn }}' ] 384 | 385 | ``allow`` and ``deny`` 386 | Lists of hosts or CIDR ranges to allow or deny, lack of deny implies 387 | ``deny all;`` at the end of the allow list. 388 | 389 | ``options`` 390 | String or YAML text block with options for this location block, semicolons 391 | at the end of each line are required. 392 | 393 | ``locations`` 394 | Nested list of locations to create in this location section. 395 | 396 | 397 | .. _nginx__ref_servers_http_security_headers: 398 | 399 | HTTP security headers 400 | ~~~~~~~~~~~~~~~~~~~~~ 401 | 402 | ``csp`` 403 | Optional, string. Defaults to: ``default-src https: ;`` (force all assets to be loaded over HTTPS). 404 | Sets the first part of the ``Content-Security-Policy`` header. 405 | The string MUST end with a semicolon but MUST NOT begin with one. 406 | Make sure that you only use single quotes and no double quotes in the string. 407 | If no ``item.csp_report`` is given, it also determines the first part of the 408 | ``Content-Security-Policy-Report-Only`` header. 409 | Which headers are actually enabled is defined by ``item.csp_enabled`` 410 | and ``item.csp_report_enabled``. 411 | Refer to the `Content Security Policy Reference`_. 412 | 413 | ``csp_report`` 414 | Optional, string. This allows to set a different/potentially experimental 415 | ``Content-Security-Policy-Report-Only`` header than defined by ``item.csp``. 416 | 417 | ``csp_append`` 418 | Optional, string. Defaults to: :envvar:`nginx__http_csp_append`. 419 | CSP directives to append to all policies (``item.csp`` and ``item.csp_report``). 420 | This can be used to overwrite the default :envvar:`nginx__http_csp_append` as needed. 421 | The string MUST end with a semicolon but MUST NOT begin with one. 422 | 423 | ``csp_enabled`` 424 | Optional, boolean. Defaults to ``False``. 425 | If set to ``True`` and HTTPS is enabled for this item, the 426 | ``Content-Security-Policy`` header is set in server responses. 427 | 428 | ``csp_report_enabled`` 429 | Optional, boolean. Defaults to ``False``. 430 | If this is set to ``True`` and HTTPS is enabled for this item, the 431 | ``Content-Security-Policy-Report-Only`` header is set in the server responses. 432 | 433 | .. _nginx__ref_http_xss_protection: 434 | 435 | ``xss_protection`` 436 | Optional, string. Value of the ``X-XSS-Protection`` HTTP header field. Set to 437 | ``{{ omit }}`` to not send the header field. Defaults to :envvar:`nginx__http_xss_protection`. 438 | 439 | ``1`` 440 | Browsers should enable there build in cross-site scripting protection. 441 | 442 | ``mode=block`` 443 | In case a cross-site scripting attack is detected, block the page from rendering. 444 | 445 | Note that the this option might create 446 | `a vulnerability in old versions of Internet Explorer 447 | `. 448 | 449 | For more details and discussion see `What is the http-header 450 | “X-XSS-Protection”? 451 | `_. 452 | 453 | .. _nginx__ref_http_referrer_policy: 454 | 455 | ``http_referrer_policy`` 456 | Optional, string. Value of the ``Referrer-Policy`` HTTP header field. Set to 457 | ``{{ omit }}`` to not send the header field. Defaults to :envvar:`nginx__http_referrer_policy`. 458 | Refer to `Referrer Policy`_ for more details. Note that this header is a 459 | draft as of 2016-10-11 but it is already supported by the majority of web 460 | browsers. 461 | 462 | .. _nginx__ref_permitted_cross_domain_policies: 463 | 464 | ``permitted_cross_domain_policies`` 465 | Optional, string. Value of the ``X-Permitted-Cross-Domain-Policies`` HTTP header field. Set to 466 | ``{{ omit }}`` to not send the header field. Defaults to 467 | :envvar:`nginx__http_permitted_cross_domain_policies`. 468 | 469 | Should cross domain policies be permitted? 470 | 471 | 472 | Search engine optimization 473 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 474 | 475 | .. _nginx__ref_http_robots_tag: 476 | 477 | ``robots_tag`` 478 | Optional, list of strings or string. Value of the ``X-Robots-Tag`` HTTP header field. Set to 479 | ``{{ omit }}`` to not send the header field. Defaults to 480 | :envvar:`nginx__http_robots_tag`. 481 | 482 | This allows you to give search engine bots hints how they should handle the 483 | website. For example, when you don’t want that search engines don’t "index" 484 | your website, you can set this variable to ``none``. 485 | 486 | .. note:: This header field is merely a hint for the search engine bot, 487 | nothing more and they might ignore it. For example, Google sets this 488 | straight in their first sentence in the documentation which says "This 489 | document details how the page-level indexing settings allow you to control 490 | how Google `makes content available through search results`." 491 | So you will need to prevent the search engine bots from crawling the site 492 | in the first place in case you want to prevent that. 493 | 494 | Refer to `robots meta tag and X-Robots-Tag HTTP header specifications 495 | `_ 496 | for more details. 497 | 498 | 499 | Access control 500 | ~~~~~~~~~~~~~~ 501 | 502 | ``access_policy`` 503 | Optional, string. Specify a named "access policy" to use for this server. Refer to 504 | ``nginx_access_policy_allow_map`` and similar variables for more 505 | information. 506 | 507 | ``satisfy`` 508 | Optional, string. Defaults to ``nginx_default_satisfy``. 509 | Set the server behaviour to either accept any of ``allow, auth`` 510 | configuration restrictions, or require all of them to match. By default, any 511 | restriction by itself will match. Choices: ``any``, ``all`` 512 | 513 | ``allow`` 514 | Optional, string or list of strings. 515 | IP addresses or CIDR networks which can access the given server. 516 | Automatically adds ``deny: all`` at the end of the list. 517 | 518 | ``options`` 519 | Optional, string. Add custom options to this server configuration using a 520 | YAML text block (semicolons at the end of each line are required). 521 | 522 | 523 | Logging and monitoring 524 | ~~~~~~~~~~~~~~~~~~~~~~ 525 | 526 | ``log_path`` 527 | Optional, string. Absolute path where log files should be stored. If not 528 | specified, logs will be saved to :file:`/var/log/nginx/` directory. You 529 | should take care of log rotation if you specify a custom log path. 530 | The specified path needs to exist before nginx is reloaded/restarted. 531 | 532 | ``access_log`` 533 | Optional, string. Defaults to ``<``name[0]>_access``. 534 | Name of the access log file. 535 | The suffix ``.log`` will be added automatically. 536 | 537 | ``error_log`` 538 | Optional, string. Defaults to ``<``name[0]>_error``. 539 | Name of the error log file. 540 | The suffix ``.log`` will be added automatically. 541 | 542 | ``access_log_format`` 543 | Optional. Name of the access log format. 544 | Custom log formats can be defined using ``nginx_http_options`` variable. 545 | 546 | ``status`` 547 | Optional, list of strings. 548 | Enable nginx server status page and allow access from the given list of IP 549 | addresses or CIDR ranges. 550 | 551 | ``status_name`` 552 | Optional, string. Defaults to ``nginx_status_name``. 553 | Set the name of the location which should be used for the nginx status page. 554 | 555 | 556 | Error pages 557 | ~~~~~~~~~~~ 558 | 559 | ``error_pages`` 560 | Optional. Dict of error codes in string format as keys and 561 | corresponding error pages to display. Example: 562 | 563 | .. code-block:: yaml 564 | :linenos: 565 | 566 | error_pages: 567 | '403 404': '/400.html' 568 | '502': '/500.html' 569 | 570 | ``error_pages_list`` 571 | Optional. List of dictionaries, each one describing an error page. List 572 | of known keys and their descriptions: 573 | 574 | ``code`` 575 | Required, String or list strings. Error codes to include in this 576 | configuration section. 577 | 578 | ``uri`` 579 | Required. URI or location to redirect the request to. 580 | 581 | ``location`` and ``location_options`` 582 | Optional. If specified, an additional location section will be added 583 | with contents of the ``location_options`` parameter. If only 584 | ``location_options`` is present, the ``uri`` parameter will be used as 585 | the location. 586 | 587 | Examples: 588 | 589 | .. code-block:: yaml 590 | :linenos: 591 | 592 | error_pages_list: 593 | - code: [ '403', '404', '=500' ] 594 | uri: '/error.html' 595 | location: '= /error.html' 596 | location_options: | 597 | internal; 598 | 599 | ``maintenance_file`` 600 | Optional. Path of the maintenance HTML page (by default 601 | :file:`maintenance.html`) located in the website's document root directory. 602 | If the file is present, all requests will be redirected to the maintenance 603 | page with error "503 Service Unavailable". 604 | 605 | Welcome page 606 | ~~~~~~~~~~~~ 607 | 608 | ``welcome`` 609 | Optional, boolean. Defaults to ``False``. 610 | If ``True`` a welcome :file:`index.html` page is generated in website root 611 | directory using a template. 612 | 613 | ``welcome_force`` 614 | Optional, boolean. 615 | Ensure that the templated file is up-to-date if ``True``. 616 | Set to ``False`` by default to ensure idempotent operation. 617 | 618 | ``welcome_template`` 619 | Optional. Specify absolute path to a Jinja2 template which should be 620 | used to generate a welcome page. 621 | 622 | ``welcome_domain`` 623 | Optional. Specify a DNS domain which should be used in the generated 624 | welcome page. By default, a domain is detected from ``name[0]``, or 625 | if it's not specified, ``ansible_domain`` variables. 626 | 627 | ``welcome_css`` 628 | Optional. If specified and False, omit custom stylesheet in the 629 | generated :file:`index.html`' file. 630 | 631 | 632 | User directories 633 | ~~~~~~~~~~~~~~~~ 634 | 635 | ``userdir`` 636 | Optional, boolean. Enable UserDir support. 637 | Web pages on https://host/~/ will be read from 638 | :file:`/srv/www//userdir/public` directories. 639 | 640 | ``userdir_regexp`` 641 | Optional, string. Specify location regexp pattern used by nginx to determine if 642 | a specified URL is an userdir URL. 643 | 644 | ``userdir_alias`` 645 | Optional, string. Specify the absolute path to user directories used as an 646 | alias pattern which uses the parameters from location regexp to select 647 | the correct user and file to display. 648 | 649 | ``userdir_index`` 650 | Optional, string. Specify space separated list of index files which will be 651 | used by nginx automatically to display a HTML page, if found in the current 652 | directory. 653 | 654 | ``userdir_options`` 655 | Optional, string. Specify additional options added to the userdir location 656 | block. 657 | 658 | .. _nginx__ref_servers_php: 659 | 660 | Type: php 661 | ~~~~~~~~~ 662 | 663 | Available when ``item.type`` is set to ``php`` for a server. 664 | 665 | ``php_upstream`` 666 | Required, string. Name of nginx upstream to use. 667 | 668 | If undefined, :file:`.php` files will be protected by =403. 669 | 670 | ``index`` 671 | Optional, string. 672 | Space separated list of index filenames. 673 | Refer to :ref:`nginx__ref_servers_common_webserver_options` for details. 674 | 675 | If undefined, add :file:`index.php` at the end of list of index files. 676 | 677 | ``php_limit_except`` or False 678 | Optional, string or list of strings or boolean (``False``). 679 | Methods to allow for all hosts. 680 | 681 | If undefined, default limits will be applied (block all requests except GET, 682 | HEAD and POST). 683 | 684 | If set to ``False``, limits are disabled. 685 | 686 | Refer to the `Nginx limit_except directive documentation`_ for details. 687 | 688 | ``php_include`` 689 | Optional, string or boolean (``False``). 690 | File to include instead of :file:`fastcgi_params` or :file:`fastcgi.conf`, 691 | relative to :file:`/etc/nginx/`. 692 | 693 | If set to ``False``, nothing is included. 694 | 695 | ``php_options`` 696 | Optional, string. Additional options to append to php location. 697 | 698 | ``php_status`` 699 | Optional, boolean. Enable php-fpm server status page. 700 | 701 | ``php_status_name`` 702 | Optional, string. Defaults to ``php_status``. 703 | Set the name of the location which should be used for php fpm 704 | status page 705 | 706 | ``php_ping_name`` 707 | Optional, string. Defaults to ``php_ping``. 708 | Set the name of the location which should be used for php fpm 709 | ping page 710 | 711 | ``php_status_allow`` 712 | Optional, string or list of strings. 713 | Allow access the given IP addresses or CIDR ranges. 714 | 715 | Type: php5 716 | ~~~~~~~~~~ 717 | 718 | Deprecated, use :ref:`nginx__ref_servers_php`. 719 | 720 | Type: proxy 721 | ~~~~~~~~~~~ 722 | 723 | Available when ``item.type`` is set to ``proxy`` for a server. 724 | 725 | FIXME: Documentation missing. 726 | 727 | Type: rails 728 | ~~~~~~~~~~~ 729 | 730 | Available when ``item.type`` is set to ``rails`` for a server. 731 | 732 | FIXME: Documentation missing. 733 | -------------------------------------------------------------------------------- /docs/getting-started.rst: -------------------------------------------------------------------------------- 1 | Getting started 2 | =============== 3 | 4 | .. include:: includes/all.rst 5 | 6 | .. contents:: 7 | :local: 8 | 9 | 10 | Example inventory 11 | ----------------- 12 | 13 | To manage Nginx on a given host or set of hosts, they need to be added 14 | to the ``[debops_service_nginx]`` Ansible group in the inventory: 15 | 16 | .. code:: ini 17 | 18 | [debops_service_nginx] 19 | hostname 20 | 21 | Example playbook 22 | ---------------- 23 | 24 | If you are using this role without DebOps, here's an example Ansible playbook 25 | that uses the ``debops.nginx`` role: 26 | 27 | .. literalinclude:: playbooks/nginx.yml 28 | :language: yaml 29 | 30 | Ansible tags 31 | ------------ 32 | 33 | You can use Ansible ``--tags`` or ``--skip-tags`` parameters to limit what 34 | tasks are performed during Ansible run. This can be used after a host was first 35 | configured to speed up playbook execution, when you are sure that most of the 36 | configuration is already in the desired state. 37 | 38 | Available role tags: 39 | 40 | ``role::nginx`` 41 | Main role tag, should be used in the playbook to execute all of the role 42 | tasks as well as role dependencies. 43 | 44 | ``type::dependency`` 45 | This tag specifies which tasks are defined in role dependencies. You can use 46 | this to omit them using ``--skip-tags`` parameter. 47 | 48 | ``depend-of::nginx`` 49 | Execute all ``debops.nginx`` role dependencies in its context. 50 | 51 | ``depend::secret:nginx`` 52 | Run debops.secret_ dependent role in ``debops.nginx`` context. 53 | 54 | ``depend::apt_preferences:nginx`` 55 | Run debops.apt_preferences_ dependent role in ``debops.nginx`` context. 56 | 57 | ``depend::ferm:nginx`` 58 | Run debops.ferm_ dependent role in ``debops.nginx`` context. 59 | 60 | ``role::nginx:servers`` 61 | Configure nginx servers configuration as configured by the ``nginx_servers`` 62 | variable. 63 | -------------------------------------------------------------------------------- /docs/includes/all.rst: -------------------------------------------------------------------------------- 1 | .. include:: includes/global.rst 2 | .. include:: includes/role.rst 3 | -------------------------------------------------------------------------------- /docs/includes/role.rst: -------------------------------------------------------------------------------- 1 | .. _Nginx server_name option documentation: https://nginx.org/en/docs/http/server_names.html 2 | .. _Nginx server block documentation: https://nginx.org/en/docs/http/ngx_http_core_module.html#server 3 | .. _Nginx try_files directive documentation: https://nginx.org/en/docs/http/ngx_http_core_module.html#try_files 4 | .. _Nginx limit_except directive documentation: https://nginx.org/en/docs/http/ngx_http_core_module.html#limit_except 5 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. _debops.nginx: 2 | 3 | Ansible role: debops.nginx 4 | ========================== 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | introduction 10 | getting-started 11 | defaults 12 | defaults-detailed 13 | acme-support 14 | copyright 15 | changelog 16 | 17 | .. 18 | Local Variables: 19 | mode: rst 20 | ispell-local-dictionary: "american" 21 | End: 22 | -------------------------------------------------------------------------------- /docs/introduction.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | .. include:: includes/all.rst 5 | 6 | Nginx_ is a fast and light webserver with extensible configuration. 7 | 8 | The ``debops.nginx`` role can be used to install and manage `nginx` configuration 9 | for multiple websites at the same time. The server is configured using 10 | inventory variables. This role can also be used as a dependency of another role 11 | to configure a webserver for that role using dependency variables. 12 | 13 | Installation 14 | ~~~~~~~~~~~~ 15 | 16 | This role requires at least Ansible ``v2.1.5``. To install it, run:: 17 | 18 | ansible-galaxy install debops.nginx 19 | 20 | .. 21 | Local Variables: 22 | mode: rst 23 | ispell-local-dictionary: "american" 24 | End: 25 | -------------------------------------------------------------------------------- /docs/playbooks/nginx.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Manage nginx webserver 4 | hosts: [ 'debops_service_nginx' ] 5 | become: True 6 | 7 | environment: '{{ inventory__environment | d({}) 8 | | combine(inventory__group_environment | d({})) 9 | | combine(inventory__host_environment | d({})) }}' 10 | 11 | roles: 12 | 13 | - role: debops.apt_preferences 14 | tags: [ 'role::apt_preferences' ] 15 | apt_preferences__dependent_list: 16 | - '{{ nginx__apt_preferences__dependent_list }}' 17 | 18 | - role: debops.ferm 19 | tags: [ 'role::ferm' ] 20 | ferm__dependent_rules: 21 | - '{{ nginx__ferm__dependent_rules }}' 22 | 23 | - role: debops.nginx 24 | tags: [ 'role::nginx' ] 25 | -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Test nginx and restart 4 | command: nginx -t 5 | notify: [ 'Restart nginx' ] 6 | 7 | - name: Test nginx and reload 8 | command: nginx -t 9 | notify: [ 'Reload nginx' ] 10 | 11 | - name: Restart nginx 12 | service: 13 | name: 'nginx' 14 | state: 'restarted' 15 | 16 | - name: Reload nginx 17 | service: 18 | name: 'nginx' 19 | state: 'reloaded' 20 | -------------------------------------------------------------------------------- /meta/ansigenome.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ansigenome_info: 4 | galaxy_url: "https://galaxy.ansible.com/debops/nginx" 5 | github_url: "https://github.com/debops/ansible-nginx" 6 | git_branch: "master" 7 | 8 | travis: True 9 | 10 | license_url: 'https://tldrlegal.com/license/gnu-general-public-license-v3-(gpl-3)' 11 | 12 | authors: 13 | 14 | - name: 'Maciej Delmanowski' 15 | email: 'drybjed@gmail.com' 16 | twitter: 'drybjed' 17 | github: 'drybjed' 18 | 19 | - name: 'Robin Schneider' 20 | email: 'ypid@riseup.net' 21 | github: 'ypid' 22 | 23 | synopsis: | 24 | [Nginx](https://nginx.org/) is a fast and light webserver with extensible 25 | configuration. 26 | 27 | The `debops.nginx` role can be used to install and manage `nginx` configuration 28 | for multiple websites at the same time. The server is configured using 29 | inventory variables. This role can also be used as a dependency of another role 30 | to configure a webserver for that role using dependency variables. 31 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | dependencies: 4 | 5 | - role: debops.secret 6 | 7 | galaxy_info: 8 | author: 'Maciej Delmanowski, Robin Schneider' 9 | description: 'Install and manage nginx webserver' 10 | company: 'DebOps' 11 | license: 'GPL-3.0' 12 | min_ansible_version: '2.1.5' 13 | platforms: 14 | - name: Ubuntu 15 | versions: 16 | - precise 17 | - quantal 18 | - raring 19 | - saucy 20 | - trusty 21 | - name: Debian 22 | versions: 23 | - wheezy 24 | - jessie 25 | galaxy_tags: 26 | - nginx 27 | - web 28 | - webserver 29 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Assert that no legacy options are used 4 | assert: 5 | that: 6 | - '((item.csp is defined and item.csp is string) or item.csp is undefined)' 7 | - '(item.csp_policy is undefined)' 8 | run_once: True 9 | delegate_to: 'localhost' 10 | with_flattened: 11 | - '{{ nginx__servers }}' 12 | - '{{ nginx__default_servers }}' 13 | - '{{ nginx__internal_servers }}' 14 | - '{{ nginx__dependent_servers }}' 15 | - '{{ nginx_servers | d([]) }}' 16 | - '{{ nginx_default_servers | d([]) }}' 17 | - '{{ nginx_internal_servers | d([]) }}' 18 | - '{{ nginx_dependent_servers | d([]) }}' 19 | 20 | - name: DebOps pre_tasks hook 21 | include: '{{ lookup("task_src", "nginx/pre_main.yml") }}' 22 | when: (nginx__deploy_state in [ 'present' ]) 23 | 24 | - name: Check if nginx is installed 25 | stat: 26 | path: '/usr/sbin/nginx' 27 | register: nginx_register_installed 28 | 29 | - name: Add flavor APT GPG key 30 | apt_key: 31 | id: '{{ nginx__flavor_apt_key_id }}' 32 | state: '{{ "present" if (nginx__deploy_state == "present") else "absent" }}' 33 | keyserver: '{{ ansible_local.core.keyserver 34 | if (ansible_local|d() and ansible_local.core|d() and 35 | ansible_local.core.keyserver) 36 | else "hkp://pool.sks-keyservers.net" }}' 37 | when: (nginx_flavor in [ 'nginx.org', 'passenger' ]) 38 | 39 | - name: Add flavor APT repository 40 | apt_repository: 41 | repo: '{{ nginx__flavor_apt_repository }}' 42 | state: '{{ "present" if (nginx__deploy_state == "present") else "absent" }}' 43 | update_cache: True 44 | when: (nginx_flavor in [ 'nginx.org', 'passenger' ]) 45 | 46 | - name: Ensure base packages are installed 47 | package: 48 | name: '{{ item }}' 49 | state: 'present' 50 | when: (nginx__deploy_state in [ 'present' ]) 51 | with_flattened: 52 | - '{{ nginx_base_packages }}' 53 | 54 | - name: Ensure Nginx packages are in their desired state 55 | package: 56 | name: '{{ item }}' 57 | state: '{{ "present" if (nginx__deploy_state == "present") else "absent" }}' 58 | with_flattened: 59 | - '{{ nginx__flavor_packages }}' 60 | 61 | - name: Create default nginx directories 62 | file: 63 | path: '{{ item }}' 64 | state: 'directory' 65 | owner: 'root' 66 | group: 'root' 67 | mode: '0755' 68 | with_items: 69 | - '/etc/nginx/sites-default.d' 70 | - '/etc/nginx/sites-available' 71 | - '/etc/nginx/sites-enabled' 72 | - '/etc/nginx/snippets' 73 | - '{{ nginx_temp_root_path }}' 74 | when: (nginx__deploy_state in [ 'present' ]) 75 | 76 | - name: Divert default.conf in case nginx nginx.org flavor is used 77 | command: dpkg-divert --quiet --local --divert /etc/nginx/conf.d/default.conf.dpkg-divert 78 | --rename /etc/nginx/conf.d/default.conf 79 | args: 80 | creates: '/etc/nginx/conf.d/default.conf.dpkg-divert' 81 | when: (nginx_flavor == 'nginx.org' and nginx__deploy_state in [ 'present' ]) 82 | 83 | - include: 'passenger_config.yml' 84 | when: (nginx_flavor == 'passenger' and nginx__deploy_state in [ 'present' ]) 85 | 86 | - name: Restart nginx on first install to bypass missing pid bug 87 | service: 88 | name: 'nginx' 89 | state: 'restarted' 90 | when: (nginx_register_installed|d() and not nginx_register_installed.stat.exists and nginx__deploy_state in [ 'present' ]) 91 | 92 | - name: Get list of nameservers configured in /etc/resolv.conf 93 | shell: awk '$1=="nameserver" {if(/%/){sub(/[0-9a-fA-F:]+/, "[&]", $2)}; print $2}' /etc/resolv.conf 94 | args: 95 | executable: '/bin/sh' 96 | register: nginx_register_nameservers 97 | changed_when: False 98 | check_mode: False 99 | when: (nginx__deploy_state in [ 'present' ]) 100 | tags: [ 'role::nginx:servers' ] 101 | 102 | - name: Convert list of nameservers to Ansible list 103 | set_fact: 104 | nginx_ocsp_resolvers: "{{ nginx_register_nameservers.stdout_lines }}" 105 | when: ((nginx_register_nameservers.stdout is defined and nginx_register_nameservers.stdout) and 106 | (nginx_ocsp_resolvers is undefined or 107 | (nginx_ocsp_resolvers is defined and not nginx_ocsp_resolvers)) and 108 | (nginx__deploy_state in [ 'present' ])) 109 | tags: [ 'role::nginx:servers' ] 110 | 111 | - name: Ensure that webadmins privileged group exists 112 | group: 113 | name: '{{ nginx_privileged_group }}' 114 | state: 'present' 115 | system: True 116 | when: (nginx__deploy_state in [ 'present' ]) 117 | 118 | - name: Create directory for webadmins configuration 119 | file: 120 | path: '/etc/nginx/sites-local' 121 | state: 'directory' 122 | owner: 'root' 123 | group: '{{ nginx_privileged_group }}' 124 | mode: '0775' 125 | when: (nginx__deploy_state in [ 'present' ]) 126 | 127 | - name: Allow webadmins to reload nginx using sudo 128 | template: 129 | src: 'etc/sudoers.d/nginx_webadmins.j2' 130 | dest: '/etc/sudoers.d/nginx_webadmins' 131 | owner: 'root' 132 | group: 'root' 133 | mode: '0440' 134 | when: (nginx__deploy_state in [ 'present' ]) 135 | 136 | - name: Divert original /etc/nginx/nginx.conf 137 | command: dpkg-divert --quiet --local --divert /etc/nginx/nginx.conf.dpkg-divert 138 | --rename /etc/nginx/nginx.conf 139 | args: 140 | creates: '/etc/nginx/nginx.conf.dpkg-divert' 141 | when: (nginx__deploy_state in [ 'present' ]) 142 | 143 | - name: Setup /etc/nginx/nginx.conf 144 | template: 145 | src: 'etc/nginx/nginx.conf.j2' 146 | dest: '/etc/nginx/nginx.conf' 147 | owner: 'root' 148 | group: 'root' 149 | mode: '0644' 150 | notify: [ 'Test nginx and reload' ] 151 | when: (nginx__deploy_state in [ 'present' ]) 152 | 153 | - name: Generate custom nginx snippets 154 | template: 155 | src: 'etc/nginx/snippets/{{ item }}.conf.j2' 156 | dest: '/etc/nginx/snippets/{{ item }}.conf' 157 | owner: 'root' 158 | group: 'root' 159 | mode: '0644' 160 | with_items: [ 'acme-challenge' ] 161 | when: (nginx__deploy_state in [ 'present' ]) 162 | notify: [ 'Test nginx and reload' ] 163 | 164 | # Remove temporary old files if they are present 165 | - name: Remove remnants of dpkg-diverted configuration 166 | command: rm -f /etc/nginx/fastcgi_params.dpkg-divert /etc/nginx/fastcgi_params.dpkg-divert.lock 167 | args: 168 | removes: '/etc/nginx/fastcgi_params.dpkg-divert.lock' 169 | notify: [ 'Test nginx and reload' ] 170 | when: (nginx__deploy_state in [ 'present' ]) 171 | 172 | - name: Disable default nginx site 173 | file: 174 | path: '/etc/nginx/sites-enabled/default' 175 | state: 'absent' 176 | notify: [ 'Test nginx and reload' ] 177 | when: (nginx__deploy_state in [ 'present' ]) 178 | 179 | - name: Manage local server definitions - create symlinks 180 | file: 181 | src: '/etc/nginx/sites-local/{{ item.value }}' 182 | path: '/etc/nginx/sites-enabled/{{ item.key }}' 183 | state: 'link' 184 | owner: 'root' 185 | group: 'root' 186 | mode: '0644' 187 | when: (item.value and nginx__deploy_state in [ 'present' ]) 188 | with_dict: '{{ nginx_local_servers|d({}) }}' 189 | notify: [ 'Test nginx and reload' ] 190 | 191 | - name: Manage local server definitions - remove symlinks 192 | file: 193 | path: '/etc/nginx/sites-enabled/{{ item.key }}' 194 | state: 'absent' 195 | when: ((not item.value|d()) and nginx__deploy_state in [ 'present' ]) 196 | with_dict: '{{ nginx_local_servers|d({}) }}' 197 | notify: [ 'Test nginx and reload' ] 198 | 199 | # If nginx local facts are not present, assume that configuration 200 | # is being reset and move all symlinks out of the way to prevent 201 | # accidental failures because of old wrong configuration files 202 | - name: Remove all configuration symlinks during config reset 203 | shell: rm -f /etc/nginx/sites-enabled/* 204 | args: 205 | executable: '/bin/sh' 206 | creates: '/etc/ansible/facts.d/nginx.fact' 207 | warn: False 208 | when: (nginx__deploy_state in [ 'present' ]) 209 | 210 | - name: Make sure that Ansible local facts directory is present 211 | file: 212 | path: '/etc/ansible/facts.d' 213 | state: 'directory' 214 | owner: 'root' 215 | group: 'root' 216 | mode: '0755' 217 | when: (nginx__deploy_state in [ 'present' ]) 218 | 219 | - name: Save nginx local facts 220 | template: 221 | src: 'etc/ansible/facts.d/nginx.fact.j2' 222 | dest: '/etc/ansible/facts.d/nginx.fact' 223 | owner: 'root' 224 | group: 'root' 225 | mode: '0755' 226 | register: nginx_register_local_facts 227 | when: (nginx__deploy_state in [ 'present' ]) 228 | 229 | - name: Gather facts if they were modified 230 | action: setup 231 | when: (nginx_register_local_facts.changed and nginx__deploy_state in [ 'present' ]) 232 | 233 | - include: 'nginx_htpasswd.yml' 234 | when: (nginx__deploy_state in [ 'present' ]) 235 | 236 | - include: 'nginx_configs.yml' 237 | when: (nginx__deploy_state in [ 'present' ]) 238 | 239 | - include: 'nginx_servers.yml' 240 | tags: [ 'role::nginx:servers' ] 241 | when: (nginx__deploy_state in [ 'present' ]) 242 | 243 | - name: Make sure that PKI hook directory exists 244 | file: 245 | path: '{{ nginx_pki_hook_path }}' 246 | state: 'directory' 247 | owner: 'root' 248 | group: 'root' 249 | mode: '0755' 250 | when: (nginx_pki|bool and nginx__deploy_state in [ 'present' ]) 251 | 252 | - name: Manage PKI nginx hook 253 | template: 254 | src: 'etc/pki/hooks/nginx.j2' 255 | dest: '{{ nginx_pki_hook_path + "/" + nginx_pki_hook_name }}' 256 | owner: 'root' 257 | group: 'root' 258 | mode: '0755' 259 | when: (nginx_pki|bool and nginx__deploy_state in [ 'present' ]) 260 | 261 | - name: Ensure the PKI nginx hook is absent 262 | file: 263 | path: '{{ nginx_pki_hook_path }}' 264 | state: 'absent' 265 | when: (nginx__deploy_state in [ 'absent' ]) 266 | 267 | - name: Save nginx local facts 268 | template: 269 | src: 'etc/ansible/facts.d/nginx.fact.j2' 270 | dest: '/etc/ansible/facts.d/nginx.fact' 271 | owner: 'root' 272 | group: 'root' 273 | mode: '0755' 274 | register: nginx_register_local_facts 275 | 276 | - name: Gather facts if they were modified 277 | action: setup 278 | when: (nginx_register_local_facts.changed and nginx__deploy_state in [ 'present' ]) 279 | 280 | - name: DebOps post_tasks hook 281 | include: '{{ lookup("task_src", "nginx/post_main.yml") }}' 282 | when: (nginx__deploy_state in [ 'present' ]) 283 | -------------------------------------------------------------------------------- /tasks/nginx/post_main.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debops/ansible-nginx/22da74eba7d13154d266f3a6fe32da8f0ab1ef95/tasks/nginx/post_main.yml -------------------------------------------------------------------------------- /tasks/nginx/pre_main.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debops/ansible-nginx/22da74eba7d13154d266f3a6fe32da8f0ab1ef95/tasks/nginx/pre_main.yml -------------------------------------------------------------------------------- /tasks/nginx_configs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Make sure configuration directory exists 4 | file: 5 | path: '/etc/nginx/conf.d' 6 | state: 'directory' 7 | owner: 'root' 8 | group: 'root' 9 | mode: '0755' 10 | 11 | - name: Remove nginx maps if requested 12 | file: 13 | dest: '/etc/nginx/conf.d/map_{{ item.name }}.conf' 14 | state: 'absent' 15 | with_flattened: 16 | - '{{ nginx__maps }}' 17 | - '{{ nginx__default_maps }}' 18 | - '{{ nginx__dependent_maps }}' 19 | - '{{ nginx_maps |d([]) }}' 20 | - '{{ nginx_default_maps | d([]) }}' 21 | - '{{ nginx_dependent_maps | d([]) }}' 22 | notify: [ 'Test nginx and reload' ] 23 | when: (item.name|d() and 24 | ((item.state|d() and item.state == 'absent') or 25 | (item.delete|d() and item.delete|bool))) 26 | 27 | - name: Configure nginx maps 28 | template: 29 | src: 'etc/nginx/conf.d/map.conf.j2' 30 | dest: '/etc/nginx/conf.d/map_{{ item.name }}.conf' 31 | owner: 'root' 32 | group: 'root' 33 | mode: '0644' 34 | with_flattened: 35 | - '{{ nginx__maps }}' 36 | - '{{ nginx__default_maps }}' 37 | - '{{ nginx__dependent_maps }}' 38 | - '{{ nginx_maps |d([]) }}' 39 | - '{{ nginx_default_maps | d([]) }}' 40 | - '{{ nginx_dependent_maps | d([]) }}' 41 | notify: [ 'Test nginx and reload' ] 42 | when: (item.name|d() and item.state|d('present') != 'absent' and 43 | (item.delete is undefined or not item.delete|bool)) 44 | 45 | - name: Remove nginx upstreams if requested 46 | file: 47 | dest: '/etc/nginx/conf.d/upstream_{{ item.name }}.conf' 48 | state: 'absent' 49 | with_flattened: 50 | - '{{ nginx__upstreams }}' 51 | - '{{ nginx__default_upstreams }}' 52 | - '{{ nginx__dependent_upstreams }}' 53 | - '{{ nginx_upstreams | d([]) }}' 54 | - '{{ nginx_default_upstreams | d([]) }}' 55 | - '{{ nginx_dependent_upstreams | d([]) }}' 56 | notify: [ 'Test nginx and reload' ] 57 | when: (item.name|d() and 58 | ((item.state|d('present') == 'absent') or 59 | (item.delete|d() and item.delete|bool))) 60 | 61 | - name: Configure nginx upstreams 62 | template: 63 | src: 'etc/nginx/conf.d/upstream_{{ item.type | d("default") }}.conf.j2' 64 | dest: '/etc/nginx/conf.d/upstream_{{ item.name }}.conf' 65 | owner: 'root' 66 | group: 'root' 67 | mode: '0644' 68 | with_flattened: 69 | - '{{ nginx__upstreams }}' 70 | - '{{ nginx__default_upstreams }}' 71 | - '{{ nginx__dependent_upstreams }}' 72 | - '{{ nginx_upstreams | d([]) }}' 73 | - '{{ nginx_default_upstreams | d([]) }}' 74 | - '{{ nginx_dependent_upstreams | d([]) }}' 75 | notify: [ 'Test nginx and reload' ] 76 | when: (item.name|d() and item.state|d('present') != 'absent' and 77 | (item.delete is undefined or not item.delete|bool)) 78 | 79 | - name: Remove custom nginx configuration if requested 80 | file: 81 | dest: '/etc/nginx/conf.d/{{ item.filename | d("custom_" + item.name + ".conf") }}' 82 | state: 'absent' 83 | with_flattened: 84 | - '{{ nginx__custom_config }}' 85 | - '{{ nginx_custom_config | d([]) }}' 86 | notify: [ 'Test nginx and reload' ] 87 | when: (item.name|d() and 88 | ((item.state|d() and item.state == 'absent') or 89 | (item.delete|d() and item.delete|bool))) 90 | 91 | - name: Add custom nginx configuration 92 | template: 93 | src: 'etc/nginx/conf.d/custom.conf.j2' 94 | dest: '/etc/nginx/conf.d/{{ item.filename | d("custom_" + item.name + ".conf") }}' 95 | owner: 'root' 96 | group: 'root' 97 | mode: '0644' 98 | with_flattened: 99 | - '{{ nginx__custom_config }}' 100 | - '{{ nginx_custom_config | d([]) }}' 101 | notify: [ 'Test nginx and reload' ] 102 | when: (item.name|d() and item.state|d('present') != 'absent' and 103 | (item.delete is undefined or not item.delete|bool)) 104 | -------------------------------------------------------------------------------- /tasks/nginx_htpasswd.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Create directory for htpasswd files 4 | file: 5 | path: '{{ nginx_private_path }}' 6 | state: 'directory' 7 | owner: 'root' 8 | group: '{{ nginx_user }}' 9 | mode: '0750' 10 | 11 | - name: Remove htpasswd files if requested 12 | file: 13 | dest: '{{ nginx_private_path + "/" + item.name }}' 14 | state: 'absent' 15 | with_flattened: 16 | - '{{ nginx__htpasswd }}' 17 | - '{{ nginx__default_htpasswd }}' 18 | - '{{ nginx__dependent_htpasswd }}' 19 | - '{{ nginx_htpasswd | d([]) }}' 20 | when: (item.name|d() and (item.state|d() and item.state == 'absent')) 21 | no_log: True 22 | 23 | - name: Manage users in htpasswd files 24 | htpasswd: 25 | path: '{{ nginx_private_path + "/" + item.0.name }}' 26 | name: '{{ item.1 }}' 27 | crypt_scheme: '{{ nginx__htpasswd_crypt_scheme }}' 28 | password: '{{ item.0.password if item.0.password|d() else lookup("password", nginx_htpasswd_secret_path + "/" + item.0.name + "/" + item.1) }}' 29 | state: '{{ "present" if not (item.0.delete | d(False) |bool) else "absent" }}' 30 | owner: 'root' 31 | group: '{{ nginx_user }}' 32 | mode: '0640' 33 | with_subelements: 34 | - '{{ nginx__htpasswd + nginx__default_htpasswd + nginx__dependent_htpasswd + nginx_htpasswd|d([]) }}' 35 | - 'users' 36 | when: (item.0.name|d() and item.0.state|d('present') != 'absent' and item.1|d()) 37 | no_log: True 38 | 39 | -------------------------------------------------------------------------------- /tasks/nginx_servers.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Create global webroot directories if allowed 4 | file: 5 | path: '{{ nginx_www }}/sites/{{ item.name if item.name is string else item.name[0] | d("default") }}/public' 6 | state: 'directory' 7 | owner: '{{ nginx_webroot_owner }}' 8 | group: '{{ nginx_webroot_group }}' 9 | mode: '{{ nginx_webroot_mode }}' 10 | with_flattened: 11 | - '{{ nginx__servers }}' 12 | - '{{ nginx__default_servers }}' 13 | - '{{ nginx__internal_servers }}' 14 | - '{{ nginx__dependent_servers }}' 15 | - '{{ nginx_servers | d([]) }}' 16 | - '{{ nginx_default_servers | d([]) }}' 17 | - '{{ nginx_internal_servers | d([]) }}' 18 | - '{{ nginx_dependent_servers | d([]) }}' 19 | when: (nginx_webroot_create|bool and item.state|d('present') != 'absent' and 20 | (item.root is undefined and item.owner is undefined and item.group is undefined)) 21 | 22 | - name: Create default welcome page if enabled 23 | template: 24 | src: '{{ item.welcome_template | d(nginx_welcome_template) }}' 25 | dest: '{{ nginx_www }}/sites/{{ item.name if item.name is string else item.name[0] | d("default") }}/public/index.html' 26 | owner: '{{ item.owner | d(nginx_webroot_owner) }}' 27 | group: '{{ item.owner | d(nginx_webroot_group) }}' 28 | force: '{{ item.welcome_force|d() | bool }}' 29 | with_flattened: 30 | - '{{ nginx__servers }}' 31 | - '{{ nginx__default_servers }}' 32 | - '{{ nginx__internal_servers }}' 33 | - '{{ nginx__dependent_servers }}' 34 | - '{{ nginx_servers | d([]) }}' 35 | - '{{ nginx_default_servers | d([]) }}' 36 | - '{{ nginx_internal_servers | d([]) }}' 37 | - '{{ nginx_dependent_servers | d([]) }}' 38 | when: (nginx_webroot_create|bool and item.state|d('present') != 'absent' and 39 | (item.delete is undefined or not item.delete|bool) and 40 | (item.welcome|d() | bool)) 41 | 42 | - name: Remove nginx server configuration if requested 43 | file: 44 | path: '/etc/nginx/sites-available/{{ item.filename | d(item.name if item.name is string else item.name[0] | d("default")) }}.conf' 45 | state: 'absent' 46 | with_flattened: 47 | - '{{ nginx__servers }}' 48 | - '{{ nginx__default_servers }}' 49 | - '{{ nginx__internal_servers }}' 50 | - '{{ nginx__dependent_servers }}' 51 | - '{{ nginx_servers | d([]) }}' 52 | - '{{ nginx_default_servers | d([]) }}' 53 | - '{{ nginx_internal_servers | d([]) }}' 54 | - '{{ nginx_dependent_servers | d([]) }}' 55 | when: (item.name is defined and 56 | ((item.state|d("present") == 'absent') or (item.delete|d()|bool))) 57 | 58 | - name: Get last used HTTP default_server for default_server roulette 59 | set_fact: 60 | nginx_register_default_server_saved: '{{ ansible_local.nginx.default_server }}' 61 | when: (ansible_local is defined and ansible_local.nginx is defined and 62 | ansible_local.nginx.default_server is defined) 63 | 64 | - name: Get last used HTTPS default_server for default_server roulette 65 | set_fact: 66 | nginx_register_default_server_ssl_saved: '{{ ansible_local.nginx.default_server_ssl }}' 67 | when: (ansible_local is defined and ansible_local.nginx is defined and 68 | ansible_local.nginx.default_server_ssl is defined) 69 | 70 | - name: Get HTTP server from nginx defaults for default_server roulette 71 | set_fact: 72 | nginx_register_default_server_name: '{{ nginx_default_name }}' 73 | when: nginx_default_name is defined and nginx_default_name 74 | 75 | - name: Get HTTPS server from nginx defaults for default_server roulette 76 | set_fact: 77 | nginx_register_default_server_ssl_name: '{{ nginx_default_ssl_name }}' 78 | when: nginx_default_ssl_name is defined and nginx_default_ssl_name 79 | 80 | - name: Get first server that listens on http port for default_server roulette 81 | set_fact: 82 | nginx_register_default_server_http: '{{ item.name if item.name is string else item.name[0] | d("default") }}' 83 | with_flattened: '{{ (nginx__servers + nginx__default_servers + nginx__internal_servers + nginx__dependent_servers + 84 | nginx_servers|d([]) + nginx_default_servers|d([]) + nginx_internal_servers|d([]) + nginx_dependent_servers|d([]))[::-1] }}' 85 | when: (item.state|d('present') != 'absent' and 86 | (item.enabled|d(True)|bool) and 87 | (item.listen|d(True)) and 88 | ((item.ssl is undefined or not item.ssl | bool) or not nginx_pki | bool)) 89 | 90 | - name: Get first server that listens on https port for default_server roulette 91 | set_fact: 92 | nginx_register_default_server_https: '{{ item.name if item.name is string else item.name[0] | d("default") }}' 93 | with_flattened: '{{ (nginx__servers + nginx__default_servers + nginx__internal_servers + nginx__dependent_servers + 94 | nginx_servers|d([]) + nginx_default_servers|d([]) + nginx_internal_servers|d([]) + nginx_dependent_servers|d([]))[::-1] }}' 95 | when: (item.state|d('present') != 'absent' and (item.enabled is undefined or item.enabled|bool) and 96 | (item.listen_ssl is undefined or item.listen_ssl|d()) and 97 | ((item.ssl|d() and item.ssl | bool) or 98 | (item.ssl is undefined and nginx_pki | bool))) 99 | 100 | - name: Spin the HTTP default_server roulette! 101 | set_fact: 102 | nginx_register_default_server: '{{ nginx_register_default_server_saved | 103 | default(nginx_register_default_server_name | 104 | default(nginx_register_default_server_http | 105 | default(""))) }}' 106 | 107 | - name: Spin the HTTPS default_server roulette! 108 | set_fact: 109 | nginx_register_default_server_ssl: '{{ nginx_register_default_server_ssl_saved | 110 | default(nginx_register_default_server_ssl_name | 111 | default(nginx_register_default_server_https | 112 | default(""))) }}' 113 | 114 | - name: Generate nginx server configuration 115 | template: 116 | src: 'etc/nginx/sites-available/{{ item.type | d(nginx_default_type) }}.conf.j2' 117 | dest: '/etc/nginx/sites-available/{{ item.filename | d(item.name if item.name is string else item.name[0] | d("default")) }}.conf' 118 | owner: 'root' 119 | group: 'root' 120 | mode: '0644' 121 | with_flattened: 122 | - '{{ nginx__servers }}' 123 | - '{{ nginx__default_servers }}' 124 | - '{{ nginx__internal_servers }}' 125 | - '{{ nginx__dependent_servers }}' 126 | - '{{ nginx_servers | d([]) }}' 127 | - '{{ nginx_default_servers | d([]) }}' 128 | - '{{ nginx_internal_servers | d([]) }}' 129 | - '{{ nginx_dependent_servers | d([]) }}' 130 | notify: [ 'Test nginx and reload' ] 131 | when: (item.state|d('present') != 'absent' and 132 | (item.delete is undefined or not item.delete|bool)) 133 | 134 | - name: Disable nginx server configuration 135 | file: 136 | path: '/etc/nginx/sites-enabled/{{ item.filename | d(item.name if item.name is string else item.name[0] | d("default")) }}.conf' 137 | state: 'absent' 138 | with_flattened: 139 | - '{{ nginx__servers }}' 140 | - '{{ nginx__default_servers }}' 141 | - '{{ nginx__internal_servers }}' 142 | - '{{ nginx__dependent_servers }}' 143 | - '{{ nginx_servers | d([]) }}' 144 | - '{{ nginx_default_servers | d([]) }}' 145 | - '{{ nginx_internal_servers | d([]) }}' 146 | - '{{ nginx_dependent_servers | d([]) }}' 147 | notify: [ 'Test nginx and restart' ] 148 | when: (item.name is defined and 149 | ((item.state|d("present") == 'absent') or 150 | (item.enabled|d() and not item.enabled|bool) or 151 | (item.delete|d()|bool))) 152 | 153 | ## https://stackoverflow.com/a/28888474 154 | - name: Test if Ansible is running in check mode 155 | command: /bin/true 156 | changed_when: False 157 | register: nginx__register_check_mode 158 | 159 | - name: Save fact if Ansible is running in check mode in variable 160 | set_fact: 161 | nginx__fact_check_mode: '{{ nginx__register_check_mode|skipped }}' 162 | 163 | - name: Enable nginx server configuration 164 | file: 165 | path: '/etc/nginx/sites-enabled/{{ item.filename | d(item.name if item.name is string else item.name[0] | d("default")) }}.conf' 166 | src: '/etc/nginx/sites-available/{{ item.filename | d(item.name if item.name is string else item.name[0] | d("default")) }}.conf' 167 | state: 'link' 168 | owner: 'root' 169 | group: 'root' 170 | mode: '0644' 171 | with_flattened: 172 | - '{{ nginx__servers }}' 173 | - '{{ nginx__default_servers }}' 174 | - '{{ nginx__internal_servers }}' 175 | - '{{ nginx__dependent_servers }}' 176 | - '{{ nginx_servers | d([]) }}' 177 | - '{{ nginx_default_servers | d([]) }}' 178 | - '{{ nginx_internal_servers | d([]) }}' 179 | - '{{ nginx_dependent_servers | d([]) }}' 180 | notify: [ 'Test nginx and restart' ] 181 | when: (item.state|d('present') != 'absent' and 182 | (item.enabled|d(True)|bool) and 183 | (item.delete is undefined or not item.delete|bool) and 184 | not nginx__fact_check_mode|bool) 185 | -------------------------------------------------------------------------------- /tasks/passenger_config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Detect passenger root 4 | command: passenger-config about root 5 | register: nginx_register_passenger_root 6 | changed_when: False 7 | 8 | - name: Set passenger_root value 9 | set_fact: 10 | nginx_passenger_root: '{{ nginx_register_passenger_root.stdout }}' 11 | when: nginx_passenger_root is undefined or not nginx_passenger_root 12 | 13 | - name: Detect passenger ruby 14 | shell: "passenger-config about ruby-command | grep Command | tail -1 | awk -F: '{print $2}'" 15 | args: 16 | executable: '/bin/sh' 17 | register: nginx_register_passenger_ruby 18 | changed_when: False 19 | 20 | - name: Set passenger_ruby value 21 | set_fact: 22 | nginx_passenger_ruby: '{{ nginx_register_passenger_ruby.stdout | trim }}' 23 | when: nginx_passenger_ruby is undefined or not nginx_passenger_ruby 24 | 25 | -------------------------------------------------------------------------------- /templates/etc/ansible/facts.d/nginx.fact.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Check nginx version 4 | nginx_version="$(/usr/sbin/nginx -v 2>&1 | sed -e 's#^.*/##')" 5 | 6 | {% set nginx_tpl_default_server = '' %} 7 | {% if (ansible_local is defined and ansible_local.nginx is defined and 8 | ansible_local.nginx.default_server is defined) %} 9 | {% set nginx_tpl_default_server = ansible_local.nginx.default_server %} 10 | {% elif nginx_register_default_server|d() %} 11 | {% set nginx_tpl_default_server = nginx_register_default_server %} 12 | {% endif %} 13 | {% set nginx_tpl_default_server_ssl = '' %} 14 | {% if (ansible_local is defined and ansible_local.nginx is defined and 15 | ansible_local.nginx.default_server_ssl is defined) %} 16 | {% set nginx_tpl_default_server_ssl = ansible_local.nginx.default_server_ssl %} 17 | {% elif nginx_register_default_server_ssl|d() %} 18 | {% set nginx_tpl_default_server_ssl = nginx_register_default_server_ssl %} 19 | {% endif %} 20 | cat << EOF 21 | { 22 | "acme": "{{ nginx_acme | bool | lower }}", 23 | "acme_domain": "{{ nginx_acme_domain }}", 24 | "acme_root": "{{ nginx_acme_root }}", 25 | "acme_server": "{{ nginx_acme_server | bool | lower }}", 26 | {% if nginx_tpl_default_server %} 27 | "default_server": "{{ nginx_tpl_default_server }}", 28 | {% endif %} 29 | {% if nginx_tpl_default_server_ssl %} 30 | "default_server_ssl": "{{ nginx_tpl_default_server_ssl }}", 31 | {% endif %} 32 | "enabled": {{ (nginx__deploy_state in [ 'present' ]) | lower }}, 33 | "manage_ipv6only": "{{ nginx_manage_ipv6only | bool | lower }}", 34 | "user": "{{ nginx_user }}", 35 | "www": "{{ nginx_www }}", 36 | "flavor": "{{ nginx_flavor }}", 37 | "version": "$nginx_version", 38 | "status_localhost": {{ nginx_status_localhost | to_nice_json }}, 39 | "status": {{ nginx_status | to_nice_json }} 40 | } 41 | EOF 42 | -------------------------------------------------------------------------------- /templates/etc/nginx/conf.d/custom.conf.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | 3 | {% if item.name is defined and item.name %} 4 | # Custom configuration: {{ item.name }} 5 | {{ item.custom }} 6 | {% endif %} 7 | 8 | -------------------------------------------------------------------------------- /templates/etc/nginx/conf.d/map.conf.j2: -------------------------------------------------------------------------------- 1 | {# 2 | # ==== nginx map template for debops.nginx role ==== 3 | # 4 | # More information about maps: https://nginx.org/en/docs/http/ngx_http_map_module.html 5 | # List of parameters supported by this template: 6 | # 7 | # - item.name: '' 8 | # Name of the map configuration file. Required. 9 | # 10 | # - item.map: '' 11 | # Lookup string and variable name to set, for example: 12 | # '$http_host $name' 13 | # '$http_user_agent $mobile' 14 | # 15 | # - item.default: '' 16 | # Default value to set if no mapping can be selected 17 | # (without the semicolon). 18 | # 19 | # - item.include: [] 20 | # List of files to include in this map { } definition (use absolute paths 21 | # to files). 22 | # 23 | # - item.hostnames: True 24 | # If this key is defined and True, nginx will treat list of source values 25 | # as hostnames. 26 | # 27 | # - item.mapping: | 28 | # Text block with value mapping specified in the nginx configuration 29 | # format (semicolons need to be included at the end of each line). 30 | # 31 | # - item.delete: True 32 | # If this parameter is defined and True, map configuration file will 33 | # be removed from the host. 34 | # 35 | #} 36 | # {{ ansible_managed }} 37 | 38 | {% if item.name is defined and item.name %} 39 | map {{ item.map }} { 40 | {% if item.hostnames is defined and item.hostnames %} 41 | hostnames; 42 | {% endif %} 43 | {% if item.default is defined and item.default %} 44 | default {{ item.default }}; 45 | {% endif %} 46 | {% if item.mapping is defined and item.mapping %} 47 | {{ item.mapping | indent(8) | regex_replace("(?m)^\s*$", "") }} 48 | {% endif %} 49 | {% if item.include is defined and item.include %} 50 | {% for file in item.include %} 51 | include {{ file }}; 52 | {% endfor %} 53 | {% endif %} 54 | } 55 | {% endif %} 56 | 57 | -------------------------------------------------------------------------------- /templates/etc/nginx/conf.d/upstream_default.conf.j2: -------------------------------------------------------------------------------- 1 | {# 2 | # ==== Default nginx upstream template for debops.nginx role ==== 3 | # 4 | # List of parameters supported by this template: 5 | # 6 | # - item.name: '' 7 | # Name of the upstream backend. Required. 8 | # 9 | # - item.type: '' 10 | # Name of the template used by nginx to generate upstream configuration. 11 | # 12 | # - item.server: '' or [] 13 | # Address and additional parameters of a server. You can specify either 14 | # a string (to configure one server instance) or a list (to configure 15 | # multiple servers. If 'item.port' is not defined, each list element can 16 | # be in the form 'address:port' to specify different ports. 17 | # 18 | # - item.cluster: [] 19 | # Alternative configuration of servers. You can specify list of inventory 20 | # hosts (for example using 'groups.group_name' syntax in a variable); 21 | # template will check if a host can be found in Ansible facts and will 22 | # use the FQDN hostname in the server configuration. If 'item.port' is 23 | # specified, it will be added to all listed hosts. 24 | # 25 | # - item.cluster_ip_all: [] 26 | # Alternative configuration of servers. You can specify list of inventory 27 | # hosts (for example using 'groups.group_name' syntax in a variable); 28 | # template will check if a host can be found in Ansible facts and will 29 | # use all of the available IP addresses in the server configuration. If 30 | # 'item.port' is specified, it will be added to all listed hosts. 31 | # 32 | # - item.port: '' 33 | # Port number to add to lists of servers if nginx should connect to port 34 | # other than 80. 35 | # 36 | # - item.enabled: True/False 37 | # If this parameter is defined and False, upstream will be configured 38 | # with all servers in a "down" state. 39 | # 40 | # - item.delete: True 41 | # If this parameter is defined and True, upstream configuration file will 42 | # be removed from the host. 43 | # 44 | # - item.hash: True 45 | # If this parameter is defined and True, 'ip_hash' option will be added 46 | # to the upstream configuration. 47 | # 48 | #} 49 | # {{ ansible_managed }} 50 | 51 | {% block nginx_tpl_block_upstream %} 52 | {% if item.name is defined and item.name %} 53 | upstream {{ item.name }} { 54 | {% if item.hash is defined and item.hash %} 55 | ip_hash; 56 | {% endif %} 57 | {% if item.server is defined and item.server %} 58 | {% if item.server is not string %} 59 | {% for line in item.server %} 60 | server {{ line }}{% if item.port is defined and item.port %}:{{ item.port }}{% endif %}{% if item.enabled is defined and not item.enabled %} down{% endif %}; 61 | {% endfor %} 62 | {% else %} 63 | server {{ item.server }}{% if item.enabled is defined and not item.enabled %} down{% endif %}; 64 | {% endif %} 65 | {% endif %} 66 | {% if item.cluster is defined and item.cluster %} 67 | {% for host in item.cluster %} 68 | {% if host == inventory_hostname %} 69 | server localhost{% if item.port is defined and item.port %}:{{ item.port }}{% endif %}{% if item.enabled is defined and not item.enabled %} down{% endif %}; 70 | {% else %} 71 | {% if host in play_hosts and hostvars[host].ansible_fqdn is defined %} 72 | server {{ hostvars[host].ansible_fqdn }}{% if item.port is defined and item.port %}:{{ item.port }}{% endif %}{% if item.enabled is defined and not item.enabled %} down{% endif %}; 73 | {% else %} 74 | # Unknown domain name of {{ host }} 75 | {% endif %} 76 | {% endif %} 77 | {% endfor %} 78 | {% endif %} 79 | {% if item.cluster_ip_all is defined and item.cluster_ip_all %} 80 | {% for host in item.cluster_ip_all %} 81 | {% if host == inventory_hostname %} 82 | server 127.0.0.1{% if item.port is defined and item.port %}:{{ item.port }}{% endif %}{% if item.enabled is defined and not item.enabled %} down{% endif %}; 83 | {% else %} 84 | {% if host in play_hosts and hostvars[host].ansible_all_ipv4_addresses is defined %} 85 | {% for address in hostvars[host].ansible_all_ipv4_addresses | unique %} 86 | server {{ address }}{% if item.port is defined and item.port %}:{{ item.port }}{% endif %}{% if item.enabled is defined and not item.enabled %} down{% endif %}; 87 | {% endfor %} 88 | {% else %} 89 | # Unknown IP address of {{ host }} %} 90 | {% endif %} 91 | {% endif %} 92 | {% endfor %} 93 | {% endif %} 94 | {% if item.options|d() %} 95 | {{ item.options | indent(8, true) | regex_replace("(?m)^\s*$", "") }} 96 | {% endif %} 97 | {% block nginx_tpl_block_custom_upstreams %} 98 | {% endblock %} 99 | } 100 | {% endif %} 101 | {% endblock %} 102 | 103 | -------------------------------------------------------------------------------- /templates/etc/nginx/conf.d/upstream_php.conf.j2: -------------------------------------------------------------------------------- 1 | {% extends "upstream_default.conf.j2" %} 2 | {# 3 | # 4 | # ---- nginx upstream template for PHP websites ---- 5 | # 6 | # List of parameters supported by this template: 7 | # 8 | # - item.php_pool / item.php_pools 9 | # Name of PHP-FPM pool which should be used as the upstream server. The 10 | # role will automatically generate the path to the UNIX socket in the 11 | # expected location. 12 | # 13 | #} 14 | {% set nginx__tpl_php_version = (ansible_local.php.version if (ansible_local|d() and ansible_local.php|d() and ansible_local.php.version|d()) else ('5' if (ansible_distribution_release in [ 'wheezy', 'jessie', 'precise', 'trusty', 'wily' ]) else '7.0')) %} 15 | {% block nginx_tpl_block_custom_upstreams %} 16 | {% set nginx__tpl_php_pools = item.php_pools | d(item.php_pool | d([]) ) %} 17 | {% if nginx__tpl_php_pools %} 18 | {% for pool in ([ nginx__tpl_php_pools ] if nginx__tpl_php_pools is string else nginx__tpl_php_pools) %} 19 | server unix:{{ nginx_run_path }}/php{{ nginx__tpl_php_version }}-fpm-{{ pool }}.sock{% if ((item.enabled|d() and not item.enabled|bool) or (item.down|d() and item.down|bool)) %} down{% endif %}; 20 | {% endfor %} 21 | {% endif %} 22 | {% endblock %} 23 | -------------------------------------------------------------------------------- /templates/etc/nginx/conf.d/upstream_php5.conf.j2: -------------------------------------------------------------------------------- 1 | {% extends "upstream_default.conf.j2" %} 2 | {# 3 | # 4 | # ---- nginx upstream template for PHP5 websites ---- 5 | # 6 | # List of parameters supported by this template: 7 | # 8 | # - item.php5: '' 9 | # Partial name of the UNIX socket created by php5-fpm process to use in 10 | # the upstream configuration. 11 | # 12 | #} 13 | {% block nginx_tpl_block_custom_upstreams %} 14 | {% if item.php5 is defined and item.php5 %} 15 | server unix:{{ nginx_run_path }}/php{{ ansible_local.php.version if (ansible_local|d() and ansible_local.php|d() and ansible_local.php.version|d()) else '5' }}-fpm-{{ item.php5 }}.sock{% if item.enabled is defined and item.enabled == False %} down{% endif %}; 16 | {% endif %} 17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /templates/etc/nginx/conf.d/upstream_rails.conf.j2: -------------------------------------------------------------------------------- 1 | {% extends "upstream_default.conf.j2" %} 2 | {% block nginx_tpl_block_upstream %} 3 | {% if nginx_flavor != 'passenger' %} 4 | {% if item.name is defined and item.name %} 5 | upstream {{ item.name }} { 6 | {% if item.hash is defined and item.hash %} 7 | ip_hash; 8 | {% endif %} 9 | {% if item.server is defined and item.server %} 10 | {% if item.server is not string %} 11 | {% for line in item.server %} 12 | server {{ line }}{% if item.port is defined and item.port %}:{{ item.port }}{% endif %}{% if item.enabled is defined and not item.enabled %} down{% endif %}; 13 | {% endfor %} 14 | {% else %} 15 | server {{ item.server }}{% if item.enabled is defined and not item.enabled %} down{% endif %}; 16 | {% endif %} 17 | {% endif %} 18 | {% if item.cluster is defined and item.cluster %} 19 | {% for host in item.cluster %} 20 | {% if host == inventory_hostname %} 21 | server localhost{% if item.port is defined and item.port %}:{{ item.port }}{% endif %}{% if item.enabled is defined and not item.enabled %} down{% endif %}; 22 | {% else %} 23 | {% if host in play_hosts and hostvars[host].ansible_fqdn is defined %} 24 | server {{ hostvars[host].ansible_fqdn }}{% if item.port is defined and item.port %}:{{ item.port }}{% endif %}{% if item.enabled is defined and not item.enabled %} down{% endif %}; 25 | {% else %} 26 | # Unknown domain name of {{ host }} 27 | {% endif %} 28 | {% endif %} 29 | {% endfor %} 30 | {% endif %} 31 | {% if item.cluster_ip_all is defined and item.cluster_ip_all %} 32 | {% for host in item.cluster_ip_all %} 33 | {% if host == inventory_hostname %} 34 | server 127.0.0.1{% if item.port is defined and item.port %}:{{ item.port }}{% endif %}{% if item.enabled is defined and not item.enabled %} down{% endif %}; 35 | {% else %} 36 | {% if host in play_hosts and hostvars[host].ansible_all_ipv4_addresses is defined %} 37 | {% for address in hostvars[host].ansible_all_ipv4_addresses | unique %} 38 | server {{ address }}{% if item.port is defined and item.port %}:{{ item.port }}{% endif %}{% if item.enabled is defined and not item.enabled %} down{% endif %}; 39 | {% endfor %} 40 | {% else %} 41 | # Unknown IP address of {{ host }} %} 42 | {% endif %} 43 | {% endif %} 44 | {% endfor %} 45 | {% endif %} 46 | {% block nginx_tpl_block_custom_upstreams %} 47 | {% endblock %} 48 | } 49 | {% endif %} 50 | {% elif nginx_flavor == 'passenger' %} 51 | # Upstream configuration disabled. 52 | # Rails applications are served using Phusion Passenger 53 | {% endif %} 54 | {% endblock %} 55 | 56 | -------------------------------------------------------------------------------- /templates/etc/nginx/nginx.conf.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | 3 | {% if nginx_user|d() %} 4 | user {{ nginx_user }}; 5 | 6 | {% endif %} 7 | pid {{ nginx_run_path }}/nginx.pid; 8 | 9 | include /etc/nginx/modules-enabled/*.conf; 10 | 11 | # Default error_log 12 | error_log {{ nginx_http_error_log | default(nginx_log_path + '/error.log') }}; 13 | 14 | # Nicenness, from 20 (nice) to -20 (not nice) 15 | worker_priority {{ nginx_worker_priority | default('0') }}; 16 | 17 | # Number of workers to run, usually equals number of CPU cores 18 | worker_processes {{ nginx_worker_processes | default(ansible_processor_vcpus) }}; 19 | 20 | # Maximum number of opened files per process, must be higher than worker_connections 21 | worker_rlimit_nofile {{ nginx_worker_rlimit_nofile | default('4096') }}; 22 | 23 | {% if nginx_flavor == 'passenger' %} 24 | # Phusion Passenger requires correct $PATH to work 25 | env PATH; 26 | 27 | {% endif %} 28 | events { 29 | worker_connections {{ nginx_worker_connections | default('1024') }}; 30 | } 31 | 32 | http { 33 | include /etc/nginx/mime.types; 34 | default_type application/octet-stream; 35 | 36 | server_tokens {{ nginx_http_server_tokens | default('off') }}; 37 | 38 | server_names_hash_bucket_size {{ nginx_http_server_names_hash_bucket_size | default('64') }}; 39 | server_names_hash_max_size {{ nginx_http_server_names_hash_max_size | default('512') }}; 40 | 41 | # Custom file paths 42 | client_body_temp_path {{ nginx_client_body_temp_path | d("/var/lib/nginx/body") }}; 43 | fastcgi_temp_path {{ nginx_fastcgi_temp_path | d("/var/lib/nginx/fastcgi") }}; 44 | proxy_temp_path {{ nginx_proxy_temp_path | d("/var/lib/nginx/proxy") }}; 45 | scgi_temp_path {{ nginx_scgi_temp_path | d("/var/lib/nginx/scgi") }}; 46 | uwsgi_temp_path {{ nginx_uwsgi_temp_path | d("/var/lib/nginx/uwsgi") }}; 47 | 48 | # Logging 49 | access_log {{ nginx_http_access_log | default(nginx_log_path + '/access.log') }}; 50 | error_log {{ nginx_http_error_log | default(nginx_log_path + '/error.log') }}; 51 | 52 | add_header X-Clacks-Overhead "GNU Terry Pratchett"; 53 | 54 | {% if nginx_http_options|d() and nginx_http_options %} 55 | {{ nginx_http_options | indent(8, true) | regex_replace("(?m)^\s*$", "") }} 56 | 57 | {% endif %} 58 | {% if nginx_http_client_max_body_size|d() and nginx_http_client_max_body_size %} 59 | client_max_body_size {{ nginx_http_client_max_body_size }}; 60 | 61 | {% endif %} 62 | {% if ((nginx_http_allow is defined and nginx_http_allow) or 63 | (nginx_http_auth_basic is defined and nginx_http_auth_basic) or 64 | (nginx_http_satisfy is defined and nginx_http_satisfy)) %} 65 | satisfy {{ nginx_http_satisfy | default(nginx_default_satisfy) }}; 66 | 67 | {% endif %} 68 | {% if nginx_http_allow is defined and nginx_http_allow %} 69 | {% for address in ([ nginx_http_allow ] if nginx_http_allow is string else nginx_http_allow) %} 70 | allow {{ address }}; 71 | {% endfor %} 72 | deny all; 73 | 74 | {% endif %} 75 | {% if nginx_http_auth_basic is defined and nginx_http_auth_basic %} 76 | auth_basic "{% if nginx_http_auth_basic_realm is defined and nginx_http_auth_basic_realm %}{{ nginx_http_auth_basic_realm }}{% else %}{{ nginx_default_auth_basic_realm }}{% endif %}"; 77 | auth_basic_user_file {{ nginx_http_auth_basic_filename | default(nginx_private_path + "/" + nginx_http_auth_basic_name) }}; 78 | 79 | {% endif %} 80 | {% if nginx_flavor == 'passenger' %} 81 | # Configuration options for Phusion Passenger 82 | {# These values are set at playbook runtime #} 83 | passenger_root {{ nginx_passenger_root }}; 84 | passenger_ruby {{ nginx_passenger_ruby }}; 85 | 86 | passenger_max_pool_size {{ nginx_passenger_max_pool_size }}; 87 | 88 | {% if nginx_passenger_options %} 89 | {{ nginx_passenger_options | indent(8,true) | regex_replace("(?m)^\s*$", "") }} 90 | 91 | {% endif %} 92 | {% endif %} 93 | # Virtual Hosts Configs 94 | include {{ nginx_etc_path }}/conf.d/*.conf; 95 | include {{ nginx_etc_path }}/sites-enabled/*.conf; 96 | } 97 | {% if nginx_extra_options %} 98 | 99 | {{ nginx_extra_options }} 100 | {% endif %} 101 | 102 | # vim:ft=nginx 103 | -------------------------------------------------------------------------------- /templates/etc/nginx/sites-available/custom.conf.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | 3 | {% if item.name|d() %} 4 | # Custom configuration: {{ item.name if item.name is string else item.name[0] | d('default') }} 5 | {{ item.custom }} 6 | {% endif %} 7 | -------------------------------------------------------------------------------- /templates/etc/nginx/sites-available/debops__tpl_macros.j2: -------------------------------------------------------------------------------- 1 | {# vim: foldmarker=[[[,]]]:foldmethod=marker 2 | # Copyright [[[ 3 | # Commonly used set of macros in DebOps. 4 | # It can be included in repositories as needed. 5 | # Changes to this file should go upstream: FIXME 6 | # 7 | # Copyright (C) 2014-2016 Maciej Delmanowski 8 | # Copyright (C) 2015-2016 Robin Schneider 9 | # Copyright (C) 2014-2016 DebOps https://debops.org/ 10 | # 11 | # This file is part of DebOps. 12 | # 13 | # DebOps is free software; you can redistribute it and/or modify 14 | # it under the terms of the GNU General Public License version 3, as 15 | # published by the Free Software Foundation. 16 | # 17 | # DebOps is distributed in the hope that it will be useful, 18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | # GNU General Public License for more details. 21 | # 22 | # You should have received a copy of the GNU General Public License 23 | # along with DebOps. If not, see https://www.gnu.org/licenses/. 24 | # ]]] #} 25 | 26 | {% macro get_yaml_list_for_elem(list_or_elem) %}{# [[[ #} 27 | {{ ([ list_or_elem ] 28 | if (list_or_elem is string or list_or_elem in [True, False]) 29 | else (list_or_elem|list)) | to_nice_yaml }} 30 | {% endmacro %}{# ]]] #} 31 | 32 | {% macro get_realm_yaml_list(domains, fallback_realm) %}{# [[[ #} 33 | {% set custom_realm_list = [] %} 34 | {% if domains and (ansible_local|d() and ansible_local.pki|d() and ansible_local.pki.known_realms|d()) %} 35 | {% for domain in (get_yaml_list_for_elem(domains) | from_yaml) %} 36 | {% if domain in ansible_local.pki.known_realms %} 37 | {% set _ = custom_realm_list.append(domain) %} 38 | {% elif (domain.split('.')[1:] | join('.')) in ansible_local.pki.known_realms %} 39 | {% set _ = custom_realm_list.append(domain.split('.')[1:] | join('.')) %} 40 | {% endif %} 41 | {% endfor %} 42 | {% endif %} 43 | {% if custom_realm_list|length == 0 %} 44 | {% set _ = custom_realm_list.append(fallback_realm) %} 45 | {% endif %} 46 | {{ custom_realm_list | to_nice_yaml }} 47 | {% endmacro %}{# ]]] #} 48 | 49 | {% macro get_apache_version() %}{# [[[ #} 50 | {{ ansible_local.apache.version 51 | if (ansible_local|d() and ansible_local.apache|d() and 52 | ansible_local.apache.version|d()) 53 | else "2.4.0" }} 54 | {% endmacro %}{# ]]] #} 55 | 56 | {% macro get_openssl_version() %}{# [[[ #} 57 | {{ ansible_local.pki.openssl_version 58 | if (ansible_local|d() and ansible_local.pki|d() and 59 | ansible_local.pki.openssl_version|d()) 60 | else "0.0.0" }} 61 | {% endmacro %}{# ]]] #} 62 | 63 | {% macro get_gnutls_version() %}{# [[[ #} 64 | {{ ansible_local.pki.gnutls_version 65 | if (ansible_local|d() and ansible_local.pki|d() and 66 | ansible_local.pki.gnutls_version|d()) 67 | else "0.0.0" }} 68 | {% endmacro %}{# ]]] #} 69 | -------------------------------------------------------------------------------- /templates/etc/nginx/sites-available/default.conf.j2: -------------------------------------------------------------------------------- 1 | {% import 'debops__tpl_macros.j2' as debops__tpl_macros with context %} 2 | {# 3 | # 4 | # ==== Default server template for debops.nginx role ==== 5 | # 6 | # Refer to the documentation in /docs/defaults-detailed.rst 7 | # 8 | #} 9 | 10 | {# 11 | # 12 | # ---- HTTPS, ports to listen on, default server, HTTPS redirect ---- 13 | #} 14 | {% set nginx_version = ansible_local.nginx.version if (ansible_local|d() and ansible_local.nginx|d() and ansible_local.nginx.version|d()) else "0.0" %} 15 | {% set nginx_tpl_robots_tag = [] if (item.robots_tag|d(nginx__http_robots_tag) is string and item.robots_tag|d(nginx__http_robots_tag) == omit) 16 | else ( 17 | [ item.robots_tag|d(nginx__http_robots_tag) ] 18 | if (item.robots_tag|d(nginx__http_robots_tag) is string) 19 | else (item.robots_tag|d(nginx__http_robots_tag))) %} 20 | {% set nginx_tpl_acme = (item.acme | default(nginx_acme)) | bool %} 21 | {% set nginx_tpl_ssl = (item.ssl | default(nginx_pki)) | bool %} 22 | {% set nginx_tpl_listen = item.listen | default(nginx_listen_port if nginx_listen_port else nginx_listen_socket) %} 23 | {% if nginx_tpl_ssl | bool %} 24 | {% set nginx_tpl_listen_ssl = item.listen_ssl | default(nginx_listen_ssl_port if nginx_listen_ssl_port else nginx_listen_ssl_socket) %} 25 | {% endif %} 26 | {% set nginx_tpl_default_server = [] %} 27 | {% set nginx_tpl_ipv6only = [] %} 28 | {% if nginx_register_default_server|d() %} 29 | {% for name in ([ item.name ] if item.name is string else item.name) %} 30 | {% if (name == nginx_register_default_server or 31 | not name and nginx_register_default_server == "default") %} 32 | {% set _ = nginx_tpl_default_server.append('default_server') %} 33 | {% if (nginx_manage_ipv6only | bool) %} 34 | {% set _ = nginx_tpl_ipv6only.append('ipv6only=off') %} 35 | {% endif %} 36 | {% endif %} 37 | {% endfor %} 38 | {% endif %} 39 | {% set nginx_tpl_default_server_ssl = [] %} 40 | {% set nginx_tpl_ipv6only_ssl = [] %} 41 | {% if nginx_register_default_server_ssl|d() %} 42 | {% for name in ([ item.name ] if item.name is string else item.name) %} 43 | {% if (name == nginx_register_default_server_ssl or 44 | not name and nginx_register_default_server_ssl == "default") %} 45 | {% set _ = nginx_tpl_default_server_ssl.append('default_server') %} 46 | {% if (nginx_manage_ipv6only | bool) %} 47 | {% set _ = nginx_tpl_ipv6only_ssl.append('ipv6only=off') %} 48 | {% endif %} 49 | {% endif %} 50 | {% endfor %} 51 | {% endif %} 52 | {% set nginx_tpl_default_redirect_code = '307' %} 53 | {% set nginx_tpl_default_redirect_code_ssl = '301' %} 54 | {% set nginx_tpl_http_redirect = item.redirect | default('https://$host$request_uri') %} 55 | {% set nginx_tpl_pki_custom_realm = '' %} 56 | {% set nginx_tpl_pki_custom_realm_list = [] %} 57 | {% if (nginx_pki | bool and item.name | d() and (ansible_local|d() and ansible_local.pki|d() and ansible_local.pki.known_realms|d())) %} 58 | {# TODO: Legacy code. Use debops__tpl_macros.j2 #} 59 | {% for name in ([ item.name ] if item.name is string else item.name) %} 60 | {% if name in ansible_local.pki.known_realms %} 61 | {% set _ = nginx_tpl_pki_custom_realm_list.append(name) %} 62 | {% elif (name.split('.')[1:] | join('.')) in ansible_local.pki.known_realms %} 63 | {% set _ = nginx_tpl_pki_custom_realm_list.append(name.split('.')[1:] | join('.')) %} 64 | {% endif %} 65 | {% endfor %} 66 | {% endif %} 67 | {% if nginx_tpl_pki_custom_realm_list %} 68 | {% set nginx_tpl_pki_custom_realm = nginx_tpl_pki_custom_realm_list[0] %} 69 | {% endif %} 70 | {# 71 | # 72 | # ---- SSL certificate ---- 73 | #} 74 | {% set nginx_tpl_ssl_certificate = item.ssl_crt|d(nginx_pki_path + "/" + item.pki_realm | d(nginx_tpl_pki_custom_realm if nginx_tpl_pki_custom_realm else nginx_pki_realm) + "/" + (item.pki_crt|d(nginx_pki_crt))) %} 75 | {# 76 | # 77 | # ---- SSL certificate key ---- 78 | #} 79 | {% set nginx_tpl_ssl_certificate_key = item.ssl_key|d(nginx_pki_path + "/" + item.pki_realm | d(nginx_tpl_pki_custom_realm if nginx_tpl_pki_custom_realm else nginx_pki_realm) + "/" + (item.pki_key|d(nginx_pki_key))) %} 80 | {# 81 | # 82 | # ---- SSL client CA certificate ---- 83 | #} 84 | {% set nginx_tpl_ssl_client_certificate = item.ssl_ca|d(nginx_pki_path + "/" + item.pki_ca_realm | d(nginx_tpl_pki_custom_realm if nginx_tpl_pki_custom_realm else nginx_pki_ca_realm) + "/" + (item.pki_ca|d(nginx_pki_ca))) %} 85 | {# 86 | # 87 | # ---- SSL trusted CA certificate ---- 88 | #} 89 | {% set nginx_tpl_ssl_trusted_certificate = item.ssl_trusted 90 | |d(nginx_pki_path + "/" + (item.pki_realm | d(nginx_tpl_pki_custom_realm if nginx_tpl_pki_custom_realm else nginx_pki_realm) + "/" + item.pki_trusted | d(nginx_pki_trusted))) %} 91 | {# 92 | # 93 | # ---- Diffie-Hellman Key Exchange parameters ---- 94 | #} 95 | {% set nginx_tpl_ssl_dhparam = item.ssl_dhparam | default(nginx_ssl_dhparam) %} 96 | {# 97 | # 98 | # ---- root directory ---- 99 | #} 100 | {% if item.owner|d() %} 101 | {% set nginx_tpl_root = item.root | default(nginx_www + '/' + item.owner + '/sites/' + (item.name if item.name is string else item.name[0] | d('default')) + '/public') %} 102 | {% else %} 103 | {% set nginx_tpl_root = item.root | default(nginx_www + '/sites/' + (item.name if item.name is string else item.name[0] | d('default')) + '/public') %} 104 | {% endif %} 105 | {% if item.root_suffix|d() %} 106 | {% set nginx_tpl_root = nginx_tpl_root + '/' + item.root_suffix %} 107 | {% endif %} 108 | {% macro print_root() %} 109 | {% block nginx_tpl_block_root %} 110 | {% if (not (item.root|d() | bool)) and nginx_tpl_root %} 111 | root {{ nginx_tpl_root }}; 112 | {% endif %} 113 | {% endblock %} 114 | {% endmacro %} 115 | {# 116 | # 117 | # ---- macro which prints location entries from 'location' hash, flat ---- 118 | #} 119 | {% macro print_location(location,location_allow=[],location_referers=[],location_deny=[]) %} 120 | {% if location is defined %} 121 | {% for path in location.keys() %} 122 | {% if location[path] %} 123 | location {{ path }} { 124 | {% if location_referers is defined %} 125 | {% if location_referers[path] is defined and location_referers[path] %} 126 | valid_referers none blocked {{ location_referers[path] | unique | join(' ') }}; 127 | if ($invalid_referer) { 128 | return 403; 129 | } 130 | {% endif %} 131 | {% endif %} 132 | {{ item.location[path] | indent(16) | regex_replace("(?m)^\s*$", "") }} 133 | {% if location_allow is defined %} 134 | {% if location_allow[path] is defined and location_allow[path] %} 135 | {% for address in location_allow[path] | unique %} 136 | allow {{ address }}; 137 | {% endfor %} 138 | {% if location_deny is defined %} 139 | {% if location_deny[path] is defined %} 140 | {% for address in location_deny[path] | unique %} 141 | deny {{ address }}; 142 | {% endfor %} 143 | {% endif %} 144 | {% else %} 145 | deny all; 146 | {% endif %} 147 | {% endif %} 148 | {% endif %} 149 | } 150 | 151 | {% endif %} 152 | {% endfor %} 153 | {% endif %} 154 | {% endmacro %} 155 | {# 156 | # 157 | # ---- macro which prints location entries from 'location_list' list, recursive 158 | #} 159 | {% macro print_location_list(location_list) %} 160 | {% if location_list is defined %} 161 | {% for entry in location_list %} 162 | {% if entry.pattern|d() and (entry.enabled|d(True) | bool) %} 163 | location {{ entry.pattern_prefix | default('') + entry.pattern }} { 164 | {% if entry.referers|d() %} 165 | valid_referers none blocked {{ entry.referers | unique | join(' ') }}; 166 | if ($invalid_referer) { 167 | return 403; 168 | } 169 | {% endif %} 170 | {% if entry.options|d() %} 171 | {{ entry.options | indent(16) | regex_replace("(?m)^\s*$", "") }} 172 | {% endif %} 173 | {% if entry.allow|d() %} 174 | {% for address in entry.allow | unique %} 175 | allow {{ address }}; 176 | {% endfor %} 177 | {% if entry.deny|d() %} 178 | {% for address in entry.deny | unique %} 179 | deny {{ address }}; 180 | {% endfor %} 181 | {% else %} 182 | deny all; 183 | {% endif %} 184 | {% endif %} 185 | {% if entry.locations|d() %} 186 | {{ print_location_list(entry.locations) | indent(8, true) | regex_replace("(?m)^\s*$", "") }} 187 | {% endif %} 188 | } 189 | {% endif %} 190 | {% if not loop.last %} 191 | 192 | {% endif %} 193 | {% endfor %} 194 | {% endif %} 195 | {% endmacro %} 196 | {# 197 | # 198 | # ---- macro which prints the nginx server block which is shared between HTTP and HTTPS 199 | #} 200 | {% macro print_shared_nginx_server_block() %} 201 | {% if (not nginx_tpl_ssl and ((item.redirect|d()) or (item.redirect_to_ssl|d() | bool))) %} 202 | return {{ item.redirect_code_ssl | default(nginx_tpl_default_redirect_code_ssl) }} {{ nginx_tpl_http_redirect }}; 203 | {% elif nginx_tpl_ssl and item.redirect_ssl|d() %} 204 | return {{ item.redirect_code | default(nginx_tpl_default_redirect_code) }} {{ item.redirect_ssl }}; 205 | {% else %} 206 | keepalive_timeout {{ item.keepalive | default(nginx_default_keepalive_timeout) | string }}; 207 | 208 | {% block nginx_tpl_block_log %} 209 | {% if item.name|d() %} 210 | {% set nginx_tpl_access_log_format = '' %} 211 | {% if item.access_log_format is defined %} 212 | {% set nginx_tpl_access_log_format = ' ' + item.access_log_format %} 213 | {% elif nginx_access_log_format is defined %} 214 | {% set nginx_tpl_access_log_format = ' ' + nginx_access_log_format %} 215 | {% endif %} 216 | access_log {{ (item.log_path | d(nginx_log_path)) + '/' + item.access_log | d(item.filename | d(item.name if item.name is string else item.name[0]) + '_access') }}.log{{ nginx_tpl_access_log_format }}; 217 | error_log {{ (item.log_path | d(nginx_log_path)) + '/' + item.error_log | d(item.filename | d(item.name if item.name is string else item.name[0]) + '_error') }}.log; 218 | {% endif %} 219 | {% endblock %} 220 | {% block nginx_tpl_block_index %} 221 | {% if item.index|d(True) %} 222 | index {{ item.index | default('index.html index.htm') }}; 223 | 224 | {% endif %} 225 | {% endblock %} 226 | {% if nginx_real_ip_from|d() and nginx_real_ip_from %} 227 | {% if nginx_real_ip_header|d() and nginx_real_ip_header %} 228 | real_ip_header {{ nginx_real_ip_header }}; 229 | {% endif %} 230 | {% if nginx_real_ip_recursive | bool %} 231 | real_ip_recursive on; 232 | {% elif not nginx_real_ip_recursive | bool %} 233 | real_ip_recursive off; 234 | {% endif %} 235 | {% if nginx_real_ip_from is string %} 236 | set_real_ip_from {{ nginx_real_ip_from }}; 237 | 238 | {% else %} 239 | {% for element in nginx_real_ip_from %} 240 | set_real_ip_from {{ element }}; 241 | {% endfor %} 242 | {% endif %} 243 | 244 | {% endif %} 245 | if (-f $document_root/{{ item.maintenance_file | d('maintenance.html') }}) { 246 | return 503; 247 | } 248 | error_page 503 @maintenance; 249 | location @maintenance { 250 | rewrite ^(.*)$ /{{ item.maintenance_file | d('maintenance.html') }} break; 251 | } 252 | 253 | {% if item.error_pages|d() %} 254 | {% for code, location in item.error_pages.iteritems() %} 255 | error_page {{ code }} {{ location }}; 256 | location {{ location }} { 257 | internal; 258 | } 259 | 260 | {% endfor %} 261 | {% endif %} 262 | {% if item.include_files_begin|d() %} 263 | {% for file in item.include_files_begin %} 264 | include {{ file }}; 265 | {% endfor %} 266 | 267 | {% endif %} 268 | {% if item.error_pages_list|d() %} 269 | {% for element in item.error_pages_list %} 270 | {% if element.location|d() or element.location_options|d() %} 271 | 272 | {% endif %} 273 | error_page {{ element.code if (element.code is string) else (element.code | join(" ")) }} {{ element.uri }}; 274 | {% if element.location|d() or element.location_options|d() %} 275 | location {{ element.location | d(element.uri) }} { 276 | {{ element.location_options | indent(16, true) | regex_replace("(?m)^\s*$", "") }} 277 | } 278 | {% if not loop.last %} 279 | 280 | {% endif %} 281 | {% endif %} 282 | {% endfor %} 283 | 284 | {% endif %} 285 | {% if item.options|d() %} 286 | {{ item.options | indent(8) | regex_replace("(?m)^\s*$", "") }} 287 | 288 | {% endif %} 289 | {% if item.access_policy|d() and (item.access_policy in nginx_access_policy_satisfy_map) %} 290 | satisfy {{ nginx_access_policy_satisfy_map[item.access_policy] }}; 291 | 292 | {% elif item.allow|d() or 293 | (item.auth_basic|d() | bool) or 294 | (item.satisfy|d()) or 295 | (item.access_policy|d() and 296 | item.access_policy not in nginx_access_policy_satisfy_map) %} 297 | satisfy {{ item.satisfy | default(nginx_default_satisfy) }}; 298 | 299 | {% endif %} 300 | {% if item.allow|d() or item.access_policy|d() %} 301 | {% if item.allow is defined %} 302 | {% if item.allow is string %} 303 | allow {{ item.allow }}; 304 | {% else %} 305 | {% for address in item.allow | unique %} 306 | allow {{ address }}; 307 | {% endfor %} 308 | {% endif %} 309 | {% endif %} 310 | {% if item.access_policy|d() and (item.access_policy in nginx_access_policy_allow_map) %} 311 | {% for address in nginx_access_policy_allow_map[item.access_policy] | unique %} 312 | allow {{ address }}; 313 | {% endfor %} 314 | {% endif %} 315 | deny all; 316 | 317 | {% endif %} 318 | {% if item.access_policy|d() and (item.access_policy in nginx_access_policy_auth_basic_map) %} 319 | auth_basic "{{ item.auth_basic_realm|d(nginx_default_auth_basic_realm) }}"; 320 | auth_basic_user_file {{ nginx_private_path + "/" + nginx_access_policy_auth_basic_map[item.access_policy] }}; 321 | 322 | {% elif item.auth_basic|d() | bool %} 323 | auth_basic "{{ item.auth_basic_realm|d(nginx_default_auth_basic_realm) }}"; 324 | auth_basic_user_file {{ item.auth_basic_filename | default(nginx_private_path + "/" + item.auth_basic_name) }}; 325 | 326 | {% endif %} 327 | {% if item.userdir|d()|bool %} 328 | location ~ {{ item.userdir_regexp | d('^/~(.+?)(/.*)?$') }} { 329 | alias {{ item.userdir_alias | d(nginx_www + '/$1/userdir/public$2') }}; 330 | index {{ item.userdir_index | d(item.index | default('index.html index.htm')) }}; 331 | {% if item.userdir_options|d() %} 332 | {{ item.userdir_options | indent(16, true) | regex_replace("(?m)^\s*$", "") }} 333 | {% else %} 334 | autoindex on; 335 | autoindex_exact_size off; 336 | {% endif %} 337 | } 338 | 339 | {% endif %} 340 | {% if item.deny_hidden|d(True) | bool %} 341 | # Disallow access to hidden files and directories 342 | location ~ /\. { 343 | return 404; 344 | } 345 | 346 | {% endif %} 347 | {% if item.favicon|d(True) | bool %} 348 | location = /favicon.ico { 349 | try_files /favicon.ico =204; 350 | access_log off; 351 | log_not_found off; 352 | } 353 | 354 | {% endif %} 355 | {% if item.status|d(True) and (nginx_status or nginx_status_localhost) %} 356 | location = {{ item.status_name | default(nginx_status_name) }} { 357 | stub_status on; 358 | access_log off; 359 | {% if nginx_status_localhost %} 360 | {% for address in nginx_status_localhost | unique %} 361 | allow {{ address }}; 362 | {% endfor %} 363 | {% endif %} 364 | {% if nginx_status %} 365 | {% for address in nginx_status | unique %} 366 | allow {{ address }}; 367 | {% endfor %} 368 | {% endif %} 369 | {% if item.status|d() %} 370 | {% for address in item.status | unique %} 371 | allow {{ address }}; 372 | {% endfor %} 373 | {% endif %} 374 | deny all; 375 | } 376 | 377 | {% endif %} 378 | {% block nginx_tpl_block_custom_status_locations %} 379 | {% endblock %} 380 | {% if nginx_tpl_default_server or nginx_tpl_default_server_ssl %} 381 | include /etc/nginx/sites-default.d/*.conf; 382 | 383 | {% endif %} 384 | {% block nginx_tpl_block_location %} 385 | {% if item.location|d() or item.location_list|d() %} 386 | {% if item.location_list|d() %} 387 | {{ print_location_list(item.location_list) }} 388 | {% endif %} 389 | {% if item.location|d() %} 390 | {{ print_location(item.location, item.location_allow, item.location_referers, item.location_deny) }} 391 | {% endif %} 392 | {% else %} 393 | location / { 394 | {% block nginx_tpl_block_location_root %} 395 | try_files {{ item.try_files|default(nginx_default_try_files) | join(' ') }} =404; 396 | {% endblock %} 397 | } 398 | {% endif %} 399 | {% endblock %} 400 | {% block nginx_tpl_block_custom_locations %} 401 | {% endblock %} 402 | {% if item.include_files_end|d() %} 403 | 404 | {% for file in item.include_files_end %} 405 | include {{ file }}; 406 | {% endfor %} 407 | {% endif %} 408 | {% endif %} 409 | {% endmacro %} 410 | {# 411 | # 412 | # ---- nginx server template begins here ---- 413 | #} 414 | # {{ ansible_managed }} 415 | 416 | {% if item.name|d() %} 417 | # nginx server configuration for: 418 | {% for address in ([ item.name ] if item.name is string else item.name) %} 419 | {% if nginx_tpl_ssl and not (item.redirect_to_ssl|d(True) | bool) %} 420 | # - http://{{ address }}/ 421 | {% endif %} 422 | # - {{ "https" if nginx_tpl_ssl else "http" }}://{{ address }}/ 423 | {% endfor %} 424 | {% if item.by_role|d() %} 425 | # generated by Ansible role: {{ item.by_role }} 426 | {% endif %} 427 | 428 | {% else %} 429 | # nginx server default configuration 430 | {% if item.by_role|d() %} 431 | # generated by Ansible role: {{ item.by_role }} 432 | {% endif %} 433 | 434 | {% endif %} 435 | {% if item.redirect_from|d() and item.name|d() %} 436 | {% if item.name is string or item.name | length == 1 %} 437 | # Cannot redirect from only one name: {{ item.name if item.name is string else item.name | join(', ') }} 438 | 439 | {% else %} 440 | server { 441 | 442 | {% if item.listen|d(True) %} 443 | {% for port in nginx_tpl_listen %} 444 | listen {{ port|string }}; 445 | {% endfor %} 446 | {% if nginx_tpl_ssl | bool %} 447 | {% for port in nginx_tpl_listen_ssl %} 448 | listen {{ port|string }} ssl; 449 | {% endfor %} 450 | 451 | ssl_certificate {{ nginx_tpl_ssl_certificate }}; 452 | ssl_certificate_key {{ nginx_tpl_ssl_certificate_key }}; 453 | {% endif %} 454 | 455 | {% if item.redirect_from is iterable %} 456 | {% for name in item.redirect_from %} 457 | server_name {{ name }}; 458 | {% endfor %} 459 | {% else %}{# item.redirect_from is True #} 460 | {% for name in item.name[1:] %} 461 | server_name {{ name }}; 462 | {% endfor %} 463 | {% endif %} 464 | 465 | {% endif %} 466 | return {{ item.redirect_code | default(nginx_tpl_default_redirect_code) }} $scheme://{{ item.name if item.name is string else item.name[0] }}$request_uri; 467 | 468 | } 469 | 470 | {% endif %} 471 | {% elif item.redirect_to|d() and item.name|d() %} 472 | server { 473 | 474 | {% if item.listen|d(True) %} 475 | {% for port in nginx_tpl_listen %} 476 | listen {{ port }}; 477 | {% endfor %} 478 | 479 | {% for address in ([ item.name ] if item.name is string else item.name) %} 480 | {% if address != item.redirect_to %} 481 | server_name {{ address }}; 482 | {% endif %} 483 | {% endfor %} 484 | 485 | {% endif %} 486 | return {{ item.redirect_code | default(nginx_tpl_default_redirect_code) }} $scheme://{{ item.redirect_to }}$request_uri; 487 | 488 | } 489 | 490 | {% endif %} 491 | server { 492 | 493 | {% if item.listen|d(True) %} 494 | {% for port in nginx_tpl_listen %} 495 | listen {{ port }}{% if nginx_tpl_default_server %} {{ nginx_tpl_default_server | join(" ") }}{% endif %}{% if (loop.first and nginx_tpl_ipv6only) %} {{ nginx_tpl_ipv6only | join(" ") }}{% endif %}; 496 | {% endfor %} 497 | 498 | {% if item.name|d() %} 499 | {% if item.redirect_from|d() %} 500 | server_name {{ item.name if item.name is string else item.name[0] }}; 501 | 502 | {% elif item.redirect_to|d() %} 503 | server_name {{ item.redirect_to }}; 504 | 505 | {% else %} 506 | {% for name in ([ item.name ] if item.name is string else item.name) %} 507 | server_name {{ name }}; 508 | {% endfor %} 509 | 510 | {% endif %} 511 | {% endif %} 512 | {{ print_root() }} 513 | {% if nginx_tpl_acme | bool %} 514 | include snippets/acme-challenge.conf; 515 | 516 | {% endif %} 517 | {% endif %} 518 | {% if nginx_tpl_ssl %} 519 | {% if item.listen|d(True) %} 520 | {% if not (item.redirect_to_ssl|d(True) | bool) %} 521 | {{ print_shared_nginx_server_block() }} 522 | {% else %} 523 | location / { 524 | return {{ item.redirect_code_ssl | default(nginx_tpl_default_redirect_code_ssl) }} {{ nginx_tpl_http_redirect }}; 525 | } 526 | {% endif %} 527 | 528 | } 529 | 530 | server { 531 | 532 | {% endif %} 533 | {% for port in nginx_tpl_listen_ssl %} 534 | listen {{ port }} ssl{% if nginx_version | version_compare('1.9.5','>=') %} http2{% elif nginx_version | version_compare('1.4','>=') %} spdy{% endif %}{% if nginx_tpl_default_server_ssl %} {{ nginx_tpl_default_server_ssl | join(" ") }}{% endif %}{% if (loop.first and nginx_tpl_ipv6only_ssl) %} {{ nginx_tpl_ipv6only_ssl | join(" ") }}{% endif %}; 535 | {% endfor %} 536 | 537 | ssl_certificate {{ nginx_tpl_ssl_certificate }}; 538 | ssl_certificate_key {{ nginx_tpl_ssl_certificate_key }}; 539 | ssl_protocols {{ (item.tls_protocols | d(nginx_default_tls_protocols)) | join(" ") }}; 540 | ssl_prefer_server_ciphers on; 541 | ssl_ciphers "{{ nginx_ssl_ciphers[item.ssl_ciphers | default(nginx_default_ssl_ciphers)] }}"; # TLS cipher suites set: {{ item.ssl_ciphers | default(nginx_default_ssl_ciphers) }} 542 | {% if nginx_tpl_ssl_dhparam %} 543 | ssl_dhparam {{ nginx_tpl_ssl_dhparam }}; 544 | {% endif %} 545 | {% if item.ssl_curve | default(nginx_default_ssl_curve) %} 546 | ssl_ecdh_curve {{ item.ssl_curve | default(nginx_default_ssl_curve) }}; 547 | {% endif %} 548 | {% if nginx_version | version_compare('1.4','>=') and (item.ocsp | d(nginx_ocsp)) | bool %} 549 | ssl_stapling on; 550 | {% if (item.ocsp_verify | d(nginx_ocsp_verify)) | bool %} 551 | ssl_stapling_verify on; 552 | ssl_trusted_certificate {{ nginx_tpl_ssl_trusted_certificate }}; 553 | {% endif %} 554 | resolver {{ (item.ocsp_resolvers | d(nginx_ocsp_resolvers)) | ipwrap | join(" ") }} valid=300s; 555 | resolver_timeout 5s; 556 | {% endif %} 557 | {% if (item.ssl_verify_client | d(nginx_default_ssl_verify_client)) | bool %} 558 | {% if item.ssl_client_certificate | d(nginx_default_ssl_client_certificate) %} 559 | ssl_verify_client on; 560 | ssl_client_certificate {{ item.ssl_client_certificate | d(nginx_default_ssl_client_certificate) }}; 561 | {% endif %} 562 | {% if item.ssl_crl | d(nginx_default_ssl_crl) %} 563 | ssl_crl {{ item.ssl_crl | d(nginx_default_ssl_crl) }}; 564 | {% endif %} 565 | {% endif %} 566 | {% if item.hsts_enabled|d(True) | bool %} 567 | add_header Strict-Transport-Security "max-age={{ nginx_hsts_age }}{{ "; includeSubDomains" if nginx_hsts_subdomains|bool else "" }}{{ "; preload" if ((item.hsts_preload | d(nginx_hsts_preload)) | bool) else "" }}"; 568 | {% endif %} 569 | {% if item.csp_enabled|d(False) | bool %} 570 | add_header Content-Security-Policy "{{ item.csp|d("default-src https: ;") + (" " + item.csp_append|d(nginx__http_csp_append) if (item.csp_append|d(nginx__http_csp_append)) else "") }}"; 571 | {% endif %} 572 | {% if item.csp_report_enabled|d(False) | bool %} 573 | add_header Content-Security-Policy-Report-Only "{{ item.csp_report|d(item.csp|d("default-src https: ;")) + (" " + item.csp_append|d(nginx__http_csp_append) if (item.csp_append|d(nginx__http_csp_append)) else "") }}"; 574 | {% endif %} 575 | {% if item.content_type_options|d(True) != omit %} 576 | add_header X-Content-Type-Options "{{ item.content_type_options | d('nosniff') }}"{% if nginx_version | version_compare('1.7.5','>=') %} always{% endif %}; 577 | {% endif %} 578 | {% if item.frame_options|d() != omit %} 579 | add_header X-Frame-Options "{{ item.frame_options | d('SAMEORIGIN') }}"{% if nginx_version | version_compare('1.7.5','>=') %} always{% endif %}; 580 | {% endif %} 581 | {% if item.xss_protection | d(nginx__http_xss_protection) != omit %} 582 | add_header X-XSS-Protection "{{ item.xss_protection | d(nginx__http_xss_protection) }}"; 583 | {% endif %} 584 | {% if item.http_referrer_policy | d(nginx__http_referrer_policy) != omit %} 585 | add_header Referrer-Policy "{{ item.http_referrer_policy | d(nginx__http_referrer_policy) }}"; 586 | {% endif %} 587 | {% for robots_tag in nginx_tpl_robots_tag %} 588 | add_header X-Robots-Tag "{{ robots_tag }}"; 589 | {% endfor %} 590 | {% if item.permitted_cross_domain_policies | d(nginx__http_permitted_cross_domain_policies) != omit %} 591 | add_header X-Permitted-Cross-Domain-Policies "{{ item.permitted_cross_domain_policies | d(nginx__http_permitted_cross_domain_policies) }}"; 592 | {% endif %} 593 | 594 | {% if item.name|d() %} 595 | {% if item.redirect_from|d() %} 596 | server_name {{ item.name if item.name is string else item.name[0] }}; 597 | 598 | {% elif item.redirect_to|d() %} 599 | server_name {{ item.redirect_to }}; 600 | 601 | {% else %} 602 | {% for name in ([ item.name ] if item.name is string else item.name) %} 603 | server_name {{ name }}; 604 | {% endfor %} 605 | 606 | {% endif %} 607 | {% endif %} 608 | {% endif %} 609 | {% if nginx_tpl_ssl | bool %} 610 | {{ print_root() }} 611 | {% if nginx_tpl_acme | bool %} 612 | include snippets/acme-challenge.conf; 613 | 614 | {% endif %} 615 | {% endif %} 616 | {# ---- end of nginx_tpl_ssl ---- #} 617 | {{ print_shared_nginx_server_block() }} 618 | } 619 | -------------------------------------------------------------------------------- /templates/etc/nginx/sites-available/php.conf.j2: -------------------------------------------------------------------------------- 1 | {% extends "default.conf.j2" %} 2 | {# 3 | # 4 | # ---- nginx server template for PHP websites ---- 5 | # 6 | # Refer to the documentation in /docs/defaults-detailed.rst 7 | # 8 | #} 9 | {% block nginx_tpl_block_index %} 10 | index {{ item.index | default('index.html index.htm index.php') }}; 11 | 12 | {% endblock %} 13 | {% block nginx_tpl_block_location_root %} 14 | try_files $uri $uri/ $uri.php; 15 | {% endblock %} 16 | {% block nginx_tpl_block_custom_status_locations %} 17 | {% if item.php_status|d()|bool %} 18 | location ~ ^/({{ item.php_status_name | d('php_status') }}|{{ item.php_ping_name | d('php_ping') }})$ { 19 | access_log off; 20 | {% set nginx__tpl_status_allow = [] %} 21 | {% if nginx_status_localhost %} 22 | {% for address in nginx_status_localhost | unique %} 23 | {% set _ = nginx__tpl_status_allow.append(address) %} 24 | {% endfor %} 25 | {% endif %} 26 | {% if nginx_status %} 27 | {% for address in nginx_status | unique %} 28 | {% set _ = nginx__tpl_status_allow.append(address) %} 29 | {% endfor %} 30 | {% endif %} 31 | {% if item.php_status_allow|d() %} 32 | {% for address in ([ item.php_status_allow ] if item.php_status_allow is string else item.php_status_allow) %} 33 | {% set _ = nginx__tpl_status_allow.append(address) %} 34 | {% endfor %} 35 | {% endif %} 36 | {% for address in nginx__tpl_status_allow | sort | unique %} 37 | allow {{ address }}; 38 | {% endfor %} 39 | deny all; 40 | include fastcgi_params; 41 | fastcgi_index index.php; 42 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 43 | fastcgi_pass {{ item.php_upstream }}; 44 | } 45 | 46 | {% endif %} 47 | {% endblock %} 48 | {% block nginx_tpl_block_custom_locations %} 49 | {% if item.php_upstream|d() %} 50 | 51 | location ~ ^(?!.+\.php/)(?.+\.php)$ { 52 | {% if item.php_limit_except is undefined %} 53 | limit_except GET HEAD POST { deny all; } 54 | 55 | {% elif item.php_limit_except|d() %} 56 | limit_except {{ item.php_limit_except if item.php_limit_except is string else item.php_limit_except | join(' ') }} { deny all; } 57 | 58 | {% endif %} 59 | try_files $script_name =404; 60 | 61 | {% if item.php_include|d(True) %} 62 | {% if (nginx_version|d() and nginx_version | version_compare('1.6.1','<')) %} 63 | include {{ item.php_include | d('fastcgi_params') }}; 64 | {% else %} 65 | {% if nginx_flavor in [ 'passenger', 'nginx.org' ] %} 66 | include {{ item.php_include | d('fastcgi_params') }}; 67 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 68 | {% else %} 69 | include {{ item.php_include | d('fastcgi.conf') }}; 70 | {% endif %} 71 | {% endif %} 72 | 73 | {% endif %} 74 | {% if item.php_options|d() %} 75 | {{ item.php_options | indent(16) | regex_replace("(?m)^\s*$", "") }} 76 | 77 | {% endif %} 78 | # Mitigate HTTPOXY attacks (https://httpoxy.org/) 79 | fastcgi_param HTTP_PROXY ""; 80 | 81 | fastcgi_index index.php; 82 | fastcgi_pass {{ item.php_upstream }}; 83 | } 84 | 85 | location ~ ^(?.+\.php)(?/.*)$ { 86 | {% if item.php_limit_except is undefined %} 87 | limit_except GET HEAD POST { deny all; } 88 | 89 | {% elif item.php_limit_except|d() %} 90 | limit_except {{ item.php_limit_except if item.php_limit_except is string else item.php_limit_except | join(' ') }} { deny all; } 91 | 92 | {% endif %} 93 | try_files $script_name =404; 94 | 95 | {% if (item.php_include is undefined or item.php_include|d()) %} 96 | include {{ item.php_include | default('fastcgi_params') }}; 97 | {% if (item.php_include is undefined) %} 98 | fastcgi_param SCRIPT_FILENAME $document_root$script_name; 99 | fastcgi_param PATH_INFO $path_info; 100 | {# This option doesn't work yet... #} 101 | #fastcgi_param PATH_TRANSLATED $document_root$path_info; 102 | 103 | {% endif %} 104 | {% endif %} 105 | {% if item.php_options|d() %} 106 | {{ item.php_options | indent(16) | regex_replace("(?m)^\s*$", "") }} 107 | 108 | {% endif %} 109 | # Mitigate HTTPOXY attacks (https://httpoxy.org/) 110 | fastcgi_param HTTP_PROXY ""; 111 | 112 | fastcgi_index index.php; 113 | fastcgi_pass {{ item.php_upstream }}; 114 | } 115 | 116 | {% else %} 117 | location ~ \.php$ { 118 | return 403; 119 | } 120 | {% endif %} 121 | {% endblock %} 122 | -------------------------------------------------------------------------------- /templates/etc/nginx/sites-available/php5.conf.j2: -------------------------------------------------------------------------------- 1 | {% extends "default.conf.j2" %} 2 | {# 3 | # 4 | # ---- nginx server template for PHP5 websites ---- 5 | # 6 | # This template is deprecated. 7 | # Refer to the documentation in /docs/defaults-detailed.rst for alternatives. 8 | # 9 | # List of parameters supported by this template: 10 | # 11 | # - item.php5: '' 12 | # Name of nginx upstream to use. 13 | # 14 | # If undefined, '.php' files will be protected by =403. 15 | # 16 | # 17 | # - item.index: '' 18 | # String of index files to enable. 19 | # 20 | # If undefined, add 'index.php' at the end of list of index files. 21 | # 22 | # 23 | # - item.php5_limit_except: '' or False 24 | # String of methods to allow for all hosts. 25 | # 26 | # If undefined, default limits will be applied (block all requests execpt 27 | # GET, HEAD and POST). 28 | # 29 | # If set to False, limits are disabled. 30 | # 31 | # More information: 32 | # https://nginx.org/en/docs/http/ngx_http_core_module.html#limit_except 33 | # 34 | # 35 | # - item.php5_include: '' 36 | # File to include instead of 'fastcgi_params', relative to /etc/nginx/ 37 | # 38 | # If set to False, nothing is included. 39 | # 40 | # 41 | # - item.php5_options: | 42 | # Additional options to append to php location, in text block format. 43 | # 44 | # 45 | # - item.php5_status: [] 46 | # Optional. Enable php5 fpm server status page and allow access from list of 47 | # IP addresses or CIDR ranges. 48 | # 49 | # 50 | # - item.php5_status_name: '' 51 | # Optional. Set the name of the location which should be used for php5 fpm 52 | # status page 53 | # 54 | # 55 | # - item.php5_ping_name: '' 56 | # Optional. Set the name of the location which should be used for php5 fpm 57 | # ping page 58 | # 59 | # 60 | #} 61 | {% block nginx_tpl_block_index %} 62 | index {{ item.index | default('index.html index.htm index.php') }}; 63 | 64 | {% endblock %} 65 | {% block nginx_tpl_block_location_root %} 66 | try_files $uri $uri/ $uri.php; 67 | {% endblock %} 68 | {% block nginx_tpl_block_custom_status_locations %} 69 | {% if item.php5_status|d(nginx_php5_status) and (nginx_status or nginx_status_localhost) %} 70 | location ~ ^/({{ item.php5_status_name | default(nginx_php5_status_name) }}|{{ item.php5_ping_name | default(nginx_php5_ping_name) }})$ { 71 | access_log off; 72 | {% if nginx_status_localhost %} 73 | {% for address in nginx_status_localhost | unique %} 74 | allow {{ address }}; 75 | {% endfor %} 76 | {% endif %} 77 | {% if nginx_status %} 78 | {% for address in nginx_status | unique %} 79 | allow {{ address }}; 80 | {% endfor %} 81 | {% endif %} 82 | {% if item.php5_status is defined and item.php5_status %} 83 | {% for address in item.php5_status | unique %} 84 | allow {{ address }}; 85 | {% endfor %} 86 | {% endif %} 87 | deny all; 88 | include fastcgi_params; 89 | fastcgi_index index.php; 90 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 91 | fastcgi_pass {{ item.php5 }}; 92 | } 93 | 94 | {% endif %} 95 | {% endblock %} 96 | {% block nginx_tpl_block_custom_locations %} 97 | {% if item.php5 is defined and item.php5 %} 98 | location ~ ^(?!.+\.php/)(?.+\.php)$ { 99 | {% if (item.php5_limit_except is undefined or (item.php5_limit_except is defined and item.php5_limit_except)) %} 100 | limit_except {{ item.php5_limit_except | default('GET HEAD POST') }} { deny all; } 101 | 102 | {% endif %} 103 | try_files $script_name =404; 104 | 105 | {% if (item.php5_include is undefined or (item.php5_include is defined and item.php5_include)) %} 106 | {% if (nginx_version is defined and nginx_version | version_compare('1.6.1','<')) %} 107 | include {{ item.php5_include | default('fastcgi_params') }}; 108 | {% else %} 109 | {% if nginx_flavor in [ 'passenger', 'nginx.org' ] %} 110 | include {{ item.php5_include | default('fastcgi_params') }}; 111 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 112 | {% else %} 113 | include {{ item.php5_include | default('fastcgi.conf') }}; 114 | {% endif %} 115 | {% endif %} 116 | 117 | {% endif %} 118 | {% if item.php5_options is defined and item.php5_options %} 119 | {{ item.php5_options | indent(16) | regex_replace("(?m)^\s*$", "") }} 120 | 121 | {% endif %} 122 | # Mitigate HTTPOXY attacks (https://httpoxy.org/) 123 | fastcgi_param HTTP_PROXY ""; 124 | 125 | fastcgi_index index.php; 126 | fastcgi_pass {{ item.php5 }}; 127 | } 128 | 129 | location ~ ^(?.+\.php)(?/.*)$ { 130 | {% if (item.php5_limit_except is undefined or (item.php5_limit_except is defined and item.php5_limit_except)) %} 131 | limit_except {{ item.php5_limit_except | default('GET HEAD POST') }} { deny all; } 132 | 133 | {% endif %} 134 | try_files $script_name =404; 135 | 136 | {% if (item.php5_include is undefined or (item.php5_include is defined and item.php5_include)) %} 137 | include {{ item.php5_include | default('fastcgi_params') }}; 138 | {% if (item.php5_include is undefined) %} 139 | fastcgi_param SCRIPT_FILENAME $document_root$script_name; 140 | fastcgi_param PATH_INFO $path_info; 141 | {# This option doesn't work yet... #} 142 | #fastcgi_param PATH_TRANSLATED $document_root$path_info; 143 | 144 | {% endif %} 145 | {% endif %} 146 | {% if item.php5_options is defined and item.php5_options %} 147 | {{ item.php5_options | indent(16) | regex_replace("(?m)^\s*$", "") }} 148 | 149 | {% endif %} 150 | # Mitigate HTTPOXY attacks (https://httpoxy.org/) 151 | fastcgi_param HTTP_PROXY ""; 152 | 153 | fastcgi_index index.php; 154 | fastcgi_pass {{ item.php5 }}; 155 | } 156 | 157 | {% else %} 158 | location ~ \.php$ { 159 | return 403; 160 | } 161 | {% endif %} 162 | {% endblock %} 163 | -------------------------------------------------------------------------------- /templates/etc/nginx/sites-available/proxy.conf.j2: -------------------------------------------------------------------------------- 1 | {% extends "default.conf.j2" %} 2 | {% block nginx_tpl_block_index %} 3 | {% endblock %} 4 | {% block nginx_tpl_block_location %} 5 | {% if (item.location is defined and item.location) or (item.location_list is defined and item.location_list) %} 6 | {% if item.location_list is defined and item.location_list %} 7 | {{ print_location_list(item.location_list) }} 8 | {% endif %} 9 | {% if item.location is defined and item.location %} 10 | {{ print_location(item.location, item.location_allow, item.location_referers, item.location_deny) }} 11 | {% endif %} 12 | {% else %} 13 | location {{ item.proxy_location if item.proxy_location|d() else "/" }} { 14 | {% if item.proxy_headers is undefined or item.proxy_headers | bool %} 15 | proxy_set_header Host $host; 16 | proxy_set_header X-Real-IP $remote_addr; 17 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 18 | {% if not (item.redirect_to_ssl|d(True) | bool) %} 19 | set $X_Forwarded_Proto "http"; 20 | set $X_Forwarded_Port 80; 21 | if ($https) { 22 | set $X_Forwarded_Proto "https"; 23 | set $X_Forwarded_Port 443; 24 | } 25 | proxy_set_header X-Forwarded-Proto $X_Forwarded_Proto; 26 | proxy_set_header X-Forwarded-Port $X_Forwarded_Port; 27 | {% else %} 28 | proxy_set_header X-Forwarded-Proto $scheme; 29 | proxy_set_header X-Forwarded-Port {{ "443" if (nginx_tpl_ssl|d() and nginx_tpl_ssl | bool) else "80" }}; 30 | {% endif %} 31 | {% elif item.proxy_headers|d() %} 32 | {{ item.proxy_headers | indent(16, true) | regex_replace("(?m)^\s*$", "") }} 33 | {% endif %} 34 | {% if item.proxy_options|d() and item.proxy_options %} 35 | {{ item.proxy_options | indent(16, true) | regex_replace("(?m)^\s*$", "") }} 36 | {% endif %} 37 | proxy_pass {{ item.proxy_pass }}; 38 | } 39 | 40 | {% endif %} 41 | {% endblock %} 42 | -------------------------------------------------------------------------------- /templates/etc/nginx/sites-available/rails.conf.j2: -------------------------------------------------------------------------------- 1 | {% extends "default.conf.j2" %} 2 | {% block nginx_tpl_block_location %} 3 | 4 | {% if (item.location is defined and item.location) or (item.location_list is defined and item.location_list) %} 5 | {% if item.location_list is defined and item.location_list %} 6 | {{ print_location_list(item.location_list) }} 7 | {% endif %} 8 | {% if item.location is defined and item.location %} 9 | {{ print_location(item.location, item.location_allow, item.location_referers, item.location_deny) }} 10 | {% endif %} 11 | {% else %} 12 | location / { 13 | {% block nginx_tpl_block_location_root %} 14 | try_files {{ item.try_files|default(nginx_default_try_files) | join(' ') }} =404; 15 | {% endblock %} 16 | } 17 | {% endif %} 18 | 19 | {% if nginx_flavor == 'passenger' %} 20 | passenger_enabled on; 21 | {% if item.passenger_ruby is defined and item.passenger_ruby %} 22 | passenger_ruby {{ item.passenger_ruby }}; 23 | {% endif %} 24 | {% if item.passenger_app_env is defined and item.passenger_app_env %} 25 | passenger_app_env {{ item.passenger_app_env }}; 26 | {% endif %} 27 | {% if item.passenger_user is defined and item.passenger_user %} 28 | passenger_user {{ item.passenger_user }}; 29 | {% if item.passenger_group is defined and item.passenger_group %} 30 | passenger_group {{ item.passenger_group }}; 31 | {% else %} 32 | passenger_group {{ item.passenger_user }}; 33 | {% endif %} 34 | {% endif %} 35 | passenger_min_instances {{ item.passenger_min_instances | default(nginx_passenger_default_min_instances) }}; 36 | {% if item.passenger_options is defined and item.passenger_options %} 37 | {{ item.passenger_options | indent(8, true) | regex_replace("(?m)^\s*$", "") }} 38 | {% endif %} 39 | 40 | {% endif %} 41 | {% endblock %} 42 | -------------------------------------------------------------------------------- /templates/etc/nginx/snippets/acme-challenge.conf.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | 3 | # Automatic Certificate Management Environment (ACME) support. 4 | # https://tools.ietf.org/html/draft-ietf-acme-acme-01 5 | # https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment 6 | 7 | 8 | # Return the ACME challenge present in the server public root. 9 | # If not found, switch to global web server root. 10 | location ^~ /.well-known/acme-challenge/ { 11 | default_type "text/plain"; 12 | try_files $uri @well-known-acme-challenge; 13 | } 14 | 15 | # Return the ACME challenge present in the global server public root. 16 | # If not present, redirect request to a specified domain. 17 | location @well-known-acme-challenge { 18 | root {{ nginx_acme_root }}; 19 | default_type "text/plain"; 20 | try_files $uri @redirect-acme-challenge; 21 | } 22 | 23 | {% if nginx_acme_domain %} 24 | # Redirect the ACME challenge to a different host. If a redirect loop is 25 | # detected, return 404. 26 | location @redirect-acme-challenge { 27 | if ($arg_redirect) { 28 | return 404; 29 | } 30 | return 307 $scheme://{{ nginx_acme_domain }}$request_uri?redirect=yes; 31 | } 32 | 33 | {% endif %} 34 | # Return 404 if ACME challenge well known path is accessed directly. 35 | location = /.well-known/acme-challenge/ { 36 | return 404; 37 | } 38 | -------------------------------------------------------------------------------- /templates/etc/pki/hooks/nginx.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # {{ ansible_managed }} 4 | 5 | # Reload or restart nginx on a certificate state change 6 | 7 | set -o nounset -o pipefail -o errexit 8 | 9 | nginx_config="/etc/nginx/nginx.conf" 10 | nginx_sites="/etc/nginx/sites-enabled" 11 | nginx_action="{{ nginx_pki_hook_action }}" 12 | 13 | # Check if current PKI realm is used by the 'nginx' webserver 14 | certificate=$(grep -r "${PKI_SCRIPT_DEFAULT_CRT:-}" ${nginx_sites}/* || true) 15 | 16 | # Get list of current realm states 17 | states=( $(echo "${PKI_SCRIPT_STATE:-}" | tr "," " ") ) 18 | 19 | if [ -n "${certificate}" -a "${{ '{#' }}states[@]}" -gt 0 ] ; then 20 | 21 | for state in "${states[@]}" ; do 22 | 23 | if [ "${state}" = "changed-certificate" -o "${state}" = "changed-dhparam" ] ; then 24 | 25 | # Check if current init is systemd 26 | if $(pidof systemd > /dev/null 2>&1) ; then 27 | 28 | nginx_state="$(systemctl is-active nginx.service)" 29 | if [ ${nginx_state} = "active" ] ; then 30 | if $(/usr/sbin/nginx -c ${nginx_config} -t > /dev/null 2>&1) ; then 31 | systemctl ${nginx_action} nginx.service 32 | fi 33 | fi 34 | 35 | else 36 | 37 | nginx_pidfile="$(grep -E '^pid\s+' ${nginx_config} | awk '{print $2}' | cut -d\; -f1)" 38 | if $(kill -0 $(<${nginx_pidfile}) > /dev/null 2>&1) ; then 39 | if $(/usr/sbin/nginx -c ${nginx_config} -t > /dev/null 2>&1) ; then 40 | /usr/sbin/service nginx ${nginx_action} > /dev/null 41 | fi 42 | fi 43 | 44 | fi 45 | 46 | break 47 | fi 48 | 49 | done 50 | 51 | fi 52 | -------------------------------------------------------------------------------- /templates/etc/sudoers.d/nginx_webadmins.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | 3 | Cmnd_Alias NGINX_RELOAD = /etc/init.d/nginx reload 4 | 5 | %{{ nginx_privileged_group }} ALL = (root) NOPASSWD: NGINX_RELOAD 6 | 7 | 8 | -------------------------------------------------------------------------------- /templates/srv/www/sites/welcome/public/index.html.j2: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% set nginx_tpl_domain = item.welcome_domain | d(item.name if (item.name is string) else item.name[0] | d(ansible_domain)) %} 4 | {% if nginx_tpl_domain %} 5 | {% set nginx_tpl_welcome_title = '' + nginx_tpl_domain + '' %} 6 | {% else %} 7 | {% set nginx_tpl_welcome_title = 'CompanyName.website' %} 8 | {% endif %} 9 | 10 | 11 | 12 | 13 | 14 | {{ nginx_tpl_domain|d("CompanyName.website") }} 15 | {% if item.welcome_css|d(True) | bool %} 16 | 19 | 57 | {% endif %} 58 | 59 | 60 | 61 |
62 | 63 |

{{ nginx_tpl_welcome_title }}

64 | 65 | {% if nginx_tpl_domain %} 66 |

{{ item.welcome_status_choices|d([ '200 OK', "418 I'm a teapot" ]) | random }}

67 | {% elif not nginx_tpl_domain %} 68 |

If you're reading this, the web server was installed correctly.

69 | {% endif %} 70 | 71 |
72 | 73 | 74 | -------------------------------------------------------------------------------- /templates/srv/www/sites/welcome/public/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS and IE text size adjust after device orientation change, 6 | * without disabling user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 29 | * and Firefox. 30 | * Correct `block` display not defined for `main` in IE 11. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | hgroup, 41 | main, 42 | menu, 43 | nav, 44 | section, 45 | summary { 46 | display: block; 47 | } 48 | 49 | /** 50 | * 1. Correct `inline-block` display not defined in IE 8/9. 51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 52 | */ 53 | 54 | audio, 55 | canvas, 56 | progress, 57 | video { 58 | display: inline-block; /* 1 */ 59 | vertical-align: baseline; /* 2 */ 60 | } 61 | 62 | /** 63 | * Prevent modern browsers from displaying `audio` without controls. 64 | * Remove excess height in iOS 5 devices. 65 | */ 66 | 67 | audio:not([controls]) { 68 | display: none; 69 | height: 0; 70 | } 71 | 72 | /** 73 | * Address `[hidden]` styling not present in IE 8/9/10. 74 | * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22. 75 | */ 76 | 77 | [hidden], 78 | template { 79 | display: none; 80 | } 81 | 82 | /* Links 83 | ========================================================================== */ 84 | 85 | /** 86 | * Remove the gray background color from active links in IE 10. 87 | */ 88 | 89 | a { 90 | background-color: transparent; 91 | } 92 | 93 | /** 94 | * Improve readability of focused elements when they are also in an 95 | * active/hover state. 96 | */ 97 | 98 | a:active, 99 | a:hover { 100 | outline: 0; 101 | } 102 | 103 | /* Text-level semantics 104 | ========================================================================== */ 105 | 106 | /** 107 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 108 | */ 109 | 110 | abbr[title] { 111 | border-bottom: 1px dotted; 112 | } 113 | 114 | /** 115 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 116 | */ 117 | 118 | b, 119 | strong { 120 | font-weight: bold; 121 | } 122 | 123 | /** 124 | * Address styling not present in Safari and Chrome. 125 | */ 126 | 127 | dfn { 128 | font-style: italic; 129 | } 130 | 131 | /** 132 | * Address variable `h1` font-size and margin within `section` and `article` 133 | * contexts in Firefox 4+, Safari, and Chrome. 134 | */ 135 | 136 | h1 { 137 | font-size: 2em; 138 | margin: 0.67em 0; 139 | } 140 | 141 | /** 142 | * Address styling not present in IE 8/9. 143 | */ 144 | 145 | mark { 146 | background: #ff0; 147 | color: #000; 148 | } 149 | 150 | /** 151 | * Address inconsistent and variable font size in all browsers. 152 | */ 153 | 154 | small { 155 | font-size: 80%; 156 | } 157 | 158 | /** 159 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 160 | */ 161 | 162 | sub, 163 | sup { 164 | font-size: 75%; 165 | line-height: 0; 166 | position: relative; 167 | vertical-align: baseline; 168 | } 169 | 170 | sup { 171 | top: -0.5em; 172 | } 173 | 174 | sub { 175 | bottom: -0.25em; 176 | } 177 | 178 | /* Embedded content 179 | ========================================================================== */ 180 | 181 | /** 182 | * Remove border when inside `a` element in IE 8/9/10. 183 | */ 184 | 185 | img { 186 | border: 0; 187 | } 188 | 189 | /** 190 | * Correct overflow not hidden in IE 9/10/11. 191 | */ 192 | 193 | svg:not(:root) { 194 | overflow: hidden; 195 | } 196 | 197 | /* Grouping content 198 | ========================================================================== */ 199 | 200 | /** 201 | * Address margin not present in IE 8/9 and Safari. 202 | */ 203 | 204 | figure { 205 | margin: 1em 40px; 206 | } 207 | 208 | /** 209 | * Address differences between Firefox and other browsers. 210 | */ 211 | 212 | hr { 213 | box-sizing: content-box; 214 | height: 0; 215 | } 216 | 217 | /** 218 | * Contain overflow in all browsers. 219 | */ 220 | 221 | pre { 222 | overflow: auto; 223 | } 224 | 225 | /** 226 | * Address odd `em`-unit font size rendering in all browsers. 227 | */ 228 | 229 | code, 230 | kbd, 231 | pre, 232 | samp { 233 | font-family: monospace, monospace; 234 | font-size: 1em; 235 | } 236 | 237 | /* Forms 238 | ========================================================================== */ 239 | 240 | /** 241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 242 | * styling of `select`, unless a `border` property is set. 243 | */ 244 | 245 | /** 246 | * 1. Correct color not being inherited. 247 | * Known issue: affects color of disabled elements. 248 | * 2. Correct font properties not being inherited. 249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | color: inherit; /* 1 */ 258 | font: inherit; /* 2 */ 259 | margin: 0; /* 3 */ 260 | } 261 | 262 | /** 263 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 264 | */ 265 | 266 | button { 267 | overflow: visible; 268 | } 269 | 270 | /** 271 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 272 | * All other form control elements do not inherit `text-transform` values. 273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 274 | * Correct `select` style inheritance in Firefox. 275 | */ 276 | 277 | button, 278 | select { 279 | text-transform: none; 280 | } 281 | 282 | /** 283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 284 | * and `video` controls. 285 | * 2. Correct inability to style clickable `input` types in iOS. 286 | * 3. Improve usability and consistency of cursor style between image-type 287 | * `input` and others. 288 | */ 289 | 290 | button, 291 | html input[type="button"], /* 1 */ 292 | input[type="reset"], 293 | input[type="submit"] { 294 | -webkit-appearance: button; /* 2 */ 295 | cursor: pointer; /* 3 */ 296 | } 297 | 298 | /** 299 | * Re-set default cursor for disabled elements. 300 | */ 301 | 302 | button[disabled], 303 | html input[disabled] { 304 | cursor: default; 305 | } 306 | 307 | /** 308 | * Remove inner padding and border in Firefox 4+. 309 | */ 310 | 311 | button::-moz-focus-inner, 312 | input::-moz-focus-inner { 313 | border: 0; 314 | padding: 0; 315 | } 316 | 317 | /** 318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 319 | * the UA stylesheet. 320 | */ 321 | 322 | input { 323 | line-height: normal; 324 | } 325 | 326 | /** 327 | * It's recommended that you don't attempt to style these elements. 328 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 329 | * 330 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 331 | * 2. Remove excess padding in IE 8/9/10. 332 | */ 333 | 334 | input[type="checkbox"], 335 | input[type="radio"] { 336 | box-sizing: border-box; /* 1 */ 337 | padding: 0; /* 2 */ 338 | } 339 | 340 | /** 341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 342 | * `font-size` values of the `input`, it causes the cursor style of the 343 | * decrement button to change from `default` to `text`. 344 | */ 345 | 346 | input[type="number"]::-webkit-inner-spin-button, 347 | input[type="number"]::-webkit-outer-spin-button { 348 | height: auto; 349 | } 350 | 351 | /** 352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome. 354 | */ 355 | 356 | input[type="search"] { 357 | -webkit-appearance: textfield; /* 1 */ 358 | box-sizing: content-box; /* 2 */ 359 | } 360 | 361 | /** 362 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 363 | * Safari (but not Chrome) clips the cancel button when the search input has 364 | * padding (and `textfield` appearance). 365 | */ 366 | 367 | input[type="search"]::-webkit-search-cancel-button, 368 | input[type="search"]::-webkit-search-decoration { 369 | -webkit-appearance: none; 370 | } 371 | 372 | /** 373 | * Define consistent border, margin, and padding. 374 | */ 375 | 376 | fieldset { 377 | border: 1px solid #c0c0c0; 378 | margin: 0 2px; 379 | padding: 0.35em 0.625em 0.75em; 380 | } 381 | 382 | /** 383 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 384 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 385 | */ 386 | 387 | legend { 388 | border: 0; /* 1 */ 389 | padding: 0; /* 2 */ 390 | } 391 | 392 | /** 393 | * Remove default vertical scrollbar in IE 8/9/10/11. 394 | */ 395 | 396 | textarea { 397 | overflow: auto; 398 | } 399 | 400 | /** 401 | * Don't inherit the `font-weight` (applied by a rule above). 402 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 403 | */ 404 | 405 | optgroup { 406 | font-weight: bold; 407 | } 408 | 409 | /* Tables 410 | ========================================================================== */ 411 | 412 | /** 413 | * Remove most spacing between table cells. 414 | */ 415 | 416 | table { 417 | border-collapse: collapse; 418 | border-spacing: 0; 419 | } 420 | 421 | td, 422 | th { 423 | padding: 0; 424 | } 425 | --------------------------------------------------------------------------------