├── files └── empty ├── tests ├── inventory ├── vars │ ├── _bionic.yml │ ├── _default.yml │ └── main.yml ├── test.yml └── vagrant.yml ├── molecule └── default │ ├── collections.yml │ ├── prepare.yml │ ├── verify.yml │ ├── molecule.yml │ └── converge.yml ├── requirements.yml ├── .ansible-lint ├── templates ├── etc │ └── haproxy │ │ ├── acl.j2 │ │ ├── mailers.cfg.j2 │ │ ├── program.cfg.j2 │ │ ├── cache.cfg.j2 │ │ ├── haproxy.cfg.j2 │ │ ├── userlist.cfg.j2 │ │ ├── rings.cfg.j2 │ │ ├── logforward.cfg.j2 │ │ ├── resolvers.cfg.j2 │ │ ├── defaults.cfg.j2 │ │ ├── global.cfg.j2 │ │ ├── frontend.cfg.j2 │ │ ├── backend.cfg.j2 │ │ └── listen.cfg.j2 └── usr │ └── local │ └── bin │ ├── haproxy-letsencrypt-ssl-deploy.j2 │ └── haproxy-letsencrypt-ocsp-deploy.j2 ├── handlers └── main.yml ├── .yamllint ├── .github └── workflows │ ├── release.yml │ └── ci.yml ├── tasks ├── install.yml ├── configuration.yml ├── acl.yml ├── certificates.yml ├── letsencrypt.yml ├── repository.yml └── main.yml ├── .gitignore ├── meta └── main.yml ├── Dockerfile ├── LICENSE.txt ├── vars └── main.yml ├── Vagrantfile ├── defaults └── main.yml └── README.md /files/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/inventory: -------------------------------------------------------------------------------- 1 | localhost 2 | -------------------------------------------------------------------------------- /molecule/default/collections.yml: -------------------------------------------------------------------------------- 1 | --- 2 | collections: [] 3 | -------------------------------------------------------------------------------- /requirements.yml: -------------------------------------------------------------------------------- 1 | # requirements file 2 | --- 3 | collections: [] 4 | -------------------------------------------------------------------------------- /tests/vars/_bionic.yml: -------------------------------------------------------------------------------- 1 | # vars file 2 | --- 3 | haproxy_version: 2.6 4 | -------------------------------------------------------------------------------- /tests/vars/_default.yml: -------------------------------------------------------------------------------- 1 | # vars file 2 | --- 3 | haproxy_version: 2.9 4 | -------------------------------------------------------------------------------- /.ansible-lint: -------------------------------------------------------------------------------- 1 | --- 2 | warn_list: 3 | - role-name 4 | - name[play] 5 | - name[casing] 6 | -------------------------------------------------------------------------------- /molecule/default/prepare.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Prepare 3 | hosts: all 4 | become: true 5 | tasks: [] 6 | -------------------------------------------------------------------------------- /molecule/default/verify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Verify 3 | hosts: all 4 | become: true 5 | tasks: [] 6 | -------------------------------------------------------------------------------- /templates/etc/haproxy/acl.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | 3 | {% for content in item.content | default([]) %} 4 | {{ content }} 5 | {% endfor %} 6 | -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | # handlers file 2 | --- 3 | - name: restart haproxy 4 | ansible.builtin.service: 5 | name: haproxy 6 | state: "{{ haproxy_restart_handler_state }}" 7 | when: service_default_state | default('started') == 'started' 8 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | 4 | rules: 5 | braces: 6 | max-spaces-inside: 1 7 | level: error 8 | brackets: 9 | max-spaces-inside: 1 10 | level: error 11 | line-length: disable 12 | truthy: disable 13 | 14 | ignore: | 15 | .tox/ 16 | -------------------------------------------------------------------------------- /templates/etc/haproxy/mailers.cfg.j2: -------------------------------------------------------------------------------- 1 | {% for mailer in haproxy_mailers | default([]) %} 2 | mailers {{ mailer.name }} 3 | {% for server in mailer.servers | default([]) %} 4 | mailer {{ server.name }} {{ server.host }}:{{ server.port | default(25) }} 5 | {% endfor %} 6 | {% endfor %} -------------------------------------------------------------------------------- /templates/etc/haproxy/program.cfg.j2: -------------------------------------------------------------------------------- 1 | {% for program in haproxy_program | default([]) %} 2 | program {{ program.name }} 3 | {% if program.command is defined %} 4 | command {{ program.command }} 5 | {% endif %} 6 | {% for option in program.option | default([]) %} 7 | option {{ option }} 8 | {% endfor %} 9 | {% for option in program.no_option | default([]) %} 10 | no option {{ option }} 11 | {% endfor %} 12 | {% endfor %} 13 | -------------------------------------------------------------------------------- /templates/etc/haproxy/cache.cfg.j2: -------------------------------------------------------------------------------- 1 | {% for cache in haproxy_cache | default([]) %} 2 | cache {{ cache.name }} 3 | {% if cache.total_max_size is defined %} 4 | total-max-size {{ cache.total_max_size }} 5 | {% endif %} 6 | {% if cache.max_object_size is defined %} 7 | max-object-size {{ cache.max_object_size }} 8 | {% endif %} 9 | {% if cache.max_age is defined %} 10 | max-age {{ cache.max_age }} 11 | {% endif %} 12 | {% endfor %} 13 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Release 3 | 'on': 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | 10 | release: 11 | name: Release 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Check out the codebase 15 | uses: actions/checkout@v3 16 | 17 | - name: Publish to Galaxy 18 | uses: robertdebock/galaxy-action@1.2.0 19 | with: 20 | galaxy_api_key: ${{ secrets.GALAXY_API_KEY }} 21 | -------------------------------------------------------------------------------- /tasks/install.yml: -------------------------------------------------------------------------------- 1 | # tasks file 2 | --- 3 | - name: install | dependencies 4 | ansible.builtin.apt: 5 | name: "{{ item.name }}" 6 | state: "{{ item.state }}" 7 | with_items: "{{ haproxy_dependencies }}" 8 | tags: 9 | - haproxy-install-dependencies 10 | 11 | - name: install | additional 12 | ansible.builtin.apt: 13 | name: "{{ haproxy_install }}" 14 | state: "{{ apt_install_state | default('latest') }}" 15 | tags: 16 | - haproxy-install-additional 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS generated files # 2 | ###################### 3 | .DS_Store 4 | .DS_Store? 5 | ._* 6 | .Spotlight-V100 7 | .Trashes 8 | Icon? 9 | ehthumbs.db 10 | Thumbs.db 11 | 12 | # IDE files # 13 | ################# 14 | /.settings 15 | /.buildpath 16 | /.project 17 | /nbproject 18 | *.komodoproject 19 | *.kpf 20 | /.idea 21 | 22 | # Vagrant files # 23 | .virtualbox/ 24 | .vagrant/ 25 | vagrant_ansible_inventory_* 26 | ansible.cfg 27 | 28 | # Other files # 29 | ############### 30 | !empty 31 | *.sw[po] 32 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | # meta file 2 | --- 3 | galaxy_info: 4 | author: oefenweb 5 | role_name: haproxy 6 | company: Oefenweb.nl B.V. 7 | description: Set up the latest version of HAProxy in Ubuntu systems 8 | license: MIT 9 | min_ansible_version: 2.10.0 10 | platforms: 11 | - name: Ubuntu 12 | versions: 13 | - bionic 14 | - focal 15 | - jammy 16 | - noble 17 | galaxy_tags: 18 | - system 19 | - clustering 20 | - networking 21 | - web 22 | dependencies: [] 23 | -------------------------------------------------------------------------------- /templates/etc/haproxy/haproxy.cfg.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | 3 | global 4 | {% include 'global.cfg.j2' %} 5 | 6 | {% include 'logforward.cfg.j2' %} 7 | 8 | {% include 'rings.cfg.j2' %} 9 | 10 | defaults 11 | {% include 'defaults.cfg.j2' %} 12 | 13 | {% include 'userlist.cfg.j2' %} 14 | 15 | {% include 'resolvers.cfg.j2' %} 16 | 17 | {% include 'program.cfg.j2' %} 18 | 19 | {% include 'listen.cfg.j2' %} 20 | 21 | {% include 'cache.cfg.j2' %} 22 | 23 | {% include 'mailers.cfg.j2' %} 24 | 25 | {% include 'frontend.cfg.j2' %} 26 | 27 | {% include 'backend.cfg.j2' %} 28 | -------------------------------------------------------------------------------- /molecule/default/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: docker 6 | platforms: 7 | - name: instance 8 | image: "geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2004}-ansible:latest" 9 | command: ${MOLECULE_DOCKER_COMMAND:-""} 10 | volumes: 11 | - /sys/fs/cgroup:/sys/fs/cgroup:rw 12 | - /var/lib/containerd 13 | cgroupns_mode: host 14 | privileged: true 15 | pre_build_image: true 16 | provisioner: 17 | name: ansible 18 | playbooks: 19 | prepare: prepare.yml 20 | converge: converge.yml 21 | verify: verify.yml 22 | -------------------------------------------------------------------------------- /molecule/default/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | become: true 5 | pre_tasks: 6 | - name: include (first found) variables 7 | ansible.builtin.include_vars: "{{ item }}" 8 | with_first_found: 9 | - "{{ playbook_dir }}/../../tests/vars/_{{ ansible_distribution_release }}.yml" 10 | - "{{ playbook_dir }}/../../tests/vars/_{{ ansible_distribution | lower }}.yml" 11 | - "{{ playbook_dir }}/../../tests/vars/_default.yml" 12 | - name: include variables 13 | ansible.builtin.include_vars: "{{ playbook_dir }}/../../tests/vars/main.yml" 14 | -------------------------------------------------------------------------------- /tests/test.yml: -------------------------------------------------------------------------------- 1 | # test file 2 | --- 3 | - name: converge 4 | hosts: localhost 5 | connection: local 6 | become: true 7 | pre_tasks: 8 | - name: include (first found) variables 9 | ansible.builtin.include_vars: "{{ item }}" 10 | with_first_found: 11 | - "{{ playbook_dir }}/vars/_{{ ansible_distribution_release }}.yml" 12 | - "{{ playbook_dir }}/vars/_{{ ansible_distribution | lower }}.yml" 13 | - "{{ playbook_dir }}/vars/_default.yml" 14 | - name: include variables 15 | ansible.builtin.include_vars: "{{ playbook_dir }}/vars/main.yml" 16 | roles: 17 | - ../../ 18 | -------------------------------------------------------------------------------- /tests/vagrant.yml: -------------------------------------------------------------------------------- 1 | # test file 2 | --- 3 | - name: converge 4 | hosts: all 5 | remote_user: vagrant 6 | become: true 7 | pre_tasks: 8 | - name: include (first found) variables 9 | ansible.builtin.include_vars: "{{ item }}" 10 | with_first_found: 11 | - "{{ playbook_dir }}/vars/_{{ ansible_distribution_release }}.yml" 12 | - "{{ playbook_dir }}/vars/_{{ ansible_distribution | lower }}.yml" 13 | - "{{ playbook_dir }}/vars/_default.yml" 14 | - name: include variables 15 | ansible.builtin.include_vars: "{{ playbook_dir }}/vars/main.yml" 16 | roles: 17 | - ../../ 18 | -------------------------------------------------------------------------------- /tasks/configuration.yml: -------------------------------------------------------------------------------- 1 | # tasks file 2 | --- 3 | - name: configuration | warn or fail # noqa: ignore-errors 4 | ansible.builtin.fail: 5 | msg: "haproxy_global_nbproc is deprecated" 6 | ignore_errors: "{{ haproxy_version is version('2.5', '<') }}" 7 | when: 8 | - haproxy_version is version('2.3', '>=') 9 | - haproxy_global_nbproc > 1 10 | tags: 11 | - haproxy-configuration-warn-or-fail 12 | 13 | - name: configuration | update file 14 | ansible.builtin.template: 15 | src: "{{ haproxy_conf_template }}" 16 | dest: /etc/haproxy/haproxy.cfg 17 | owner: root 18 | group: root 19 | mode: '0640' 20 | validate: 'haproxy -f %s -c' 21 | notify: restart haproxy 22 | tags: 23 | - haproxy-configuration-update-file 24 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | MAINTAINER Mischa ter Smitten 3 | 4 | ENV LANG C.UTF-8 5 | ENV LC_ALL C.UTF-8 6 | 7 | # python 8 | RUN apt-get update && \ 9 | DEBIAN_FRONTEND=noninteractive apt-get install -y python3-minimal python3-dev curl && \ 10 | apt-get clean 11 | RUN curl -sL https://bootstrap.pypa.io/pip/3.6/get-pip.py | python3 - 12 | RUN rm -rf $HOME/.cache 13 | 14 | # ansible 15 | RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python3-apt && \ 16 | apt-get clean 17 | RUN pip3 install ansible==2.10.7 18 | RUN rm -rf $HOME/.cache 19 | 20 | # provision 21 | COPY . /etc/ansible/roles/ansible-role 22 | WORKDIR /etc/ansible/roles/ansible-role 23 | RUN ansible-playbook -i tests/inventory tests/test.yml --connection=local 24 | -------------------------------------------------------------------------------- /templates/etc/haproxy/userlist.cfg.j2: -------------------------------------------------------------------------------- 1 | {% for userlist in haproxy_userlists %} 2 | {% set groups = [] %} 3 | userlist {{ userlist.name }} 4 | {% for user in userlist.users %} 5 | {% if user['groups'] is defined %} 6 | {% set _ = groups.extend(user['groups']) %} 7 | {% set user_groups = ' groups ' ~ user['groups'] | join(',') %} 8 | {% else %} 9 | {% set user_groups = '' %} 10 | {% endif %} 11 | {% if user['password'] is defined %} 12 | user {{ user.name }} password {{ user.password }}{{ user_groups }} 13 | {% elif user['insecure_password'] is defined %} 14 | user {{ user.name }} insecure-password {{ user.insecure_password }}{{ user_groups }} 15 | {% endif %} 16 | {% endfor %} 17 | {% for group in groups | unique %} 18 | group {{ group }} 19 | {% endfor %} 20 | 21 | {% endfor %} 22 | -------------------------------------------------------------------------------- /tasks/acl.yml: -------------------------------------------------------------------------------- 1 | # tasks file 2 | --- 3 | - name: acl | create directories 4 | ansible.builtin.file: 5 | path: "{{ item.dest | dirname }}" 6 | state: directory 7 | owner: "{{ item.owner | default('root') }}" 8 | group: "{{ item.group | default('root') }}" 9 | mode: '0750' 10 | with_items: "{{ haproxy_acl_files }}" 11 | tags: 12 | - haproxy-acl-create-directories 13 | 14 | - name: acl | update files 15 | ansible.builtin.template: 16 | src: etc/haproxy/acl.j2 17 | dest: "{{ item.dest }}" 18 | owner: "{{ item.owner | default('root') }}" 19 | group: "{{ item.group | default('root') }}" 20 | mode: "{{ item.mode | default('0640') }}" 21 | with_items: "{{ haproxy_acl_files }}" 22 | notify: restart haproxy 23 | tags: 24 | - haproxy-acl-update-files 25 | -------------------------------------------------------------------------------- /templates/etc/haproxy/rings.cfg.j2: -------------------------------------------------------------------------------- 1 | {% for ring in haproxy_rings %} 2 | ring {{ ring.name }} 3 | {% if ring.description is defined %} 4 | description {{ ring.description }} 5 | {% endif %} 6 | {%- if ring.format is defined %} 7 | format {{ ring.format }} 8 | {% endif %} 9 | {%- if ring.maxlen is defined %} 10 | maxlen {{ ring.maxlen }} 11 | {% endif %} 12 | {%- if ring.size is defined %} 13 | size {{ ring.size }} 14 | {% endif %} 15 | {%- if ring.timeout.connect is defined %} 16 | timeout connect {{ ring.timeout.connect }} 17 | {% endif %} 18 | {%- if ring.timeout.server is defined %} 19 | timeout server {{ ring.timeout.server }} 20 | {% endif %} 21 | {%- for server in ring.server %} 22 | server {{ server.name }} {{ server.listen }}{% for param in server.param | default([]) %} {{ param }}{% endfor %} 23 | {% endfor %} 24 | 25 | {% endfor %} 26 | -------------------------------------------------------------------------------- /templates/etc/haproxy/logforward.cfg.j2: -------------------------------------------------------------------------------- 1 | {% if haproxy_version is version('2.3', '>=') %} 2 | {% for logforward in haproxy_logforwards %} 3 | log-forward {{ logforward.name }} 4 | {% if logforward.bind is defined %} 5 | bind {{ logforward.bind }} 6 | {% endif %} 7 | {%- if logforward.dgram_bind is defined %} 8 | dgram-bind {{ logforward.dgram_bind }} 9 | {% endif %} 10 | {%- if logforward.backlog is defined %} 11 | backlog {{ logforward.backlog }} 12 | {% endif %} 13 | {%- if logforward.maxconn is defined %} 14 | maxconn {{ logforward.maxconn }} 15 | {% endif %} 16 | {%- if logforward.timeout is defined %} 17 | timeout client {{ logforward.timeout }} 18 | {% endif %} 19 | {%- for log in logforward.log %} 20 | log {{ log.address }}{% for param in log.param | default([]) %} {{ param }}{% endfor %} 21 | {% endfor %} 22 | 23 | {% endfor %} 24 | {% endif %} 25 | -------------------------------------------------------------------------------- /templates/etc/haproxy/resolvers.cfg.j2: -------------------------------------------------------------------------------- 1 | {% for resolver in haproxy_resolvers %} 2 | resolvers {{ resolver.name }} 3 | {% for nameserver in resolver.nameservers %} 4 | nameserver {{ nameserver.name }} {{ nameserver.listen }} 5 | {% endfor %} 6 | {% if resolver.accepted_payload_size is defined -%} 7 | accepted_payload_size {{ resolver.accepted_payload_size }} 8 | {% endif %} 9 | {% if resolver.parse_resolv_conf is defined and resolver.parse_resolv_conf -%} 10 | parse-resolv-conf 11 | {% endif %} 12 | {% if resolver.hold is defined -%} 13 | {% for status, period in resolver.hold.items() | sort -%} 14 | hold {{ status }} {{ period }} 15 | {%- endfor %} 16 | {%- endif %} 17 | {% if resolver.resolve_retries is defined -%} 18 | resolve_retries {{ resolver.resolve_retries }} 19 | {% endif %} 20 | {% if resolver.timeout is defined -%} 21 | {% for event, time in resolver.timeout.items() | sort -%} 22 | timeout {{ event }} {{ time }} 23 | {%- endfor %} 24 | {%- endif %} 25 | {% endfor %} 26 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) Oefenweb.nl 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /templates/usr/local/bin/haproxy-letsencrypt-ssl-deploy.j2: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # {{ ansible_managed }} 4 | # 5 | # set -x; 6 | set -e; 7 | set -o pipefail; 8 | # 9 | thisFile="$(readlink -f "${0}")"; 10 | thisFilePath="$(dirname "${thisFile}")"; 11 | 12 | for path in $(ls -1d {{ haproxy_letsencrypt_ssl_src_path }}/*/); do 13 | cert="$(basename ${path})"; 14 | 15 | prefix="100"; 16 | removePrefix="000"; 17 | 18 | if [ "${cert}" == "{{ haproxy_letsencrypt_ssl_first_cert }}" ]; then 19 | prefix="000"; 20 | removePrefix="100"; 21 | fi 22 | 23 | cat "${path}{{ haproxy_letsencrypt_ssl_fullchain_name }}" "${path}{{ haproxy_letsencrypt_ssl_privkey_name }}" > "{{ haproxy_global_crt_base }}/${prefix}-${cert}.pem"; 24 | 25 | chown --reference="{{ haproxy_global_crt_base }}" "{{ haproxy_global_crt_base }}/${prefix}-${cert}.pem"; 26 | chmod --reference="{{ haproxy_global_crt_base }}" "{{ haproxy_global_crt_base }}/${prefix}-${cert}.pem"; 27 | chmod -x "{{ haproxy_global_crt_base }}/${prefix}-${cert}.pem"; 28 | 29 | rm -f "{{ haproxy_global_crt_base }}/${removePrefix}-${cert}.pem"; 30 | done 31 | 32 | find "{{ haproxy_global_crt_base }}" -mindepth 1 -name "*.pem" -mtime +0 -delete; 33 | 34 | {{ haproxy_letsencrypt_ocsp_deploy }}; 35 | -------------------------------------------------------------------------------- /tasks/certificates.yml: -------------------------------------------------------------------------------- 1 | # tasks file 2 | --- 3 | - name: certificates | create directories 4 | ansible.builtin.file: 5 | path: "{{ item.dest | dirname }}" 6 | state: directory 7 | owner: "{{ item.owner | default('root') }}" 8 | group: "{{ item.group | default('root') }}" 9 | mode: '0750' 10 | with_items: "{{ haproxy_ssl_map }}" 11 | when: item.state is undefined or item.state == 'present' 12 | tags: 13 | - haproxy-certificates-create-directories 14 | 15 | - name: certificates | copy files 16 | ansible.builtin.copy: 17 | src: "{{ item.src }}" 18 | dest: "{{ item.dest }}" 19 | owner: "{{ item.owner | default('root') }}" 20 | group: "{{ item.group | default('root') }}" 21 | mode: "{{ item.mode | default('0640') }}" 22 | with_items: "{{ haproxy_ssl_map }}" 23 | when: item.state is undefined or item.state == 'present' 24 | notify: restart haproxy 25 | tags: 26 | - haproxy-certificates-copy-files 27 | 28 | - name: certificates | remove files 29 | ansible.builtin.file: 30 | path: "{{ item.dest }}" 31 | state: absent 32 | with_items: "{{ haproxy_ssl_map }}" 33 | when: item.state is defined and item.state == 'absent' 34 | notify: restart haproxy 35 | tags: 36 | - haproxy-certificates-remove-files 37 | -------------------------------------------------------------------------------- /vars/main.yml: -------------------------------------------------------------------------------- 1 | # vars file 2 | --- 3 | haproxy_versions_supported: 4 | - '1.5' 5 | - '1.6' 6 | - '1.7' 7 | - '1.8' 8 | - '1.9' 9 | - '2.0' 10 | - '2.1' 11 | - '2.2' 12 | - '2.3' 13 | - '2.4' 14 | - '2.5' 15 | - '2.6' 16 | - '2.7' 17 | - '2.8' 18 | - '2.9' 19 | - '3.0' 20 | - '3.1' 21 | - '3.2' 22 | 23 | haproxy_keyring_id: 3D653970FBAB0A890E4E4E9A0F14D8B0CF4EFE96 24 | haproxy_keyring_dst: /usr/share/keyrings/haproxy.gpg 25 | haproxy_repositories: 26 | - type: "deb [signed-by={{ haproxy_keyring_dst }}]" 27 | url: "https://ppa.launchpadcontent.net/vbernat/haproxy-{{ haproxy_version }}/{{ ansible_distribution | lower }} {{ ansible_distribution_release }}" 28 | component: main 29 | _haproxy_version_sources_list_d: "{{ (haproxy_version | string).split('.') | join('_') }}" 30 | haproxy_repository_files_absent: 31 | - "/etc/apt/sources.list.d/ppa_vbernat_haproxy_{{ _haproxy_version_sources_list_d }}_{{ ansible_distribution_release }}.list" 32 | - "/etc/apt/sources.list.d/ppa_vbernat_haproxy_{{ _haproxy_version_sources_list_d }}_{{ ansible_distribution_release }}.list.save" 33 | 34 | haproxy_dependencies_pre: 35 | - software-properties-common 36 | - dirmngr 37 | - apt-transport-https 38 | 39 | haproxy_letsencrypt_ssl_deploy: /usr/local/bin/haproxy-letsencrypt-ssl-deploy 40 | 41 | haproxy_letsencrypt_ocsp_deploy: /usr/local/bin/haproxy-letsencrypt-ocsp-deploy 42 | -------------------------------------------------------------------------------- /tasks/letsencrypt.yml: -------------------------------------------------------------------------------- 1 | # tasks file 2 | --- 3 | - name: letsencrypt | copy SSL deploy script 4 | ansible.builtin.template: 5 | src: "{{ haproxy_letsencrypt_ssl_deploy_template }}" 6 | dest: "{{ haproxy_letsencrypt_ssl_deploy }}" 7 | owner: root 8 | group: root 9 | mode: '0755' 10 | tags: 11 | - haproxy-letsencrypt-ssl-deploy 12 | 13 | - name: letsencrypt | copy OCSP deploy script 14 | ansible.builtin.template: 15 | src: "{{ haproxy_letsencrypt_ocsp_deploy_template }}" 16 | dest: "{{ haproxy_letsencrypt_ocsp_deploy }}" 17 | owner: root 18 | group: root 19 | mode: '0755' 20 | tags: 21 | - haproxy-letsencrypt-ocsp-deploy 22 | 23 | - name: letsencrypt | configure (cron) job for OCSP deploy 24 | ansible.builtin.cron: 25 | name: haproxy-letsencrypt-ocsp-deploy 26 | job: "{{ haproxy_letsencrypt_ocsp_deploy }}" 27 | state: "{{ haproxy_letsencrypt_ocsp_deploy_job.state | default('absent') }}" 28 | day: "{{ haproxy_letsencrypt_ocsp_deploy_job.day | default('*') }}" 29 | hour: "{{ haproxy_letsencrypt_ocsp_deploy_job.hour | default(0) }}" 30 | minute: "{{ haproxy_letsencrypt_ocsp_deploy_job.minute | default(0) }}" 31 | month: "{{ haproxy_letsencrypt_ocsp_deploy_job.month | default('*') }}" 32 | weekday: "{{ haproxy_letsencrypt_ocsp_deploy_job.weekday | default('*') }}" 33 | cron_file: haproxy-letsencrypt-ocsp-deploy 34 | user: root 35 | tags: 36 | - haproxy-letsencrypt-cron-ocsp-deploy 37 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby ts=2 sw=2 tw=0 et : 3 | 4 | role = File.basename(File.expand_path(File.dirname(__FILE__))) 5 | 6 | boxes = [ 7 | { 8 | :name => "ubuntu-1804", 9 | :box => "bento/ubuntu-18.04", 10 | :ip => '10.0.0.13', 11 | :cpu => "50", 12 | :ram => "384" 13 | }, 14 | { 15 | :name => "ubuntu-2004", 16 | :box => "bento/ubuntu-20.04", 17 | :ip => '10.0.0.14', 18 | :cpu => "50", 19 | :ram => "512" 20 | }, 21 | { 22 | :name => "ubuntu-2204", 23 | :box => "bento/ubuntu-22.04", 24 | :ip => '10.0.0.15', 25 | :cpu => "50", 26 | :ram => "512" 27 | }, 28 | { 29 | :name => "ubuntu-2404", 30 | :box => "bento/ubuntu-24.04", 31 | :ip => '10.0.0.16', 32 | :cpu => "50", 33 | :ram => "512" 34 | }, 35 | ] 36 | 37 | Vagrant.configure("2") do |config| 38 | boxes.each do |box| 39 | config.vm.define box[:name] do |vms| 40 | vms.vm.box = box[:box] 41 | vms.vm.hostname = "ansible-#{role}-#{box[:name]}" 42 | 43 | vms.vm.provider "virtualbox" do |v| 44 | v.customize ["modifyvm", :id, "--cpuexecutioncap", box[:cpu]] 45 | v.customize ["modifyvm", :id, "--memory", box[:ram]] 46 | end 47 | 48 | vms.vm.network :private_network, ip: box[:ip] 49 | 50 | vms.vm.provision :ansible do |ansible| 51 | ansible.playbook = "tests/vagrant.yml" 52 | ansible.verbose = "vv" 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /templates/etc/haproxy/defaults.cfg.j2: -------------------------------------------------------------------------------- 1 | {% if haproxy_defaults_log != false %} 2 | log {{ haproxy_defaults_log }} 3 | {% endif %} 4 | {% if haproxy_defaults_logformat is defined %} 5 | log-format {{ haproxy_defaults_logformat }} 6 | {% endif %} 7 | {% if haproxy_defaults_mode != false %} 8 | mode {{ haproxy_defaults_mode }} 9 | {% endif %} 10 | {% if haproxy_defaults_source is defined %} 11 | source {{ haproxy_defaults_source }} 12 | {% endif %} 13 | {% if haproxy_defaults_option != false %} 14 | {% for option in haproxy_defaults_option %} 15 | option {{ option }} 16 | {% endfor %} 17 | {% endif %} 18 | {% for option in haproxy_defaults_no_option | default([]) %} 19 | no option {{ option }} 20 | {% endfor %} 21 | {% if haproxy_defaults_timeout != false %} 22 | {% for timeout in haproxy_defaults_timeout %} 23 | timeout {{ timeout.type }} {{ timeout.timeout }} 24 | {% endfor %} 25 | {% endif %} 26 | {% if haproxy_defaults_errorfile != false %} 27 | {% for errorfile in haproxy_defaults_errorfile %} 28 | errorfile {{ errorfile.code }} {{ errorfile.file }} 29 | {% endfor %} 30 | {% endif %} 31 | {% for compression in haproxy_defaults_compression | default([]) %} 32 | compression {{ compression.name }} {{ compression.value }} 33 | {% endfor %} 34 | 35 | {% if haproxy_default_server_params | default([]) %} 36 | default-server {% for param in haproxy_default_server_params | default([]) %} {{ param }}{% endfor %} 37 | {% endif %} 38 | 39 | {% for line in haproxy_default_raw_options | default([]) %} 40 | {{ line }} 41 | {% endfor %} 42 | -------------------------------------------------------------------------------- /tasks/repository.yml: -------------------------------------------------------------------------------- 1 | # tasks file 2 | --- 3 | - name: repository | install dependencies (pre) 4 | ansible.builtin.apt: 5 | name: "{{ haproxy_dependencies_pre }}" 6 | state: "{{ apt_install_state | default('latest') }}" 7 | update_cache: true 8 | cache_valid_time: "{{ apt_update_cache_valid_time | default(3600) }}" 9 | tags: 10 | - haproxy-repository-install-dependencies 11 | 12 | - name: repository | (keyrings) directory | create 13 | ansible.builtin.file: 14 | path: "{{ haproxy_keyring_dst | dirname }}" 15 | state: directory 16 | owner: root 17 | group: root 18 | mode: '0755' 19 | tags: 20 | - haproxy-repository-keyrings-directory-create 21 | 22 | - name: repository | (keyring) file | download 23 | ansible.builtin.apt_key: 24 | id: "{{ haproxy_keyring_id }}" 25 | keyserver: "{{ apt_key_keyserver | default('keyserver.ubuntu.com') }}" 26 | keyring: "{{ haproxy_keyring_dst }}" 27 | state: present 28 | tags: 29 | - haproxy-repository-keyring-file-download 30 | 31 | - name: repository | cleanup 32 | ansible.builtin.file: 33 | path: "{{ item }}" 34 | state: absent 35 | with_items: "{{ haproxy_repository_files_absent }}" 36 | tags: 37 | - haproxy-repository-cleanup 38 | 39 | - name: repository | add 40 | ansible.builtin.apt_repository: 41 | repo: "{{ item.type }} {{ item.url }} {{ item.component }}" 42 | state: "{{ item.state | default('present') }}" 43 | update_cache: true 44 | mode: '0644' 45 | with_items: "{{ haproxy_repositories }}" 46 | tags: 47 | - haproxy-repository-add 48 | -------------------------------------------------------------------------------- /templates/usr/local/bin/haproxy-letsencrypt-ocsp-deploy.j2: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # {{ ansible_managed }} 4 | # 5 | # set -x; 6 | set -e; 7 | set -o pipefail; 8 | # 9 | thisFile="$(readlink -f "${0}")"; 10 | thisFilePath="$(dirname "${thisFile}")"; 11 | 12 | for path in $(ls -1d {{ haproxy_letsencrypt_ssl_src_path }}/*/); do 13 | cert="$(basename ${path})"; 14 | 15 | prefix="100"; 16 | removePrefix="000"; 17 | 18 | if [ "${cert}" == "{{ haproxy_letsencrypt_ssl_first_cert }}" ]; then 19 | prefix="000"; 20 | removePrefix="100"; 21 | fi 22 | 23 | ocspUrl="$(openssl x509 -noout -ocsp_uri -in ${path}{{ haproxy_letsencrypt_ssl_cert_name }})"; 24 | 25 | openssl ocsp -no_nonce -respout "{{ haproxy_global_crt_base }}/${prefix}-${cert}.pem.ocsp" \ 26 | -issuer "${path}{{ haproxy_letsencrypt_ssl_chain_name }}" \ 27 | -verify_other "${path}{{ haproxy_letsencrypt_ssl_chain_name }}" \ 28 | -cert "${path}{{ haproxy_letsencrypt_ssl_cert_name }}" \ 29 | -url "${ocspUrl}" || true; 30 | 31 | chown --reference="{{ haproxy_global_crt_base }}" "{{ haproxy_global_crt_base }}/${prefix}-${cert}.pem.ocsp"; 32 | chmod --reference="{{ haproxy_global_crt_base }}" "{{ haproxy_global_crt_base }}/${prefix}-${cert}.pem.ocsp"; 33 | chmod -x "{{ haproxy_global_crt_base }}/${prefix}-${cert}.pem.ocsp"; 34 | 35 | rm -f "{{ haproxy_global_crt_base }}/${removePrefix}-${cert}.pem.ocsp"; 36 | done 37 | 38 | find "{{ haproxy_global_crt_base }}" -mindepth 1 -name "*.ocsp" -mtime +0 -delete; 39 | 40 | {% if ansible_service_mgr == 'systemd' %} 41 | systemctl reload haproxy; 42 | {% else %} 43 | service haproxy reload; 44 | {% endif %} 45 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | # tasks file 2 | --- 3 | - name: check version support 4 | ansible.builtin.fail: 5 | msg: "HAProxy version {{ haproxy_version }} is not supported" 6 | when: haproxy_version | string not in haproxy_versions_supported 7 | tags: 8 | - configuration 9 | - haproxy 10 | - haproxy-check-version-support 11 | 12 | - name: repository 13 | ansible.builtin.import_tasks: repository.yml 14 | when: haproxy_use_ppa | bool 15 | tags: 16 | - configuration 17 | - haproxy 18 | - haproxy-repository 19 | 20 | - name: install 21 | ansible.builtin.import_tasks: install.yml 22 | tags: 23 | - configuration 24 | - haproxy 25 | - haproxy-install 26 | 27 | - name: certificates 28 | ansible.builtin.import_tasks: certificates.yml 29 | tags: 30 | - configuration 31 | - haproxy 32 | - haproxy-certificates 33 | 34 | - name: acl 35 | ansible.builtin.import_tasks: acl.yml 36 | tags: 37 | - configuration 38 | - haproxy 39 | - haproxy-acl 40 | 41 | - name: configuration 42 | ansible.builtin.import_tasks: configuration.yml 43 | tags: 44 | - configuration 45 | - haproxy 46 | - haproxy-configuration 47 | 48 | - name: letsencrypt 49 | ansible.builtin.import_tasks: letsencrypt.yml 50 | tags: 51 | - configuration 52 | - haproxy 53 | - haproxy-letsencrypt 54 | 55 | - name: start and enable service 56 | ansible.builtin.service: 57 | name: haproxy 58 | state: "{{ service_default_state | default('started') }}" 59 | enabled: "{{ service_default_enabled | default(true) | bool }}" 60 | tags: 61 | - configuration 62 | - haproxy 63 | - haproxy-start-enable-service 64 | -------------------------------------------------------------------------------- /tests/vars/main.yml: -------------------------------------------------------------------------------- 1 | # vars file 2 | --- 3 | # global parameters 4 | haproxy_global_raw_options: 5 | - cpu-map 1/all 0-3 6 | - server-state-file /tmp/haproxy.state 7 | 8 | # default section 9 | haproxy_default_raw_options: 10 | - load-server-state-from-file global 11 | 12 | # front-end section 13 | haproxy_frontend: 14 | - name: http 15 | bind: 16 | - listen: '0.0.0.0:80' 17 | mode: http 18 | default_backend: webservers 19 | http_request: 20 | - action: replace-header 21 | param: 'Host (.*) foo.com' 22 | http_response: 23 | - action: deny 24 | cond: 'if { hdr(Content-type) -i -m end ms-word }' 25 | 26 | # back-end section 27 | haproxy_backend: 28 | - name: webservers 29 | mode: http 30 | balance: roundrobin 31 | option: 32 | - forwardfor 33 | http_check: 'send meth HEAD uri / ver HTTP/1.1 hdr Host localhost' 34 | server: [] 35 | http_response: 36 | - action: deny 37 | cond: 'if { hdr(Content-type) -m end ms-word }' 38 | 39 | # user-lists section 40 | haproxy_userlists: 41 | - name: test_userlist 42 | users: 43 | - name: testuser1 44 | # secrete 45 | password: $6$gLMr0TwOYURPhpXh$onP.5aHZGPE3xufyF8U0/wEKHMz71ECFBx4.uiO7t2ypgyvXS6MNFKHTo16qLttYJYObb0WbXyDmoNRsO4jtq. 46 | groups: 47 | - test_grp1 48 | - test_grp2 49 | - name: testuser2 50 | insecure_password: secrete 51 | groups: 52 | - test_grp2 53 | 54 | # ACL files 55 | haproxy_acl_files: 56 | - dest: /etc/haproxy/acl/ported-paths.list 57 | content: 58 | - | 59 | ^/users/add_player$ 60 | ^/users/view.*$ 61 | 62 | - dest: /etc/haproxy/acl/api.map 63 | content: 64 | - | 65 | v1.0 be_alpha 66 | v1.1 be_bravo 67 | v2.5 be_charlie 68 | v2.2 be_alpha 69 | v1.1 be_delta 70 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: CI 3 | 'on': 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | schedule: 9 | - cron: '30 1 * * 3' 10 | 11 | jobs: 12 | 13 | lint: 14 | name: Lint 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Check out the codebase 18 | uses: actions/checkout@v3 19 | 20 | - name: Set up Python 3 21 | uses: actions/setup-python@v4 22 | with: 23 | python-version: '3.x' 24 | 25 | - name: Install test dependencies 26 | run: | 27 | pip install ansible-lint 28 | ansible-galaxy install -r requirements.yml 29 | 30 | - name: Lint code 31 | run: | 32 | yamllint . 33 | ansible-lint 34 | 35 | molecule: 36 | name: Molecule 37 | runs-on: ubuntu-latest 38 | defaults: 39 | run: 40 | working-directory: "${{ github.repository }}" 41 | needs: 42 | - lint 43 | strategy: 44 | fail-fast: false 45 | matrix: 46 | include: 47 | - distro: ubuntu1804 48 | ansible-version: '>=9, <10' 49 | - distro: ubuntu2004 50 | - distro: ubuntu2204 51 | - distro: ubuntu2404 52 | 53 | steps: 54 | - name: Check out the codebase 55 | uses: actions/checkout@v3 56 | with: 57 | path: "${{ github.repository }}" 58 | 59 | - name: Set up Python 3 60 | uses: actions/setup-python@v4 61 | with: 62 | python-version: '3.x' 63 | 64 | - name: Install test dependencies 65 | run: | 66 | pip install 'ansible${{ matrix.ansible-version }}' molecule-plugins[docker] docker 67 | - name: Run Molecule tests 68 | run: | 69 | molecule test 70 | env: 71 | ANSIBLE_FORCE_COLOR: '1' 72 | ANSIBLE_VERBOSITY: '2' 73 | MOLECULE_DEBUG: '1' 74 | MOLECULE_DISTRO: "${{ matrix.distro }}" 75 | PY_COLORS: '1' 76 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | # defaults file 2 | --- 3 | haproxy_use_ppa: true 4 | haproxy_version: 2.8 5 | 6 | haproxy_dependencies: 7 | - name: haproxy 8 | state: latest 9 | haproxy_install: [] 10 | 11 | haproxy_restart_handler_state: restarted 12 | 13 | haproxy_conf_template: "etc/haproxy/haproxy.cfg.j2" 14 | 15 | # global section 16 | haproxy_global_log: 17 | - address: /dev/log 18 | facility: local0 19 | - address: /dev/log 20 | facility: local1 21 | level: notice 22 | haproxy_global_stats: 23 | sockets: 24 | - listen: /run/haproxy/admin.sock 25 | timeout: 30s 26 | haproxy_global_user: haproxy 27 | haproxy_global_group: haproxy 28 | haproxy_global_daemon: true 29 | haproxy_global_ca_base: /etc/ssl/certs 30 | haproxy_global_crt_base: /etc/ssl/private 31 | haproxy_global_ssl_default_bind_ciphers: 'kEECDH+aRSA+AES:kRSA+AES:+AES256:RC4-SHA:!kEDH:!LOW:!EXP:!MD5:!aNULL:!eNULL' 32 | haproxy_global_ssl_default_bind_ciphersuites: '' 33 | haproxy_global_ssl_default_bind_options: 'no-sslv3' 34 | haproxy_global_ssl_default_server_ciphers: 'kEECDH+aRSA+AES:kRSA+AES:+AES256:RC4-SHA:!kEDH:!LOW:!EXP:!MD5:!aNULL:!eNULL' 35 | haproxy_global_ssl_default_server_ciphersuites: '' 36 | haproxy_global_ssl_default_server_options: 'no-sslv3' 37 | haproxy_global_nbproc: 1 38 | haproxy_global_option: [] 39 | 40 | # Logforward section 41 | haproxy_logforwards: [] 42 | 43 | # Rings section 44 | haproxy_rings: [] 45 | 46 | # defaults section 47 | haproxy_defaults_log: global 48 | haproxy_defaults_mode: http 49 | haproxy_defaults_option: 50 | - httplog 51 | - dontlognull 52 | haproxy_default_server_params: [] 53 | haproxy_defaults_timeout: 54 | - type: connect 55 | timeout: 5000 56 | - type: client 57 | timeout: 50000 58 | - type: server 59 | timeout: 50000 60 | haproxy_defaults_errorfile: 61 | - code: 400 62 | file: /etc/haproxy/errors/400.http 63 | - code: 403 64 | file: /etc/haproxy/errors/403.http 65 | - code: 408 66 | file: /etc/haproxy/errors/408.http 67 | - code: 500 68 | file: /etc/haproxy/errors/500.http 69 | - code: 502 70 | file: /etc/haproxy/errors/502.http 71 | - code: 503 72 | file: /etc/haproxy/errors/503.http 73 | - code: 504 74 | file: /etc/haproxy/errors/504.http 75 | 76 | # ssl (file) map 77 | haproxy_ssl_map: [] 78 | 79 | # listen section 80 | haproxy_listen: [] 81 | 82 | # mailers section 83 | haproxy_mailers: [] 84 | 85 | # front-end section 86 | haproxy_frontend: [] 87 | 88 | # back-end section 89 | haproxy_backend: [] 90 | 91 | # user-lists section 92 | haproxy_userlists: [] 93 | 94 | # resolvers section: 95 | haproxy_resolvers: [] 96 | 97 | # ACL files 98 | haproxy_acl_files: [] 99 | 100 | # Letsencrypt (SSL/OCSP deploy) 101 | haproxy_letsencrypt_ssl_deploy_template: usr/local/bin/haproxy-letsencrypt-ssl-deploy.j2 102 | haproxy_letsencrypt_ssl_first_cert: "{{ inventory_hostname }}" 103 | haproxy_letsencrypt_ssl_src_path: /etc/letsencrypt/live 104 | haproxy_letsencrypt_ssl_fullchain_name: fullchain.pem 105 | haproxy_letsencrypt_ssl_chain_name: chain.pem 106 | haproxy_letsencrypt_ssl_privkey_name: privkey.pem 107 | haproxy_letsencrypt_ssl_cert_name: cert.pem 108 | haproxy_letsencrypt_ocsp_deploy_template: usr/local/bin/haproxy-letsencrypt-ocsp-deploy.j2 109 | haproxy_letsencrypt_ocsp_deploy_job: {} 110 | -------------------------------------------------------------------------------- /templates/etc/haproxy/global.cfg.j2: -------------------------------------------------------------------------------- 1 | {% if haproxy_global_log != false %} 2 | {% for log in haproxy_global_log %} 3 | log {{ log.address }}{% if log.length is defined %} len {{log.length }}{% endif %} {{ log.facility }}{% if log.level is defined %} {{log.level }}{% endif %}{% if log.minlevel is defined %} {{ log.minlevel }}{% endif %} 4 | 5 | {% endfor %} 6 | {% endif %} 7 | {% if haproxy_global_chroot is defined %} 8 | chroot {{ haproxy_global_chroot }} 9 | {% endif %} 10 | {% if haproxy_global_stats != false %} 11 | {% for socket in haproxy_global_stats.sockets | default([]) %} 12 | stats socket {{ socket.listen }}{% for param in socket.param | default([]) %} {{ param }}{% endfor %} 13 | 14 | {% endfor %} 15 | {% if haproxy_global_stats.timeout is defined %} 16 | stats timeout {{ haproxy_global_stats.timeout }} 17 | {% endif -%} 18 | {% endif %} 19 | {% if haproxy_global_user != false %} 20 | user {{ haproxy_global_user }} 21 | {% endif %} 22 | {% if haproxy_global_group != false %} 23 | group {{ haproxy_global_group }} 24 | {% endif %} 25 | {% if haproxy_global_daemon | bool == true %} 26 | daemon 27 | {% endif %} 28 | {% if haproxy_global_master_worker | default(false) | bool %} 29 | master-worker 30 | {% endif %} 31 | {% if haproxy_global_maxconn is defined %} 32 | maxconn {{ haproxy_global_maxconn }} 33 | {% endif %} 34 | {% if haproxy_global_ca_base != false %} 35 | # Default SSL material locations 36 | ca-base {{ haproxy_global_ca_base }} 37 | {% endif %} 38 | {% if haproxy_global_crt_base != false %} 39 | crt-base {{ haproxy_global_crt_base }} 40 | {% endif %} 41 | {% if haproxy_version is version('2.2', '>=') and haproxy_global_ssl_default_bind_curves is defined %} 42 | ssl-default-bind-curves {{ haproxy_global_ssl_default_bind_curves }} 43 | {% endif %} 44 | {% if haproxy_global_ssl_default_bind_ciphers != false %} 45 | ssl-default-bind-ciphers {{ haproxy_global_ssl_default_bind_ciphers }} 46 | {% endif %} 47 | {% if haproxy_global_ssl_default_bind_ciphersuites %} 48 | ssl-default-bind-ciphersuites {{ haproxy_global_ssl_default_bind_ciphersuites }} 49 | {% endif %} 50 | {% if haproxy_global_ssl_default_bind_options != false %} 51 | ssl-default-bind-options {{ haproxy_global_ssl_default_bind_options }} 52 | {% endif %} 53 | {% if haproxy_version is version('2.9', '>=') and haproxy_global_ssl_default_server_curves is defined %} 54 | ssl-default-server-curves {{ haproxy_global_ssl_default_server_curves }} 55 | {% endif %} 56 | {% if haproxy_global_ssl_default_server_ciphers != false %} 57 | ssl-default-server-ciphers {{ haproxy_global_ssl_default_server_ciphers }} 58 | {% endif %} 59 | {% if haproxy_global_ssl_default_server_ciphersuites %} 60 | ssl-default-server-ciphersuites {{ haproxy_global_ssl_default_server_ciphersuites }} 61 | {% endif %} 62 | {% if haproxy_global_ssl_default_server_options != false %} 63 | ssl-default-server-options {{ haproxy_global_ssl_default_server_options }} 64 | {% endif %} 65 | {% for ssl_engine in haproxy_global_ssl_engines | default([]) %} 66 | ssl-engine {{ ssl_engine.name }}{% if ssl_engine.algos | default([]) | length > 0 %} algo {{ ssl_engine.algos | join(', ') }}{% endif %} 67 | 68 | {% endfor %} 69 | {% if haproxy_global_ssl_mode_async | default(false) | bool %} 70 | ssl-mode-async 71 | {% endif %} 72 | {% if haproxy_version is version('2.5', '<') %} 73 | nbproc {{ haproxy_global_nbproc }} 74 | {% endif %} 75 | {% if haproxy_global_nbthread is defined %} 76 | nbthread {{ haproxy_global_nbthread }} 77 | {% endif %} 78 | {% for tune in haproxy_global_tune | default([]) %} 79 | tune.{{ tune.key }} {{ tune.value }} 80 | {% endfor %} 81 | {% for option in haproxy_global_option | default([]) %} 82 | {{ option }} 83 | {% endfor %} 84 | {% for line in haproxy_global_raw_options | default([]) %} 85 | {{ line }} 86 | {% endfor %} 87 | {% for peers in haproxy_global_peers | default([]) %} 88 | peers {{ peers.name }} 89 | {% for peer in peers.peers | default([]) %} 90 | peer {{ peer.name }} {{ peer.listen }} 91 | {% endfor %} 92 | {% endfor %} 93 | -------------------------------------------------------------------------------- /templates/etc/haproxy/frontend.cfg.j2: -------------------------------------------------------------------------------- 1 | {% for frontend in haproxy_frontend %} 2 | frontend {{ frontend.name }} 3 | {% if frontend.description is defined %} 4 | description {{ frontend.description }} 5 | {% endif %} 6 | {% for bind in frontend.bind %} 7 | bind {{ bind.listen }}{% for param in bind.param | default([]) %} {{ param }}{% endfor %} 8 | 9 | {% endfor %} 10 | {% if frontend.bind_process is defined %} 11 | bind-process {{ frontend.bind_process | join(' ') }} 12 | {% endif %} 13 | {% if frontend.mode is defined %} 14 | mode {{ frontend.mode }} 15 | {% endif %} 16 | {% if frontend.maxconn is defined %} 17 | maxconn {{ frontend.maxconn }} 18 | {% endif %} 19 | {% for stick in frontend.stick | default([]) %} 20 | stick-table {{ stick.table }} 21 | {% endfor %} 22 | {% for option in frontend.option | default([]) %} 23 | option {{ option }} 24 | {% endfor %} 25 | {% for option in frontend.no_option | default([]) %} 26 | no option {{ option }} 27 | {% endfor %} 28 | {% if frontend.logformat is defined %} 29 | log-format {{ frontend.logformat }} 30 | {% endif %} 31 | {% if frontend.no_log | default(false) == true %} 32 | no log 33 | {% endif %} 34 | {% for timeout in frontend.timeout | default([]) %} 35 | timeout {{ timeout.type }} {{ timeout.timeout }} 36 | {% endfor %} 37 | {% for filter in frontend.filter | default([]) %} 38 | filter {{ filter.name }}{% for param in filter.param | default([]) %} {{ param }}{% endfor %} 39 | {% endfor %} 40 | {% for acl in frontend.acl | default([]) %} 41 | acl {{ acl.string }} 42 | {% endfor %} 43 | {% for capture in frontend.capture | default([]) %} 44 | capture {{ capture.type }} {{ capture.name }} len {{ capture.length }} 45 | {% endfor %} 46 | {% for tcp_request_inspect_delay in frontend.tcp_request_inspect_delay | default([]) %} 47 | tcp-request inspect-delay {{ tcp_request_inspect_delay.timeout }} 48 | {% endfor %} 49 | {% for tcp_request_connection in frontend.tcp_request_connection | default([]) %} 50 | tcp-request connection {{ tcp_request_connection.action }}{% if tcp_request_connection.cond is defined %} {{ tcp_request_connection.cond }}{% endif %} 51 | 52 | {% endfor %} 53 | {% for tcp_request_content in frontend.tcp_request_content | default([]) %} 54 | tcp-request content {{ tcp_request_content.action }}{% if tcp_request_content.cond is defined %} {{ tcp_request_content.cond }}{% endif %} 55 | 56 | {% endfor %} 57 | {% for tcp_request_session in frontend.tcp_request_session | default([]) %} 58 | tcp-request session {{ tcp_request_session.action }}{% if tcp_request_session.cond is defined %} {{ tcp_request_session.cond }}{% endif %} 59 | 60 | {% endfor %} 61 | {% for http_request in frontend.http_request | default([]) %} 62 | http-request {{ http_request.action }}{% if http_request.param is defined %} {{ http_request.param }}{% endif %}{% if http_request.cond is defined %} {{ http_request.cond }}{% endif %} 63 | 64 | {% endfor %} 65 | {% for http_response in frontend.http_response | default([]) %} 66 | http-response {{ http_response.action }}{% if http_response.param is defined %} {{ http_response.param }}{% endif %}{% if http_response.cond is defined %} {{ http_response.cond }}{% endif %} 67 | 68 | {% endfor %} 69 | {% for action in ['reqadd', 'rspadd'] %} 70 | {% for params in frontend[action] | default([]) %} 71 | {{ action }} {{ params.string }}{% if params.cond is defined %} {{ params.cond }}{% endif %} 72 | 73 | {% endfor %} 74 | {% endfor %} 75 | {% for action in ['reqdel', 'reqidel', 'rspdel', 'rspidel'] %} 76 | {% for params in frontend[action] | default([]) %} 77 | {{ action }} {{ params.search }}{% if params.cond is defined %} {{ params.cond }}{% endif %} 78 | 79 | {% endfor %} 80 | {% endfor %} 81 | {% for action in ['reqrep', 'reqirep', 'rsprep', 'rspirep'] %} 82 | {% for params in frontend[action] | default([]) %} 83 | {{ action }} {{ params.search }} {{ params.string }}{% if params.cond is defined %} {{ params.cond }}{% endif %} 84 | 85 | {% endfor %} 86 | {% endfor %} 87 | {% for redirect in frontend.redirect | default([]) %} 88 | redirect {{ redirect.string }}{% if redirect.cond is defined %} {{ redirect.cond }}{% endif %} 89 | 90 | {% endfor %} 91 | {% for compression in frontend.compression | default([]) %} 92 | compression {{ compression.name }} {{ compression.value }} 93 | {% endfor %} 94 | {% if frontend.use_backend is defined %} 95 | {% if frontend.use_backend is iterable and frontend.use_backend is not string %} 96 | {% for use_backend in frontend.use_backend | default([]) %} 97 | use_backend {{ use_backend }} 98 | {% endfor %} 99 | {% else %} 100 | use_backend {{ frontend.use_backend }} 101 | {% endif %} 102 | {% endif %} 103 | {% if frontend.default_backend is defined %} 104 | default_backend {{ frontend.default_backend }} 105 | {% endif %} 106 | {% for errorfile in frontend.errorfile | default([]) %} 107 | errorfile {{ errorfile.code }} {{ errorfile.file }} 108 | {% endfor %} 109 | {% for line in frontend.raw_options | default([]) %} 110 | {{ line }} 111 | {% endfor %} 112 | {% endfor %} 113 | -------------------------------------------------------------------------------- /templates/etc/haproxy/backend.cfg.j2: -------------------------------------------------------------------------------- 1 | {% for backend in haproxy_backend %} 2 | backend {{ backend.name }} 3 | {% if backend.description is defined %} 4 | description {{ backend.description }} 5 | {% endif %} 6 | {% if backend.bind_process is defined %} 7 | bind-process {{ backend.bind_process | join(' ') }} 8 | {% endif %} 9 | {% if backend.mode is defined %} 10 | mode {{ backend.mode }} 11 | {% endif %} 12 | balance {{ backend.balance }} 13 | {% if backend.source is defined %} 14 | source {{ backend.source }} 15 | {% endif %} 16 | {% for option in backend.option | default([]) %} 17 | option {{ option }} 18 | {% endfor %} 19 | {% for option in backend.no_option | default([]) %} 20 | no option {{ option }} 21 | {% endfor %} 22 | {% if backend.http_check is defined %} 23 | {% if backend.http_check is string %} 24 | http-check {{ backend.http_check }} 25 | {% else %} 26 | {% for http_check in backend.http_check %} 27 | http-check {{ http_check }} 28 | {% endfor %} 29 | {% endif %} 30 | {% endif %} 31 | {% if backend.cookie is defined %} 32 | cookie {{ backend.cookie }} 33 | {% endif %} 34 | {% for filter in backend.filter | default([]) %} 35 | filter {{ filter.name }}{% for param in filter.param | default([]) %} {{ param }}{% endfor %} 36 | {% endfor %} 37 | {% for acl in backend.acl | default([]) %} 38 | acl {{ acl.string }} 39 | {% endfor %} 40 | {% for stick in backend.stick | default([]) %} 41 | stick-table {{ stick.table }} 42 | {% if stick.stick_on is defined %} 43 | stick on {{ stick.stick_on }} 44 | {% endif %} 45 | {% endfor %} 46 | {% for option in backend.no_option | default([]) %} 47 | no option {{ option }} 48 | {% endfor %} 49 | {% if backend.no_log | default(false) == true %} 50 | no log 51 | {% endif %} 52 | {% for tcp_check in backend.tcp_check | default([]) %} 53 | tcp-check {{ tcp_check }} 54 | {% endfor %} 55 | {% for timeout in backend.timeout | default([]) %} 56 | timeout {{ timeout.type }} {{ timeout.timeout }} 57 | {% endfor %} 58 | {% if backend.hash_type is defined %} 59 | hash-type {{ backend.hash_type }} 60 | {% endif %} 61 | {% for action in ['reqadd', 'rspadd'] %} 62 | {% for params in backend[action] | default([]) %} 63 | {{ action }} {{ params.string }}{% if params.cond is defined %} {{ params.cond }}{% endif %} 64 | 65 | {% endfor %} 66 | {% endfor %} 67 | {% for action in ['reqdel', 'reqidel', 'rspdel', 'rspidel'] %} 68 | {% for params in backend[action] | default([]) %} 69 | {{ action }} {{ params.search }}{% if params.cond is defined %} {{ params.cond }}{% endif %} 70 | 71 | {% endfor %} 72 | {% endfor %} 73 | {% for action in ['reqrep', 'reqirep', 'rsprep', 'rspirep'] %} 74 | {% for params in backend[action] | default([]) %} 75 | {{ action }} {{ params.search }} {{ params.string }}{% if params.cond is defined %} {{ params.cond }}{% endif %} 76 | 77 | {% endfor %} 78 | {% endfor %} 79 | {% if backend.stats is defined %} 80 | {% if backend.stats.enable is defined and backend.stats.enable | bool == true %} 81 | stats enable 82 | stats uri {{ backend.stats.uri | default('/') }} 83 | {% if backend.stats.refresh is defined %} 84 | stats refresh {{ backend.stats.refresh }} 85 | {% endif %} 86 | {% if backend.stats.admin is defined %} 87 | stats admin {{ backend.stats.admin }} 88 | {% endif %} 89 | {% for option in backend.stats.options | default([]) %} 90 | stats {{ option }} 91 | {% endfor %} 92 | {% for auth in backend.stats.auth | default([]) %} 93 | stats auth {{ auth.user }}:{{ auth.passwd }} 94 | {% endfor %} 95 | {% endif %} 96 | {% endif %} 97 | {% for http_request in backend.http_request | default([]) %} 98 | http-request {{ http_request.action }}{% if http_request.param is defined %} {{ http_request.param }}{% endif %}{% if http_request.cond is defined %} {{ http_request.cond }}{% endif %} 99 | 100 | {% endfor %} 101 | {% for tcp_request_inspect_delay in backend.tcp_request_inspect_delay | default([]) %} 102 | tcp-request inspect-delay {{ tcp_request_inspect_delay.timeout }} 103 | {% endfor %} 104 | {% for tcp_request_content in backend.tcp_request_content | default([]) %} 105 | tcp-request content {{ tcp_request_content.action }}{% if tcp_request_content.cond is defined %} {{ tcp_request_content.cond }}{% endif %} 106 | 107 | {% endfor %} 108 | {% for http_response in backend.http_response | default([]) %} 109 | http-response {{ http_response.action }}{% if http_response.param is defined %} {{ http_response.param }}{% endif %}{% if http_response.cond is defined %} {{ http_response.cond }}{% endif %} 110 | 111 | {% endfor %} 112 | {% for compression in backend.compression | default([]) %} 113 | compression {{ compression.name }} {{ compression.value }} 114 | {% endfor %} 115 | {% if backend.default_server_params | default([]) %} 116 | default-server {% for param in backend.default_server_params | default([]) %} {{ param }}{% endfor %} 117 | 118 | {% endif %} 119 | {% for server in backend.server | default([]) %} 120 | server {{ server.name }} {{ server.listen }}{% for param in server.param | default([]) %} {{ param }}{% endfor %} 121 | 122 | {% endfor %} 123 | {% for server_dynamic in backend.server_dynamic | default([]) %} 124 | {% for server_name in groups[server_dynamic.group] %} 125 | {% set server = hostvars[server_name] %} 126 | server {{ server.inventory_hostname }} {% if server.ansible_host is defined %}{{ server.ansible_host }}{% else %}{{ server_name }}{% endif %}{% if server_dynamic.listen_port is defined %}:{{ server_dynamic.listen_port }}{% endif %}{% for param in server_dynamic.param | default([]) %} {{ param }}{% endfor %} 127 | 128 | {% endfor %} 129 | {% endfor %} 130 | {% if backend.server_template is defined %} 131 | server-template {{ backend.server_template.name }} {{ backend.server_template.num}} {{ backend.server_template.fqdn }}{% if backend.server_template.port is defined %}:{{ backend.server_template.port }}{% endif %} {% for param in backend.server_template.param | default([]) %} {{ param }}{% endfor %} 132 | 133 | {% endif %} 134 | {% if backend.retry_on is defined %} 135 | retry-on {% for r in backend.retry_on %}{{ r }} {% endfor %} 136 | 137 | {% endif %} 138 | {% if backend.retries is defined %} 139 | retries {{ backend.retries }} 140 | {% endif %} 141 | {% for errorfile in backend.errorfile | default([]) %} 142 | errorfile {{ errorfile.code }} {{ errorfile.file }} 143 | {% endfor %} 144 | {% for email_alert in backend.email_alert | default([]) %} 145 | email-alert {{ email_alert.code }} {{ email_alert.value }} 146 | {% endfor %} 147 | {% for line in backend.raw_options | default([]) %} 148 | {{ line }} 149 | {% endfor %} 150 | {% endfor %} 151 | -------------------------------------------------------------------------------- /templates/etc/haproxy/listen.cfg.j2: -------------------------------------------------------------------------------- 1 | {% for listen in haproxy_listen %} 2 | listen {{ listen.name }} 3 | {% if listen.description is defined %} 4 | description {{ listen.description }} 5 | {% endif %} 6 | {% for bind in listen.bind %} 7 | bind {{ bind.listen }}{% for param in bind.param | default([]) %} {{ param }}{% endfor %} 8 | 9 | {% endfor %} 10 | {% if listen.bind_process is defined %} 11 | bind-process {{ listen.bind_process | join(' ') }} 12 | {% endif %} 13 | {% if listen.mode is defined %} 14 | mode {{ listen.mode }} 15 | {% endif %} 16 | {% if listen.balance is defined %} 17 | balance {{ listen.balance }} 18 | {% endif %} 19 | {% if listen.hash_type is defined %} 20 | hash-type {{ listen.hash_type }} 21 | {% endif %} 22 | {% if listen.maxconn is defined %} 23 | maxconn {{ listen.maxconn }} 24 | {% endif %} 25 | {% if listen.http_check is defined %} 26 | http-check {{ listen.http_check }} 27 | {% endif %} 28 | {% for stick in listen.stick | default([]) %} 29 | stick-table {{ stick.table }} 30 | {% if stick.stick_on is defined %} 31 | stick on {{ stick.stick_on }} 32 | {% endif %} 33 | {% endfor %} 34 | {% if listen.source is defined %} 35 | source {{ listen.source }} 36 | {% endif %} 37 | {% for option in listen.option | default([]) %} 38 | option {{ option }} 39 | {% endfor %} 40 | {% for option in listen.no_option | default([]) %} 41 | no option {{ option }} 42 | {% endfor %} 43 | {% if listen.logformat is defined %} 44 | log-format {{ listen.logformat }} 45 | {% endif %} 46 | {% if listen.no_log | default(false) == true %} 47 | no log 48 | {% endif %} 49 | {% for tcp_check in listen.tcp_check | default([]) %} 50 | tcp-check {{ tcp_check }} 51 | {% endfor %} 52 | {% for timeout in listen.timeout | default([]) %} 53 | timeout {{ timeout.type }} {{ timeout.timeout }} 54 | {% endfor %} 55 | {% for filter in listen.filter | default([]) %} 56 | filter {{ filter.name }}{% for param in filter.param | default([]) %} {{ param }}{% endfor %} 57 | {% endfor %} 58 | {% for acl in listen.acl | default([]) %} 59 | acl {{ acl.string }} 60 | {% endfor %} 61 | {% for capture in listen.capture | default([]) %} 62 | capture {{ capture.type }} {{ capture.name }} len {{ capture.length }} 63 | {% endfor %} 64 | {% if listen.stats is defined %} 65 | {% if listen.stats.enable is defined and listen.stats.enable | bool == true %} 66 | stats enable 67 | stats uri {{ listen.stats.uri | default('/') }} 68 | {% if listen.stats.refresh is defined %} 69 | stats refresh {{ listen.stats.refresh }} 70 | {% endif %} 71 | {% if listen.stats.admin is defined %} 72 | stats admin {{ listen.stats.admin }} 73 | {% endif %} 74 | {% for option in listen.stats.options | default([]) %} 75 | stats {{ option }} 76 | {% endfor %} 77 | {% for auth in listen.stats.auth | default([]) %} 78 | stats auth {{ auth.user }}:{{ auth.passwd }} 79 | {% endfor %} 80 | {% endif %} 81 | {% endif %} 82 | {% for tcp_request_inspect_delay in listen.tcp_request_inspect_delay | default([]) %} 83 | tcp-request inspect-delay {{ tcp_request_inspect_delay.timeout }} 84 | {% endfor %} 85 | {% for tcp_request_connection in listen.tcp_request_connection | default([]) %} 86 | tcp-request connection {{ tcp_request_connection.action }}{% if tcp_request_connection.cond is defined %} {{ tcp_request_connection.cond }}{% endif %} 87 | 88 | {% endfor %} 89 | {% for tcp_request_content in listen.tcp_request_content | default([]) %} 90 | tcp-request content {{ tcp_request_content.action }}{% if tcp_request_content.cond is defined %} {{ tcp_request_content.cond }}{% endif %} 91 | 92 | {% endfor %} 93 | {% for tcp_request_session in listen.tcp_request_session | default([]) %} 94 | tcp-request session {{ tcp_request_session.action }}{% if tcp_request_session.cond is defined %} {{ tcp_request_session.cond }}{% endif %} 95 | 96 | {% endfor %} 97 | {% for http_request in listen.http_request | default([]) %} 98 | http-request {{ http_request.action }}{% if http_request.param is defined %} {{ http_request.param }}{% endif %}{% if http_request.cond is defined %} {{ http_request.cond }}{% endif %} 99 | 100 | {% endfor %} 101 | {% for http_response in listen.http_response | default([]) %} 102 | http-response {{ http_response.action }}{% if http_response.param is defined %} {{ http_response.param }}{% endif %}{% if http_response.cond is defined %} {{ http_response.cond }}{% endif %} 103 | 104 | {% endfor %} 105 | {% for action in ['reqadd', 'rspadd'] %} 106 | {% for params in listen[action] | default([]) %} 107 | {{ action }} {{ params.string }}{% if params.cond is defined %} {{ params.cond }}{% endif %} 108 | 109 | {% endfor %} 110 | {% endfor %} 111 | {% for action in ['reqdel', 'reqidel', 'rspdel', 'rspidel'] %} 112 | {% for params in listen[action] | default([]) %} 113 | {{ action }} {{ params.search }}{% if params.cond is defined %} {{ params.cond }}{% endif %} 114 | 115 | {% endfor %} 116 | {% endfor %} 117 | {% for action in ['reqrep', 'reqirep', 'rsprep', 'rspirep'] %} 118 | {% for params in listen[action] | default([]) %} 119 | {{ action }} {{ params.search }} {{ params.string }}{% if params.cond is defined %} {{ params.cond }}{% endif %} 120 | 121 | {% endfor %} 122 | {% endfor %} 123 | {% for redirect in listen.redirect | default([]) %} 124 | redirect {{ redirect.string }}{% if redirect.cond is defined %} {{ redirect.cond }}{% endif %} 125 | 126 | {% endfor %} 127 | {% for compression in listen.compression | default([]) %} 128 | compression {{ compression.name }} {{ compression.value }} 129 | {% endfor %} 130 | {% if listen.default_server_params | default([]) %} 131 | default-server {% for param in listen.default_server_params | default([]) %} {{ param }}{% endfor %} 132 | 133 | {% endif %} 134 | {% for server in listen.server | default([]) %} 135 | server {{ server.name }} {{ server.listen }}{% for param in server.param | default([]) %} {{ param }}{% endfor %} 136 | 137 | {% endfor %} 138 | {% if listen.server_template is defined %} 139 | server-template {{ listen.server_template.name }} {{ listen.server_template.num}} {{ listen.server_template.fqdn }}{% if listen.server_template.port is defined %}:{{ listen.server_template.port }}{% endif %} {% for param in listen.server_template.param | default([]) %} {{ param }}{% endfor %} 140 | 141 | {% endif %} 142 | {% if listen.retry_on is defined %} 143 | retry-on {% for r in listen.retry_on %}{{ r }}{% endfor %} 144 | 145 | {% endif %} 146 | {% if listen.retries is defined %} 147 | retries {{ listen.retries }} 148 | {% endif %} 149 | {% for errorfile in listen.errorfile | default([]) %} 150 | errorfile {{ errorfile.code }} {{ errorfile.file }} 151 | {% endfor %} 152 | {% for line in listen.raw_options | default([]) %} 153 | {{ line }} 154 | {% endfor %} 155 | {% endfor %} 156 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## haproxy 2 | 3 | [![CI](https://github.com/Oefenweb/ansible-haproxy/workflows/CI/badge.svg)](https://github.com/Oefenweb/ansible-haproxy/actions?query=workflow%3ACI) 4 | [![Ansible Galaxy](http://img.shields.io/badge/ansible--galaxy-haproxy-blue.svg)](https://galaxy.ansible.com/Oefenweb/haproxy) 5 | 6 | Set up (the latest version of) [HAProxy](http://www.haproxy.org/) in Ubuntu systems. 7 | 8 | #### Requirements 9 | 10 | * `python-apt` 11 | * `software-properties-common` (will be installed) 12 | * `dirmngr` (will be installed) 13 | 14 | #### Variables 15 | 16 | * `haproxy_use_ppa`: [default: `true`]: Whether to add the PPA (for installation) 17 | 18 | * `haproxy_version`: [default: `2.8`]: Version to install (e.g. `1.5` ... `3.2`) 19 | 20 | * `haproxy_install`: [default: `[]`]: Additional packages to install (e.g. `socat`) 21 | 22 | * `haproxy_global_log`: [default: See `defaults/main.yml`]: Log declarations 23 | * `haproxy_global_log.{n}.address`: [required]: Indicates where to send the logs (e.g. `/dev/log`) 24 | * `haproxy_global_log.{n}.facility`: [required]: Must be one of the 24 standard syslog facilities (e.g. `local0`, `local1`) 25 | * `haproxy_global_log.{n}.level`: [optional]: Can be specified to filter outgoing messages (e.g. `notice`) 26 | * `haproxy_global_log.{n}.minlevel`: [optional]: Can be specified to filter outgoing messages (e.g. `notice`) 27 | * `haproxy_global_log.{n}.length`: [optional]: Can be specified to adjust message length in log (e.g. `2048`) 28 | * `haproxy_global_chroot`: [optional]: Changes current directory to `` and performs a `chroot()` there before dropping privileges 29 | * `haproxy_global_stats`: [default: See `defaults/main.yml`]: Stats declarations 30 | * `haproxy_global_stats.sockets`: [default: `[{listen: /run/haproxy/admin.sock }}"}]`]: Sockets declarations 31 | * `haproxy_global_stats.sockets.{n}.listen`: [required]: Defines a listening address and/or ports (e.g. `/run/haproxy/admin.sock`) 32 | * `haproxy_global_stats.sockets.{n}.param`: [optional]: A list of parameters common to this bind declarations (e.g. `['mode 660', 'level admin', 'process 1']`) 33 | * `haproxy_global_stats.timeout`: [optional]: The default timeout on the stats socket 34 | * `haproxy_global_user`: [default: `haproxy`]: Similar to `"uid"` but uses the UID of user name `` from `/etc/passwd` 35 | * `haproxy_global_group`: [default: `haproxy`]: Similar to `"gid"` but uses the GID of group name `` from `/etc/group` 36 | * `haproxy_global_daemon`: [default: `true`]: Makes the process fork into background. This is the recommended mode of operation 37 | * `haproxy_global_master_worker`: [optional, default: `false`]: Whether to use master/worker mode (`>= 1.8.0` only) 38 | * `haproxy_global_maxconn`: [optional]: Sets the maximum per-process number of concurrent connections 39 | * `haproxy_global_ca_base`: [default: `/etc/ssl/certs`]: Assigns a default directory to fetch SSL CA certificates and CRLs from when a relative path is used with `"ca-file"` or `"crl-file"` directives 40 | * `haproxy_global_crt_base`: [default: `/etc/ssl/private`]: Assigns a default directory to fetch SSL certificates from when a relative path is used with `"crtfile"` directives 41 | * `haproxy_global_ssl_default_bind_curves`: [optional]: This setting is only available when support for OpenSSL was built in. It sets the default string describing the list of elliptic curves algorithms ("curve suite") that are negotiated during the SSL/TLS handshake with ECDHE (`>= 2.2` only) 42 | * `haproxy_global_ssl_default_bind_ciphers`: [default: `kEECDH+aRSA+AES:kRSA+AES:+AES256:RC4-SHA:!kEDH:!LOW:!EXP:!MD5:!aNULL:!eNULL`]: This setting is only available when support for OpenSSL was built in. It sets the default string describing the list of cipher algorithms ("cipher suite") that are negotiated during the SSL/TLS handshake for all `"bind"` lines which do not explicitly define theirs 43 | * `haproxy_global_ssl_default_bind_ciphersuites`: [default: ``]: This setting is only available when support for OpenSSL was built in and OpenSSL 1.1.1 or later was used to build HAProxy. It sets the default string describing the list of cipher algorithms ("cipher suite") that are negotiated during the TLSv1.3 handshake for all `"bind"` lines which do not explicitly define theirs 44 | * `haproxy_global_ssl_default_bind_options`: [default: `no-sslv3`]: This setting is only available when support for OpenSSL was built in. It sets default ssl-options to force on all `"bind"` lines 45 | * `haproxy_global_ssl_default_server_curves`: [optional]: This setting is only available when support for OpenSSL was built in. It sets the default string describing the list of elliptic curves algorithms ("curve suite") that are negotiated during the SSL/TLS handshake with ECDHE (`>= 2.9` only) 46 | * `haproxy_global_ssl_default_server_ciphers`: [default: `kEECDH+aRSA+AES:kRSA+AES:+AES256:RC4-SHA:!kEDH:!LOW:!EXP:!MD5:!aNULL:!eNULL`]: This setting is only available when support for OpenSSL was built in. It sets the default string describing the list of cipher algorithms that are negotiated during the SSL/TLS handshake with the server, for all `"server"` lines which do not explicitly define theirs 47 | * `haproxy_global_ssl_default_server_ciphersuites`: [default: ``]: This setting is only available when support for OpenSSL was built in and OpenSSL 1.1.1 or later was used to build HAProxy. It sets the default string describing the list of cipher algorithms that are negotiated duringthe TLSv1.3 handshake with the server, for all `"server"` lines which do not explicitly define theirs 48 | * `haproxy_global_ssl_default_server_options`: [default: `no-sslv3`]: This setting is only available when support for OpenSSL was built in. It sets default ssl-options to force on all `"server"` lines 49 | * `haproxy_global_ssl_engines`: [optional, default `[]`]: OpenSSL engine declarations (`>= 1.8.0` only) 50 | * `haproxy_global_ssl_engines.{n}.name`: [required]: Sets the OpenSSL engine to use (e.g. `rdrand`) 51 | * `haproxy_global_ssl_engines.{n}.algos`: [optional]: Sets the OpenSSL algorithms to use (e.g. `['RSA']`) 52 | * `haproxy_global_ssl_mode_async`: [optional: default `false`]: Enables asynchronous TLS I/O operations if asynchronous capable SSL engines are used (`>= 1.8.0` only) 53 | * `haproxy_global_nbproc`: [default: `1`]: Number of processes to create when going daemon. This requires the `daemon` mode. By default, only one process is created, which is the recommended mode of operation 54 | * `haproxy_global_nbthread`: [optional]: This setting is only available when support for threads was built in. It creates `` threads for each created processes (`>= 1.8.0` only) 55 | * `haproxy_global_tune`: [default: `[]`]: (Performance) tuning declarations 56 | * `haproxy_global_tune.{n}.key`: [required]: Setting name (e.g. `ssl.cachesize`) 57 | * `haproxy_global_tune.{n}.value`: [required]: Setting value (e.g. `50000`) 58 | * `haproxy_global_option`: [default: `[]`]: Options (e.g. `['lua-load /etc/haproxy/acme-http01-webroot.lua', 'ssl-dh-param-file /etc/haproxy/dhparams.pem']`) 59 | * `haproxy_global_peers`: Peer list declarations 60 | * `haproxy_global_peers.{n}.name`: Peer list name (e.g. `mypeers`) 61 | * `haproxy_global_peers.{n}.peers`: Peer declarations 62 | * `haproxy_global_peers.{n}.peers.{n}.name`: [required]: Name of the host (recommended to be `hostname`) (e.g. `haproxy1`) 63 | * `haproxy_global_peers.{n}.peers.{n}.listen`: [required]: IP and port for peer to listen/connect to (e.g. `192.168.0.1:1024`) 64 | * `haproxy_global_raw_options`: [default: `[]`]: Additional arbitrary lines to insert in the section 65 | 66 | * `haproxy_defaults_log`: [default: `global`]: Enable per-instance logging of events and traffic. `global` should be used when the instance's logging parameters are the same as the global ones. This is the most common usage 67 | * `haproxy_defaults_logformat`: [optional]: Allows you to customize the logs in http mode and tcp mode (e.g. `'"%{+Q}o\ %t\ %s\ %{-Q}r"'`) 68 | * `haproxy_defaults_mode`: [default: `http`]: Set the running mode or protocol of the instance 69 | * `haproxy_defaults_source`: [optional]: Set the source address or interface for connections from the proxy 70 | * `haproxy_defaults_option`: [default: `[httplog, dontlognull]`]: Options (default) 71 | * `haproxy_defaults_no_option`: [optional]: Options to unset (e.g. `[redispatch]`) 72 | * `haproxy_defaults_timeout`: [default: See `defaults/main.yml`]: Timeout declarations 73 | * `haproxy_defaults_timeout.type`: [required]: The type (e.g. `connect`, `client`, `server`) 74 | * `haproxy_defaults_timeout.timeout`: [required]: The timeout (in milliseconds by default, but can be in any other unit if the number is suffixed by the unit) (e.g. `5000`, `50000`) 75 | * `haproxy_defaults_errorfile`: [default: See `defaults/main.yml`]: Errorfile declarations 76 | * `haproxy_defaults_errorfile.code`: [required]: The HTTP status code. Currently, HAProxy is capable of generating codes 200, 400, 403, 408, 500, 502, 503, and 504 (e.g. `400`) 77 | * `haproxy_defaults_errorfile.file`: [required]: A file containing the full HTTP response (e.g `/etc/haproxy/errors/400.http`) 78 | * `haproxy_defaults_compression`: [optional]: Compression declarations 79 | * `haproxy_defaults_compression.{}.name`: [required]: The compression name (e.g. `algo`, `type`, `offload`) 80 | * `haproxy_defaults_compression.{}.value`: [required]: The compression value, (e.g. if name = algo : one of this values `identity`, `gzip`, `deflate`, `raw-deflate` / if name = type : list of mime type separated by space for example `text/html text/plain text/css` / if name = `offload` value is empty) 81 | * `haproxy_default_server_params`: [optional]: Default server backend parameters passed to each backend/listen server 82 | * `haproxy_default_raw_options`: [default: `[]`]: Additional arbitrary lines to insert in the section 83 | 84 | * `haproxy_ssl_map`: [default: `[]`]: SSL declarations 85 | * `haproxy_ssl_map.{n}.state`: [default: `present`]: Whether to ensure the file is present or absent 86 | * `haproxy_ssl_map.{n}.src`: The local path of the file to copy, can be absolute or relative (e.g. `../../../files/haproxy/etc/haproxy/ssl/star-example-com.pem`) 87 | * `haproxy_ssl_map.{n}.dest`: The remote path of the file to copy (e.g. `/etc/haproxy/ssl/star-example-com.pem`) 88 | * `haproxy_ssl_map.{n}.owner`: The name of the user that should own the file (optional, default `root`) 89 | * `haproxy_ssl_map.{n}.group`: The name of the group that should own the file (optional, default `root`) 90 | * `haproxy_ssl_map.{n}.mode`: The mode of the file, such as 0644 (optional, default `0640`) 91 | 92 | * `haproxy_listen`: [default: `[]`]: Listen declarations 93 | * `haproxy_listen.{n}.name`: [required]: The name of the section (e.g. `stats`) 94 | * `haproxy_listen.{n}.description`: [optional]: A description of the section (e.g. `Global statistics`) 95 | * `haproxy_listen.{n}.bind`: [required]: Bind declarations 96 | * `haproxy_listen.{n}.bind.{n}.listen`: [required]: Defines one or several listening addresses and/or ports (e.g. `0.0.0.0:1936`) 97 | * `haproxy_listen.{n}.bind.{n}.param`: [optional]: A list of parameters common to this bind declarations 98 | * `haproxy_listen.{n}.bind_process`: [optional]: Limits the declaration to a certain set of processes numbers (e.g. `[all]`, `[1]`, `[2 ,3, 4]`) 99 | * `haproxy_listen.{n}.mode`: [optional]: Set the running mode or protocol of the section (e.g. `http`) 100 | * `haproxy_listen.{n}.balance`: [required]: The load balancing algorithm to be used (e.g. `roundrobin`) 101 | * `haproxy_listen.{n}.hash_type`: [optional]: The hashing type to be used for balancing (e.g. `consistent`) 102 | * `haproxy_listen.{n}.maxconn`: [optional]: Fix the maximum number of concurrent connections 103 | * `haproxy_listen.{n}.logformat`: [optional]: Specifies the log format string to use for traffic logs (e.g. `'"%{+Q}o\ %t\ %s\ %{-Q}r"'`) 104 | * `haproxy_listen.{n}.source`: [optional]: Set the source address or interface for connections from the proxy 105 | * `haproxy_listen.{n}.option`: [optional]: Options to set (e.g. `[dontlog-normal]`) 106 | * `haproxy_listen.{n}.no_option`: [optional]: Options to set (e.g. `[dontlog-normal]`) 107 | * `haproxy_listen.{n}.no_log`: [optional, default `false`]: Used when the logger list must be flushed. For example, if you don't want to inherit from the default logger list 108 | * `haproxy_listen.{n}.tcp_check`: [optional]: Perform health checks using tcp-check send/expect sequences (e.g. `['expect string +OK\ POP3\ ready']`) 109 | * `haproxy_listen.{n}.http_check`: [optional]: Make HTTP health checks consider response contents or specific status codes (e.g. `expect status 403`) 110 | * `haproxy_listen.{n}.stick`: [optional]: Stick declarations 111 | * `haproxy_listen.{n}.stick.{n}.table`: [required]: Configure the stickiness table for the current section (e.g. `type ip size 500k`) 112 | * `haproxy_listen.{n}.stick.{n}.stick_on`: [optional]: Define a request pattern to associate a user to a server (e.g. `src`) 113 | * `haproxy_listen.{n}.timeout`: [optional]: Timeout declarations 114 | * `haproxy_listen.{n}.timeout.type`: [required]: The type (e.g. `connect`, `client`, `server`) 115 | * `haproxy_listen.{n}.timeout.timeout`: [required]: The timeout (in milliseconds by default, but can be in any other unit if the number is suffixed by the unit) (e.g. `5000`, `50000`) 116 | * `haproxy_listen.{n}.acl`: [optional]: Create an ACL check which can be later used in evaluations/conditionals 117 | * `haproxy_listen.{n}.acl.{n}.string`: [required]: ACL entry to be used in conditional check later 118 | * `haproxy_listen.{n}.capture`: [optional]: Capture fields from request or response 119 | * `haproxy_listen.{n}.capture.type`: [required]: What to capture (`cookie`, `request header`, `response header`) 120 | * `haproxy_listen.{n}.capture.name`: [required]: Name of the header or cookie to capture 121 | * `haproxy_listen.{n}.capture.length`: [required]: Maximum number of characters to capture and report in the logs 122 | * `haproxy_listen.{n}.filter`: [optional]: Content filters to apply to this section 123 | * `haproxy_listen.{n}.filter.{n}.name`: [required]: The name of the filter 124 | * `haproxy_listen.{n}.filter.{n}.param`: [default: `[]`]: Parameters for the filter 125 | * `haproxy_listen.{n}.http_request`: [optional]: Access control for Layer 7 requests 126 | * `haproxy_listen.{n}.http_request.{n}.action`: [required]: The rules action (e.g. `add-header`) 127 | * `haproxy_listen.{n}.http_request.{n}.param`: [optional]: The complete line to be added (e.g. `X-Forwarded-Proto https`) 128 | * `haproxy_listen.{n}.http_request.{n}.cond`: [optional]: A matching condition built from ACLs (e.g. `if { ssl_fc }`) 129 | * `haproxy_listen.{n}.http_response`: [optional]: Access control for Layer 7 responses 130 | * `haproxy_listen.{n}.http_response.{n}.action`: [required]: The rules action (e.g. `del-header`) 131 | * `haproxy_listen.{n}.http_response.{n}.param`: [optional]: The complete line to be added (e.g. `X-Varnish`) 132 | * `haproxy_listen.{n}.http_response.{n}.cond`: [optional]: A matching condition built from ACLs 133 | * `haproxy_listen.{n}.tcp_request_content`: [optional]: Perform an action on a new session depending on a layer 4-7 condition 134 | * `haproxy_listen.{n}.tcp_request_content.{n}.action`: [required]: The action for the `tcp-request content` rule 135 | * `haproxy_listen.{n}.tcp_request_content.{n}.cond`: [optional]: A matching condition for the `tcp-request content` rule 136 | * `haproxy_listen.{n}.tcp_request_connection`: [optional]: Perform an action on an incoming connection depending on a layer 4 condition 137 | * `haproxy_listen.{n}.tcp_request_connection.{n}.action`: [required]: The action for the `tcp-request connection` rule 138 | * `haproxy_listen.{n}.tcp_request_connection.{n}.cond`: [optional]: A matching condition for the `tcp-request connection` rule 139 | * `haproxy_listen.{n}.tcp_request_session`: [optional]: Perform an action on a validated session depending on a layer 5 condition 140 | * `haproxy_listen.{n}.tcp_request_session.{n}.action`: [required]: The action for the `tcp-request session` rule 141 | * `haproxy_listen.{n}.tcp_request_session.{n}.cond`: [optional]: A matching condition for the `tcp-request session` rule 142 | * `haproxy_listen.{n}.tcp_request_inspect_delay`: [optional]: Set the maximum allowed time to wait for data during content inspection 143 | * `haproxy_listen.{n}.tcp_request_inspect_delay.{n}.timeout`: [required]: The timeout value in millisecond for the `tcp-request inspect-delay` rule 144 | * `haproxy_listen.{n}.stats`: [optional]: Stats declarations 145 | * `haproxy_listen.{n}.stats.enable`: [required]: Enables statistics reporting with default settings 146 | * `haproxy_listen.{n}.stats.uri`: [optional, default `/`]: Define the URI prefix to access statistics 147 | * `haproxy_listen.{n}.stats.options`: [optional]: List of boolean stats options (e.g. `hide-version`, `show-node`, `show-desc`, `show-legends`) 148 | * `haproxy_listen.{n}.stats.refresh`: [optional]: Defined the refresh delay, specified in seconds (e.g. `5s`) 149 | * `haproxy_listen.{n}.stats.admin`: [optional]: Define / enable admin part of web interface with conditional attached 150 | * `haproxy_listen.{n}.stats.auth`: [optional]: Auth declarations 151 | * `haproxy_listen.{n}.stats.auth.{n}.user`: [required]: A user name to grant access to 152 | * `haproxy_listen.{n}.stats.auth.{n}.passwd`: [required]: The cleartext password associated to this user 153 | * `haproxy_listen.{n}.compression`: [optional]: Compression declarations 154 | * `haproxy_listen.{n}.compression.{n}.name`: [required]: The compression name (e.g. `algo`, `type`, `offload`) 155 | * `haproxy_listen.{n}.compression.{n}.value`: [required]: The compression value, (e.g. if name = algo : one of this values `identity`, `gzip`, `deflate`, `raw-deflate` / if name = type : list of mime type separated by space for example `text/html text/plain text/css` / if name = `offload` value is empty) 156 | * `haproxy_listen.{n}.server`: [optional]: Server declarations 157 | * `haproxy_listen.{n}.server.{n}.name`: [required]: The internal name assigned to this server 158 | * `haproxy_listen.{n}.server.{n}.listen`: [required]: Defines a listening address and/or ports 159 | * `haproxy_listen.{n}.server.{n}.param`: [optional]: A list of parameters for this server 160 | * `haproxy_listen.{n}.server_template`: [optional]: Server template declarations 161 | * `haproxy_listen.{n}.server_template.name`: [required]: A prefix for the server names to be built 162 | * `haproxy_listen.{n}.server_template.num`: [required]: Number or range of servers. If specified as ``, this template initializes `` servers with 1 up to `` as server name suffixes. If specified as `-`, initializes with `` up to `` as server name suffixes 163 | * `haproxy_listen.{n}.server_template.fqdn`: [required]: A FQDN for all the servers this template initializes 164 | * `haproxy_listen.{n}.server_template.port`: [optional]: Port specification 165 | * `haproxy_listen.{n}.server_template.{n}.param`: [optional]: A list of parameters for this server template 166 | * `haproxy_listen.{n}.retry_on`: [optional, default `[]`]: Specify when to attempt to automatically retry a failed request. Provide a list of keywords or HTTP status codes, each representing a type of failure event on which an attempt to retry the request is desired. For details, see HAProxy documentation 167 | * `haproxy_listen.{n}.retries`: [optional]: Number of retries to perform on a server after a connection failure 168 | * `haproxy_listen.{n}.reqadd`: [optional]: Adds headers at the end of the HTTP request 169 | * `haproxy_listen.{n}.reqadd.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 170 | * `haproxy_listen.{n}.reqadd.{n}.cond`: [optional]: A matching condition built from ACLs 171 | * `haproxy_listen.{n}.rspadd`: [optional]: Adds headers at the end of the HTTP response 172 | * `haproxy_listen.{n}.rspadd.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 173 | * `haproxy_listen.{n}.rspadd.{n}.cond`: [optional]: A matching condition built from ACLs 174 | * `haproxy_listen.{n}.reqdel`: [optional]: Delete all headers matching a regular expression in an HTTP request 175 | * `haproxy_listen.{n}.reqdel.{n}.search`: [required]: The regular expression applied to HTTP headers and to the request line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 176 | * `haproxy_listen.{n}.reqdel.{n}.cond`: [optional]: A matching condition built from ACLs 177 | * `haproxy_listen.{n}.reqidel`: [optional]: Delete all headers matching a regular expression in an HTTP request (ignore case) 178 | * `haproxy_listen.{n}.reqidel.{n}.search`: [required]: The regular expression applied to HTTP headers and to the request line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 179 | * `haproxy_listen.{n}.reqidel.{n}.cond`: [optional]: A matching condition built from ACLs 180 | * `haproxy_listen.{n}.rspdel`: [optional]: Delete all headers matching a regular expression in an HTTP response 181 | * `haproxy_listen.{n}.rspdel.{n}.search`: [required]: The regular expression applied to HTTP headers and to the response line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 182 | * `haproxy_listen.{n}.rspdel.{n}.cond`: [optional]: A matching condition built from ACLs 183 | * `haproxy_listen.{n}.rspidel`: [optional]: Delete all headers matching a regular expression in an HTTP response (ignore case) 184 | * `haproxy_listen.{n}.rspidel.{n}.search`: [required]: The regular expression applied to HTTP headers and to the response line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 185 | * `haproxy_listen.{n}.rspidel.{n}.cond`: [optional]: A matching condition built from ACLs 186 | * `haproxy_listen.{n}.reqrep`: [optional]: Replace a regular expression with a string in an HTTP request line 187 | * `haproxy_listen.{n}.reqrep.{n}.search`: [required]: The regular expression applied to HTTP headers and to the request line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 188 | * `haproxy_listen.{n}.reqrep.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 189 | * `haproxy_listen.{n}.reqrep.{n}.cond`: [optional]: Matching condition built from ACLs 190 | * `haproxy_listen.{n}.reqirep`: [optional]: Replace a regular expression with a string in an HTTP request line (ignore case) 191 | * `haproxy_listen.{n}.reqirep.{n}.search`: [required]: The regular expression applied to HTTP headers and to the request line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 192 | * `haproxy_listen.{n}.reqirep.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 193 | * `haproxy_listen.{n}.reqirep.{n}.cond`: [optional]: Matching condition built from ACLs 194 | * `haproxy_listen.{n}.rsprep`: [optional]: Replace a regular expression with a string in an HTTP response line 195 | * `haproxy_listen.{n}.rsprep.{n}.search`: [required]: The regular expression applied to HTTP headers and to the response line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 196 | * `haproxy_listen.{n}.rsprep.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 197 | * `haproxy_listen.{n}.rsprep.{n}.cond`: [optional]: Matching condition built from ACLs 198 | * `haproxy_listen.{n}.rspirep`: [optional]: Replace a regular expression with a string in an HTTP response line (ignore case) 199 | * `haproxy_listen.{n}.rspirep.{n}.search`: [required]: The regular expression applied to HTTP headers and to the response line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 200 | * `haproxy_listen.{n}.rspirep.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 201 | * `haproxy_listen.{n}.rspirep.{n}.cond`: [optional]: Matching condition built from ACLs 202 | * `haproxy_listen.{n}.redirect`: [optional]: Return an HTTP redirection if/unless a condition is matched 203 | * `haproxy_listen.{n}.redirect.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 204 | * `haproxy_listen.{n}.redirect.{n}.cond`: [optional]: A condition to apply this rule 205 | * `haproxy_listen.{n}.errorfile`: [optional]: Errorfile declarations 206 | * `haproxy_listen.{n}.errorfile.{n}.code`: [required]: The HTTP status code. Currently, HAProxy is capable of generating codes 200, 400, 403, 408, 500, 502, 503, and 504 (e.g. `400`) 207 | * `haproxy_listen.{n}.errorfile.{n}.file`: [required]: A file containing the full HTTP response (e.g `/etc/haproxy/errors/400.http`) 208 | * `haproxy_listen.{n}.default_server_params`: [optional]: Default server params applied for each server for this particular listen entry 209 | * `haproxy_listen.{n}.raw_options`: [default: `[]`]: Additional arbitrary lines to insert in the section 210 | 211 | * `haproxy_frontend`: [default: `[]`]: Front-end declarations 212 | * `haproxy_frontend.{n}.name`: [required]: The name of the section (e.g. `https`) 213 | * `haproxy_frontend.{n}.description`: [optional]: A description of the section (e.g. `Front-end for all HTTPS traffic`) 214 | * `haproxy_frontend.{n}.bind`: [required]: Bind declarations 215 | * `haproxy_frontend.{n}.bind.{n}.listen`: [required]: Defines one or several listening addresses and/or ports (e.g. `0.0.0.0:443`) 216 | * `haproxy_frontend.{n}.bind.{n}.param`: [optional]: A list of parameters common to this bind declarations 217 | * `haproxy_frontend.{n}.bind_process`: [optional]: Limits the declaration to a certain set of processes numbers (e.g. `[all]`, `[1]`, `[2 ,3, 4]`) 218 | * `haproxy_frontend.{n}.mode`: [optional]: Set the running mode or protocol of the section (e.g. `http`) 219 | * `haproxy_frontend.{n}.maxconn`: [optional]: Fix the maximum number of concurrent connections 220 | * `haproxy_frontend.{n}.logformat`: [optional]: Specifies the log format string to use for traffic logs (e.g. `'"%{+Q}o\ %t\ %s\ %{-Q}r"'`) 221 | * `haproxy_frontend.{n}.stick`: [optional]: Stick declarations 222 | * `haproxy_frontend.{n}.stick.{n}.table`: [required]: Configure the stickiness table for the current section (e.g. `type ip size 500k`) 223 | * `haproxy_frontend.{n}.option`: [optional]: Options to set (e.g. `[tcplog]`) 224 | * `haproxy_frontend.{n}.no_option`: [optional]: Options to unset (e.g. `[forceclose]`) 225 | * `haproxy_frontend.{n}.no_log`: [optional, default `false`]: Used when the logger list must be flushed. For example, if you don't want to inherit from the default logger list 226 | * `haproxy_frontend.{n}.timeout`: [optional]: Timeout declarations 227 | * `haproxy_frontend.{n}.timeout.type`: [required]: The type (e.g. `client`) 228 | * `haproxy_frontend.{n}.timeout.timeout`: [required]: The timeout (in milliseconds by default, but can be in any other unit if the number is suffixed by the unit) (e.g. `5000`, `50000`) 229 | * `haproxy_frontend.{n}.acl`: [optional]: Create an ACL check which can be later used in evaluations/conditionals 230 | * `haproxy_frontend.{n}.acl.{n}.string`: [required]: ACL entry to be used in conditional check later 231 | * `haproxy_frontend.{n}.capture`: [optional]: Capture fields from request or response 232 | * `haproxy_frontend.{n}.capture.type`: [required]: What to capture (`cookie`, `request header`, `response header`) 233 | * `haproxy_frontend.{n}.capture.name`: [required]: Name of the header or cookie to capture 234 | * `haproxy_frontend.{n}.capture.length`: [required]: Maximum number of characters to capture and report in the logs 235 | * `haproxy_frontend.{n}.filter`: [optional]: Content filters to apply to this section 236 | * `haproxy_frontend.{n}.filter.{n}.name`: [required]: The name of the filter 237 | * `haproxy_frontend.{n}.filter.{n}.param`: [default: `[]`]: Parameters for the filter 238 | * `haproxy_frontend.{n}.http_request`: [optional]: Access control for Layer 7 requests 239 | * `haproxy_frontend.{n}.http_request.{n}.action`: [required]: The rules action (e.g. `add-header`) 240 | * `haproxy_frontend.{n}.http_request.{n}.param`: [optional]: The complete line to be added (e.g. `X-Forwarded-Proto https`) 241 | * `haproxy_frontend.{n}.http_request.{n}.cond`: [optional]: A matching condition built from ACLs (e.g. `if { ssl_fc }`) 242 | * `haproxy_frontend.{n}.http_response`: [optional]: Access control for Layer 7 responses 243 | * `haproxy_frontend.{n}.http_response.{n}.action`: [required]: The rules action (e.g. `del-header`) 244 | * `haproxy_frontend.{n}.http_response.{n}.param`: [optional]: The complete line to be added (e.g. `X-Varnish`) 245 | * `haproxy_frontend.{n}.http_response.{n}.cond`: [optional]: A matching condition built from ACLs 246 | * `haproxy_frontend.{n}.tcp_request_content`: [optional]: Perform an action on a new session depending on a layer 4-7 condition 247 | * `haproxy_frontend.{n}.tcp_request_content.{n}.action`: [required]: The action for the `tcp-request content` rule 248 | * `haproxy_frontend.{n}.tcp_request_content.{n}.cond`: [optional]: A matching condition for the `tcp-request content` rule 249 | * `haproxy_frontend.{n}.tcp_request_connection`: [optional]: Perform an action on an incoming connection depending on a layer 4 condition 250 | * `haproxy_frontend.{n}.tcp_request_connection.{n}.action`: [required]: The action for the `tcp-request connection` rule 251 | * `haproxy_frontend.{n}.tcp_request_connection.{n}.cond`: [optional]: A matching condition for the `tcp-request connection` rule 252 | * `haproxy_frontend.{n}.tcp_request_session`: [optional]: Perform an action on a validated session depending on a layer 5 condition 253 | * `haproxy_frontend.{n}.tcp_request_session.{n}.action`: [required]: The action for the `tcp-request session` rule 254 | * `haproxy_frontend.{n}.tcp_request_session.{n}.cond`: [optional]: A matching condition for the `tcp-request session` rule 255 | * `haproxy_frontend.{n}.tcp_request_inspect_delay`: [optional]: Set the maximum allowed time to wait for data during content inspection 256 | * `haproxy_frontend.{n}.tcp_request_inspect_delay.{n}.timeout`: [required]: The timeout value in millisecond for the `tcp-request inspect-delay` rule 257 | * `haproxy_frontend.{n}.use_backend`: [optional]: Switch to a specific backend if/unless a Layer 7 condition is matched. (e.g. '%[req.hdr(host),lower,map_dom(/etc/haproxy/haproxy_backend.map,bk_default)]' or `['foo-backend if is_foo', 'bar-backend if is_bar']`) 258 | * `haproxy_frontend.{n}.default_backend`: [optional]: The backend to use when no `"use_backend"` rule has been matched (e.g. `webservers`) 259 | * `haproxy_frontend.{n}.reqadd`: [optional]: Adds headers at the end of the HTTP request 260 | * `haproxy_frontend.{n}.reqadd.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 261 | * `haproxy_frontend.{n}.reqadd.{n}.cond`: [optional]: A matching condition built from ACLs 262 | * `haproxy_frontend.{n}.rspadd`: [optional]: Adds headers at the end of the HTTP response 263 | * `haproxy_frontend.{n}.rspadd.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 264 | * `haproxy_frontend.{n}.rspadd.{n}.cond`: [optional]: A matching condition built from ACLs 265 | * `haproxy_frontend.{n}.reqdel`: [optional]: Delete all headers matching a regular expression in an HTTP request 266 | * `haproxy_frontend.{n}.reqdel.{n}.search`: [required]: The regular expression applied to HTTP headers and to the request line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 267 | * `haproxy_frontend.{n}.reqdel.{n}.cond`: [optional]: A matching condition built from ACLs 268 | * `haproxy_frontend.{n}.reqidel`: [optional]: Delete all headers matching a regular expression in an HTTP request (ignore case) 269 | * `haproxy_frontend.{n}.reqidel.{n}.search`: [required]: The regular expression applied to HTTP headers and to the request line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 270 | * `haproxy_frontend.{n}.reqidel.{n}.cond`: [optional]: A matching condition built from ACLs 271 | * `haproxy_frontend.{n}.rspdel`: [optional]: Delete all headers matching a regular expression in an HTTP response 272 | * `haproxy_frontend.{n}.rspdel.{n}.search`: [required]: The regular expression applied to HTTP headers and to the response line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 273 | * `haproxy_frontend.{n}.rspdel.{n}.cond`: [optional]: A matching condition built from ACLs 274 | * `haproxy_frontend.{n}.rspidel`: [optional]: Delete all headers matching a regular expression in an HTTP response (ignore case) 275 | * `haproxy_frontend.{n}.rspidel.{n}.search`: [required]: The regular expression applied to HTTP headers and to the response line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 276 | * `haproxy_frontend.{n}.rspidel.{n}.cond`: [optional]: A matching condition built from ACLs 277 | * `haproxy_frontend.{n}.reqrep`: [optional]: Replace a regular expression with a string in an HTTP request line 278 | * `haproxy_frontend.{n}.reqrep.{n}.search`: [required]: The regular expression applied to HTTP headers and to the request line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 279 | * `haproxy_frontend.{n}.reqrep.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 280 | * `haproxy_frontend.{n}.reqrep.{n}.cond`: [optional]: Matching condition built from ACLs 281 | * `haproxy_frontend.{n}.reqirep`: [optional]: Replace a regular expression with a string in an HTTP request line (ignore case) 282 | * `haproxy_frontend.{n}.reqirep.{n}.search`: [required]: The regular expression applied to HTTP headers and to the request line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 283 | * `haproxy_frontend.{n}.reqirep.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 284 | * `haproxy_frontend.{n}.reqirep.{n}.cond`: [optional]: Matching condition built from ACLs 285 | * `haproxy_frontend.{n}.rsprep`: [optional]: Replace a regular expression with a string in an HTTP response line 286 | * `haproxy_frontend.{n}.rsprep.{n}.search`: [required]: The regular expression applied to HTTP headers and to the response line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 287 | * `haproxy_frontend.{n}.rsprep.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 288 | * `haproxy_frontend.{n}.rsprep.{n}.cond`: [optional]: Matching condition built from ACLs 289 | * `haproxy_frontend.{n}.rspirep`: [optional]: Replace a regular expression with a string in an HTTP response line (ignore case) 290 | * `haproxy_frontend.{n}.rspirep.{n}.search`: [required]: The regular expression applied to HTTP headers and to the response line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 291 | * `haproxy_frontend.{n}.rspirep.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 292 | * `haproxy_frontend.{n}.rspirep.{n}.cond`: [optional]: Matching condition built from ACLs 293 | * `haproxy_frontend.{n}.redirect`: [optional]: Return an HTTP redirection if/unless a condition is matched 294 | * `haproxy_frontend.{n}.redirect.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 295 | * `haproxy_frontend.{n}.redirect.{n}.cond`: [optional]: A condition to apply this rule 296 | * `haproxy_frontend.{n}.compression`: [optional]: Compression declarations 297 | * `haproxy_frontend.{n}.compression.{n}.name`: [required]: The compression name (e.g. `algo`, `type`, `offload`) 298 | * `haproxy_frontend.{n}.compression.{n}.value`: [required]: The compression value, (e.g. if name = algo : one of this values `identity`, `gzip`, `deflate`, `raw-deflate` / if name = type : list of mime type separated by space for example `text/html text/plain text/css` / if name = `offload` value is empty) 299 | * `haproxy_frontend.{n}.errorfile`: [optional]: Errorfile declarations 300 | * `haproxy_frontend.{n}.errorfile.{n}.code`: [required]: The HTTP status code. Currently, HAProxy is capable of generating codes 200, 400, 403, 408, 500, 502, 503, and 504 (e.g. `400`) 301 | * `haproxy_frontend.{n}.errorfile.{n}.file`: [required]: A file containing the full HTTP response (e.g `/etc/haproxy/errors/400.http`) 302 | * `haproxy_frontend.{n}.raw_options`: [default: `[]`]: Additional arbitrary lines to insert in the section 303 | 304 | * `haproxy_backend`: [default: `[]`]: Back-end declarations 305 | * `haproxy_backend.{n}.name`: [required]: The name of the section (e.g. `webservers`) 306 | * `haproxy_backend.{n}.description`: [optional]: A description of the section (e.g. `Back-end with all (Apache) webservers`) 307 | * `haproxy_backend.{n}.bind_process`: [optional]: Limits the declaration to a certain set of processes numbers (e.g. `[all]`, `[1]`, `[2 ,3, 4]`) 308 | * `haproxy_backend.{n}.mode`: [optional]: Set the running mode or protocol of the section (e.g. `http`) 309 | * `haproxy_backend.{n}.balance`: [required]: The load balancing algorithm to be used (e.g. `roundrobin`) 310 | * `haproxy_backend.{n}.source`: [optional]: Set the source address or interface for connections from the proxy 311 | * `haproxy_backend.{n}.option`: [optional]: Options to set (e.g. `[forwardfor]`) 312 | * `haproxy_backend.{n}.no_option`: [optional]: Options to unset (e.g. `[redispatch]`) 313 | * `haproxy_backend.{n}.http_check.{n}`: [optional]: Configure HTTP health checks (e.g. `expect status 403`, `send meth GET uri /healthz`) 314 | * `haproxy_backend.{n}.stick`: [optional]: Stick declarations 315 | * `haproxy_backend.{n}.stick.{n}.table`: [required]: Configure the stickiness table for the current section (e.g. `type ip size 500k`) 316 | * `haproxy_backend.{n}.stick.{n}.stick_on`: [optional]: Define a request pattern to associate a user to a server (e.g. `src`) 317 | * `haproxy_backend.{n}.hash_type`: [optional]: The hashing type to be used for balancing (e.g. `consistent`) 318 | * `haproxy_backend.{n}.no_option`: [optional]: Options to unset (e.g. `[forceclose]`) 319 | * `haproxy_backend.{n}.no_log`: [optional, default `false`]: Used when the logger list must be flushed. For example, if you don't want to inherit from the default logger list 320 | * `haproxy_backend.{n}.tcp_check`: [optional]: Perform health checks using tcp-check send/expect sequences (e.g. `['expect string +OK\ POP3\ ready']`) 321 | * `haproxy_backend.{n}.timeout`: [optional]: Timeout declarations 322 | * `haproxy_backend.{n}.timeout.type`: [required]: The type (e.g. `server`) 323 | * `haproxy_backend.{n}.timeout.timeout`: [required]: The timeout (in milliseconds by default, but can be in any other unit if the number is suffixed by the unit) (e.g. `5000`, `50000`) 324 | * `haproxy_backend.{n}.acl`: [optional]: Create an ACL check which can be later used in evaluations/conditionals 325 | * `haproxy_backend.{n}.acl.{n}.string`: [required]: ACL entry to be used in conditional check later 326 | * `haproxy_backend.{n}.reqadd`: [optional]: Adds headers at the end of the HTTP request 327 | * `haproxy_backend.{n}.reqadd.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 328 | * `haproxy_backend.{n}.reqadd.{n}.cond`: [optional]: A matching condition built from ACLs 329 | * `haproxy_backend.{n}.rspadd`: [optional]: Adds headers at the end of the HTTP response 330 | * `haproxy_backend.{n}.rspadd.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 331 | * `haproxy_backend.{n}.rspadd.{n}.cond`: [optional]: A matching condition built from ACLs 332 | * `haproxy_backend.{n}.reqdel`: [optional]: Delete all headers matching a regular expression in an HTTP request 333 | * `haproxy_backend.{n}.reqdel.{n}.search`: [required]: The regular expression applied to HTTP headers and to the request line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 334 | * `haproxy_backend.{n}.reqdel.{n}.cond`: [optional]: A matching condition built from ACLs 335 | * `haproxy_backend.{n}.reqidel`: [optional]: Delete all headers matching a regular expression in an HTTP request (ignore case) 336 | * `haproxy_backend.{n}.reqidel.{n}.search`: [required]: The regular expression applied to HTTP headers and to the request line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 337 | * `haproxy_backend.{n}.reqidel.{n}.cond`: [optional]: A matching condition built from ACLs 338 | * `haproxy_backend.{n}.rspdel`: [optional]: Delete all headers matching a regular expression in an HTTP response 339 | * `haproxy_backend.{n}.rspdel.{n}.search`: [required]: The regular expression applied to HTTP headers and to the response line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 340 | * `haproxy_backend.{n}.rspdel.{n}.cond`: [optional]: A matching condition built from ACLs 341 | * `haproxy_backend.{n}.rspidel`: [optional]: Delete all headers matching a regular expression in an HTTP response (ignore case) 342 | * `haproxy_backend.{n}.rspidel.{n}.search`: [required]: The regular expression applied to HTTP headers and to the response line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 343 | * `haproxy_backend.{n}.rspidel.{n}.cond`: [optional]: A matching condition built from ACLs 344 | * `haproxy_backend.{n}.reqrep`: [optional]: Replace a regular expression with a string in an HTTP request line 345 | * `haproxy_backend.{n}.reqrep.{n}.search`: [required]: The regular expression applied to HTTP headers and to the request line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 346 | * `haproxy_backend.{n}.reqrep.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 347 | * `haproxy_backend.{n}.reqrep.{n}.cond`: [optional]: Matching condition built from ACLs 348 | * `haproxy_backend.{n}.reqirep`: [optional]: Replace a regular expression with a string in an HTTP request line (ignore case) 349 | * `haproxy_backend.{n}.reqirep.{n}.search`: [required]: The regular expression applied to HTTP headers and to the request line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 350 | * `haproxy_backend.{n}.reqirep.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 351 | * `haproxy_backend.{n}.reqirep.{n}.cond`: [optional]: Matching condition built from ACLs 352 | * `haproxy_backend.{n}.rsprep`: [optional]: Replace a regular expression with a string in an HTTP response line 353 | * `haproxy_backend.{n}.rsprep.{n}.search`: [required]: The regular expression applied to HTTP headers and to the response line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 354 | * `haproxy_backend.{n}.rsprep.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 355 | * `haproxy_backend.{n}.rsprep.{n}.cond`: [optional]: Matching condition built from ACLs 356 | * `haproxy_backend.{n}.rspirep`: [optional]: Replace a regular expression with a string in an HTTP response line (ignore case) 357 | * `haproxy_backend.{n}.rspirep.{n}.search`: [required]: The regular expression applied to HTTP headers and to the response line. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 358 | * `haproxy_backend.{n}.rspirep.{n}.string`: [required]: The complete line to be added. Any space or known delimiter must be escaped using a backslash (`'\'`) (in version < 1.6) 359 | * `haproxy_backend.{n}.rspirep.{n}.cond`: [optional]: Matching condition built from ACLs 360 | * `haproxy_backend.{n}.cookie`: [optional]: Enable cookie-based persistence in a backend (e.g. `JSESSIONID prefix nocache`) 361 | * `haproxy_backend.{n}.filter`: [optional]: Content filters to apply to this section 362 | * `haproxy_backend.{n}.filter.{n}.name`: [required]: The name of the filter 363 | * `haproxy_backend.{n}.filter.{n}.param`: [default: `[]`]: Parameters for the filter 364 | * `haproxy_backend.{n}.http_request`: [optional]: Access control for Layer 7 requests 365 | * `haproxy_backend.{n}.http_request.{n}.action`: [required]: The rules action (e.g. `add-header`) 366 | * `haproxy_backend.{n}.http_request.{n}.param`: [optional]: The complete line to be added (e.g. `X-Forwarded-Proto https`) 367 | * `haproxy_backend.{n}.http_request.{n}.cond`: [optional]: A matching condition built from ACLs (e.g. `if { ssl_fc }`) 368 | * `haproxy_backend.{n}.http_response`: [optional]: Access control for Layer 7 responses 369 | * `haproxy_backend.{n}.http_response.{n}.action`: [required]: The rules action (e.g. `del-header`) 370 | * `haproxy_backend.{n}.http_response.{n}.param`: [optional]: The complete line to be added (e.g. `X-Varnish`) 371 | * `haproxy_backend.{n}.http_response.{n}.cond`: [optional]: A matching condition built from ACLs 372 | * `haproxy_backend.{n}.tcp_request_content`: [optional]: Perform an action on a new session depending on a layer 4-7 condition 373 | * `haproxy_backend.{n}.tcp_request_content.{n}.action`: [required]: The action for the `tcp-request content` rule 374 | * `haproxy_backend.{n}.tcp_request_content.{n}.cond`: [optional]: A matching condition for the `tcp-request content` rule 375 | * `haproxy_backend.{n}.tcp_request_inspect_delay`: [optional]: Set the maximum allowed time to wait for data during content inspection 376 | * `haproxy_backend.{n}.tcp_request_inspect_delay.{n}.timeout`: [required]: The timeout value in millisecond for the `tcp-request inspect-delay` rule 377 | * `haproxy_backend.{n}.stats`: [optional]: Stats declarations 378 | * `haproxy_backend.{n}.stats.enable`: [required]: Enables statistics reporting with default settings 379 | * `haproxy_backend.{n}.stats.uri`: [optional, default `/`]: Define the URI prefix to access statistics 380 | * `haproxy_backend.{n}.stats.options`: [optional]: List of boolean stats options (e.g. `hide-version`, `show-node`, `show-desc`, `show-legends`) 381 | * `haproxy_backend.{n}.stats.refresh`: [optional]: Defined the refresh delay, specified in seconds (e.g. `5s`) 382 | * `haproxy_backend.{n}.stats.admin`: [optional]: Define / enable admin part of web interface with conditional attached 383 | * `haproxy_backend.{n}.stats.auth`: [optional]: Auth declarations 384 | * `haproxy_backend.{n}.stats.auth.{n}.user`: [required]: A user name to grant access to 385 | * `haproxy_backend.{n}.stats.auth.{n}.passwd`: [required]: The cleartext password associated to this user 386 | * `haproxy_backend.{n}.compression`: [optional]: Compression declarations 387 | * `haproxy_backend.{n}.compression.{n}.name`: [required]: The compression name (e.g. `algo`, `type`, `offload`) 388 | * `haproxy_backend.{n}.compression.{n}.value`: [required]: The compression value, (e.g. if name = algo : one of this values `identity`, `gzip`, `deflate`, `raw-deflate` / if name = type : list of mime type separated by space for example `text/html text/plain text/css` / if name = `offload` value is empty) 389 | * `haproxy_backend.{n}.server`: [optional]: Server declarations 390 | * `haproxy_backend.{n}.server.{n}.name`: [required]: The internal name assigned to this server 391 | * `haproxy_backend.{n}.server.{n}.listen`: [required]: Defines a listening address and/or ports 392 | * `haproxy_backend.{n}.server.{n}.param`: [optional]: A list of parameters for this server 393 | * `haproxy_backend.{n}.server_template`: [optional]: Server template declarations 394 | * `haproxy_backend.{n}.server_template.name`: [required]: A prefix for the server names to be built 395 | * `haproxy_backend.{n}.server_template.num`: [required]: Number or range of servers. If specified as ``, this template initializes `` servers with 1 up to `` as server name suffixes. If specified as `-`, initializes with `` up to `` as server name suffixes 396 | * `haproxy_backend.{n}.server_template.fqdn`: [required]: A FQDN for all the servers this template initializes 397 | * `haproxy_backend.{n}.server_template.port`: [optional]: Port specification 398 | * `haproxy_backend.{n}.server_template.{n}.param`: [optional]: A list of parameters for this server template 399 | * `haproxy_backend.{n}.server_dynamic`: [optional]: Dynamic backend server declaration 400 | * `haproxy_backend.{n}.server_dynamic.{n}.group`: [required]: An ansible group containing hosts to be added as backend servers. Uses `inventory_hostname` for name and either `ansible_host` (if defined) or `inventory_hostname` for the listen address of each host 401 | * `haproxy_backend.{n}.server_dynamic.{n}.listen_port`: [optional]: The port to use with each dynamic backend (translates to `listen :`) 402 | * `haproxy_backend.{n}.server_dynamic.{n}.param`: [optional]: A list of parameters to apply on each backend server 403 | * `haproxy_backend.{n}.retry_on`: [optional, default `[]`]: Specify when to attempt to automatically retry a failed request. Provide a list of keywords or HTTP status codes, each representing a type of failure event on which an attempt to retry the request is desired. For details, see HAProxy documentation 404 | * `haproxy_backend.{n}.retries`: [optional]: Number of retries to perform on a server after a connection failure 405 | * `haproxy_backend.{n}.email_alert`: [default: `[]`]: Specify email alerts option 406 | * `haproxy_backend.{n}.email_alert.{n}.code`: [required]: Email alert key can be : mailers, from, to or level 407 | * `haproxy_backend.{n}.email_alert.{n}.value`: [required]: Email alert key value 408 | 409 | * `haproxy_backend.{n}.errorfile`: [optional]: Errorfile declarations 410 | * `haproxy_backend.{n}.errorfile.{n}.code`: [required]: The HTTP status code. Currently, HAProxy is capable of generating codes 200, 400, 403, 408, 500, 502, 503, and 504 (e.g. `400`) 411 | * `haproxy_backend.{n}.errorfile.{n}.file`: [required]: A file containing the full HTTP response (e.g `/etc/haproxy/errors/400.http`) 412 | * `haproxy_backend.{n}.default_server_params`: [optional]: Default server params applied for each server for this particular backend entry 413 | * `haproxy_backend.{n}.raw_options`: [default: `[]`]: Additional arbitrary lines to insert in the section 414 | 415 | * `haproxy_userlists`: [default: `[]`]: Userlist declarations 416 | * `haproxy_userlists.{n}.name`: [required]: The name of the userlist 417 | * `haproxy_userlists.{n}.users`: [required] Userlist users declarations 418 | * `haproxy_userlists.{n}.users.{n}.name`: [required] The username of this user 419 | * `haproxy_userlists.{n}.users.{n}.password`: [optional] Password hash of this user. **One of `password` or `insecure_password` must be set** 420 | * `haproxy_userlists.{n}.users.{n}.insecure_password`: [optional] Plaintext password of this user. **One of `password` or `insecure_password` must be set** 421 | * `haproxy_userlists.{n}.users.{n}.groups`: [optional] List of groups to add the user to 422 | 423 | * `haproxy_resolvers`: [default: `[]`]: Resolvers (name servers) declarations 424 | * `haproxy_resolvers.{n}.name`: [required]: The name of the name server list 425 | * `haproxy_resolvers.{n}.nameservers`: [required] list of DNS servers 426 | * `haproxy_resolvers.{n}.nameservers.{n}.name`: [required] label of the server, should be unique 427 | * `haproxy_resolvers.{n}.nameservers.{n}.listen`: [required] Defines a listening address and/or ports, e.g. `8.8.8.8:53` 428 | * `haproxy_resolvers.{n}.accepted_payload_size`: [optional]: Defines the maximum payload size (in bytes) accepted by HAProxy and announced to all the name servers configured in this resolvers section. If not set, HAProxy announces 512. (minimal value defined by RFC 6891) 429 | * `haproxy_resolvers.{n}.parse_resolv_conf`: [optional]: If set to `true`, adds all nameservers found in `/etc/resolv.conf` to this resolver's nameservers list 430 | * `haproxy_resolvers.{n}.resolve_retries`: [optional]: Defines the number of queries to send to resolve a server name before giving up 431 | * `haproxy_resolvers.{n}.hold`: [optional]: A list of directives defining `` during which the last name resolution should be kept based on last resolution `` 432 | * `haproxy_resolvers.{n}.hold.{status}`: [optional]: hold directives in `:` format. Key must be one of (`nx`, `other`, `refused`, `timeout`, `valid`, `obsolete`). Value is interval between two successive name resolutions in HAProxy time format 433 | * `haproxy_resolvers.{n}.timeout`: [optional]: Defines timeouts related to name resolution 434 | * `haproxy_resolvers.{n}.timeout.{event}`: [optional]: timeout directives in `: