From b2b1270b8583110d3c3cb5c9fa7eda9da2b160ee Mon Sep 17 00:00:00 2001
From: Andrea Dell'Amico <andrea.dellamico@isti.cnr.it>
Date: Wed, 7 Aug 2019 18:40:48 +0200
Subject: [PATCH] keepalived: Add support for VRRP over unicast. Run the check
 script using an unprivileged user.

---
 library/roles/keepalived/defaults/main.yml    |  9 +++++
 library/roles/keepalived/tasks/main.yml       | 38 ++++++++++++++-----
 .../keepalived/templates/keepalived.conf.j2   | 38 +++++++++++++++++--
 3 files changed, 72 insertions(+), 13 deletions(-)

diff --git a/library/roles/keepalived/defaults/main.yml b/library/roles/keepalived/defaults/main.yml
index d960786c..7a24eff4 100644
--- a/library/roles/keepalived/defaults/main.yml
+++ b/library/roles/keepalived/defaults/main.yml
@@ -10,11 +10,20 @@ keepalived_pkg_state: latest
 keepalived_pkgs:
   - keepalived
 
+keepalived_use_unicast: False
+keepalived_unicast_src_ip: '{{ ansible_default_ipv4.address }}'
+keepalived_unicast_peer_ip:
+  - 127.0.0.1
+  - 127.0.0.2
 keepalived_mcast_addr: 224.0.0.0/8
 keepalived_floating_ip1: 127.0.0.1
 keepalived_script1_name: chk_haproxy
 keepalived_inst_priority: 100
 keepalived_vrouter_id: 51
+keepalived_non_local_bind: True
+
+keepalived_enable_script_security: True
+keepalived_script_username: keepalived_script
 
 keepalived_nagios_check: False
 keepalived_notify_script: /usr/local/bin/keepalived_notify
diff --git a/library/roles/keepalived/tasks/main.yml b/library/roles/keepalived/tasks/main.yml
index d0fa892c..b76a6fa7 100644
--- a/library/roles/keepalived/tasks/main.yml
+++ b/library/roles/keepalived/tasks/main.yml
@@ -1,22 +1,43 @@
 ---
 - block:
-    
     - name: Install the keepalived repository
       apt_repository: repo={{ keepalived_repo }} update_cache=yes
       when: ansible_distribution_major_version <= '16'
 
     - name: Install the keepalived package
-      apt: name={{ item }} state={{ keepalived_pkg_state }}  update_cache=yes cache_valid_time=1800
-      with_items: '{{ keepalived_pkgs }}'
+      apt: name={{ keepalived_pkgs }} state={{ keepalived_pkg_state }} cache_valid_time=1800
+
+    - name: Install the user that the keepalived scripts will run under
+      user: name={{ keepalived_script_username }} home=/var/lib/keepalived createhome=no shell=/usr/sbin/nologin system=yes
 
     - name: Install the keepalived configuration
-      template: src=keepalived.conf.j2 dest=/etc/keepalived/keepalived.conf
+      template: src=keepalived.conf.j2 dest=/etc/keepalived/keepalived.conf owner=root group=root mode=0600
       notify: restart keepalived
 
     - name: Install the keepalived notify scripts
-      template: src=keepalived_notify.sh.j2 dest={{ item.notify }} owner=root group=root mode=0754
+      template: src=keepalived_notify.sh.j2 dest={{ item.notify }} owner=root group={{ keepalived_script_username }} mode=0754
       with_items: '{{ keepalived_instances }}'
-      
+
+    - name: Set the kernel net.ipv4.ip_nonlocal_bind sysctl
+      sysctl: 
+        reload: yes
+        state: present
+        name: net.ipv4.ip_nonlocal_bind
+        sysctl_set: yes
+        sysctl_file: /etc/sysctl.d/90-keepalived
+        value: 1
+      when: keepalived_non_local_bind
+
+    - name: Disable the kernel net.ipv4.ip_nonlocal_bind sysctl if not needed
+      sysctl: 
+        reload: yes
+        state: absent
+        name: net.ipv4.ip_nonlocal_bind
+        sysctl_set: yes
+        sysctl_file: /etc/sysctl.d/90-keepalived
+        value: 1
+      when: not keepalived_non_local_bind
+
     - name: Install the keepalived NRPE nagios check
       copy: src=check_keepalived_state dest={{ nagios_plugins_dir }}/check_keepalived_state  owner=root group=root mode=0555
       with_items: '{{ keepalived_instances }}'
@@ -38,12 +59,9 @@
   tags: keepalived
   when: keepalived_install
 
-
 - block:
-    
     - name: Remove the keepalived package if we do not want it
-      apt: name={{ item }} state=absent
-      with_items: '{{ keepalived_pkgs }}'
+      apt: name={{ keepalived_pkgs }} state=absent
 
     - name: Remove the keepalived notify scripts
       file: dest={{ item.notify }} state=absent
diff --git a/library/roles/keepalived/templates/keepalived.conf.j2 b/library/roles/keepalived/templates/keepalived.conf.j2
index 68c3f63c..e7ce9db7 100644
--- a/library/roles/keepalived/templates/keepalived.conf.j2
+++ b/library/roles/keepalived/templates/keepalived.conf.j2
@@ -1,11 +1,20 @@
+global_defs {
+    {% if keepalived_enable_script_security %}
+
+    enable_script_security
+    script_user {{ keepalived_script_username }}
+
+    {% endif %}
+}
+
 {% for script in keepalived_scripts %}
 vrrp_script {{ script.name }} {
         script "{{ script.script }}"
         interval {{ script.interval | default(1) }}
         weight {{ script.weight | default(2) }}
-        fall: {{ script.fall | default(3) }}
-        raise: {{ script.raise | default(2) }}
-        timeout: {{ script.timeout | default(5) }}
+        fall {{ script.fall | default(3) }}
+        rise {{ script.raise | default(2) }}
+        timeout {{ script.timeout | default(5) }}
 }
 {% endfor %}
 
@@ -13,20 +22,29 @@ vrrp_script {{ script.name }} {
 vrrp_instance {{ instance.name }} {
         interface {{ instance.interface }}
         {% if instance.state is defined %}
+
         state {{ instance.state }}
+
         {% endif %}
         virtual_router_id {{ instance.vrouter_id }}
         priority {{ instance.priority }}
         {% if instance.notify is defined %}
+
         notify {{ instance.notify }}
+
         {% endif %}
         {% if instance.nopreempt is defined and instance.nopreempt %}
+
         nopreempt
+
         {% endif %}
         {% if instance.authentication is defined %}
+
         authentication {
             auth_type {{ instance.authentication }}
             auth_pass {{ instance.authpass }}
+        }
+
         {% endif %}
         virtual_ipaddress {
         {% for addr in instance.v_addr %}
@@ -39,11 +57,25 @@ vrrp_instance {{ instance.name }} {
         {% endfor %}
         }
         {% if instance.track_interface is defined %}
+
         track_interface {
         {% for if in instance.track_interface %}
             {{ if }}
         {% endfor %}
         }
+
+        {% endif %}
+        {% if keepalived_use_unicast %}
+
+        unicast_src_ip {{ keepalived_unicast_src_ip }}
+        unicast_peer {
+
+            {% for ipaddr in keepalived_unicast_peer_ip %}
+            {{ ipaddr }}
+            {% endfor %}
+
+        }
+
         {% endif %}
 }
 {% endfor %}