From 054a3711f15baf97db42668d1e791cb4e0815f71 Mon Sep 17 00:00:00 2001 From: Tyler Hale Date: Thu, 23 Sep 2021 09:54:34 -0600 Subject: [PATCH] Initial commit --- README.md | 65 ++++++++- haproxy.yml | 6 + hosts.yml | 37 +++++ roles/haproxy/defaults/main.yml | 20 +++ roles/haproxy/handlers/main.yml | 26 ++++ roles/haproxy/tasks/main.yml | 134 ++++++++++++++++++ .../haproxy/templates/example.haproxy.cfg.j2 | 84 +++++++++++ roles/haproxy/templates/keepalived.conf.j2 | 24 ++++ site.yml | 4 + 9 files changed, 393 insertions(+), 7 deletions(-) create mode 100644 haproxy.yml create mode 100644 hosts.yml create mode 100644 roles/haproxy/defaults/main.yml create mode 100644 roles/haproxy/handlers/main.yml create mode 100644 roles/haproxy/tasks/main.yml create mode 100644 roles/haproxy/templates/example.haproxy.cfg.j2 create mode 100644 roles/haproxy/templates/keepalived.conf.j2 create mode 100644 site.yml diff --git a/README.md b/README.md index ee5d335..4683447 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,69 @@ Ansible playbook that provisions a group of servers to run HAProxy with a shared hosting IP from kepalived. -## Requirements - ## Variables -| Variable | Required | Default | Choices | Description | -| -------- | -------- | ------- | ------- | ----------- | -| | | | | | +| Variable | Required | Default | Choices | Description | +| --------------------------------- | -------- | -------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| haproxy_bind_adapter | Yes | eth0 | | Interface to use for the Shared IP | +| haproxy_keepalived_adapter | Yes | eth0 | | Interface to use for the keepalived communication | +| haproxy_keepalived_adapter_vlan | No | | | When specified, this will setup the given vlan and use that for the keepalived communication to keep the traffic out of the primary network | +| haproxy_keepalived_ip | Yes | 172.16.10.1/24 | | IP to use for the keepalived communication | +| haproxy_keepalived_process_weight | Yes | 10 | | Weight used for tracking the haproxy process on the server | +| haproxy_shared_ip | Yes | | | Shared IP that will be used by the group | +| haproxy_shared_ip_subnet | Yes | /24 | | Subnet for the shared IP | +| haproxy_shared_priority | Yes | 100 | | Keepalived priority for the host - this will | +| haproxy_shared_state | Yes | MASTER | MASTER/BACKUP | What state the keepalived configuration will start with for the desired host - After config starts it will determine if other members exist and adjust based on priority | +| haproxy_shared_virtual_router_id | Yes | 25 | | Virtual Router ID for the keepalived configuration - This should be the same for the group | ## Example +Execute playbook with needed variables + +`ansible-playbook -i hosts site.yml --ask-pass --ask-become-pass` + +Inventory File: + +```yaml +--- +# file: hosts + +haproxy: + hosts: + dev-ha01: + ansible_host: 10.1.1.3 + haproxy_shared_state: MASTER + haproxy_shared_priority: 101 + haproxy_bind_adapter: "eth0" + haproxy_keepalived_adapter: "eth0" + haproxy_keepalived_adapter_vlan: "100" + haproxy_keepalived_ip: 172.16.10.1/24 + + dev-ha02: + ansible_host: 10.1.1.4 + haproxy_shared_state: BACKUP + haproxy_shared_priority: 100 + haproxy_bind_adapter: "eth0" + haproxy_keepalived_adapter: "eth0" + haproxy_keepalived_adapter_vlan: "100" + haproxy_keepalived_ip: 172.16.10.2/24 + + dev-ha03: + ansible_host: 10.1.1.5 + haproxy_shared_state: BACKUP + haproxy_shared_priority: 99 + haproxy_bind_adapter: "eth0" + haproxy_keepalived_adapter: "eth0" + haproxy_keepalived_adapter_vlan: "100" + haproxy_keepalived_ip: 172.16.10.3/24 + + vars: + haproxy_shared_ip: "10.10.2.60" + haproxy_shared_ip_subnet: "/24" + haproxy_shared_virtual_router_id: 25 + +``` + ## License See LICENSE file for full license information. - -## Screenshots diff --git a/haproxy.yml b/haproxy.yml new file mode 100644 index 0000000..10eca9f --- /dev/null +++ b/haproxy.yml @@ -0,0 +1,6 @@ +--- +# file: haproxy.yml + +- hosts: haproxy + roles: + - haproxy diff --git a/hosts.yml b/hosts.yml new file mode 100644 index 0000000..4d7552d --- /dev/null +++ b/hosts.yml @@ -0,0 +1,37 @@ +--- +# file: hosts + +haproxy: + hosts: + dev-ha01: + ansible_host: 10.1.1.3 + haproxy_shared_state: MASTER + haproxy_shared_priority: 101 + haproxy_bind_adapter: "eth0" + haproxy_keepalived_adapter: "eth0" + haproxy_keepalived_adapter_vlan: "100" + haproxy_keepalived_ip: 172.16.10.1/24 + + dev-ha02: + ansible_host: 10.1.1.4 + haproxy_shared_state: BACKUP + haproxy_shared_priority: 100 + haproxy_bind_adapter: "eth0" + haproxy_keepalived_adapter: "eth0" + haproxy_keepalived_adapter_vlan: "100" + haproxy_keepalived_ip: 172.16.10.2/24 + + dev-ha03: + ansible_host: 10.1.1.5 + haproxy_shared_state: BACKUP + haproxy_shared_priority: 99 + haproxy_bind_adapter: "eth0" + haproxy_keepalived_adapter: "eth0" + haproxy_keepalived_adapter_vlan: "100" + haproxy_keepalived_ip: 172.16.10.3/24 + + vars: + haproxy_shared_ip: "10.10.2.60" + haproxy_shared_ip_subnet: "/24" + haproxy_shared_virtual_router_id: 25 + \ No newline at end of file diff --git a/roles/haproxy/defaults/main.yml b/roles/haproxy/defaults/main.yml new file mode 100644 index 0000000..4d96499 --- /dev/null +++ b/roles/haproxy/defaults/main.yml @@ -0,0 +1,20 @@ +--- +# file: roles/haproxy/defaults/main.yml + +haproxy_config_file: "example.haproxy.cfg.j2" + +# Set for entire group + +haproxy_shared_ip: "" +haproxy_shared_ip_subnet: "/24" +haproxy_shared_virtual_router_id: 25 +haproxy_keepalived_process_weight: 10 + +# Set per host + +haproxy_shared_state: MASTER +haproxy_shared_priority: 100 +haproxy_bind_adapter: "eth0" +haproxy_keepalived_adapter: "eth0" +haproxy_keepalived_adapter_vlan: "" +haproxy_keepalived_ip: 172.16.10.1/24 diff --git a/roles/haproxy/handlers/main.yml b/roles/haproxy/handlers/main.yml new file mode 100644 index 0000000..775d4e5 --- /dev/null +++ b/roles/haproxy/handlers/main.yml @@ -0,0 +1,26 @@ +--- +# file: roles/haproxy/handlers/main.yml + +- name: reload haproxy + service: + name: haproxy + state: reloaded + become: yes + +- name: reload keepalived + service: + name: keepalived + state: reloaded + become: yes + +- name: reload firewalld + service: + name: firewalld + state: reloaded + become: yes + +- name: Restart SSH + service: + name: sshd + state: restarted + become: yes diff --git a/roles/haproxy/tasks/main.yml b/roles/haproxy/tasks/main.yml new file mode 100644 index 0000000..96051d7 --- /dev/null +++ b/roles/haproxy/tasks/main.yml @@ -0,0 +1,134 @@ +--- +# file: roles/haproxy/tasks/main.yml + +- name: Install Ansible prereq packages + package: + name: + - policycoreutils-python-utils + - NetworkManager + state: latest + become: yes + +- name: Install packages + package: + name: + - keepalived + - haproxy + - firewalld + state: latest + become: yes + +- name: Add VLAN + nmcli: + conn_name: "{{ haproxy_keepalived_adapter }}.{{ haproxy_keepalived_adapter_vlan }}" + vlanid: "{{ haproxy_keepalived_adapter_vlan }}" + ip4: "{{ haproxy_keepalived_ip }}" + vlandev: "{{ haproxy_keepalived_adapter }}" + autoconnect: yes + type: vlan + state: present + become: yes + when: (haproxy_keepalived_adapter_vlan is defined) and (haproxy_keepalived_ip is defined) + +- name: Enable firewall rule for vrrp + firewalld: + rich_rule: 'rule protocol value="vrrp" accept' + permanent: yes + state: enabled + immediate: yes + notify: reload firewalld + become: yes + +- name: Allow binding non-local IP + sysctl: + name: net.ipv4.ip_nonlocal_bind + value: "1" + reload: true + state: present + become: yes + +- name: Allow binding ip forward + sysctl: + name: net.ipv4.ip_forward + value: "1" + reload: true + state: present + become: yes + +- name: Update keepalived configuration + template: + src: keepalived.conf.j2 + dest: /etc/keepalived/keepalived.conf + notify: reload keepalived + become: yes + +- name: Enable keepalived + service: + name: keepalived + state: started + enabled: yes + become: yes + +- name: Limit SSH to only ansible_host + lineinfile: + path: /etc/ssh/sshd_config + regexp: '^#?ListenAddress ' + line: "ListenAddress {{ ansible_host }}" + notify: + - Restart SSH + become: yes + +- name: Flush handlers + meta: flush_handlers + +- name: Set haproxy_connect_any flag and keep it persistent across reboots + seboolean: + name: haproxy_connect_any + state: yes + persistent: yes + become: yes + +- name: Update HAProxy configuration + template: + src: haproxy.cfg.j2 + dest: /etc/haproxy/haproxy.cfg + mode: 0644 + validate: haproxy -f %s -c -q + notify: reload haproxy + become: yes + +- name: Ensure HAProxy is started and enabled on boot. + service: + name: haproxy + state: started + enabled: yes + become: yes + +- name: Enable firewall ports for haproxy + firewalld: + rich_rule: 'rule family=ipv4 destination address={{ haproxy_shared_ip }}{{ haproxy_shared_ip_subnet }} port port={{ item.port }} protocol={{ item.protocol }} accept' + permanent: yes + immediate: yes + state: enabled + with_items: + - {port: "22", protocol: "tcp"} + - {port: "80", protocol: "tcp"} + - {port: "443", protocol: "tcp"} + notify: reload firewalld + become: yes + +- name: Enable firewall ports for haproxy stats + firewalld: + port: "9999/tcp" + permanent: yes + immediate: yes + state: enabled + notify: reload firewalld + become: yes + +- name: Enable firewalld + service: + name: firewalld + state: started + enabled: yes + become: yes diff --git a/roles/haproxy/templates/example.haproxy.cfg.j2 b/roles/haproxy/templates/example.haproxy.cfg.j2 new file mode 100644 index 0000000..961acf7 --- /dev/null +++ b/roles/haproxy/templates/example.haproxy.cfg.j2 @@ -0,0 +1,84 @@ +global + log /dev/log local0 + log /dev/log local1 notice + chroot /var/lib/haproxy + #stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners + stats socket /var/lib/haproxy/stats + stats timeout 30s + user haproxy + group haproxy + daemon + + # Default SSL material locations + ca-base /etc/ssl/certs + crt-base /etc/ssl/private + + # See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate + ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 + ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 + ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets + tune.ssl.default-dh-param 2048 + tune.h2.initial-window-size 1048576 + +defaults + log global + mode http + option httplog + option dontlognull + timeout connect 5000 + timeout client 50000 + timeout server 50000 +{% if ansible_os_family == 'Debian' %} + errorfile 400 /etc/haproxy/errors/400.http + errorfile 403 /etc/haproxy/errors/403.http + errorfile 408 /etc/haproxy/errors/408.http + errorfile 500 /etc/haproxy/errors/500.http + errorfile 502 /etc/haproxy/errors/502.http + errorfile 503 /etc/haproxy/errors/503.http + errorfile 504 /etc/haproxy/errors/504.http +{% endif %} + +frontend ssh-in + mode tcp + bind {{ haproxy_shared_ip }}:22 + timeout connect 0ms + timeout client 0ms + timeout server 0ms + use_backend example-ssh-backend + +frontend http-in + mode http + bind {{ haproxy_shared_ip }}:80 + + use_backend example-http-backend + +frontend https-in + mode tcp + tcp-request inspect-delay 1s + bind {{ haproxy_shared_ip }}:443 + tcp-request content accept if { req.ssl_hello_type 1 } + + use_backend example-https-backend + +backend example-ssh-backend + mode tcp + timeout connect 0ms + timeout client 0ms + timeout server 0ms + server example 10.1.1.11:22 check + +backend example-http-backend + mode http + log global + timeout connect 30000 + timeout server 30000 + retries 3 + server example 10.10.2.11:80 check + +backend example-https-backend + mode tcp + log global + timeout connect 30000 + timeout server 30000 + retries 3 + server example 10.10.2.11:443 check diff --git a/roles/haproxy/templates/keepalived.conf.j2 b/roles/haproxy/templates/keepalived.conf.j2 new file mode 100644 index 0000000..bae17c8 --- /dev/null +++ b/roles/haproxy/templates/keepalived.conf.j2 @@ -0,0 +1,24 @@ +vrrp_track_process track_haproxy { + process haproxy + weight {{ haproxy_keepalived_process_weight }} +} + + +vrrp_instance VI_1 { +{% if haproxy_keepalived_adapter_vlan != '' %} + interface {{ haproxy_keepalived_adapter }}.{{ haproxy_keepalived_adapter_vlan }} +{% else %} + interface {{ haproxy_keepalived_adapter }} +{% endif %} + state {{ haproxy_shared_state }} + virtual_router_id {{ haproxy_shared_virtual_router_id }} + priority {{ haproxy_shared_priority }} + + virtual_ipaddress { + {{ haproxy_shared_ip }}{{ haproxy_shared_ip_subnet }} dev {{ haproxy_bind_adapter }} + } + + track_process { + track_haproxy + } +} diff --git a/site.yml b/site.yml new file mode 100644 index 0000000..a745103 --- /dev/null +++ b/site.yml @@ -0,0 +1,4 @@ +--- +## This playbook deploys the whole application stack in this site. + +- import_playbook: haproxy.yml