Git Product home page Git Product logo

ansible-role-nginx-config's Introduction

Ansible Galaxy Molecule CI/CD License Project Status: Active โ€“ The project has reached a stable, usable state and is being actively developed. Community Support

๐Ÿ‘พ Help make the NGINX config Ansible role better by participating in our survey! ๐Ÿ‘พ

Ansible NGINX Configuration Role

This role configures NGINX Open Source and NGINX Plus on your target host.

Note: This role is still in active development. There may be unidentified issues and the role variables may change as development continues.

Requirements

Ansible

  • This role is developed and tested with maintained versions of Ansible core (above 2.12).

  • When using Ansible core, you will also need to install the following collections:

    ---
    collections:
      - name: ansible.posix
        version: 1.5.4
      - name: community.general
        version: 7.1.0
      - name: community.docker # Only required if you plan to use Molecule (see below)
        version: 3.4.7

    Note: You can alternatively install the Ansible community distribution (what is known as the "old" Ansible) if you don't want to manage individual collections.

  • Instructions on how to install Ansible can be found in the Ansible website.

Jinja2

  • This role uses Jinja2 templates. Ansible core installs Jinja2 by default, but depending on your install and/or upgrade path, you might be running an outdated version of Jinja2. The minimum version of Jinja2 required for the role to properly function is 3.1.
  • Instructions on how to install Jinja2 can be found in the Jinja2 website.

Molecule (Optional)

  • Molecule is used to test the various functionalities of the role. The recommended version of Molecule to test this role is 4.x.

  • Instructions on how to install Molecule can be found in the Molecule website. You will also need to install the Molecule Docker driver.

  • To run the NGINX Plus/App Protect config Molecule tests, you must copy your NGINX Plus/App Protect license to the role's files/license directory.

    You can alternatively add your NGINX Plus/App Protect repository certificate and key to the local environment. Run the following commands to export these files as base64-encoded variables and execute the Molecule tests:

    export NGINX_CRT=$( cat <path to your certificate file> | base64 )
    export NGINX_KEY=$( cat <path to your key file> | base64 )
    molecule test -s plus

Installation

This role can be installed via either Ansible Galaxy (the Ansible community marketplace) or by cloning this repo. Once installed, you will need to include the role it in your Ansible playbook using the roles keyword, the import_role module, or the include_role module.

Ansible Galaxy

To install the latest stable release of the role on your system, use:

ansible-galaxy install nginxinc.nginx_config

Alternatively, if you have already installed the role, you can update the role to the latest release by using:

ansible-galaxy install -f nginxinc.nginx_config

To use the role, include the following task in your playbook:

- name: Configure NGINX
  ansible.builtin.include_role:
    name: nginxinc.nginx_config

Git

To pull the latest edge commit of the role from GitHub, use:

git clone https://github.com/nginxinc/ansible-role-nginx-config.git

To use the role, include the following task in your playbook:

- name: Configure NGINX
  ansible.builtin.include_role:
    name: <path/to/repo> # e.g. <roles/ansible-role-nginx-config> if you clone the repo inside your project's roles directory

Platforms

The NGINX config Ansible role supports all platforms supported by NGINX Open Source and NGINX Plus.

Note: You should be able to use this role to configure any NGINX installation -- wherever/however it's been installed -- at your own risk. Any potential bugs with the role involving unsupported installation methods/platforms will be addressed in a best effort manner and might be outright dismissed.

Role Variables

This role has multiple variables. The descriptions and defaults for all these variables can be found in the defaults/main/ directory in the following files:

Name Description
main.yml NGINX simple config variables
selinux.yml Set up SELinux to allow the necessary connections to your NGINX setup
template.yml NGINX config template variables
upload.yml NGINX config/HTML/SSL upload variables

Example Playbooks

Working functional playbook examples can be found in the molecule/ directory in the following files:

Name Description
api/converge.yml Configure the NGINX Plus API and live metrics dashboard
cleanup_config/converge.yml Cleanup an NGINX config
complete/converge.yml Test all NGINX directives are correctly templated
complete_plus/converge.yml Test all NGINX Plus specific directives are correctly templated
default/converge.yml Configure NGINX with a config as close as possible to the default config
push_config/converge.yml Push a preexisting NGINX config from your system to your NGINX instance
reverse_proxy/converge.yml Configure NGINX as a reverse proxy between two web servers
stub_status/converge.yml Configure the NGINX Open Source stub status metrics
web_server/converge.yml Configure NGINX as a web server

Note: If you install this repository via Ansible Galaxy, you will need to replace the include_role variable in the example playbooks from ansible-role-nginx-config to nginxinc.nginx_config.

Other NGINX Ansible Collections and Roles

You can find the Ansible NGINX Core collection of roles to install and configure NGINX Open Source, NGINX Plus, and NGINX App Protect here.

You can find the Ansible NGINX role to install NGINX OSS and NGINX Plus here.

You can find the Ansible NGINX App Protect role to install and configure NGINX App Protect WAF and NGINX App Protect DoS here.

You can find the Ansible NGINX Unit role to install NGINX Unit here.

License

Apache License, Version 2.0

Author Information

Alessandro Fael Garcia

ยฉ F5, Inc. 2020 - 2024

ansible-role-nginx-config's People

Contributors

aknot242 avatar alessfg avatar brianclemens avatar cactushydrocodone avatar clwluvw avatar dependabot[bot] avatar dlouks avatar dudefellah avatar fpfuetsch avatar fr6nco avatar fschueller avatar hqwisen avatar igloo777 avatar jasafieddine avatar jcox10 avatar kutysam avatar moskovych avatar oxpa avatar rhombl4 avatar rolandjitsu avatar rotanid avatar ruslo avatar studiomax avatar tasooneasia avatar tcraxs avatar tugytur avatar tux-o-matic avatar uko avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ansible-role-nginx-config's Issues

[Question/Feature] template gzip on nginx.conf.j2

Is your feature request related to a problem? Please describe.
My problem was i wanted a way to enable gzip with some options on the default nginx.conf that this playbook creates but ididn't find such an option or something that i could do with the default nginx.conf except copy and add my own gzip headers/options.

Describe the solution you'd like
Adding a template option to enable gzip without creating a custom nginx.conf.j2 template.

Workaround
Creating my own nginx.conf that kinda has gzip enabled with some modifications.

Its not really a great deal but would ofcourse be a nice feature.
Here a bit of templated code i did really as an example

{% if nginx_config_main_template.http_settings.gzip %}
    gzip on;
{% if nginx_config_main_template.http_settings.gzip.vary %}
    gzip_vary on;
{% endif %}
{% if nginx_config_main_template.http_settings.gzip.min_length %}
    gzip_min_length {{ nginx_config_main_template.http_settings.gzip.min_length }};
{% endif %}
{% if nginx_config_main_template.http_settings.gzip.proxied is defined and nginx_config_main_template.http_settings.gzip.proxied | length %}
    gzip_proxied {{ nginx_config_main_template.http_settings.gzip.proxied }};
{% endif %}
{% if nginx_config_main_template.http_settings.gzip.types is defined and nginx_config_main_template.http_settings.gzip.types | length %}
    gzip_types {{ nginx_config_main_template.http_settings.gzip.types }};
{% endif %}
{% if nginx_config_main_template.http_settings.gzip.disable %}
    gzip_disable {{ nginx_config_main_template.http_settings.gzip.disable }}
{% endif %}
{% else %}
	#gzip on;
{% endif %}

proxy_read_timeout issue

Describe the bug

Based on templates/http/proxy.j2 read_timeout should be boolean. In that case, proxy_read_timeout is set as on/off. Seems like it doesn't make sense and the deployment fails.

To reproduce

Steps to reproduce the behavior:

  1. Set
    proxy:
    read_timeout: on
  2. Deploy NGINX Config role using playbook.yml
  3. View output
  4. See error nginx: [emerg] \โ€œproxy_read_timeout\โ€ directive invalid value in /etc/nginx/conf.d/default.conf

Expected behavior

Directive syntax: proxy_read_timeout time;

Your environment:

  • NGINX Config role version: main
  • Version of Ansible: 2.10.8
  • Target deployment platform: Amazon Linux 2

cleanup procedure is not cleaning up

Describe the bug

