ansible.posix.firewalld.0day

Am 3. Mai hat es mal wieder so richtig geknallt im Systemengineering Team bei Puzzle. Kurz vor Mittag mussten wir feststellen, dass die Firewalls unserer Cloud-Server alle Pakete einfach so durchlassen. Wie wir das ganze mitigiert haben, was schuld war und wie auch du sicherstellen kannst, dass deine Server unerwünschte Pakete filtern, liest du in diesem Blogpost.

TLDR; Falls du firewalld aus ansible.posix in der Version 1.5.2 verwendet hast, update diese noch heute und falls du port: verwendet hast, schliesse deine Firewall wieder.

=> ENGLISH VERSION BELOW

Am Tag nach dem Mainachtsessen in der füüri – den Rauchgeschmack immer noch in der Nase – ist das Systemengineering Team so richtig «on fire». Zufällig, durch ein Finding unseres Bug-Bounty-Programms mit Bug Bounty Switzerland, entdeckt unser Systems Architect, dass auf unseren Cloud-Servern die Firewall nicht so funktioniert, wie wir dies erwarten würden. In einem Call werden die nächsten Schritte und die Ursache geklärt und wir machen uns an die Mitigation des Problems.

How to close your Firewall the hard way

Wir waren uns ziemlich schnell einig, dass die Ursache des Problems von einer aktualisierten Ansible Collection stammt. Nach einer kurzen Kontrolle waren wir sicher, dass wir immer nur die Zone ansprechen, aber keine Interface/Zonen Zuordnung via Ansible vornehmen. Wir konnten also von Hand die betroffenen Interfaces in eine andere Zone verschieben, da die benötigten Rules wieder anwenden und alles war wieder beim Alten:

firewall-cmd --permanent --zone=block --change-interface=eth0
firewall-cmd --permanent --add-service=ssh --zone=block
firewall-cmd --reload

Soweit so einfach, jetzt stellt aber der gewiefte Netzwerker fest, dass da mit diesem IPV6 etwas nicht ganz so tut wie es soll. Die public Zone, in welcher das Interface vor der Mitigation war, hat als Target default und die neue block Zone hat das Target %%REJECT%%. Der Unterschied zwischen den beiden Targets ist, dass default zwar auch alles dropped was nicht erlaubt ist, jedoch implizit ICMP erlaubt (was man in einem firewall-cmd --list-all zwar nicht sieht, aber halt wissen müsste). Ebenfalls wissen muss man, dass IPV6 nicht wie IPV4 arp, sondern eben ICMP für die SLAAC benutzt. Somit muss, wer IPV6 auf dem Interface verwendet, zusätzlich noch folgenden Befehl verwenden:

firewall-cmd --zone=block --add-protocol ipv6-icmp

Weitere Informationen:

Sesam öffne dich per Ansible

Was hat dann nun dazu geführt, dass unsere Server mit den Hosen unter den Knien im Netz standen?
Da wir in unseren Logs schnell sahen, zu welchem Zeitpunkt ssh Zugriffe für die üblichen User begannen, konnten wir mit einer hohen Wahrscheinlichkeit das Update der ansible.posix Collection als Übeltäter identifizieren. Kurz später fanden wir auf Github ein Issue, welches unser Problem zwar relativ exakt beschreibt, aber das Ausmass nicht wirklich erfasst und somit wohl auch nicht die nötige Aufmerksamkeit erhielt. Da die Schwachstelle bereits veröffentlicht war und wir eine schnelle Problemlösung als wichtig empfanden, haben wir uns dem Issue angenommen und das Problem genauer identifiziert. (evtl. wäre ein “responsible disclosure” im Rückblick die anständigere Methode gewesen, aber der Bug kann nicht aktiv ausgenützt werden und somit gehen wir davon aus keinen weiteren Schaden angerichtet zu haben, indem wir auf den Schweregrad des Problems hingewiesen zu haben.)

