Allow haproxy to bind on the interface

In some user scenarious (like implementing DNS RR) it might be useful to
bind on 0.0.0.0 but at the same time do not conflict with other services
that are binded to the same ports. For that, we can specify a specific
interface, on which haproxy will be binded to 0.0.0.0.

In netstat it would be represented like `0.0.0.0%br-mgmt:5000`.

With that we also allow to fully override `vip_binds` if assumtions
that role make are not valid for some reason.

Change-Id: Ic4c58ef53abc5f454b6fbebbd87292a932d173ae
This commit is contained in:
Dmitriy Rabotyagov 2022-08-03 18:08:28 +02:00 committed by Dmitriy Rabotyagov
parent dd842f4eb4
commit 901523ddbb
5 changed files with 49 additions and 16 deletions

View File

@ -235,6 +235,19 @@ haproxy_bind_external_lb_vip_address: "{{ external_lb_vip_address }}"
# Option to override which address haproxy binds to for internal vip.
haproxy_bind_internal_lb_vip_address: "{{ internal_lb_vip_address }}"
# Option to define if you need haproxy to bind on specific interface.
haproxy_bind_external_lb_vip_interface:
haproxy_bind_internal_lb_vip_interface:
# Option to override haproxy frontend binds
# Example:
# haproxy_tls_vip_binds:
# - address: '*'
# interface: bond0
# - address: '192.168.0.10'
haproxy_tls_vip_binds: "{{ _haproxy_tls_vip_binds }}"
# Make the log socket available to the chrooted filesystem
haproxy_log_socket: "/dev/log"
haproxy_log_mount_point: "/var/lib/haproxy/dev/log"

View File

@ -17,9 +17,10 @@
shell: >
cat {{ item_base_path ~ '.crt' }} $(test -f {{ item_base_path ~ '-ca.crt' }} && echo {{ item_base_path ~ '-ca.crt' }}) {{ item_base_path ~ '.key' }} > {{ item_base_path ~ '.pem' }}
notify: Reload haproxy
with_items: "{{ _haproxy_tls_vip_binds }}"
vars:
item_base_path: "{{ haproxy_ssl_cert_path ~ '/haproxy_' ~ ansible_facts['hostname'] ~ '-' ~ item }}"
item_name: "{{ ('interface' in item and item['interface'] is truthy) | ternary(item['address'] ~ '-' ~ item['interface'], item['address']) }}"
item_base_path: "{{ haproxy_ssl_cert_path ~ '/haproxy_' ~ ansible_facts['hostname'] ~ '-' ~ item_name }}"
with_items: "{{ haproxy_tls_vip_binds }}"
listen:
- cert installed

View File

@ -0,0 +1,12 @@
---
features:
- |
Added variables ``haproxy_bind_external_lb_vip_interface`` and
``haproxy_bind_internal_lb_vip_interface`` that allows deployer to bind
haproxy on the specific interface only.
- |
Added variable ``haproxy_tls_vip_binds`` that allows to fully override
haproxy bindings, that are generated by the role if some assumptions are
not valid for some scenarios. It is list of mappings, that include address
and interface. Interface key is optional and can be ommited.

View File