The code that should trigger a conf.d/*.conf cleanup is not cleaning up

To reproduce

Steps to reproduce the behavior:

nginx_config_cleanup     : '{{ my_cleanup }}'

    # this clean up only happens if the above variable is true
    nginx_config_cleanup_paths:
      - directory:
          - /etc/nginx/conf.d
        recurse: false
    nginx_config_cleanup_files:
      - /etc/nginx/conf.d/default.conf
TASK [nginxinc.nginx_config : Find NGINX config files] ************************************************************************************************************************************************************************************************************************
ok: [testing-snap05.MYDOMAIN.TLD] => (item={'directory': ['/etc/nginx/conf.d'], 'recurse': False}) => {"ansible_loop_var": "item", "changed": false, "examined": 13, "files": [{"atime": 1616512425.5879788, "ctime": 1616512425.0159645, "dev": 64780, "gid": 0, "gr_name": "root", "inode": 652034, "isblk": false, "ischr": false, "isdir": false, "isfifo": false, "isgid": false, "islnk": false, "isreg": true, "issock": false, "isuid": false, "mode": "0644", "mtime": 1616512424.6879563, "nlink": 1, "path": "/etc/nginx/conf.d/testing-snap05.staging.MYDOMAIN.TLDX.conf", "pw_name": "root", "rgrp": true, "roth": true, "rusr": true, "size": 508, "uid": 0, "wgrp": false, "woth": false, "wusr": true, "xgrp": false, "xoth": false, "xusr": false}, {"atime": 1616511920.0872648, "ctime": 1616511919.343246, "dev": 64780, "gid": 0, "gr_name": "root", "inode": 652037, "isblk": false, "ischr": false, "isdir": false, "isfifo": false, "isgid": false, "islnk": false, "isreg": true, "issock": false, "isuid": false, "mode": "0644", "mtime": 1616511918.9712367, "nlink": 1, "path": "/etc/nginx/conf.d/testing-snap05.staging.MYDOMAIN.TLD.conf", "pw_name": "root", "rgrp": true, "roth": true, "rusr": true, "size": 507, "uid": 0, "wgrp": false, "woth": false, "wusr": true, "xgrp": false, "xoth": false, "xusr": false}, {"atime": 1616512481.8013923, "ctime": 1616512481.229378, "dev": 64780, "gid": 0, "gr_name": "root", "inode": 652039, "isblk": false, "ischr": false, "isdir": false, "isfifo": false, "isgid": false, "islnk": false, "isreg": true, "issock": false, "isuid": false, "mode": "0644", "mtime": 1616512480.8773692, "nlink": 1, "path": "/etc/nginx/conf.d/testing-snap05.staging.MYDOMAIN.TLDZ.conf", "pw_name": "root", "rgrp": true, "roth": true, "rusr": true, "size": 508, "uid": 0, "wgrp": false, "woth": false, "wusr": true, "xgrp": false, "xoth": false, "xusr": false}, {"atime": 1616512450.800613, "ctime": 1616512450.1845973, "dev": 64780, "gid": 0, "gr_name": "root", "inode": 652038, "isblk": false, "ischr": false, "isdir": false, "isfifo": false, "isgid": false, "islnk": false, "isreg": true, "issock": false, "isuid": false, "mode": "0644", "mtime": 1616512449.8325884, "nlink": 1, "path": "/etc/nginx/conf.d/testing-snap05.staging.MYDOMAIN.TLDY.conf", "pw_name": "root", "rgrp": true, "roth": true, "rusr": true, "size": 508, "uid": 0, "wgrp": false, "woth": false, "wusr": true, "xgrp": false, "xoth": false, "xusr": false}], "item": {"directory": ["/etc/nginx/conf.d"], "recurse": false}, "matched": 4, "msg": ""}                                                                                                                                                                                          

TASK [nginxinc.nginx_config : Remove NGINX config files] **********************************************************************************************************************************************************************************************************************
ok: [testing-snap05.MYDOMAIN.TLD] => (item=/etc/nginx/conf.d/default.conf) => {"ansible_loop_var": "item", "changed": false, "item": "/etc/nginx/conf.d/default.conf", "path": "/etc/nginx/conf.d/default.conf", "state": "absent"}

Expected behavior

I expected /etc/nginx/conf.d/*.conf to be deleted before new flies are created. But only default.conf is deleted.

Your environment:

ansible-role-nginx-config from git
nginxinc.nginx, 0.19.1
ansible: 2.9.6+dfsg-1
ubuntu 20.04

Implements Jinja2 template inheritance

I want to propose a new way to reuse and maintain templates.

Jinja2 supports template inheritance, current templates could be split into small blocks and put them in a higher one with {% include '../small-part.j2'%}

For example, i create folder lib/upstream.block.j2 with:

{% if item.value.upstreams is defined and item.value.upstreams %}
{% for upstream in item.value.upstreams %}
upstream {{ item.value.upstreams[upstream].name }} {
{% if item.value.upstreams[upstream].lb_method is defined and item.value.upstreams[upstream].lb_method | length %}
    {{ item.value.upstreams[upstream].lb_method }};
{% endif %}
{% if item.value.upstreams[upstream].zone_name is defined and item.value.upstreams[upstream].zone_name %}
    zone {{ item.value.upstreams[upstream].zone_name }} {{ item.value.upstreams[upstream].zone_size }};
{% endif %}
{% for server in item.value.upstreams[upstream].servers %}
    server {{ item.value.upstreams[upstream].servers[server].address }}:{{ item.value.upstreams[upstream].servers[server].port }} weight={{ item.value.upstreams[upstream].servers[server].weight|default("1") }} {{ item.value.upstreams[upstream].servers[server].health_check|default("") }};
{% endfor %}
{% if item.value.upstreams[upstream].sticky_cookie %}
    sticky cookie srv_id expires=1h  path=/;
{% endif %}
{% if item.value.upstreams[upstream].custom_options is defined and item.value.upstreams[upstream].custom_options | length %}
{% for inline_option in item.value.upstreams[upstream].custom_options %}
    {{ inline_option }};
{% endfor %}
{% endif %}
}
{% endfor %}
{% endif %}

And refactor the files http/default.conf.j2 and stream/default.conf.j2 with:

{#  http/default.conf.j2 #}

{{ ansible_managed | comment }}
{% include '../lib/upstream.block.j2' %}
....
{#  stream/default.conf.j2 #}

{{ ansible_managed | comment }}
{% include '../lib/upstream.block.j2' %}
....

It will also allow to build your own configurations using blocks without rewriting everything. If it is an interesting functionality i can do a pull request with the changes to see more details.

Handler - NGINX Config fails in check mode

Describe the bug
When running ansible-playbook --check (or -C) there is an error reporting config dict has no attribute rc

FAILED! => {"msg": "The conditional check 'config.rc != 0' failed. The error was: error while evaluating conditional (config.rc != 0): 'dict object' has no attribute 'rc'\n\nThe error appears to be in '/Users/<myname>/.ansible/roles/nginxinc.nginx_config/handlers/main.yml': line 9, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: (Handler - NGINX Config) Print NGINX error if syntax check fails\n  ^ here\n"}

This section of code will always fail in check mode, because the task (Handler - NGINX Config) Check NGINX is skipped and config.rc will never exist.

- name: (Handler - NGINX Config) Check NGINX
  command: nginx -t
  register: config
  ignore_errors: yes
  changed_when: false
  listen: (Handler - NGINX Config) Run NGINX

- name: (Handler - NGINX Config) Print NGINX error if syntax check fails
  debug:
    var: config.stderr_lines
  failed_when: config.rc != 0
  when: config.rc != 0
  listen: (Handler - NGINX Config) Run NGINX

mime.types not included in default nginx configuration

Hello!

There is no "include /etc/nginx/mime.types;" in default configs.
Only text/html is allowed.
So even a simple WordPress site doesn't work.
js/css don't work.

I spent a whole day to investigate that.
It would be better to keep original nginx.cfg (from nginx) in default setup.

Support alias directive

Is your feature request related to a problem? Please describe

There does not appear to be a way to add an alias directive to the config templates. I have a set of docs I'd like to host at www.server.tld/docs, where the docs are in an arbitrarily named directory created via sphinx. Let's say /some/path/to/_build/html.
The alias directive let's me do this:

location /docs {
     alias /some/path/to/_build
}

Describe the solution you'd like

I'd like to be able to add an alias directive. The html_file_location variable allows setting the root directive but there is no corresponding variable for the alias directive.

Describe alternatives you've considered

The alternatives would be to create an intermediary processing step to move the docs from the sphinx directory to a parent directory named 'docs' so the root directive could be used.

Additional context

From what I can tell, alias is is acceptable to route requests to "other" directories on the filesystem different from the main root of the server. If that's intentionally blocked from usage with ansible, would be good to know.

Thanks!

Add `include` support in default.conf.j2 template

Is your feature request related to a problem? Please describe

It seems impossible to use the include (doc) directive directly in nginx.conf (aka using nginx_config_main_template with default.conf.j2), which is sad because of Debian's /etc/nginx/modules-available/ and /etc/nginx/modules-enabled/ directories.

I always had a nginx.conf like this :

xxx

include /etc/nginx/modules-enabled/*.conf;

events {
        xxx
}

http {
        include /etc/nginx/mime.types;

        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
}

Describe the solution you'd like

I'd like to add support to the include keyword in default.conf.j2.

Describe alternatives you've considered

I tried to use include /etc/nginx/modules-enabled/*.conf; directly in http config (aka inside nginx_config_http_template), but the load_modules directive can't be used inside http entries, so that's not possible.

I currently list all my modules like this :

# cat modules-enabled/*
load_module modules/ngx_http_auth_pam_module.so;
load_module modules/ngx_http_dav_ext_module.so;
load_module modules/ngx_http_echo_module.so;
load_module modules/ngx_http_geoip_module.so;
load_module modules/ngx_http_image_filter_module.so;
load_module modules/ngx_http_subs_filter_module.so;
load_module modules/ngx_http_upstream_fair_module.so;
load_module modules/ngx_http_xslt_filter_module.so;
load_module modules/ngx_mail_module.so;
load_module modules/ngx_stream_module.so;

And then list all modules inside of the load_module directive :

load_module:
        - modules/ngx_http_auth_pam_module.so
        - modules/ngx_http_dav_ext_module.so
        - modules/ngx_http_echo_module.so
        - modules/ngx_http_geoip_module.so
        - modules/ngx_http_image_filter_module.so
        - modules/ngx_http_subs_filter_module.so
        - modules/ngx_http_upstream_fair_module.so
        - modules/ngx_http_xslt_filter_module.so
        - modules/ngx_mail_module.so
        - modules/ngx_stream_module.so

But I lose the possibility to remove symlinks in modules-enabled + restart nginx for disabling a module.

Additional context

N/A

PS : Thanks for this amazing role !

Not a bug: basic usage question

Describe the bug

This is not a bug. Just a question on basic usage. New to Ansible and nginx.

To reproduce

Steps to reproduce the behavior:

I want to change default root path (Apache-ism is DocumentRoot) from /usr/share/nginx/html to /var/www/html. I know the config file to change would be /etc/nginx/conf.d/default.conf and the section:

    location / {
        root   /var/www/html;
        index  index.html index.htm;
    }

But I can't seem to get a single change from my playbook. I'm experimenting with another issue I saw here in the github issues. I'm experimenting with this --but it does nothing.

I realize this won't change location / --but it's just a starting point.

This is how I run the playbook: ansible-playbook nginxinc.nginx_config.yml -K

nginxinc.nginx_config.yml:

- hosts: testing
  become: true
  roles:
    - {role: nginxinc.nginx_config}
  vars:
    # nginx_config_debug_output: true
    nginx_config_http_template:
      - template_file: http/default.conf.j2
        conf_file_name: 50_example.com.conf
        conf_file_location: /etc/nginx/conf.d/
        servers:
          - listen:
              - port: 8080
              - 

Expected behavior

Could you show me an example of how to change location / { root /var/www/html; ...

Your environment

  • installed from ansible galaxy
  • ansible 2.9.6+dfsg-1
  • ubuntu Ubuntu 20.04.3 LTS

Additional context

None.

Latest examples available on main don't work on the latest Ansible Galaxy release (0.3.3)

Describe the bug

I'm trying to setup a simple HTTP server on a Debian machine with the following config:

# my_role/meta/main.yml
---
allow_duplicates: no
dependencies:
  - role: nginxinc.nginx
    become: yes
  - role: nginxinc.nginx_config
    become: yes
    vars:
      nginx_config_debug_output: true
      nginx_config_debug_tasks: true
      nginx_config_cleanup: true
      nginx_config_cleanup_files:
        - /etc/nginx/conf.d/default.conf
      nginx_config_main_template_enable: true
      nginx_config_main_template:
        user: pi
        http_enable: true
        http_settings:
          gzip:
            enable: true
          access_log_format:
            - name: main
              format: |-
                '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" '
                '"$http_user_agent" "$http_x_forwarded_for"'
          access_log_location:
            - name: main
              location: /var/log/nginx/access.log
          keepalive_timeout: 65
      nginx_config_http_template_enable: true
      nginx_config_http_template:
        # - template_file: http/default.conf.j2
        # conf_file_name: default.v1.conf
        # conf_file_location: /etc/nginx/conf.d/
        - servers:
            - server_name: localhost
              listen:
                - ip: localhost
                  port: 80
              gzip:
                enable: true
              root: "{{ ui_pkg_dst }}"
              index: index.html
              try_files: $uri $uri/index.html $uri.html =404
              locations:
                - location: /
                  root: "{{ ui_pkg_dst }}"
                  index: index.html
                  try_files: $uri $uri/index.html $uri.html =404

But it seems like the config file on the remote host is always empty or contains just the comment:

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************
ok: [dum-e]

TASK [nginxinc.nginx : Check whether you are using a supported NGINX distribution] *******************************************************************************************************************************
ok: [dum-e] => {
    "changed": false,
    "msg": "Your OS, Debian is supported by NGINX Open Source"
}

TASK [nginxinc.nginx : Set up prerequisites] *********************************************************************************************************************************************************************
included: /Users/rolandgroza/.ansible/roles/nginxinc.nginx/tasks/prerequisites/prerequisites.yml for dum-e

TASK [nginxinc.nginx : Install dependencies] *********************************************************************************************************************************************************************
included: /Users/rolandgroza/.ansible/roles/nginxinc.nginx/tasks/prerequisites/install-dependencies.yml for dum-e

TASK [nginxinc.nginx : (Alpine Linux) Install dependencies] ******************************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx : (Debian/Ubuntu) Install dependencies] *****************************************************************************************************************************************************
ok: [dum-e]

TASK [nginxinc.nginx : (Amazon Linux/CentOS/Oracle Linux/RHEL) Install dependencies] *****************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx : (SLES) Install dependencies] **************************************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx : (FreeBSD) Install dependencies using package(s)] ******************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx : (FreeBSD) Install dependencies using port(s)] *********************************************************************************************************************************************
skipping: [dum-e] => (item=security/ca_root_nss) 

TASK [nginxinc.nginx : Check if SELinux is enabled] **************************************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx : Configure SELinux] ************************************************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx : Set up signing keys] **********************************************************************************************************************************************************************
included: /Users/rolandgroza/.ansible/roles/nginxinc.nginx/tasks/keys/setup-keys.yml for dum-e

TASK [nginxinc.nginx : (Alpine Linux) Set up NGINX signing key URL] **********************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx : (Alpine Linux) Download NGINX signing key] ************************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx : (Debian/Red Hat/SLES OSs) Set up NGINX signing key URL] ***********************************************************************************************************************************
ok: [dum-e]

TASK [nginxinc.nginx : (Debian/Ubuntu) Add NGINX signing key] ****************************************************************************************************************************************************
ok: [dum-e]

TASK [nginxinc.nginx : (Amazon Linux/CentOS/Oracle Linux/RHEL/SLES) Add NGINX signing key] ***********************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx : Install NGINX Open Source] ****************************************************************************************************************************************************************
included: /Users/rolandgroza/.ansible/roles/nginxinc.nginx/tasks/opensource/install-oss.yml for dum-e

TASK [nginxinc.nginx : Install NGINX from repository] ************************************************************************************************************************************************************
included: /Users/rolandgroza/.ansible/roles/nginxinc.nginx/tasks/opensource/install-debian.yml for dum-e

TASK [nginxinc.nginx : (Debian/Ubuntu) Configure NGINX repository] ***********************************************************************************************************************************************
ok: [dum-e] => (item=deb [arch=amd64] https://nginx.org/packages/mainline/debian/ buster nginx)
ok: [dum-e] => (item=deb-src https://nginx.org/packages/mainline/debian/ buster nginx)

TASK [nginxinc.nginx : (Debian/Ubuntu) Install NGINX] ************************************************************************************************************************************************************
ok: [dum-e]

TASK [nginxinc.nginx : Install NGINX from source] ****************************************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx : Install NGINX from package] ***************************************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx : Install NGINX in Unix systems] ************************************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx : Set up NGINX Plus license] ****************************************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx : Install NGINX Plus] ***********************************************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx : Install NGINX modules] ********************************************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx : Remove NGINX Plus license] ****************************************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx : Modify systemd parameters] ****************************************************************************************************************************************************************
skipping: [dum-e]
[WARNING]: flush_handlers task does not support when conditional

TASK [nginxinc.nginx : Debug NGINX output] ***********************************************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx : Configure logrotate for NGINX] ************************************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx : Install NGINX Amplify] ********************************************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx_config : Set up SELinux] ********************************************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx_config : Cleanup NGINX config] **************************************************************************************************************************************************************
included: /Users/rolandgroza/.ansible/roles/nginxinc.nginx_config/tasks/config/cleanup-config.yml for dum-e

TASK [nginxinc.nginx_config : Find NGINX config files] ***********************************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx_config : Remove NGINX config files] *********************************************************************************************************************************************************
changed: [dum-e] => (item=/etc/nginx/conf.d/default.conf)

TASK [nginxinc.nginx_config : Upload NGINX config] ***************************************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx_config : Create NGINX config] ***************************************************************************************************************************************************************
included: /Users/rolandgroza/.ansible/roles/nginxinc.nginx_config/tasks/config/template-config.yml for dum-e

TASK [nginxinc.nginx_config : Ensure HTML directory exists] ******************************************************************************************************************************************************
skipping: [dum-e] => (item={'key': 'default', 'value': {'template_file': 'www/index.html.j2', 'html_file_name': 'index.html', 'html_file_location': '/usr/share/nginx/html', 'web_server_name': 'Default'}}) 

TASK [nginxinc.nginx_config : Dynamically generate HTML files] ***************************************************************************************************************************************************
skipping: [dum-e] => (item={'key': 'default', 'value': {'template_file': 'www/index.html.j2', 'html_file_name': 'index.html', 'html_file_location': '/usr/share/nginx/html', 'web_server_name': 'Default'}}) 

TASK [nginxinc.nginx_config : Configure NGINX modules] ***********************************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx_config : Ensure NGINX main directory exists] ************************************************************************************************************************************************
ok: [dum-e]

TASK [nginxinc.nginx_config : Dynamically generate NGINX main configuration file] ********************************************************************************************************************************
ok: [dum-e]

TASK [nginxinc.nginx_config : Ensure NGINX HTTP directory exists] ************************************************************************************************************************************************
ok: [dum-e] => (item={'key': 'servers', 'value': [{'server_name': 'localhost', 'listen': [{'ip': 'localhost', 'port': 80}], 'gzip': {'enable': True}, 'root': '/home/pi/ui', 'index': 'index.html', 'try_files': '$uri $uri/index.html $uri.html =404', 'locations': [{'location': '/', 'root': '/home/pi/ui', 'index': 'index.html', 'try_files': '$uri $uri/index.html $uri.html =404'}]}]})

TASK [nginxinc.nginx_config : Ensure NGINX proxy cache directories exist] ****************************************************************************************************************************************

TASK [nginxinc.nginx_config : Dynamically generate NGINX HTTP config files] **************************************************************************************************************************************
changed: [dum-e] => (item={'key': 'servers', 'value': [{'server_name': 'localhost', 'listen': [{'ip': 'localhost', 'port': 80}], 'gzip': {'enable': True}, 'root': '/home/pi/ui', 'index': 'index.html', 'try_files': '$uri $uri/index.html $uri.html =404', 'locations': [{'location': '/', 'root': '/home/pi/ui', 'index': 'index.html', 'try_files': '$uri $uri/index.html $uri.html =404'}]}]})

TASK [nginxinc.nginx_config : Dynamically generate NGINX stub status config file] ********************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx_config : Dynamically generate NGINX API config file] ****************************************************************************************************************************************
skipping: [dum-e]

TASK [nginxinc.nginx_config : Ensure NGINX stream directory exists] **********************************************************************************************************************************************
skipping: [dum-e] => (item={'key': 'default', 'value': {'template_file': 'stream/default.conf.j2', 'conf_file_name': 'default.conf', 'conf_file_location': '/etc/nginx/conf.d/stream/', 'network_streams': {'default': {'listen': {'listen_localhost': {'ip': '0.0.0.0', 'port': 80, 'ssl': False, 'opts': []}}, 'ssl': {'cert': '/etc/ssl/certs/default.crt', 'key': '/etc/ssl/private/default.key', 'dhparam': '/etc/ssl/private/dh_param.pem', 'protocols': 'TLSv1 TLSv1.1 TLSv1.2', 'ciphers': 'HIGH:!aNULL:!MD5', 'prefer_server_ciphers': True, 'session_cache': 'none', 'session_timeout': '5m', 'disable_session_tickets': False, 'trusted_cert': '/etc/ssl/certs/root_CA_cert_plus_intermediates.crt', 'ecdh_curve': 'auto'}, 'include_files': [], 'proxy_pass': 'backend', 'proxy_timeout': '3s', 'proxy_connect_timeout': '1s', 'proxy_protocol': False, 'proxy_ssl': {'cert': '/etc/ssl/certs/proxy_default.crt', 'key': '/etc/ssl/private/proxy_default.key', 'trusted_cert': '/etc/ssl/certs/proxy_ca.crt', 'protocols': 'TLSv1 TLSv1.1 TLSv1.2', 'ciphers': 'HIGH:!aNULL:!MD5', 'verify': False, 'verify_depth': 1, 'session_reuse': True}, 'health_check_plus': False}}, 'upstreams': {'upstream1': {'name': 'backend', 'lb_method': 'least_conn', 'zone_name': 'backend', 'zone_size': '64k', 'sticky_cookie': False, 'servers': {'server1': {'address': 'localhost', 'port': 8080, 'weight': 1, 'health_check': 'max_fails=1 fail_timeout=10s'}}}}}}) 

TASK [nginxinc.nginx_config : Dynamically generate NGINX stream config files] ************************************************************************************************************************************
skipping: [dum-e] => (item={'key': 'default', 'value': {'template_file': 'stream/default.conf.j2', 'conf_file_name': 'default.conf', 'conf_file_location': '/etc/nginx/conf.d/stream/', 'network_streams': {'default': {'listen': {'listen_localhost': {'ip': '0.0.0.0', 'port': 80, 'ssl': False, 'opts': []}}, 'ssl': {'cert': '/etc/ssl/certs/default.crt', 'key': '/etc/ssl/private/default.key', 'dhparam': '/etc/ssl/private/dh_param.pem', 'protocols': 'TLSv1 TLSv1.1 TLSv1.2', 'ciphers': 'HIGH:!aNULL:!MD5', 'prefer_server_ciphers': True, 'session_cache': 'none', 'session_timeout': '5m', 'disable_session_tickets': False, 'trusted_cert': '/etc/ssl/certs/root_CA_cert_plus_intermediates.crt', 'ecdh_curve': 'auto'}, 'include_files': [], 'proxy_pass': 'backend', 'proxy_timeout': '3s', 'proxy_connect_timeout': '1s', 'proxy_protocol': False, 'proxy_ssl': {'cert': '/etc/ssl/certs/proxy_default.crt', 'key': '/etc/ssl/private/proxy_default.key', 'trusted_cert': '/etc/ssl/certs/proxy_ca.crt', 'protocols': 'TLSv1 TLSv1.1 TLSv1.2', 'ciphers': 'HIGH:!aNULL:!MD5', 'verify': False, 'verify_depth': 1, 'session_reuse': True}, 'health_check_plus': False}}, 'upstreams': {'upstream1': {'name': 'backend', 'lb_method': 'least_conn', 'zone_name': 'backend', 'zone_size': '64k', 'sticky_cookie': False, 'servers': {'server1': {'address': 'localhost', 'port': 8080, 'weight': 1, 'health_check': 'max_fails=1 fail_timeout=10s'}}}}}}) 

RUNNING HANDLER [nginxinc.nginx_config : (Handler - NGINX Config) Check NGINX] ***********************************************************************************************************************************
ok: [dum-e]

RUNNING HANDLER [nginxinc.nginx_config : (Handler - NGINX Config) Print NGINX error if syntax check fails] *******************************************************************************************************
skipping: [dum-e]

RUNNING HANDLER [nginxinc.nginx_config : (Handler - NGINX Config) Start/reload NGINX] ****************************************************************************************************************************
changed: [dum-e]

TASK [nginxinc.nginx_config : Debug output] **********************************************************************************************************************************************************************
included: /Users/rolandgroza/.ansible/roles/nginxinc.nginx_config/tasks/config/debug-output.yml for dum-e

TASK [nginxinc.nginx_config : Register NGINX config] *************************************************************************************************************************************************************
ok: [dum-e]

TASK [nginxinc.nginx_config : Print NGINX config] ****************************************************************************************************************************************************************
ok: [dum-e] => {
    "config_full.stdout_lines": [
        "# configuration file /etc/nginx/nginx.conf:",
        "#",
        "# Ansible managed",
        "#",
        "",
        "",
        "user pi;",
        "",
        "",
        "",
        "events {",
        "}",
        "",
        "http {",
        "    include       /etc/nginx/mime.types;",
        "    default_type  application/octet-stream;",
        "    log_format  main  '$remote_addr - $remote_user [$time_local] \"$request\" '",
        "'$status $body_bytes_sent \"$http_referer\" '",
        "'\"$http_user_agent\" \"$http_x_forwarded_for\"';",
        "    access_log  /var/log/nginx/access.log  main;",
        "    keepalive_timeout  65;",
        "    gzip on;",
        "",
        "    include /etc/nginx/conf.d/*.conf;",
        "}",
        "",
        "",
        "# configuration file /etc/nginx/mime.types:",
        "",
        "types {",
        "    text/html                             html htm shtml;",
        "    text/css                              css;",
        "    text/xml                              xml;",
        "    image/gif                             gif;",
        "    image/jpeg                            jpeg jpg;",
        "    application/javascript                js;",
        "    application/atom+xml                  atom;",
        "    application/rss+xml                   rss;",
        "",
        "    text/mathml                           mml;",
        "    text/plain                            txt;",
        "    text/vnd.sun.j2me.app-descriptor      jad;",
        "    text/vnd.wap.wml                      wml;",
        "    text/x-component                      htc;",
        "",
        "    image/png                             png;",
        "    image/tiff                            tif tiff;",
        "    image/vnd.wap.wbmp                    wbmp;",
        "    image/x-icon                          ico;",
        "    image/x-jng                           jng;",
        "    image/x-ms-bmp                        bmp;",
        "    image/svg+xml                         svg svgz;",
        "    image/webp                            webp;",
        "",
        "    application/font-woff                 woff;",
        "    application/java-archive              jar war ear;",
        "    application/json                      json;",
        "    application/mac-binhex40              hqx;",
        "    application/msword                    doc;",
        "    application/pdf                       pdf;",
        "    application/postscript                ps eps ai;",
        "    application/rtf                       rtf;",
        "    application/vnd.apple.mpegurl         m3u8;",
        "    application/vnd.ms-excel              xls;",
        "    application/vnd.ms-fontobject         eot;",
        "    application/vnd.ms-powerpoint         ppt;",
        "    application/vnd.wap.wmlc              wmlc;",
        "    application/vnd.google-earth.kml+xml  kml;",
        "    application/vnd.google-earth.kmz      kmz;",
        "    application/x-7z-compressed           7z;",
        "    application/x-cocoa                   cco;",
        "    application/x-java-archive-diff       jardiff;",
        "    application/x-java-jnlp-file          jnlp;",
        "    application/x-makeself                run;",
        "    application/x-perl                    pl pm;",
        "    application/x-pilot                   prc pdb;",
        "    application/x-rar-compressed          rar;",
        "    application/x-redhat-package-manager  rpm;",
        "    application/x-sea                     sea;",
        "    application/x-shockwave-flash         swf;",
        "    application/x-stuffit                 sit;",
        "    application/x-tcl                     tcl tk;",
        "    application/x-x509-ca-cert            der pem crt;",
        "    application/x-xpinstall               xpi;",
        "    application/xhtml+xml                 xhtml;",
        "    application/xspf+xml                  xspf;",
        "    application/zip                       zip;",
        "",
        "    application/octet-stream              bin exe dll;",
        "    application/octet-stream              deb;",
        "    application/octet-stream              dmg;",
        "    application/octet-stream              iso img;",
        "    application/octet-stream              msi msp msm;",
        "",
        "    application/vnd.openxmlformats-officedocument.wordprocessingml.document    docx;",
        "    application/vnd.openxmlformats-officedocument.spreadsheetml.sheet          xlsx;",
        "    application/vnd.openxmlformats-officedocument.presentationml.presentation  pptx;",
        "",
        "    audio/midi                            mid midi kar;",
        "    audio/mpeg                            mp3;",
        "    audio/ogg                             ogg;",
        "    audio/x-m4a                           m4a;",
        "    audio/x-realaudio                     ra;",
        "",
        "    video/3gpp                            3gpp 3gp;",
        "    video/mp2t                            ts;",
        "    video/mp4                             mp4;",
        "    video/mpeg                            mpeg mpg;",
        "    video/quicktime                       mov;",
        "    video/webm                            webm;",
        "    video/x-flv                           flv;",
        "    video/x-m4v                           m4v;",
        "    video/x-mng                           mng;",
        "    video/x-ms-asf                        asx asf;",
        "    video/x-ms-wmv                        wmv;",
        "    video/x-msvideo                       avi;",
        "}",
        "",
        "# configuration file /etc/nginx/conf.d/default.conf:",
        "#",
        "# Ansible managed",
        "#"
    ]
}

To reproduce

Steps to reproduce the behavior:

  1. Deploy NGINX Config role using a role that depends on this role and has the above config
  2. Check /etc/nginx/conf.d/default.conf contents

Expected behavior

I'd expect the config file on the remote host to be generated with the config I've setup.

Your environment:

  • Version of the NGINX Config role or specific commit
---
roles:
  # https://github.com/nginxinc/ansible-role-nginx
  - src: nginxinc.nginx
    version: 0.19.1
  # https://github.com/nginxinc/ansible-role-nginx-config
  - src: nginxinc.nginx_config
    version: 0.3.3
  • Version of Ansible
โฏ ansible --version                                                                            
ansible 2.10.2
  config file = /Users/rolandgroza/Work/jigs/ansible.cfg
  configured module search path = ['/Users/rolandgroza/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /Users/rolandgroza/.pyenv/versions/3.8.6/envs/python-3.8/lib/python3.8/site-packages/ansible
  executable location = /Users/rolandgroza/.pyenv/versions/python-3.8/bin/ansible
  python version = 3.8.6 (default, Nov  1 2020, 20:18:23) [Clang 11.0.3 (clang-1103.0.32.62)]
  • Target deployment platform: Debian 10

Additional context

Add any other context about the problem here.

nginx_config_main_template not deploying

I am trying to execute the following playbook

---
- name: Basic setup
  hosts: gates
  collections:
    - nginxinc.nginx_core
  tasks:
    - name: Install NGINX
      include_role:
        name: nginx
    - name: Configure NGINX
      include_role:
        name: nginx_config
      vars:
        nginx_config_debug_output: true

        nginx_config_cleanup: true
        nginx_config_cleanup_paths:
          - directory:
              - /etc/nginx/conf.d
            recurse: false
        nginx_config_cleanup_files:
          - /etc/nginx/conf.d/default.conf
        nginx_config_main_template_enable: false
        nginx_config_main_template:
          template_file: nginx.conf.j2
          conf_file_name: nginx.conf
          conf_file_location: /etc/nginx/
          user: nginx
          worker_processes: auto
          worker_rlimit_nofile: 1024
          pid: /var/run/nginx.pid
          error_log:
            location: /var/log/nginx/error.log
            level: warn
          worker_connections: 1024
          http_enable: true
          access_log_format:
            - name: upstream_info
              format: |-
                '$remote_addr - $remote_user [$time_local] "$request" '
                '$status $body_bytes_sent "$http_referer" "$http_user_agent" '
                'rt=$request_time uct="$upstream_connect_time" uht="$upstream_header_time" urt="$upstream_response_time"'
          access_log_location:
            - name: upstream_info
              location: /var/log/nginx/access.log

but the nginx.conf is getting the default values:

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

I am running Ansible 4.1 and I have the 0.3.3 nginxinc.nginx_core collection

(cluster) joan@DESKTOP-OM8Q4NE:~/Abzu/cluster$ ansible --version
ansible [core 2.11.1]
  config file = /home/joan/Abzu/cluster/ansible.cfg
  configured module search path = ['/home/joan/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/joan/.local/share/virtualenvs/cluster-DtMsh1G5/lib/python3.8/site-packages/ansible
  ansible collection location = /home/joan/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/joan/.local/share/virtualenvs/cluster-DtMsh1G5/bin/ansible
  python version = 3.8.5 (default, Jan 27 2021, 15:41:15) [GCC 9.3.0]
  jinja version = 3.0.0
  libyaml = True

Configuring nginx.conf with default values fails with nginx_config_main_template_enable set to true

Describe the bug

I am trying to override juste one setting (server_names_hash_bucket_size) in the default main template.
Comments in the file state "# Defaults are the values found in a fresh NGINX installation." so I thought enabling nginx_config_main_template_enable and just overriding the right subkey would do the trick as it would keep the others values.

This didn't work, so I commented out my modification about the custom setting and tried to apply the role with only nginx_config_main_template_enable set to true but it fails as well.

Using the role with nginx_config_main_template_enable set to true fails with default role values.

To reproduce

Steps to reproduce the behavior:

  1. Deploy NGINX Config role using playbook.yml
- name: NGINX
  hosts: nginx
  debugger: on_failed
  become: true

  pre_tasks:
  - name: Enabling nginx_config_main_template
    set_fact:
      nginx_config_main_template_enable: true
  roles:
  - role: nginxinc.nginx
  - role: nginxinc.nginx-config
  1. Output error is (with -vvv option for verbosity)
TASK [nginxinc.nginx-config : Dynamically generate NGINX main configuration file] ***********************************************************************************************************
task path: /home/xxx/ansible/yyy/roles/nginxinc.nginx-config/tasks/config/template-config.yml:37
fatal: [nginx_0]: FAILED! => {
    "changed": false,
    "msg": "AnsibleUndefinedVariable: 'dict object' has no attribute 'dest'"
}

I don't have any more info on which 'dict object' it fails on, as the task Dynamically generate NGINX main configuration file has fallback default values for the dest file (/etc/nginx/nginx.conf) and thus shouldn't fail.

Expected behavior

Comments say "# Defaults are the values found in a fresh NGINX installation." so it should create a standard nginx.conf file with default values.

Your environment:

  • main
  • Version of Ansible: ansible 2.9.17
  • Version of Jinja2: 2.11.2
  • Target deployment platform: Debian Buster

How to omit load_module: modules/ngx_http_js_module.so

Good day!

In your file "defaults/main/template.yml" there is "load_module" list :

nginx_config_main_template:
config:
main:
load_module: modules/ngx_http_js_module.so

In my nginx configuration, there is no real file "ngx_http_js_module.so" in file system.
So I have error during role run.

I don't want to change your role files by removing "load_module: modules" element from dictionary.

How can I disable/omit this step in playbook ? Probably by re-setting "load_module" variable for "empty/undefined" value in playbook. To avoid such errors.

Thank you for your work for community!

BTW: do you have the support for sites-available/sites-enabled config file structure?

add support for grpc

Is your feature request related to a problem? Please describe.
i have an infrastructure that have production applications use grpc and currently automation for them is able to be done only by using a custom template.

so this is the problem - the only way to automate grpc with nginx is to use custom options which is barely readable and hard to support in that case or though making your own template.

Describe the solution you'd like
i would like nginx role to add an options to template to use grpc directives from grpc module https://nginx.org/en/docs/http/ngx_http_grpc_module.html#directives

Describe alternatives you've considered
have separate template specifically for grpc but it complicates things since it is still a part of 'http'

Additional context

Manage custom directories

Is your feature request related to a problem? Please describe

I'm always frustrated when I need to use the collection and specify custom paths in nginx.conf and need to manage them with custom task. Example:
I want to add to /etc/nginx/nginx.conf
client_body_temp_path /var/nginx/client_body_temp;

This directory is /var/nginx not created and have not found a way to create custom configs using this role.

Describe the solution you'd like

Add a role variable that would allow creation of custom dirs.

Describe alternatives you've considered

A clear and concise description of any alternative solutions or features you've considered.

Additional context

Add any other context or screenshots about the feature request here.

Missing support for the ip_hash directive in upstream blocks

Is your feature request related to a problem? Please describe.
The playbook I am developing using this role requires the use of ip_hash in upstream {} blocks. It does not currently seem to be supported.

Describe the solution you'd like
I would like to have support for the ip_hash directive added to the http/default.conf.j2 template. I may open a PR myself to fix this issue given enough spare time.

Describe alternatives you've considered
I have considered manually copying and altering the template file default.conf.j2 but decided it would be best if this is fixed upstream.

Additional context
Example:

upstream some_name {
    ip_hash;
    server 10.10.10.10;
    server 10.20.20.20;
}

Note: I erroneously submitted the template issue by accident after changing the issue type.

Allow simple rewriting

Is your feature request related to a problem? Please describe.

I painfully came up with a raw nginx conf for a simple old domain / uri scheme to new one

 server {
     listen 0.0.0.0:80;
     server_name documentation.old.domain;
     rewrite (.*).html(.*) $1$2;
     rewrite (.*)/en/stable(.*) $1$2;
     return 301 https://new.domain/uc-doc$uri;
 }

I can't implement this one.

Describe the solution you'd like

Something that work. Outside of a homemade raw Ansible copy:

Describe alternatives you've considered

Tried quickly, ended up with this mess that's never gonna work with the rewrite after the proxy pass (or so I believe)

    listen 0.0.0.0:80;
    server_name documentation.old.domain;
    location ^ {
        proxy_pass https://new.domain/uc-doc;
        rewrite rewrite (.*).html(.*) $1$2;;
        rewrite rewrite (.*)/en/stable(.*) $1$2;;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

Refactor module related templates

Is your feature request related to a problem? Please describe.
Now that module related installation tasks live in https://github.com/nginxinc/ansible-role-nginx, it makes little sense to keep module related templating working as it does right now where both the installation and configuration tasks live side by side.

Describe the solution you'd like
A better way to implement module templating would be to let users use a load_modules list variable.

Describe alternatives you've considered
A different alternative would be to keep adding module specific templating variables (like they exist right now), but using a load_modules list variable removes the need to keep implementing new module specific templates every time a new module becomes available.

Additional context
Some more context is available here #13

Support a Workflow for Fixing Config Errors from this Role

It is not only possible, but very likely, that at some point when developing a playbook with this role, the role variables used will generate invalid main and/or http config files. The problem is that once the config files on the controlled host are bad, you cannot run the config role without encountering errors in the "Register NGINX Config" task, or without manual intervention to edit the files on the controlled host to make them good. It would be useful for there to be variables that allowed users to "force" the repushing of newly-generated config files (from changed role variables), i.e. skip the check config step. I think it would be even better if the config is not checked until after it is regenerated on the controlled host.

I'm using the nginxinc (open source, core) collection v0.1.3.

Molecule: Installation of NGINX Plus failing on all configured distributions

Describe the bug

While running the molecule tests, the installation of NGINX Plus is failing.

Screenshot 2021-04-28 at 09 46 29

To reproduce

Steps to reproduce the behavior:

  1. Checkout the repository on main
  2. Run molecule test --all
  3. See above error.

Expected behavior

All tests passing

Your environment:

  • NGINX Config Role on main, commit 797232e0dbef01c0ce21ea3bd5ba48d26094df5e
  • ansible 2.10.8
  • molecule 3.3.0 using python 3.9
  • MacOS 11.2.3

Additional context

I found this while testing my contribution. As it is happening on main too, I don't see this related to my changes. If you think this is an edge case related to my own setup, please don't feel compelled to process this.

add support for grpc

Is your feature request related to a problem? Please describe.
i have an infrastructure that have production applications use grpc and currently automation for them is able to be done only by using a custom template.

so this is the problem - the only way to automate grpc with nginx is to use custom options which is barely readable and hard to support in that case or though making your own template.

Describe the solution you'd like
i would like nginx role to add an options to template to use grpc directives from grpc module https://nginx.org/en/docs/http/ngx_http_grpc_module.html#directives

Describe alternatives you've considered
have separate template specifically for grpc but it complicates things since it is still a part of 'http'

Additional context

Backup boolean not adjustable

Describe the bug

All template calls use backup: yes, for example:


This creates a lot of backup files once the role has run.

To reproduce

Steps to reproduce the behavior:

  1. Deploy NGINX Config role
  2. Deploy NGINX Config role again
  3. Check the /etc/nginx/conf.d/ directory for example; this will have a lot of backup files

Expected behavior

While having backup files is fine, it should be opt-out

Is this expected behavior? Or would you like an option where this is set from a var(iable)?

Missing support for ip_hash in upstream.

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

Check Mode - Role always reports a pending change when selinux is enabled

Describe the bug

When running this role in check mode with selinux enabled, this role will alway report a change.

To reproduce

With nginx_selinux_enforcing set to true, run the role, then run the role again with --check.

Expected behavior

No changes identified when running ansible in check mode.

"Ensure NGINX proxy cache directories exist" fails despite skip_missing flag

Describe the bug
Role fails when hitting Ensure NGINX proxy cache directories existtask despite skip_missing: true.

To reproduce
Steps to reproduce the behavior:

  1. Deploy NGINX Config role using playbook.yml
  2. Create your own nginx_config_http_template, without any proxy_cache
  3. See error:
{"msg": "subelements lookup expects a dictionary, got 'None'"}

Expected behavior
The role shouldn't fail on cache directory check when proxy cache isn't configured/used.

Your environment:

  • Version of the NGINX Config role: 0.3.0
  • Version of Ansible: 2.9.9
  • Target deployment platform: RHEL 7.9

proxy_set_header is singular but we have proxy_hide_headers

Is your feature request related to a problem? Please describe.
The default template should use plural variable name when dealing with proxy_set_header and proxy_hide_heaader.
Today, there is proxy_hide_headers and proxy_set_header
Even though one uses hash and the other is a simple array, it should be expressed as plural rabble names.

Describe the solution you'd like
We should lookup for proxy_set_headers in the template and the defaults

Need a way to install custom modules

To install nginx I use my own RPM repository (for CentOS 7/8). This is necessary because standard repositories (CentOS and the official nginx RPM repository) lack many necessary modules, for example https://github.com/leev/ngx_http_geoip2_module, https://github.com/google/ngx_brotli and a few else. Therefore, instead of compiling the nginx right on the production server, I build it locally and distribute it through my RPM repository, which was installed via pre_tasks before executing this role.

Is your feature request related to a problem? Please describe.
Now we can install only those modules for which separate tasks have been created: njs, perl, waf (Nginx Plus), geoip (RHEL < 8), image_filter, rtmp (Nginx Plus), xslt.
But we are not able to install any other module, since there is no task to install the list of extra packages.

Describe the solution you'd like
All tasks for all operating systems must be changes to install any extra package BEFORE starting/restarting/reloading nginx. This must be some sort of

- name: "(Install: Linux) Install additional Package"
  package:
    name: "{{ nginx_additional_packages }}"
    state: "{{ nginx_state }}"
  when: nginx_install_from == "os_repository" # ???
  notify: "(Handler: All OSs) Reload NGINX"

Additional context

  • These custom modules cannot be installed via pre_tasks, because they will require nginx package, which wasn' installed at this moment.
  • These custom modules cannot be installed via tasks or post_tasks, because nginx.conf will contain loading of these modules via load_module "modules/ngx_http_geoip2_module.so";, so nginx won't be restarted correctly when this role will notify about this.

There is a second problem here, since neither the CentOS repositories nor the official nginx repos contain any other modules. From the ansible side, this will look exactly the same as if you set nginx_install_from == "os_repository", which means "do not install official nginx repository, just install package nginx with the system package manager".
There will be no problems here as long as there is package nginx in the system or my own repositories. But what if we distribute our package with a different name, for example nginx-custom, so that when updating the packages via yum update, we do not accidentally install package nginx from some other repositories, which may have a version higher than in my own repository.
Therefore, we need some way to specify the name of the nginx package, and, accordingly, all the modules for it (like package nginx-custom and modules nginx-custom-module-geoip2).

Ability to upload snippets

Is your feature request related to a problem? Please describe.
It would be really handy to be able to upload a snippets directory containing configuration files, so that one can reuse common configuration in other nginx http conf files. If I'm not mistaken, I don't believe that this is currently (explicitly) possible in this plugin.

Describe alternatives you've considered
Tried using nginx_main_upload_src and nginx_http_upload_src, but neither work in this case. nginx_main_upload_src expects a file, and nginx_http_upload_src uses fileglob, which does not allow for recursive copies.

I could obviously do this in a separate role, but it would be nice to have all the nginx configuration done in the same role.

Describe the solution you'd like
One solution would be to emulate existing upload variable options, such as nginx_http_upload_enable. For example, introducing the vars: nginx_snippets_upload_enable, as well as src and dest counterparts would be nice.

If you feel that this solution is too specific to snippets, perhaps there is a more general way to copy local files to the root of the remote nginx directory. This is sort of how it's done in this Ansible nginx plugin: https://github.com/tschifftner/ansible-role-nginx#use-additional-nginx-templates

Alternatively making it possible to recursively possible to upload directories to the http config directory would be another way to go, though I'm not a fan of this as it doesn't match the defacto nginx directory structure, where snippets is on the root.

Thanks!

Add galaxy.yml into repo

Is your feature request related to a problem? Please describe

I wanted to download the github code using the ansible-galaxy

(cluster) joan@DESKTOP-OM8Q4NE:~/Abzu/cluster$ ansible-galaxy collection install git+https://github.com/nginxinc/ansible-role-nginx-config.git
Cloning into '/home/joan/.ansible/tmp/ansible-local-260469n7zryua/tmp5t1xuas1/ansible-role-nginx-confignf_aip1s'...
remote: Enumerating objects: 1193, done.
remote: Counting objects: 100% (405/405), done.
remote: Compressing objects: 100% (215/215), done.
remote: Total 1193 (delta 245), reused 296 (delta 176), pack-reused 788
Receiving objects: 100% (1193/1193), 296.46 KiB | 2.18 MiB/s, done.
Resolving deltas: 100% (673/673), done.
Your branch is up to date with 'origin/main'.
Starting galaxy collection install process
Process install dependency map
ERROR! Neither the collection requirement entry key 'name', nor 'source' point to a concrete resolvable collection artifact. Also 'name' is not an FQCN. A valid collection name must be in the format <namespace>.<collection>. Please
make sure that the namespace and the collection name  contain characters from [a-zA-Z0-9_] only.

Tip: Make sure you are pointing to the right subdirectory โ€” `/home/joan/.ansible/tmp/ansible-local-260469n7zryua/tmp5t1xuas1/ansible-role-nginx-confignf_aip1s` looks like a directory but it is neither a collection, nor a namespace d
ir.

Describe the solution you'd like

Having a galaxy.yml in the root repo as described in https://docs.ansible.com/ansible/latest/dev_guide/collections_galaxy_meta.html

'Remove NGINX Configuration Files' step failing

Describe the bug
The nginx role fails on "(Setup: All OSs) Remove NGINX Configuration Files" with the given error message:
fatal: [playground.example.com]: FAILED! => { "msg": "Unexpected templating type error occurred on ({{ nginx_config_files.results | default('') | map(attribute='files') | sum(start=[]) | map(attribute='path') | list + nginx_cleanup_config_files | default('') }}): can only concatenate list (not \"str\") to list" }

To reproduce
Steps to reproduce the behavior:

  1. Deploy NGINX role using playbook.yml (configured to setup a reverse proxy only; does not seem to be the root cause though)
  2. Waiting for the "(Setup: All OSs) Remove NGINX Configuration Files" step to be executed
  3. See error

Expected behavior
The role proceeds through this step as always and does not stall provisioning.

Your environment:

  • nginxinc.nginx (0.15.0)
  • ansible 2.9.6
  • ubuntu server 18.04.5 LTS

Additional context
I'm not sure if it helps, but the machine executing the playbook is running ubuntu 20.04 LTS. The role was working flawless until my recent (1 day ago) --force install of all galaxy obtained roles.

Check Mode - Role always reports a pending change when selinux is enabled

Describe the bug

When running this role in check mode with selinux enabled, this role will alway report a change.

To reproduce

With nginx_selinux_enforcing set to true, run the role, then run the role again with --check.

Expected behavior

No changes identified when running ansible in check mode.

there is a typo in line 7 of templates/http/api.conf.j2

Describe the bug

A clear and concise description of what the bug is.

there is a typo in line 7 of templates/http/api.conf.j2, in a result, the format can never been appended to the access_log line

the value: (status_rest_api_log)
nginx_config_status_rest_api_log['format']
should be (rest_api_access_log)
nginx_config_rest_api_access_log['format']

this is the line:
access_log{{ ' off' if not nginx_config_rest_api_access_log }}{{ (' ' + nginx_config_rest_api_access_log['path']) if nginx_config_rest_api_access_log['path'] is defined }}{{ (' ' + nginx_config_rest_api_access_log['format']) if nginx_config_status_rest_api_log['format'] is defined }};

besides, it is "name" vs "format" in the defaults/main/template.yml , you may also want to make it consistent. (as well as location vs path)

nginx_config_rest_api_access_log:  # Optional -- Set to 'false' and remove/comment nested variables to disable access log
  location: /var/log/nginx/api_access.log  # Required
  name: main  # Required

To reproduce

Steps to reproduce the behavior:

  1. Deploy NGINX Config role using playbook.yml
  2. View output/logs/configuration on '...'
  3. See error

Expected behavior

A clear and concise description of what you expected to happen.

Your environment

  • Version of the NGINX Config role or specific commit
  • Version of Ansible
  • Version of Jinja2 (if you are using any templating capability)
  • Target deployment platform

Additional context

Add any other context about the problem here.

deployment_location functionality appears to be incomplete

Describe the bug

Comments in the commit history indicate that for nginx_config_main_template, the child variables conf_file_name and conf_file_location have been replaced by deployment_location. This appears to be only partially implemented. The code still looks for the removed variables:
https://github.com/nginxinc/ansible-role-nginx-config/blob/0.4.0/tasks/config/template-config.yml#L32
https://github.com/nginxinc/ansible-role-nginx-config/blob/0.4.0/tasks/config/template-config.yml#L60

If your folder is anything other than the default, it won't get created.

Recommendations:

  • use {{ ... | dirname }} on the deployment_location to extract the directory name
  • implement this for streams too

Example of ssl termination with nginx reverse proxy

Is your feature request related to a problem? Please describe.
Kind of. I wish to terminate ssl with nginx, and simultaneously apply it as reverse proxy. I do not see an example of this in the docs, even I think it is pretty common scenario.

Describe the solution you'd like
Just an example configuration on how to terminate ssl with reverse proxy

Describe alternatives you've considered
I've considered applying nginx reverse proxy with ssl termination without this role. Like just manually configuring nginx, if it is too hard with this.

proxy: section in the main configuration is ignored

Describe the bug

Unless I misunderstood the spirit of this:

https://github.com/nginxinc/ansible-role-nginx-config/blob/main/templates/http/default.conf.j2
https://github.com/nginxinc/ansible-role-nginx-config/blob/main/defaults/main/template.yml

I thought a proxy: section inside:

nginx_config_main_template:
http_settings:

would allow the definition of

proxy_connect_timeout 90s;
proxy_send_timeout 90s;
proxy_read_timeout 90s;
proxy_buffers 32 4k

To reproduce

Steps to reproduce the behavior:

  1. Declare a proxy: section inside nginx_config_main_template: http_settings:
  2. Apply ansible
  3. Note that there are no changes to nginx.conf

Expected behavior

A clear and concise description of what you expected to happen.

Your environment:

nginxinc.nginx_config, 0.3.3
nginxinc.nginx, 0.19.1
ansible: 2.9.6+dfsg-1
ubuntu 20.04

Additional context

I apologize if I misunderstood something :-)

Deploy template fails with ``no test named 'boolean'``

Describe the bug

A clear and concise description of what the bug is.

Deploy template fails with no test named 'boolean'

TASK [nginxinc.nginx_core.nginx_config : Dynamically generate NGINX HTTP config files] ******************************************************************************************************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: jinja2.exceptions.TemplateAssertionError: no test named 'boolean'
failed: [host.fqdn] (item={'template_file': 'http/default.conf.j2', 'deployment_location': '/etc/nginx/conf.d/default.conf', 'config': {'upstreams': [{'name': 'upstr', 'least_conn': True, 'servers': [{'address': '0.0.0.0:8081'}, {'address': '0.0.0.0:8082'}]}], 'servers': [{'core': {'listen': [{'port': 80}], 'server_name': 'localhost'}, 'log': {'access': [{'path': '/var/log/nginx/access.log', 'format': 'main'}]}, 'locations': [{'location': '/', 'proxy': {'pass': 'http://upstr/', 'set_header': {'field': 'Host', 'value': '$host'}}}]}, {'core': {'listen': [{'port': 8081}], 'server_name': 'localhost'}, 'log': {'access': [{'path': '/var/log/nginx/access.log', 'format': 'main'}]}, 'locations': [{'location': '/', 'core': {'root': '/usr/share/nginx/html', 'index': 'server_one.html'}}], 'sub_filter': {'sub_filters': [{'string': 'server_hostname', 'replacement': '$hostname'}, {'string': 'server_address', 'replacement': '$server_addr:$server_port'}, {'string': 'server_url', 'replacement': '$request_uri'}, {'string': 'remote_addr', 'replacement': '$remote_addr:$remote_port'}, {'string': 'server_date', 'replacement': '$time_local'}, {'string': 'client_browser', 'replacement': '$http_user_agent'}, {'string': 'request_id', 'replacement': '$request_id'}, {'string': 'nginx_version', 'replacement': '$nginx_version'}, {'string': 'document_root', 'replacement': '$document_root'}, {'string': 'proxied_for_ip', 'replacement': '$http_x_forwarded_for'}], 'once': False}}, {'core': {'listen': [{'port': 8082}], 'server_name': 'localhost'}, 'log': {'access': [{'path': '/var/log/nginx/access.log', 'format': 'main'}]}, 'locations': [{'location': '/', 'core': {'root': '/usr/share/nginx/html', 'index': 'server_two.html'}}], 'sub_filter': {'sub_filters': [{'string': 'server_hostname', 'replacement': '$hostname'}, {'string': 'server_address', 'replacement': '$server_addr:$server_port'}, {'string': 'server_url', 'replacement': '$request_uri'}, {'string': 'remote_addr', 'replacement': '$remote_addr:$remote_port'}, {'string': 'server_date', 'replacement': '$time_local'}, {'string': 'client_browser', 'replacement': '$http_user_agent'}, {'string': 'request_id', 'replacement': '$request_id'}, {'string': 'nginx_version', 'replacement': '$nginx_version'}, {'string': 'document_root', 'replacement': '$document_root'}, {'string': 'proxied_for_ip', 'replacement': '$http_x_forwarded_for'}], 'once': False}}]}}) => {"ansible_loop_var": "item", "changed": false, "item": {"config": {"servers": [{"core": {"listen": [{"port": 80}], "server_name": "localhost"}, "locations": [{"location": "/", "proxy": {"pass": "http://upstr/", "set_header": {"field": "Host", "value": "$host"}}}], "log": {"access": [{"format": "main", "path": "/var/log/nginx/access.log"}]}}, {"core": {"listen": [{"port": 8081}], "server_name": "localhost"}, "locations": [{"core": {"index": "server_one.html", "root": "/usr/share/nginx/html"}, "location": "/"}], "log": {"access": [{"format": "main", "path": "/var/log/nginx/access.log"}]}, "sub_filter": {"once": false, "sub_filters": [{"replacement": "$hostname", "string": "server_hostname"}, {"replacement": "$server_addr:$server_port", "string": "server_address"}, {"replacement": "$request_uri", "string": "server_url"}, {"replacement": "$remote_addr:$remote_port", "string": "remote_addr"}, {"replacement": "$time_local", "string": "server_date"}, {"replacement": "$http_user_agent", "string": "client_browser"}, {"replacement": "$request_id", "string": "request_id"}, {"replacement": "$nginx_version", "string": "nginx_version"}, {"replacement": "$document_root", "string": "document_root"}, {"replacement": "$http_x_forwarded_for", "string": "proxied_for_ip"}]}}, {"core": {"listen": [{"port": 8082}], "server_name": "localhost"}, "locations": [{"core": {"index": "server_two.html", "root": "/usr/share/nginx/html"}, "location": "/"}], "log": {"access": [{"format": "main", "path": "/var/log/nginx/access.log"}]}, "sub_filter": {"once": false, "sub_filters": [{"replacement": "$hostname", "string": "server_hostname"}, {"replacement": "$server_addr:$server_port", "string": "server_address"}, {"replacement": "$request_uri", "string": "server_url"}, {"replacement": "$remote_addr:$remote_port", "string": "remote_addr"}, {"replacement": "$time_local", "string": "server_date"}, {"replacement": "$http_user_agent", "string": "client_browser"}, {"replacement": "$request_id", "string": "request_id"}, {"replacement": "$nginx_version", "string": "nginx_version"}, {"replacement": "$document_root", "string": "document_root"}, {"replacement": "$http_x_forwarded_for", "string": "proxied_for_ip"}]}}], "upstreams": [{"least_conn": true, "name": "upstr", "servers": [{"address": "0.0.0.0:8081"}, {"address": "0.0.0.0:8082"}]}]}, "deployment_location": "/etc/nginx/conf.d/default.conf", "template_file": "http/default.conf.j2"}, "msg": "TemplateAssertionError: no test named 'boolean'"}
The full traceback is:
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/ansible/plugins/action/template.py", line 146, in run
    resultant = templar.do_template(template_data, preserve_trailing_newlines=True, escape_backslashes=False)
  File "/usr/local/lib/python3.6/site-packages/ansible/template/__init__.py", line 1100, in do_template
    res = j2_concat(rf)
  File "<template>", line 43, in root
  File "/usr/lib/python3.6/site-packages/jinja2/environment.py", line 830, in get_template
    return self._load_template(name, self.make_globals(globals))
  File "/usr/lib/python3.6/site-packages/jinja2/environment.py", line 804, in _load_template
    template = self.loader.load(self, name, globals)
  File "/usr/lib/python3.6/site-packages/jinja2/loaders.py", line 125, in load
    code = environment.compile(source, name, filename)
  File "/usr/lib/python3.6/site-packages/jinja2/environment.py", line 591, in compile
    self.handle_exception(exc_info, source_hint=source_hint)
  File "/usr/lib/python3.6/site-packages/jinja2/environment.py", line 780, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/lib/python3.6/site-packages/jinja2/_compat.py", line 37, in reraise
    raise value.with_traceback(tb)
  File "/opt/ansible/collections/ansible_collections/nginxinc/nginx_core/roles/nginx_config/templates/http/upstream.j2", line 16, in template
    {{- ' backup' if server['backup'] is defined and server['backup'] is boolean and server['backup'] | bool -}}
  File "/usr/lib/python3.6/site-packages/jinja2/environment.py", line 543, in _generate
    optimized=self.optimized)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 82, in generate
    generator.visit(node)
  File "/usr/lib/python3.6/site-packages/jinja2/visitor.py", line 38, in visit
    return f(node, *args, **kwargs)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 754, in visit_Template
    self.blockvisit(node.body, frame)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 378, in blockvisit
    self.visit(node, frame)
  File "/usr/lib/python3.6/site-packages/jinja2/visitor.py", line 38, in visit
    return f(node, *args, **kwargs)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 1176, in visit_Macro
    macro_frame, macro_ref = self.macro_body(node, frame)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 575, in macro_body
    self.blockvisit(node.body, frame)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 378, in blockvisit
    self.visit(node, frame)
  File "/usr/lib/python3.6/site-packages/jinja2/visitor.py", line 38, in visit
    return f(node, *args, **kwargs)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 1122, in visit_For
    self.blockvisit(node.body, loop_frame)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 378, in blockvisit
    self.visit(node, frame)
  File "/usr/lib/python3.6/site-packages/jinja2/visitor.py", line 38, in visit
    return f(node, *args, **kwargs)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 1160, in visit_If
    self.blockvisit(node.body, if_frame)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 378, in blockvisit
    self.visit(node, frame)
  File "/usr/lib/python3.6/site-packages/jinja2/visitor.py", line 38, in visit
    return f(node, *args, **kwargs)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 1160, in visit_If
    self.blockvisit(node.body, if_frame)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 378, in blockvisit
    self.visit(node, frame)
  File "/usr/lib/python3.6/site-packages/jinja2/visitor.py", line 38, in visit
    return f(node, *args, **kwargs)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 1122, in visit_For
    self.blockvisit(node.body, loop_frame)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 378, in blockvisit
    self.visit(node, frame)
  File "/usr/lib/python3.6/site-packages/jinja2/visitor.py", line 38, in visit
    return f(node, *args, **kwargs)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 1160, in visit_If
    self.blockvisit(node.body, if_frame)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 378, in blockvisit
    self.visit(node, frame)
  File "/usr/lib/python3.6/site-packages/jinja2/visitor.py", line 38, in visit
    return f(node, *args, **kwargs)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 1314, in visit_Output
    self.visit(item, frame)
  File "/usr/lib/python3.6/site-packages/jinja2/visitor.py", line 38, in visit
    return f(node, *args, **kwargs)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 70, in new_func
    return f(self, node, frame, **kwargs)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 1624, in visit_CondExpr
    self.visit(node.test, frame)
  File "/usr/lib/python3.6/site-packages/jinja2/visitor.py", line 38, in visit
    return f(node, *args, **kwargs)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 70, in new_func
    return f(self, node, frame, **kwargs)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 1482, in visitor
    self.visit(node.left, frame)
  File "/usr/lib/python3.6/site-packages/jinja2/visitor.py", line 38, in visit
    return f(node, *args, **kwargs)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 70, in new_func
    return f(self, node, frame, **kwargs)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 1484, in visitor
    self.visit(node.right, frame)
  File "/usr/lib/python3.6/site-packages/jinja2/visitor.py", line 38, in visit
    return f(node, *args, **kwargs)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 70, in new_func
    return f(self, node, frame, **kwargs)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 1607, in visit_Test
    self.fail('no test named %r' % node.name, node.lineno)
  File "/usr/lib/python3.6/site-packages/jinja2/compiler.py", line 315, in fail
    raise TemplateAssertionError(msg, lineno, self.name, self.filename)
jinja2.exceptions.TemplateAssertionError: no test named 'boolean'
failed: [host.fqdn] (item={'template_file': 'http/default.conf.j2', 'deployment_location': '/etc/nginx/conf.d/default.conf', 'config': {'upstreams': [{'name': 'upstr', 'least_conn': True, 'servers': [{'address': '0.0.0.0:8081'}, {'address': '0.0.0.0:8082'}]}], 'servers': [{'core': {'listen': [{'port': 80}], 'server_name': 'localhost'}, 'log': {'access': [{'path': '/var/log/nginx/access.log', 'format': 'main'}]}, 'locations': [{'location': '/', 'proxy': {'pass': 'http://upstr/', 'set_header': {'field': 'Host', 'value': '$host'}}}]}, {'core': {'listen': [{'port': 8081}], 'server_name': 'localhost'}, 'log': {'access': [{'path': '/var/log/nginx/access.log', 'format': 'main'}]}, 'locations': [{'location': '/', 'core': {'root': '/usr/share/nginx/html', 'index': 'server_one.html'}}], 'sub_filter': {'sub_filters': [{'string': 'server_hostname', 'replacement': '$hostname'}, {'string': 'server_address', 'replacement': '$server_addr:$server_port'}, {'string': 'server_url', 'replacement': '$request_uri'}, {'string': 'remote_addr', 'replacement': '$remote_addr:$remote_port'}, {'string': 'server_date', 'replacement': '$time_local'}, {'string': 'client_browser', 'replacement': '$http_user_agent'}, {'string': 'request_id', 'replacement': '$request_id'}, {'string': 'nginx_version', 'replacement': '$nginx_version'}, {'string': 'document_root', 'replacement': '$document_root'}, {'string': 'proxied_for_ip', 'replacement': '$http_x_forwarded_for'}], 'once': False}}, {'core': {'listen': [{'port': 8082}], 'server_name': 'localhost'}, 'log': {'access': [{'path': '/var/log/nginx/access.log', 'format': 'main'}]}, 'locations': [{'location': '/', 'core': {'root': '/usr/share/nginx/html', 'index': 'server_two.html'}}], 'sub_filter': {'sub_filters': [{'string': 'server_hostname', 'replacement': '$hostname'}, {'string': 'server_address', 'replacement': '$server_addr:$server_port'}, {'string': 'server_url', 'replacement': '$request_uri'}, {'string': 'remote_addr', 'replacement': '$remote_addr:$remote_port'}, {'string': 'server_date', 'replacement': '$time_local'}, {'string': 'client_browser', 'replacement': '$http_user_agent'}, {'string': 'request_id', 'replacement': '$request_id'}, {'string': 'nginx_version', 'replacement': '$nginx_version'}, {'string': 'document_root', 'replacement': '$document_root'}, {'string': 'proxied_for_ip', 'replacement': '$http_x_forwarded_for'}], 'once': False}}]}}) => {
    "ansible_loop_var": "item",
    "changed": false,
    "item": {
        "config": {
            "servers": [
                {
                    "core": {
                        "listen": [
                            {
                                "port": 80
                            }
                        ],
                        "server_name": "localhost"
                    },
                    "locations": [
                        {
                            "location": "/",
                            "proxy": {
                                "pass": "http://upstr/",
                                "set_header": {
                                    "field": "Host",
                                    "value": "$host"
                                }
                            }
                        }
                    ],
                    "log": {
                        "access": [
                            {
                                "format": "main",
                                "path": "/var/log/nginx/access.log"
                            }
                        ]
                    }
                },
                {
                    "core": {
                        "listen": [
                            {
                                "port": 8081
                            }
                        ],
                        "server_name": "localhost"
                    },
                    "locations": [
                        {
                            "core": {
                                "index": "server_one.html",
                                "root": "/usr/share/nginx/html"
                            },
                            "location": "/"
                        }
                    ],
                    "log": {
                        "access": [
                            {
                                "format": "main",
                                "path": "/var/log/nginx/access.log"
                            }
                        ]
                    },
                    "sub_filter": {
                        "once": false,
                        "sub_filters": [
                            {
                                "replacement": "$hostname",
                                "string": "server_hostname"
                            },
                            {
                                "replacement": "$server_addr:$server_port",
                                "string": "server_address"
                            },
                            {
                                "replacement": "$request_uri",
                                "string": "server_url"
                            },
                            {
                                "replacement": "$remote_addr:$remote_port",
                                "string": "remote_addr"
                            },
                            {
                                "replacement": "$time_local",
                                "string": "server_date"
                            },
                            {
                                "replacement": "$http_user_agent",
                                "string": "client_browser"
                            },
                            {
                                "replacement": "$request_id",
                                "string": "request_id"
                            },
                            {
                                "replacement": "$nginx_version",
                                "string": "nginx_version"
                            },
                            {
                                "replacement": "$document_root",
                                "string": "document_root"
                            },
                            {
                                "replacement": "$http_x_forwarded_for",
                                "string": "proxied_for_ip"
                            }
                        ]
                    }
                },
                {
                    "core": {
                        "listen": [
                            {
                                "port": 8082
                            }
                        ],
                        "server_name": "localhost"
                    },
                    "locations": [
                        {
                            "core": {
                                "index": "server_two.html",
                                "root": "/usr/share/nginx/html"
                            },
                            "location": "/"
                        }
                    ],
                    "log": {
                        "access": [
                            {
                                "format": "main",
                                "path": "/var/log/nginx/access.log"
                            }
                        ]
                    },
                    "sub_filter": {
                        "once": false,
                        "sub_filters": [
                            {
                                "replacement": "$hostname",
                                "string": "server_hostname"
                            },
                            {
                                "replacement": "$server_addr:$server_port",
                                "string": "server_address"
                            },
                            {
                                "replacement": "$request_uri",
                                "string": "server_url"
                            },
                            {
                                "replacement": "$remote_addr:$remote_port",
                                "string": "remote_addr"
                            },
                            {
                                "replacement": "$time_local",
                                "string": "server_date"
                            },
                            {
                                "replacement": "$http_user_agent",
                                "string": "client_browser"
                            },
                            {
                                "replacement": "$request_id",
                                "string": "request_id"
                            },
                            {
                                "replacement": "$nginx_version",
                                "string": "nginx_version"
                            },
                            {
                                "replacement": "$document_root",
                                "string": "document_root"
                            },
                            {
                                "replacement": "$http_x_forwarded_for",
                                "string": "proxied_for_ip"
                            }
                        ]
                    }
                }
            ],
            "upstreams": [
                {
                    "least_conn": true,
                    "name": "upstr",
                    "servers": [
                        {
                            "address": "0.0.0.0:8081"
                        },
                        {
                            "address": "0.0.0.0:8082"
                        }
                    ]
                }
            ]
        },
        "deployment_location": "/etc/nginx/conf.d/default.conf",
        "template_file": "http/default.conf.j2"
    },
    "msg": "TemplateAssertionError: no test named 'boolean'"
}

To reproduce

Steps to reproduce the behavior:

  1. Deploy NGINX Config role using https://github.com/nginxinc/ansible-collection-nginx/blob/main/playbooks/deploy-nginx-web-server-proxy.yml
  2. View output/logs/configuration on TASK [nginxinc.nginx_core.nginx_config : Dynamically generate NGINX HTTP config files]
  3. See error

Expected behavior

Successful deployment

Your environment

  • Version of the NGINX Config role or specific commit

Collection installed via:

# ansible-galaxy collection install nginxinc.nginx_core
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Downloading https://galaxy.ansible.com/download/nginxinc-nginx_core-0.4.0.tar.gz to ~/.ansible/tmp/ansible-local-138554ko34z3om/tmpbfjxdsv7/nginxinc-nginx_core-0.4.0-_7gl1uhk
Installing 'nginxinc.nginx_core:0.4.0' to '~/.ansible/collections/ansible_collections/nginxinc/nginx_core'
nginxinc.nginx_core:0.4.0 was installed successfully
  • Version of Ansible
  • Version of Jinja2 (if you are using any templating capability)
ansible [core 2.11.6]
  python version = 3.6.8 (default, Sep 12 2021, 04:40:35) [GCC 8.4.1 20200928 (Red Hat 8.4.1-1)]
  jinja version = 2.10.1
  libyaml = True
  • Target deployment platform
NAME="CentOS Stream"
VERSION="8"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="8"
PLATFORM_ID="platform:el8"
PRETTY_NAME="CentOS Stream 8"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:8"
HOME_URL="https://centos.org/"
BUG_REPORT_URL="https://bugzilla.redhat.com/"
REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux 8"
REDHAT_SUPPORT_PRODUCT_VERSION="CentOS Stream"

Additional context

Add any other context about the problem here.

Undo main nginx.conf changes

Is your feature request related to a problem? Please describe.
Is there any way to undo any changes done to the main nginx.conf (go back to original)? I was testing the nginx_main_template options as I couldn't get port 80 to run my app, but I later discovered that using default_server for listen opts will also work.

But now, I'm not sure how to undo all of those changes.

Describe the solution you'd like
I should be able to restore the original nginx.conf.

Describe alternatives you've considered
I can live with the current conf, but I don't like all the vars I have set just to be able to run the app on port 80.

Additional context
No additional context.

Status defaults shouldn't be to listen on port 80 without a server_name

Describe the bug
Having the stub_status module served via its own HTTP server on port 80 and without a server_name by default can give odd behaviour.
Having another HTTP host set as default_server isn't enough either to guarantee the separation.
The allow/deny statements should also be moved out of the /nginx_status location to the server block.

To reproduce
Steps to reproduce the behavior:

  1. Deploy NGINX role
  2. Enable nginx_status_enable
  3. Configure another HTTP listener on port 80 (can be default_server).
  4. See random HTTP requests from bots getting lost on

Expected behavior
The status page should by default be served in a context that does not conflict with any other port 80 listener deployed.
So either on a different port, or a location within an existing server block.

Your environment:

  • 0.16.0
  • 2.9
  • RHEL 7

Add support for the NGINX real_ip module

Is your feature request related to a problem? Please describe

Currently, the conf role does not have configuration support for the directives in the real_ip module.

Describe the solution you'd like

Add support for the real_ip directives at a minimum in the http and server NGINX contexts.

Describe alternatives you've considered

This is my current workaround, which is of course brittle:

  tasks:
    - name: Add XFF header settings to http block
      ansible.builtin.blockinfile:
        path: /etc/nginx/nginx.conf
        insertbefore: '^\s*include\s\/etc\/nginx\/conf\.d\/\*.conf;'
        block: |
              real_ip_header X-Forwarded-For;
              set_real_ip_from 0.0.0.0/0;
              real_ip_recursive on;

    - name: Add XFF header settings to server block
      ansible.builtin.lineinfile:
        path: /etc/nginx/conf.d/default.conf
        regexp: '^\s*listen.*80\sdefault_server;\n\s*real_ip_header'
        insertafter: '^\s*listen.*80\sdefault_server;'
        line: 'real_ip_header X-Forwarded-For;'

    - name: Restart NGINX
      service:
        name: nginx
        state: restarted
        enabled: true

Documentation - example or discussion forum for proxy with straight ssl passthrough (maybe stream)

Is your feature request related to a problem? Please describe

A clear and concise description of what the problem is. Ex. I'm always frustrated when ...
I've been running reverse proxies and am finally getting better at the various configurations. Now I have a server behind the proxy that handles ssl itself on 443 so I need my reverse proxy to simply pass that connection through unchanged based on the server_name.

Problem = (either no example code) | (no community forum to ask questions) | (I really don't understand how to learn this myself from the source)

Describe the solution you'd like

A clear and concise description of what you want to happen.
I think that the stream template is what I want. Can we get a sample of how to instantiate a server block for streams?
Is there a forum to ask these types of questions?

Describe alternatives you've considered

A clear and concise description of any alternative solutions or features you've considered.

Additional context

Add any other context or screenshots about the feature request here.

Upstream server cannot be set as backup

Is your feature request related to a problem? Please describe.
The current default template doesn't support the backup option on the server line.
Feature as described in Nginx doc

Describe the solution you'd like
Add backup as a boolean in the template
It should be implemented like item.value.upstreams[upstream].servers[server].downalready is.

Missing defaults for nginx_main_template.*

If you want to change only the NGINX user, while keeping defaults for other vars, you still have to define all the vars under nginx_main_template dict.

With those vars:

nginx_main_template_enable: true
nginx_main_template:
  user: www-data

It causes the following error:

TASK [nginxinc.nginx : (Setup: All NGINX) Dynamically Generate NGINX Main Configuration File] ************************
fatal: [sandbox]: FAILED! => {"changed": false, "msg": "AnsibleUndefinedVariable: 'dict object' has no attribute 'worker_processes'"}

We should be allowed to define only some vars, and keep defaults for the other ones.

https_redirect has inconsitant type in the template.

Describe the bug
The following code is not working. https_redirect is a boolean in the check, but needs to be a string for the config template

{% if item.value.servers[server].https_redirect is defined and item.value.servers[server].https_redirect %}
    return 301 https://{{ item.value.servers[server].https_redirect }}$request_uri;
{% endif %}

SELinux state is changed from "enforcing" to "permissive" when nginx_config_selinux: true

Describe the bug
On a target host with SELinux state enforcing, running the role with nginx_config_selinux: true sets the target host SELinux state to permissive

To reproduce
Steps to reproduce the behavior:

  1. On a target host with SELinux state set to enforcing (used a CentOS 7 host as target), deploy NGINX Config role using nginx_config_selinux: true ( note: the role variable nginx_config_selinux_enforcing was unchanged from the role default (true))
  2. After the role completes check SELinux state with getenforce command. It returned Permissive

Expected behavior
Expected SELinux state to remain enforcing

Your environment:

  • current main branch (commit d0c6ab1)
  • Ansible 2.9.11
  • Target deployment platform: CentOS 7.8.2003 (Digital Ocean droplet)

Additional context
Excerpt from playbook output (-v):

TASK [external-roles/nginxinc.nginx-config : Set SELinux mode to permissive] ***********************
ok: [targethost] => changed=false
  configfile: /etc/selinux/config
  msg: SELinux state changed from 'enforcing' to 'permissive', Config SELinux state changed from 'enforcing' to 'permissive'
  policy: targeted
  reboot_required: false
  state: permissive

...
...
TASK [external-roles/nginxinc.nginx-config : Set SELinux mode to enforcing] ************************
skipping: [targethost] => changed=false
  skip_reason: Conditional result was False

rewrite option is not created with default template

Describe the bug

In the default scenario in molecule test is (see here) a rewrites option configured. But in the latest molecule output on main branch, you can not find this entry in the generated /etc/nginx/conf.d/default.conf (see here).

I also tested this locally and in all my tests (with molecule), I did not get it working to have a rewrite option in the config.

To reproduce

Run locally molecule test -s default

Expected behavior

Having a rewrite entry in the generated config, e.g. in server context:

server {
  ...
  rewrite (.*).html(.*) $1$2 last;
  ...
}

Your environment

Test with debian-buster (docker) molecule the default scenario.

Additional context

I guess the changes in #189 are not covering all sequences. After changing this line

{% for rewrite in rewrite['rewrites'] if (rewrite['rewrites'] is sequence and rewrite['rewrites'] is not string) %}

to that:

{% for rewrite in rewrite['rewrites'] if (rewrite['rewrites'] is not mapping and rewrite['rewrites'] is not string) %}

the molecule test (I tested it only on debian-buster) was still good and output of the task [ansible-role-nginx-config : Print NGINX config] shows me the rewrite option in the config.

`nginx_http_template.[default].servers.[server1].listen.[listen_items]` is not insertion ordered

Describe the bug
All listen records inside nginx_http_template.[default].servers.[server1].listen.[listen_items] won't preserve their insertion position/order inside array dict.

To reproduce

nginx_http_template:
  default:
    servers:
      server1:
        listen:
          listen_ipv4_http:
            ip: "1.2.3.4"
            port: 80
            opts:
              - default_server
              - deferred
          listen_ipv4_https:
            ip: "1.2.3.4"
            port: 443
            ssl: true
            opts:
              - http2
              - default_server
              - deferred
          listen_ipv4_front:
            ip: "1.2.3.4"
            port: 5757
            opts:
              - default_server
          listen_ipv6_http1:
            ip: "[fe80::a00:abcd::aaaa]"
            port: 80
            opts:
              - default_server
              - deferred
          listen_ipv6_http2:
            ip: "[fe80::a00:abcd::bbbb]"
            port: 80
            opts:
              - default_server
              - deferred
          listen_ipv6_https1:
            ip: "[fe80::a00:abcd::aaaa]"
            port: 443
            ssl: true
            opts:
              - http2
              - default_server
              - deferred
          listen_ipv6_https2:
            ip: "[fe80::a00:abcd::bbbb]"
            port: 443
            ssl: true
            opts:
              - http2
              - default_server
              - deferred
          listen_ipv6_front:
            ip: "[fe80::a00:abcd::cccc]"
            port: 5757
            opts:
              - default_server

Expected result

listen 1.2.3.4:80 default_server deferred;
listen 1.2.3.4:443 ssl http2 default_server deferred;
listen 1.2.3.4:5757 default_server;
listen [fe80::a00:abcd::aaaa]:80 default_server deferred;
listen [fe80::a00:abcd::bbbb]:80 default_server deferred;
listen [fe80::a00:abcd::aaaa]:443 ssl http2 default_server deferred;
listen [fe80::a00:abcd::bbbb]:443 ssl http2 default_server deferred;
listen [fe80::a00:abcd::cccc]:5757 default_server;

Actual result

listen 1.2.3.4:80 default_server deferred;
listen [fe80::a00:abcd::cccc]:5757 default_server;
listen [fe80::a00:abcd::aaaa]:443 ssl http2 default_server deferred;
listen [fe80::a00:abcd::bbbb]:443 ssl http2 default_server deferred;
listen 1.2.3.4:443 ssl http2 default_server deferred;
listen [fe80::a00:abcd::bbbb]:80 default_server deferred;
listen [fe80::a00:abcd::aaaa]:80 default_server deferred;
listen 1.2.3.4:5757 default_server;

Your environment:

  • Version of the NGINX Role or specific commit: master# 0922304b12c2dc578f86537f311304ffc498057d
  • Version of Ansible: 2.9.11
  • Target deployment platform: CentOS 7

Additional context
As far as I know, this is because that the keys in dict do not preserve their order, and because of this they can be displayed with a random order.
Some additional info: ansible/ansible#36886, ansible/ansible#36644

This issue would not have happened if a list was used instead of a dict, for example:

        listen:
          - ip: "1.2.3.4"
            port: 80
            opts:
              - default_server
              - deferred
          - ip: "1.2.3.4"
            port: 443
            ssl: true
            opts:
              - http2
              - default_server
              - deferred

instead of:

        listen:
          listen_ipv4_http:
            ip: "1.2.3.4"
            port: 80
            opts:
              - default_server
              - deferred
          listen_ipv4_https:
            ip: "1.2.3.4"
            port: 443
            ssl: true
            opts:
              - http2
              - default_server
              - deferred

But this will be a BC.
Any ideas how we can keep listen directives in their insertion order? Because if there are several listen directives, the order of the lines in the finished default.conf looks weird, and you can get confused in it while trying to understood.

Task nginxinc.nginx_core.nginx_config fails due to undefined variable

Describe the bug

The deployment fails due to

The error was: ansible.errors.AnsibleUndefinedVariable: the inline if-expression on line 71 in 'http/ssl.j2' evaluated to false and no else section was defined.

To reproduce

Steps to reproduce the behavior:

  1. Deploy NGINX Config role using playbook.yml
- hosts: all
  collections:
    - nginxinc.nginx_core
  tasks:
    - name: Configure NGINX
      include_role:
        name: nginx_config
      vars:
        nginx_config_http_template_enable: true
  1. See error
TASK [nginxinc.nginx_core.nginx_config : Dynamically generate NGINX HTTP config files] ********************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ansible.errors.AnsibleUndefinedVariable: the inline if-expression on line 71 in 'http/ssl.j2' evaluated to false and no else section was defined.
failed: [gimci-vm15] (item={'template_file': 'http/default.conf.j2', 'deployment_location': '/etc/nginx/conf.d/default.conf', 'config': {'upstreams': [{'name': 'backend', 'servers': [{'address': 'localhost', 'weight': 1, 'max_conns': 0, 'max_fails': 1, 'fail_timeout': '10s', 'backup': False, 'down': False, 'resolve': False, 'service': 'http', 'route': 'a', 'slow_start': '0s', 'drain': False}], 'zone': {'name': 'backend_mem_zone', 'size': '64k'}, 'state': '/var/lib/nginx/state/servers.conf', 'hash': {'key': 'key', 'consistent': False}, 'ip_hash': False, 'least_conn': False, 'least_time': {'response': 'last_byte', 'inflight': False}, 'random': {'two': True, 'method': 'least_time=last_byte'}, 'queue': {'number': 10, 'timeout': '60s'}, 'keepalive': 32, 'keepalive_requests': 100, 'keepalive_time': '1h', 'keepalive_timeout': '60s', 'ntlm': False, 'resolver': {'address': [], 'valid': '30s', 'ipv6': False, 'status_zone': 'backend_mem_zone'}, 'resolver_timeout': '30s', 'sticky_cookie': {'name': 'cookie', 'expires': '1d', 'domain': 'example.com', 'httponly': False, 'samesite': 'none', 'secure': True, 'path': 'path'}, 'sticky_route': [], 'sticky_learn': {'create': [], 'lookup': [], 'zone': {'name': 'client_sessions', 'size': '1m'}, 'timeout': '10m', 'header': False, 'sync': False}}], 'core': {'absolute_redirect': True, 'aio': {'threads': True}, 'aio_write': False, 'alias': 'path', 'auth_delay': '0s', 'chunked_transfer_encoding': True, 'client_body_buffer_size': '8k', 'client_body_in_file_only': False, 'client_body_in_single_buffer': False, 'client_body_temp_path': {'path': '/var/cache/nginx', 'level': 2}, 'client_body_timeout': '60s', 'client_header_buffer_size': '1k', 'client_header_timeout': '60s', 'client_max_body_size': '1m', 'connection_pool_size': 256, 'default_type': 'text/plain', 'directio': False, 'directio_alignment': 512, 'disable_symlinks': {'check': 'on', 'from': '$document_root'}, 'error_page': [{'code': [404], 'response': 404, 'uri': '/404.html'}], 'etag': True, 'if_modified_since': 'exact', 'ignore_invalid_headers': True, 'include': 'path', 'internal': False, 'keepalive_disable': 'msie6', 'keepalive_requests': 1000, 'keepalive_time': '1h', 'keepalive_timeout': {'timeout': '75s', 'header_timeout': '75s'}, 'large_client_header_buffers': {'number': 4, 'size': '8k'}, 'limit_except': {'method': 'GET', 'directive': ['allow all']}, 'limit_rate': 0, 'limit_rate_after': 0, 'lingering_close': True, 'lingering_time': '30s', 'lingering_timeout': '5s', 'listen': [{'address': '0.0.0.0', 'port': 80, 'default_server': True, 'ssl': False, 'http2': False, 'spdy': False, 'proxy_protocol': False, 'fastopen': 12, 'backlog': 511, 'rcvbuf': 512, 'sndbuf': 512, 'deffered': False, 'bind': False, 'ipv6only': False, 'reuseport': False, 'so_keepalive': {'keepidle': '30m', 'keepintvl': 5, 'keepcnt': 10}}], 'log_not_found': True, 'log_subrequest': False, 'max_ranges': 0, 'merge_slashes': True, 'msie_padding': True, 'msie_refresh': False, 'open_file_cache': {'max': 10, 'inactive': '60s'}, 'open_file_cache_errors': False, 'open_file_cache_min_uses': 1, 'open_file_cache_valid': '60s', 'output_buffers': {'number': 2, 'size': '32k'}, 'port_in_redirect': True, 'postpone_output': 1460, 'read_ahead': 0, 'recursive_error_pages': False, 'request_pool_size': '4k', 'reset_timedout_connection': False, 'resolver': {'address': '127.0.0.1', 'valid': '60s', 'ipv6': False, 'status_zone': 'zone'}, 'resolver_timeout': '30s', 'root': 'html', 'index': 'path', 'satisfy': 'all', 'send_lowat': 0, 'send_timeout': '60s', 'sendfile': False, 'sendfile_max_chunk': 0, 'server_name': '', 'server_name_in_redirect': False, 'server_names_hash_bucket_size': 32, 'server_names_hash_max_size': 512, 'server_tokens': True, 'subrequest_output_buffer_size': '4k', 'tcp_nodelay': True, 'tcp_nopush': False, 'try_files': {'files': '$uri', 'uri': '/uri', 'code': 'code'}, 'types': [{'mime': 'text/html', 'extensions': 'html'}], 'types_hash_bucket_size': 64, 'types_hash_max_size': 1024, 'underscores_in_headers': False, 'variables_hash_bucket_size': 64, 'variables_hash_max_size': 1024}, 'ssl': {'buffer_size': '16k', 'certificate': '/path/to/file', 'certificate_key': '/path/to/file', 'ciphers': 'HIGH', 'client_certificate': '/path/to/file', 'conf_command': 'command', 'crl': '/path/to/file', 'dhparam': '/path/to/file', 'early_data': False, 'ecdh_curve': 'auto', 'ocsp': False, 'ocsp_cache': {'name': 'cache', 'size': '16k'}, 'ocsp_responder': '<url>', 'password_file': '/path/to/file', 'prefer_server_ciphers': False, 'protocols': 'TLSv1', 'reject_handshake': False, 'session_cache': {'builtin': {'enable': False, 'size': '16k'}}, 'session_ticket_key': '/path/to/file', 'session_tickets': True, 'session_timeout': '5m', 'stapling': False, 'stapling_file': '/path/to/file', 'stapling_responder': '<url>', 'stapling_verify': False, 'trusted_certificate': '/path/to/file', 'verify_client': False, 'verify_depth': 1}, 'app_protect_waf': {'physical_memory_util_thresholds': {'high': 100, 'low': 100}, 'cpu_thresholds': {'high': 100, 'low': 100}, 'failure_mode_action': 'pass', 'cookie_seed': 'encryptionseed', 'compressed_requests_action': 'drop', 'reconnect_period_seconds': 5, 'request_buffer_overflow_action': 'pass', 'user_defined_signatures': '/path/to/file', 'enable': False, 'policy_file': '/path/to/file', 'security_log_enable': False, 'security_log': [{'path': '/path/to/file', 'dest': 'dest'}]}, 'app_protect_dos': {'enable': True, 'policy_file': '/etc/app_protect/conf/BADOSDefaultPolicy.json', 'name': 'samplename', 'monitor': {'uri': 'http://10.1.1.1:5000/monitor', 'protocol': 'http2', 'timeout': 10}, 'security_log_enable': True, 'security_log': {'path': '/etc/app_protect_dos/log-default.json', 'dest': 'syslog:server=10.1.1.1:514'}, 'liveness': {'enable': True, 'uri': 'example.com', 'port': 80}, 'readiness': {'enable': True, 'uri': 'example.com', 'port': 80}}, 'proxy': {'bind': {'address': '0.0.0.0', 'transparent': False}, 'buffer_size': '4k', 'buffering': True, 'buffers': {'number': 8, 'size': '4k'}, 'busy_buffers_size': '8k', 'cache': False, 'cache_background_update': False, 'cache_bypass': '$cookie_seed', 'cache_convert_head': True, 'cache_key': '$scheme$proxy_host$request_uri', 'cache_lock': False, 'cache_lock_age': '5s', 'cache_lock_timeout': '5s', 'cache_max_range_offset': 1, 'cache_methods': 'GET', 'cache_min_uses': 1, 'cache_path': [{'path': '/var/cache/nginx/proxy/backend', 'levels': '1:1', 'use_temp_path': False, 'keys_zone': {'name': 'backend_proxy_cache', 'size': '10m'}, 'inactive': '10m', 'max_size': '2g', 'min_free': '1g', 'manager_files': 100, 'manager_sleep': '500ms', 'manager_threshold': '200ms', 'loader_files': 100, 'loader_sleep': '50ms', 'loader_threshold': '200ms', 'purger': False, 'purger_files': 10, 'purger_sleep': '50ms', 'purger_threshold': '50ms'}], 'cache_purge': 'sample', 'cache_revalidate': False, 'cache_use_stale': False, 'cache_valid': [{'code': 200, 'time': '10m'}, '2m'], 'connect_timeout': '60s', 'cookie_domain': [{'domain': 'localhost', 'replacement': 'example.org'}], 'cookie_flags': {'cookie': 'one', 'flag': ['httponly']}, 'cookie_path': [{'path': '$uri', 'replacement': '$someuri'}], 'force_ranges': False, 'headers_hash_bucket_size': 64, 'headers_hash_max_size': 512, 'hide_header': 'Date', 'http_version': 1.1, 'ignore_client_abort': False, 'ignore_headers': 'X-Accel-Redirect', 'intercept_errors': False, 'limit_rate': 0, 'max_temp_file_size': '1024m', 'method': 'GET', 'next_upstream': False, 'next_upstream_timeout': 0, 'next_upstream_tries': 0, 'no_cache': '$cookie_nocache', 'pass': 'http://127.0.0.1', 'pass_header': 'Date', 'pass_request_body': False, 'pass_request_headers': True, 'read_timeout': '60s', 'redirect': {'original': 'http://upstream:port/two/', 'replacement': '/one/'}, 'request_buffering': False, 'send_lowat': 0, 'send_timeout': '60s', 'set_body': 'body', 'set_header': {'field': 'Host', 'value': '$proxy_host'}, 'socket_keepalive': False, 'ssl_certificate': '/path/to/file', 'ssl_certificate_key': '/path/to/file', 'ssl_ciphers': 'DEFAULT', 'ssl_conf_command': 'command', 'ssl_crl': '/path/to/file', 'ssl_name': '$proxy_host', 'ssl_password_file': '/path/to/file', 'ssl_protocols': 'TLSv1', 'ssl_server_name': False, 'ssl_session_reuse': True, 'ssl_trusted_certificate': '/path/to/file', 'ssl_verify': False, 'ssl_verify_depth': 1, 'store': False, 'store_access': {'user': 'rw', 'group': 'rw', 'all': 'r'}, 'temp_file_write_size': '8k', 'temp_path': {'path': '/var/cache/nginx/proxy/temp', 'level': 2}}, 'grpc': {'bind': {'address': '$remote_addr', 'transparent': True}, 'buffer_size': '4k', 'connect_timeout': '60s', 'hide_header': [], 'ignore_headers': [], 'intercept_errors': False, 'next_upstream': [], 'next_upstream_timeout': 0, 'next_upstream_tries': 0, 'pass': 'uri', 'pass_header': [], 'read_timeout': '60s', 'send_timeout': '60s', 'set_header': [{'field': 'Accept-Encoding', 'value': '""'}], 'socket_keepalive': False, 'ssl_certificate': '/path/to/file', 'ssl_certificate_key': '/path/to/file', 'ssl_ciphers': 'DEFAULT', 'ssl_conf_command': 'command', 'ssl_crl': '/path/to/file', 'ssl_name': 'serverName', 'ssl_password_file': '/path/to/file', 'ssl_protocols': [], 'ssl_server_name': False, 'ssl_session_reuse': True, 'ssl_trusted_certificate': '/path/to/file', 'ssl_verify': False, 'ssl_verify_depth': 1}, 'access': {'allow': 'localhost', 'deny': '192.168.1.100'}, 'auth_basic': {'realm': False, 'user_file': '/path/to/file'}, 'auth_request': {'uri': False, 'set': {'variable': '$temp', 'value': 'auth'}}, 'auth_jwt': {'enable': {'realm': 'realm', 'token': '$cookie_auth_token'}, 'claim_set': [{'variable': '$email', 'name': ['info']}], 'header_set': [{'variable': '$job', 'name': 'info'}], 'key_file': '/path/to/file', 'key_request': '/path/to/file', 'leeway': '0s', 'type': 'signed', 'require': '$valid_jwt_iss'}, 'api': {'enable': {'write': True}, 'status_zone': 'one'}, 'stub_status': True, 'autoindex': {'enable': False, 'exact_size': True, 'format': 'html', 'localtime': False}, 'gzip': {'enable': True, 'buffers': {'number': 32, 'size': '4k'}, 'comp_level': 1, 'disable': [], 'http_version': 1.1, 'min_length': 20, 'proxied': [], 'types': [], 'vary': False}, 'headers': {'add_headers': [{'name': 'Strict-Transport-Security', 'value': '"max-age=15768000; includeSubDomains"', 'always': False}], 'add_trailers': [{'name': 'Strict-Transport-Security', 'value': '"max-age=15768000; includeSubDomains"', 'always': False}], 'expires': {'modified': True, 'time': '12h'}}, 'health_check': {'health_checks': [{'interval': '5s', 'jitter': 0, 'fails': 1, 'passes': 1, 'uri': '/', 'mandatory': False, 'persistent': False, 'match': 'match', 'port': 80, 'grpc_service': 'service', 'grpc_status': 12}], 'match': [{'name': 'name', 'conditions': []}]}, 'keyval': {'keyvals': [{'key': 'key', 'variable': '$var', 'zone': 'one'}], 'zones': [{'name': 'one', 'size': '32k', 'state': '/var/lib/nginx/state/one.keyval', 'timeout': '60m', 'type': 'string', 'sync': False}]}, 'limit_req': {'limit_reqs': [{'zone': 'one', 'burst': 5, 'delay': False}], 'dry_run': False, 'log_level': 'error', 'status': 503, 'zones': [{'key': '$binary_remote_addr', 'name': 'one', 'size': '1m', 'rate': '10r/s', 'sync': False}]}, 'log': {'format': [{'name': 'main', 'escape': 'default', 'format': '\'$remote_addr - $remote_user [$time_local] "$request" \'\n\'$status $body_bytes_sent "$http_referer" \'\n\'"$http_user_agent" "$http_x_forwarded_for"\'\n'}], 'access': [{'path': '/var/log/nginx/access.log', 'format': 'main', 'buffer': '1m', 'gzip': 5, 'flush': '10h', 'if': '$loggable'}], 'error': {'file': '/var/log/nginx/error.log', 'level': 'notice'}, 'open_log_file_cache': {'max': 1000, 'inactive': '20s', 'min_uses': 2, 'valid': '1m'}}, 'rewrite': {'return': {'code': 200, 'text': 'text', 'url': 'https://example.com'}, 'rewrites': [{'regex': '(.*).html(.*)', 'replacement': '$1$2', 'flag': 'last'}], 'log': False, 'set': [{'variable': '$var', 'value': 'var'}], 'uninitialized_variable_warn': True}, 'sub_filter': {'sub_filters': [{'string': 'server_hostname', 'replacement': '$hostname'}], 'last_modified': False, 'once': True, 'types': 'text/html'}, 'custom_directives': ['fastcgi_split_path_info ^(.+\\.php)(/.+)$;', 'fastcgi_pass unix:/run/php/php7.2-fpm.sock;'], 'servers': [{'core': None, 'proxy': None, 'locations': [{'location': '/', 'proxy': None}]}, {'core': None, 'ssl': None, 'locations': [{'location': '/backend', 'core': None}]}]}}) => {"ansible_loop_var": "item", "changed": false, "item": {"config": {"access": {"allow": "localhost", "deny": "192.168.1.100"}, "api": {"enable": {"write": true}, "status_zone": "one"}, "app_protect_dos": {"enable": true, "liveness": {"enable": true, "port": 80, "uri": "example.com"}, "monitor": {"protocol": "http2", "timeout": 10, "uri": "http://10.1.1.1:5000/monitor"}, "name": "samplename", "policy_file": "/etc/app_protect/conf/BADOSDefaultPolicy.json", "readiness": {"enable": true, "port": 80, "uri": "example.com"}, "security_log": {"dest": "syslog:server=10.1.1.1:514", "path": "/etc/app_protect_dos/log-default.json"}, "security_log_enable": true}, "app_protect_waf": {"compressed_requests_action": "drop", "cookie_seed": "encryptionseed", "cpu_thresholds": {"high": 100, "low": 100}, "enable": false, "failure_mode_action": "pass", "physical_memory_util_thresholds": {"high": 100, "low": 100}, "policy_file": "/path/to/file", "reconnect_period_seconds": 5, "request_buffer_overflow_action": "pass", "security_log": [{"dest": "dest", "path": "/path/to/file"}], "security_log_enable": false, "user_defined_signatures": "/path/to/file"}, "auth_basic": {"realm": false, "user_file": "/path/to/file"}, "auth_jwt": {"claim_set": [{"name": ["info"], "variable": "$email"}], "enable": {"realm": "realm", "token": "$cookie_auth_token"}, "header_set": [{"name": "info", "variable": "$job"}], "key_file": "/path/to/file", "key_request": "/path/to/file", "leeway": "0s", "require": "$valid_jwt_iss", "type": "signed"}, "auth_request": {"set": {"value": "auth", "variable": "$temp"}, "uri": false}, "autoindex": {"enable": false, "exact_size": true, "format": "html", "localtime": false}, "core": {"absolute_redirect": true, "aio": {"threads": true}, "aio_write": false, "alias": "path", "auth_delay": "0s", "chunked_transfer_encoding": true, "client_body_buffer_size": "8k", "client_body_in_file_only": false, "client_body_in_single_buffer": false, "client_body_temp_path": {"level": 2, "path": "/var/cache/nginx"}, "client_body_timeout": "60s", "client_header_buffer_size": "1k", "client_header_timeout": "60s", "client_max_body_size": "1m", "connection_pool_size": 256, "default_type": "text/plain", "directio": false, "directio_alignment": 512, "disable_symlinks": {"check": "on", "from": "$document_root"}, "error_page": [{"code": [404], "response": 404, "uri": "/404.html"}], "etag": true, "if_modified_since": "exact", "ignore_invalid_headers": true, "include": "path", "index": "path", "internal": false, "keepalive_disable": "msie6", "keepalive_requests": 1000, "keepalive_time": "1h", "keepalive_timeout": {"header_timeout": "75s", "timeout": "75s"}, "large_client_header_buffers": {"number": 4, "size": "8k"}, "limit_except": {"directive": ["allow all"], "method": "GET"}, "limit_rate": 0, "limit_rate_after": 0, "lingering_close": true, "lingering_time": "30s", "lingering_timeout": "5s", "listen": [{"address": "0.0.0.0", "backlog": 511, "bind": false, "default_server": true, "deffered": false, "fastopen": 12, "http2": false, "ipv6only": false, "port": 80, "proxy_protocol": false, "rcvbuf": 512, "reuseport": false, "sndbuf": 512, "so_keepalive": {"keepcnt": 10, "keepidle": "30m", "keepintvl": 5}, "spdy": false, "ssl": false}], "log_not_found": true, "log_subrequest": false, "max_ranges": 0, "merge_slashes": true, "msie_padding": true, "msie_refresh": false, "open_file_cache": {"inactive": "60s", "max": 10}, "open_file_cache_errors": false, "open_file_cache_min_uses": 1, "open_file_cache_valid": "60s", "output_buffers": {"number": 2, "size": "32k"}, "port_in_redirect": true, "postpone_output": 1460, "read_ahead": 0, "recursive_error_pages": false, "request_pool_size": "4k", "reset_timedout_connection": false, "resolver": {"address": "127.0.0.1", "ipv6": false, "status_zone": "zone", "valid": "60s"}, "resolver_timeout": "30s", "root": "html", "satisfy": "all", "send_lowat": 0, "send_timeout": "60s", "sendfile": false, "sendfile_max_chunk": 0, "server_name": "", "server_name_in_redirect": false, "server_names_hash_bucket_size": 32, "server_names_hash_max_size": 512, "server_tokens": true, "subrequest_output_buffer_size": "4k", "tcp_nodelay": true, "tcp_nopush": false, "try_files": {"code": "code", "files": "$uri", "uri": "/uri"}, "types": [{"extensions": "html", "mime": "text/html"}], "types_hash_bucket_size": 64, "types_hash_max_size": 1024, "underscores_in_headers": false, "variables_hash_bucket_size": 64, "variables_hash_max_size": 1024}, "custom_directives": ["fastcgi_split_path_info ^(.+\\.php)(/.+)$;", "fastcgi_pass unix:/run/php/php7.2-fpm.sock;"], "grpc": {"bind": {"address": "$remote_addr", "transparent": true}, "buffer_size": "4k", "connect_timeout": "60s", "hide_header": [], "ignore_headers": [], "intercept_errors": false, "next_upstream": [], "next_upstream_timeout": 0, "next_upstream_tries": 0, "pass": "uri", "pass_header": [], "read_timeout": "60s", "send_timeout": "60s", "set_header": [{"field": "Accept-Encoding", "value": "\"\""}], "socket_keepalive": false, "ssl_certificate": "/path/to/file", "ssl_certificate_key": "/path/to/file", "ssl_ciphers": "DEFAULT", "ssl_conf_command": "command", "ssl_crl": "/path/to/file", "ssl_name": "serverName", "ssl_password_file": "/path/to/file", "ssl_protocols": [], "ssl_server_name": false, "ssl_session_reuse": true, "ssl_trusted_certificate": "/path/to/file", "ssl_verify": false, "ssl_verify_depth": 1}, "gzip": {"buffers": {"number": 32, "size": "4k"}, "comp_level": 1, "disable": [], "enable": true, "http_version": 1.1, "min_length": 20, "proxied": [], "types": [], "vary": false}, "headers": {"add_headers": [{"always": false, "name": "Strict-Transport-Security", "value": "\"max-age=15768000; includeSubDomains\""}], "add_trailers": [{"always": false, "name": "Strict-Transport-Security", "value": "\"max-age=15768000; includeSubDomains\""}], "expires": {"modified": true, "time": "12h"}}, "health_check": {"health_checks": [{"fails": 1, "grpc_service": "service", "grpc_status": 12, "interval": "5s", "jitter": 0, "mandatory": false, "match": "match", "passes": 1, "persistent": false, "port": 80, "uri": "/"}], "match": [{"conditions": [], "name": "name"}]}, "keyval": {"keyvals": [{"key": "key", "variable": "$var", "zone": "one"}], "zones": [{"name": "one", "size": "32k", "state": "/var/lib/nginx/state/one.keyval", "sync": false, "timeout": "60m", "type": "string"}]}, "limit_req": {"dry_run": false, "limit_reqs": [{"burst": 5, "delay": false, "zone": "one"}], "log_level": "error", "status": 503, "zones": [{"key": "$binary_remote_addr", "name": "one", "rate": "10r/s", "size": "1m", "sync": false}]}, "log": {"access": [{"buffer": "1m", "flush": "10h", "format": "main", "gzip": 5, "if": "$loggable", "path": "/var/log/nginx/access.log"}], "error": {"file": "/var/log/nginx/error.log", "level": "notice"}, "format": [{"escape": "default", "format": "'$remote_addr - $remote_user [$time_local] \"$request\" '\n'$status $body_bytes_sent \"$http_referer\" '\n'\"$http_user_agent\" \"$http_x_forwarded_for\"'\n", "name": "main"}], "open_log_file_cache": {"inactive": "20s", "max": 1000, "min_uses": 2, "valid": "1m"}}, "proxy": {"bind": {"address": "0.0.0.0", "transparent": false}, "buffer_size": "4k", "buffering": true, "buffers": {"number": 8, "size": "4k"}, "busy_buffers_size": "8k", "cache": false, "cache_background_update": false, "cache_bypass": "$cookie_seed", "cache_convert_head": true, "cache_key": "$scheme$proxy_host$request_uri", "cache_lock": false, "cache_lock_age": "5s", "cache_lock_timeout": "5s", "cache_max_range_offset": 1, "cache_methods": "GET", "cache_min_uses": 1, "cache_path": [{"inactive": "10m", "keys_zone": {"name": "backend_proxy_cache", "size": "10m"}, "levels": "1:1", "loader_files": 100, "loader_sleep": "50ms", "loader_threshold": "200ms", "manager_files": 100, "manager_sleep": "500ms", "manager_threshold": "200ms", "max_size": "2g", "min_free": "1g", "path": "/var/cache/nginx/proxy/backend", "purger": false, "purger_files": 10, "purger_sleep": "50ms", "purger_threshold": "50ms", "use_temp_path": false}], "cache_purge": "sample", "cache_revalidate": false, "cache_use_stale": false, "cache_valid": [{"code": 200, "time": "10m"}, "2m"], "connect_timeout": "60s", "cookie_domain": [{"domain": "localhost", "replacement": "example.org"}], "cookie_flags": {"cookie": "one", "flag": ["httponly"]}, "cookie_path": [{"path": "$uri", "replacement": "$someuri"}], "force_ranges": false, "headers_hash_bucket_size": 64, "headers_hash_max_size": 512, "hide_header": "Date", "http_version": 1.1, "ignore_client_abort": false, "ignore_headers": "X-Accel-Redirect", "intercept_errors": false, "limit_rate": 0, "max_temp_file_size": "1024m", "method": "GET", "next_upstream": false, "next_upstream_timeout": 0, "next_upstream_tries": 0, "no_cache": "$cookie_nocache", "pass": "http://127.0.0.1", "pass_header": "Date", "pass_request_body": false, "pass_request_headers": true, "read_timeout": "60s", "redirect": {"original": "http://upstream:port/two/", "replacement": "/one/"}, "request_buffering": false, "send_lowat": 0, "send_timeout": "60s", "set_body": "body", "set_header": {"field": "Host", "value": "$proxy_host"}, "socket_keepalive": false, "ssl_certificate": "/path/to/file", "ssl_certificate_key": "/path/to/file", "ssl_ciphers": "DEFAULT", "ssl_conf_command": "command", "ssl_crl": "/path/to/file", "ssl_name": "$proxy_host", "ssl_password_file": "/path/to/file", "ssl_protocols": "TLSv1", "ssl_server_name": false, "ssl_session_reuse": true, "ssl_trusted_certificate": "/path/to/file", "ssl_verify": false, "ssl_verify_depth": 1, "store": false, "store_access": {"all": "r", "group": "rw", "user": "rw"}, "temp_file_write_size": "8k", "temp_path": {"level": 2, "path": "/var/cache/nginx/proxy/temp"}}, "rewrite": {"log": false, "return": {"code": 200, "text": "text", "url": "https://example.com"}, "rewrites": [{"flag": "last", "regex": "(.*).html(.*)", "replacement": "$1$2"}], "set": [{"value": "var", "variable": "$var"}], "uninitialized_variable_warn": true}, "servers": [{"core": null, "locations": [{"location": "/", "proxy": null}], "proxy": null}, {"core": null, "locations": [{"core": null, "location": "/backend"}], "ssl": null}], "ssl": {"buffer_size": "16k", "certificate": "/path/to/file", "certificate_key": "/path/to/file", "ciphers": "HIGH", "client_certificate": "/path/to/file", "conf_command": "command", "crl": "/path/to/file", "dhparam": "/path/to/file", "early_data": false, "ecdh_curve": "auto", "ocsp": false, "ocsp_cache": {"name": "cache", "size": "16k"}, "ocsp_responder": "<url>", "password_file": "/path/to/file", "prefer_server_ciphers": false, "protocols": "TLSv1", "reject_handshake": false, "session_cache": {"builtin": {"enable": false, "size": "16k"}}, "session_ticket_key": "/path/to/file", "session_tickets": true, "session_timeout": "5m", "stapling": false, "stapling_file": "/path/to/file", "stapling_responder": "<url>", "stapling_verify": false, "trusted_certificate": "/path/to/file", "verify_client": false, "verify_depth": 1}, "stub_status": true, "sub_filter": {"last_modified": false, "once": true, "sub_filters": [{"replacement": "$hostname", "string": "server_hostname"}], "types": "text/html"}, "upstreams": [{"hash": {"consistent": false, "key": "key"}, "ip_hash": false, "keepalive": 32, "keepalive_requests": 100, "keepalive_time": "1h", "keepalive_timeout": "60s", "least_conn": false, "least_time": {"inflight": false, "response": "last_byte"}, "name": "backend", "ntlm": false, "queue": {"number": 10, "timeout": "60s"}, "random": {"method": "least_time=last_byte", "two": true}, "resolver": {"address": [], "ipv6": false, "status_zone": "backend_mem_zone", "valid": "30s"}, "resolver_timeout": "30s", "servers": [{"address": "localhost", "backup": false, "down": false, "drain": false, "fail_timeout": "10s", "max_conns": 0, "max_fails": 1, "resolve": false, "route": "a", "service": "http", "slow_start": "0s", "weight": 1}], "state": "/var/lib/nginx/state/servers.conf", "sticky_cookie": {"domain": "example.com", "expires": "1d", "httponly": false, "name": "cookie", "path": "path", "samesite": "none", "secure": true}, "sticky_learn": {"create": [], "header": false, "lookup": [], "sync": false, "timeout": "10m", "zone": {"name": "client_sessions", "size": "1m"}}, "sticky_route": [], "zone": {"name": "backend_mem_zone", "size": "64k"}}]}, "deployment_location": "/etc/nginx/conf.d/default.conf", "template_file": "http/default.conf.j2"}, "msg": "AnsibleUndefinedVariable: the inline if-expression on line 71 in 'http/ssl.j2' evaluated to false and no else section was defined."}

Expected behavior

Should not fail

Your environment

  • Version of the NGINX Config role or specific commit - nginx_config @ ba55d43
  • Version of Ansible - core 2.11.8
  • Version of Jinja2 (if you are using any templating capability) - jinja version = 2.11.2
  • Target deployment platform - CentOS-7

"AnsibleUndefinedVariable: the inline if-expression on line 9 in 'http/grpc.j2' evaluated to false and no else section was defined."}

Describe the bug

Can't apply configuration when the http section is enabled. I get the following error:

"AnsibleUndefinedVariable: the inline if-expression on line 9 in 'http/grpc.j2' evaluated to false and no else section was defined."}

The configuration has been copied from this example:

https://github.com/nginxinc/ansible-role-nginx-config/blob/main/molecule/default/converge.yml

To reproduce

Steps to reproduce the behavior:

# https://github.com/nginxinc/ansible-role-nginx
- name: Install NGINX
  include_role:
    name: nginxinc.nginx
  vars:
    nginx_install_from: os_repository
    nginx_start: false
    nginx_type: opensource
    nginx_state: present

# https://github.com/nginxinc/ansible-role-nginx-config
- name: Configure NGINX
  include_role:
    name: nginxinc.nginx_config
  vars:
    nginx_config_start: false
    nginx_config_cleanup: true
    nginx_config_debug_output: true
    nginx_config_debug_tasks: true

    # top level nginx configuration
    nginx_config_main_template_enable: true
    nginx_config_main_template:
      template_file: nginx.conf.j2
      conf_file_name: nginx.conf
      conf_file_location: /etc/nginx/
      worker_connections: 1024
      http_custom_includes:
        - "/etc/nginx/sites-enabled/*.conf"
      user: www-data
      worker_processes: auto
      pid: /var/run/nginx.pid

      # http configuration section
      http_enable: true
      http_settings:
        grpc_global:
          bind:
            address: $remote_addr
            transparent: false
          buffer_size: 4k
          connect_timeout: 60s
[...]

Expected behavior

I expected the configuration to apply without errors

Your environment:

  • nginxinc.nginx_config, 0.3.3
  • nginxinc.nginx, 0.19.1
  • ansible: 2.9.6+dfsg-1
  • ubuntu 20.04

Additional context

Add any other context about the problem here.

Add server_names_hash_bucket_size config setting in nginx.conf template

๐Ÿ‘‹ Hello! I'd like to submit a feature request/discussion.

Problem

Today when provisioning one of my NGINX servers, it directed me to increase the server_names_hash_bucket_size to 64. This presumably happens when the individual servers have long names/domains.
I'm configuring my NGINX configs with this role, but I didn't find a value in the template for this setting.

Solution

Include the setting in this role.

Describe alternatives you've considered

Manual patching or provisioning of the config.

Additional context

Error returned from Ansible on server restart:
Screenshot 2021-04-23 at 17 08 49

I could potentially also help with implementing this feature.
Thanks for replying and especially for maintaining this project! ๐Ÿ‘

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.