Das Problem war, dass im Modul ein neuer Parameter hinzugefügt wurde, welcher im Pythoncode desselben Moduls bereits verwendet wurde. Somit wird bei einem Ansible Run vom folgenden Code, anstatt nur der Port, das ganze Protokoll freigeschaltet:

    - name: open ports
      ansible.posix.firewalld:
        port: "25/tcp"
        permanent: yes
        immediate: yes
        state: enabled
        zone: "public"

Da im /etc/firewalld/zones/public.xml ein <protocol value="tcp"/> hinzugefügt wird, was alles mit diesem Protokoll freischaltet.

Wann bin ich betroffen?

Betroffen sind User der ansible.posix collection in der Version 1.5.2 welche das firewalld Modul mit dem port Parameter angesprochen haben (wer nur service verwendet hat, blieb verschont).


ENGLISH VERSION

On May 3rd, the systems engineering team at Puzzle got to experience a proper mess-up. Just before lunchtime we realized that the firewalls of our cloud servers simply admitted every packet, no longer filtering what we wanted them to filter. This post will explain how we mitigated the issue, what exactly had happened and how you too can ensure that your servers filter out unwanted packets.

TLDR; if you used firewalld from ansible.posix in version 1.5.2, update it and if you used port: close your firewall again.

The day after our May company event at chillfood’s füüri, still smelling the smoke from the cooking fires, our systems engineering team was pretty “on fire” themselves. By chance, based on a finding from the bug bounty program we are running with Bug Bounty Switzerland, our systems architect discovers that the firewalls on our servers aren’t behaving as expected. We immediately set up a call to investigate the cause and decide on our next steps, focusing on mitigating the issue as soon as possible.

How to close your firewall the hard way

We quickly agreed that the issue was caused by an update to an Ansible collection. After a quick check we were sure that we were only referencing zones, and not assigning interfaces to zones via Ansible. That allowed us to manually shift the affected interfaces into a different zone, activated the desired rules and be back to normal:

firewall-cmd --permanent --zone=block --change-interface=eth0
firewall-cmd --permanent --add-service=ssh --zone=block
firewall-cmd --reload

Of course, nothing is ever that simple, and the skilled networker will quickly realize that IPv6 ain’t doing what it is supposed to. The public zone, which the interface was assigned to before, targets default, while the new block zone targets %%REJECT%%. The small, but relevant difference between the two is that while default drops everything that isn’t allowed, “allowed” also implicitly includes ICMP – even though firewall-cmd --list-all doesn’t show that. Just gotta know it! What you also have to know is that IPv6 doesn’t use ARP, like IPv4, but relies on ICMP for stateless address auto-configuration (SLAAC). So, if you want to use IPv6 on that interface, you also need to execute the following command:

firewall-cmd --zone=block --add-protocol ipv6-icmp

Further reading:

Ansible open sesame

So what did cause our servers to be exposed to the internet with their pants down?

Thanks to our logging, we were quickly able to identify when our servers became exposed (by seeing when the usual attempts at logging in to ssh began). This information in turn told us that the most likely culprit was an update to the ansible.posix collection. We were able to find an issue on GitHub, which described the problem we had encountered exactly – but missing the severity of the problem and not receiving the appropriate attention because of that. Since the vulnerability was already public and we felt that a quick solution was important, we started working on the issue and identified the problem in the code and in comments on the issue. In hindsight, a responsible disclosure approach would probably have been more polite, but the bug cannot be actively triggered and we therefore expect not to have made the situation worse by pointing out the severity of the issue.

What had happened was that a protocol parameter had been added to the firewalld module, even though that name was already in use in the source of the module. Due to this double definition, the following code doesn’t just open a specific port but the entire protocol:

    - name: open ports
      ansible.posix.firewalld:
        port: "25/tcp"
        permanent: yes
        immediate: yes
        state: enabled
        zone: "public"

This leads to /etc/firewalld/zones/public.xml containing <protocol value="tcp"/>. Additionally, the module doesn’t log this as a change that could be monitored during the run, since it is confused as to what is happening.

How to figure out if you’re affected

Anyone who used ansible.posix in version 1.5.2 and utilized the firewalld module with the port parameter is affected. If you utilized only service, you dodged this bullet.

Kommentare sind geschlossen.