Эх сурвалжийг харах

firewall: use asyncio's call_later instead of systemd to reload rules

When some expiring rules are present, it is necessary to reload firewall
when those rules expire. Previously systemd timer was used to trigger
this action, but since we have own daemon now, it isn't necessary
anymore - use this daemon for that.
Additionally automatically removing expired rules was completely broken
in R4.0.

Fixes QubesOS/qubes-issues#1173
Marek Marczykowski-Górecki 6 жил өмнө
parent
commit
5e89b23288

+ 0 - 2
linux/systemd/Makefile

@@ -7,8 +7,6 @@ install:
 	mkdir -p $(DESTDIR)$(UNITDIR)
 	cp qubes-core.service $(DESTDIR)$(UNITDIR)
 	cp qubes-vm@.service $(DESTDIR)$(UNITDIR)
-	cp qubes-reload-firewall@.service $(DESTDIR)$(UNITDIR)
-	cp qubes-reload-firewall@.timer $(DESTDIR)$(UNITDIR)
 	cp qubes-qmemman.service $(DESTDIR)$(UNITDIR)
 	cp qubesd.service $(DESTDIR)$(UNITDIR)
 	install -d $(DESTDIR)$(UNITDIR)/lvm2-pvscan@.service.d

+ 22 - 6
qubes/firewall.py

@@ -22,11 +22,12 @@
 
 import datetime
 import string
-import subprocess
 
 import itertools
 import os
 import socket
+
+import asyncio
 import lxml.etree
 
 import qubes
@@ -543,10 +544,19 @@ class Firewall(object):
             rule = Rule(xml_rule)
             self.rules.append(rule)
 
+    def _expire_rules(self):
+        '''Function called to reload expired rules'''
+        old_rules = self.rules
+        self.load()
+        if self.rules != old_rules:
+            # this will both save rules skipping those expired and trigger
+            # QubesDB update; and possibly schedule another timer
+            self.save()
+
     def save(self):
         '''Save firewall rules to a file'''
         firewall_conf = os.path.join(self.vm.dir_path, self.vm.firewall_conf)
-        expiring_rules_present = False
+        nearest_expire = False
 
         xml_root = lxml.etree.Element('firewall', version=str(2))
 
@@ -556,7 +566,9 @@ class Firewall(object):
                 if rule.expire and rule.expire.expired:
                     continue
                 else:
-                    expiring_rules_present = True
+                    if nearest_expire is None or rule.expire.datetime < \
+                            nearest_expire:
+                        nearest_expire = rule.expire.datetime
             xml_rule = lxml.etree.Element('rule')
             xml_rule.append(rule.xml_properties())
             xml_rules.append(xml_rule)
@@ -577,9 +589,13 @@ class Firewall(object):
 
         self.vm.fire_event('firewall-changed')
 
-        if expiring_rules_present and not self.vm.app.vmm.offline_mode:
-            subprocess.call(["sudo", "systemctl", "start",
-                             "qubes-reload-firewall@%s.timer" % self.vm.name])
+        if nearest_expire and not self.vm.app.vmm.offline_mode:
+            loop = asyncio.get_event_loop()
+            # by documentation call_at use loop.time() clock, which not
+            # necessary must be the same as time module; calculate delay and
+            # use call_later instead
+            expire_when = nearest_expire - datetime.datetime.now()
+            loop.call_later(expire_when, self._expire_rules)
 
     def qdb_entries(self, addr_family=None):
         '''Return firewall settings serialized for QubesDB entries

+ 0 - 2
rpm_spec/core-dom0.spec

@@ -399,8 +399,6 @@ fi
 %{_unitdir}/qubes-qmemman.service
 %{_unitdir}/qubes-vm@.service
 %{_unitdir}/qubesd.service
-%{_unitdir}/qubes-reload-firewall@.service
-%{_unitdir}/qubes-reload-firewall@.timer
 %attr(2770,root,qubes) %dir /var/lib/qubes
 %attr(2770,root,qubes) %dir /var/lib/qubes/vm-templates
 %attr(2770,root,qubes) %dir /var/lib/qubes/appvms