@ -15,16 +15,23 @@
{% if item.service.haproxy_bind is defined %}
{% set vip_binds = item.service.haproxy_bind %}
{% else %}
{% set vip_binds = _haproxy_tls_vip_binds + extra_lb_vip_addresses %}
{% set vip_binds = haproxy_tls_vip_binds + extra_lb_vip_addresses %}
{% endif %}
{% if not item.service.haproxy_backend_only | default(false) %}
{% for vip_bind in vip_binds %}
{% if vip_bind is not string and vip_bind is mapping %}
{% set vip_address = vip_bind['address'] %}
{% set vip_interface = vip_bind['interface'] %}
{% else %}
{% set vip_address = vip_bind %}
{% set vip_interface = '' %}
{% endif %}
{% if item.service.haproxy_redirect_http_port is defined and item.service.haproxy_ssl %}
{% if (loop.index == 1 or item.service.haproxy_ssl_all_vips | default(false) | bool) %}
frontend {{ item.service.haproxy_service_name }}-redirect-front-{{ loop.index }}
bind {{ vip_bind }}:{{ item.service.haproxy_redirect_http_port }}
bind {{ vip_address }}:{{ item.service.haproxy_redirect_http_port }}{{ (vip_interface is truthy) | ternary(' interface ' ~ vip_interface, '') }}
mode http
redirect scheme {{ item.service.haproxy_redirect_scheme | default('https if !{ ssl_fc }') }}
{% if item.service.haproxy_frontend_acls is defined %}
@ -38,11 +45,11 @@ bind {{ vip_bind }}:{{ item.service.haproxy_redirect_http_port }}
{# TODO: remove if and section inside if after HTTPS upgrade #}
{# During an upgrade of internal frontends from HTTP to HTTPS, need to accept both HTTP and HTTPS until client config has been changed #}
{% if (item.service.haproxy_tcp_upgrade_frontend | default(false)) and not (loop.index == 1 or vip_bind in extra_lb_tls_vip_addresses) and (item.service.haproxy_ssl_all_vips | default(false)) %}
{% if (item.service.haproxy_tcp_upgrade_frontend | default(false)) and not (loop.index == 1 or vip_address in extra_lb_tls_vip_addresses) and (item.service.haproxy_ssl_all_vips | default(false)) %}
{% include 'service-redirect.j2' %}
{% else %}
frontend {{ item.service.haproxy_service_name }}-front-{{ loop.index }}
bind {{ vip_bind }}:{{ item.service.haproxy_port }} {% if (item.service.haproxy_ssl | default(false) | bool) and (loop.index == 1 or vip_bind in extra_lb_tls_vip_addresses or (item.service.haproxy_ssl_all_vips | default(false) | bool and vip_bind not in extra_lb_vip_addresses)) %}ssl crt {{ haproxy_ssl_cert_path }}/haproxy_{{ ansible_facts['hostname'] }}-{{ vip_bind }}.pem {% endif %}
bind {{ vip_address }}:{{ item.service.haproxy_port }}{{ (vip_interface is truthy) | ternary(' interface ' ~ vip_interface, '') }} {% if (item.service.haproxy_ssl | default(false) | bool) and (loop.index == 1 or vip_address in extra_lb_tls_vip_addresses or (item.service.haproxy_ssl_all_vips | default(false) | bool and vip_address not in extra_lb_vip_addresses)) %}ssl crt {{ haproxy_ssl_cert_path }}/haproxy_{{ ansible_facts['hostname'] }}-{{ (vip_interface is truthy) | ternary(vip_address ~ '-' ~ vip_interface, vip_address) }}.pem {% endif %}
{% if request_option == "http" %}
option httplog
@ -69,7 +76,7 @@ frontend {{ item.service.haproxy_service_name }}-front-{{ loop.index }}
{% endif %}
{% endfor %}
{% endif %}
{% if (item.service.haproxy_ssl | default(false) | bool) and request_option == 'http' and (loop.index == 1 or vip_bind in extra_lb_tls_vip_addresses or (item.service.haproxy_ssl_all_vips | default(false) | bool and vip_bind not in extra_lb_vip_addresses)) %}
{% if (item.service.haproxy_ssl | default(false) | bool) and request_option == 'http' and (loop.index == 1 or vip_address in extra_lb_tls_vip_addresses or (item.service.haproxy_ssl_all_vips | default(false) | bool and vip_address not in extra_lb_vip_addresses)) %}
http-request add-header X-Forwarded-Proto https
{% endif %}
mode {{ item.service.haproxy_balance_type }}

View File

@ -14,24 +14,24 @@
# limitations under the License.
_haproxy_tls_vip_binds: |
{% set vip_binds = [haproxy_bind_external_lb_vip_address] %}
{% if haproxy_bind_internal_lb_vip_address != haproxy_bind_external_lb_vip_address %}
{% set _ = vip_binds.append(haproxy_bind_internal_lb_vip_address) %}
{% set vip_binds = [{'address': haproxy_bind_external_lb_vip_address, 'interface': haproxy_bind_external_lb_vip_interface}] %}
{% if haproxy_bind_internal_lb_vip_address != haproxy_bind_external_lb_vip_address or haproxy_bind_external_lb_vip_interface != haproxy_bind_internal_lb_vip_interface %}
{% set _ = vip_binds.append({'address': haproxy_bind_internal_lb_vip_address, 'interface': haproxy_bind_internal_lb_vip_interface}) %}
{% endif %}
{% for vip_address in extra_lb_tls_vip_addresses %}
{% set _ = vip_binds.append(vip_address) %}
{% set _ = vip_binds.append({'address': vip_address}) %}
{% endfor %}
{{ vip_binds }}
_haproxy_pki_certificates: |
{% set _pki_certs = [] %}
{% for vip in _haproxy_tls_vip_binds %}
{% for vip in haproxy_tls_vip_binds %}
{% set _ = _pki_certs.append(
{
'name': 'haproxy_' ~ ansible_facts['hostname'] ~ '-' ~ vip,
'name': 'haproxy_' ~ ansible_facts['hostname'] ~ '-' ~ ('interface' in vip and vip['interface'] is truthy) | ternary(vip['address'] ~ '-' ~ vip['interface'], vip['address']),
'provider': 'ownca',
'cn': ansible_facts['hostname'],
'san': 'DNS:' ~ ansible_facts['hostname'] ~ ',DNS:' ~ ansible_facts['fqdn'] ~ ',' ~ (vip | ansible.utils.ipaddr) | ternary('IP:', 'DNS:') ~ vip,
'san': 'DNS:' ~ ansible_facts['hostname'] ~ ',DNS:' ~ ansible_facts['fqdn'] ~ ',' ~ (vip['address'] | ansible.utils.ipaddr) | ternary('IP:', 'DNS:') ~ vip['address'],
'signed_by': haproxy_pki_intermediate_cert_name,
}
) %}
@ -40,8 +40,8 @@ _haproxy_pki_certificates: |
_haproxy_pki_install_certificates: |
{% set _pki_install = [] %}
{% for vip in _haproxy_tls_vip_binds %}
{% set _cert_basename = '/haproxy_' ~ ansible_facts['hostname'] ~ '-' ~ vip %}
{% for vip in haproxy_tls_vip_binds %}
{% set _cert_basename = '/haproxy_' ~ ansible_facts['hostname'] ~ '-' ~ ('interface' in vip and vip['interface'] is truthy) | ternary(vip['address'] ~ '-' ~ vip['interface'], vip['address']) %}
{% set _ = _pki_install.append(
{
'src': haproxy_user_ssl_cert | default(haproxy_pki_certs_path ~ _cert_basename ~ '.crt'),