From 8cd8784825d6143e945369d56e64231b09b7c3b5 Mon Sep 17 00:00:00 2001
From: Clark Boylan <clark.boylan@gmail.com>
Date: Thu, 13 Feb 2025 08:27:48 -0800
Subject: [PATCH] Fix haproxy access to rsyslogd on Noble

Ubuntu Noble ships with an enforcing rsyslogd apparmor profile. This
profile prevents our haproxy container from opening the syslog socket we
bind mount into the container. I discussed this in #ubuntu-security
which resulted in this issue:

  https://bugs.launchpad.net/ubuntu/+source/rsyslog/+bug/2098148

which includes many details on what is going on. This change implements
the suggested workaround for our haproxy nodes. I believe this is the
only place we are currently attempting to directly access rsyslog
sockets from within containers.

The tl;dr on the fix is that we have to tell rsyslogd to attach
disconnected connections as the container runs in a different filesystem
namespace which disconnects the paths for the socket. Unfortunately
sarnold indicates that we have to edit the primary profile configuration
file as this flag applies to the top level of the profile. We cannot use
one of the files this profile #includes.

Change-Id: I4e09211a1bdc4dfbf3012a66e79c181c6fb957a4
---
 playbooks/roles/haproxy/tasks/main.yaml | 14 ++++++++++++++
 testinfra/test_zuul_lb.py               |  7 +++++++
 2 files changed, 21 insertions(+)

diff --git a/playbooks/roles/haproxy/tasks/main.yaml b/playbooks/roles/haproxy/tasks/main.yaml
index 064032e0c8..723bd78312 100644
--- a/playbooks/roles/haproxy/tasks/main.yaml
+++ b/playbooks/roles/haproxy/tasks/main.yaml
@@ -22,6 +22,20 @@
     that:
       - haproxy_config_template is defined
 
+- name: Fix rsyslog apparmor profile on Noble and newer
+  when: ansible_distribution_version is version('24.04', '>=')
+  block:
+    - name: Edit rsyslogd apparmor profile
+      lineinfile:
+        path: /etc/apparmor.d/usr.sbin.rsyslogd
+        regexp: '^profile rsyslogd /usr/sbin/rsyslogd {'
+        line: 'profile rsyslogd /usr/sbin/rsyslogd flags=(attach_disconnected) {'
+      register: profile_update
+
+    - name: Reload rsyslogd apparmor profile
+      command: apparmor_parser -r /etc/apparmor.d/usr.sbin.rsyslogd
+      when: profile_update.changed
+
 - name: Write rsyslog file
   copy:
     src: rsyslog.d/49-haproxy.conf
diff --git a/testinfra/test_zuul_lb.py b/testinfra/test_zuul_lb.py
index 2543aee2d4..58b25d3044 100644
--- a/testinfra/test_zuul_lb.py
+++ b/testinfra/test_zuul_lb.py
@@ -32,3 +32,10 @@ def test_haproxy_statsd_running(host):
     out = json.loads(cmd.stdout)
     assert out[0]["State"]["Status"] == "running"
     assert out[0]["RestartCount"] == 0
+
+def test_haproxy_logging(host):
+    # rsyslog is configured to add a unix socket at this path
+    assert host.file('/var/lib/haproxy/dev/log').is_socket
+    # Haproxy logs to syslog via the above socket which produces
+    # this logfile
+    assert host.file('/var/log/haproxy.log').is_file