diff --git a/linux/systemd/Makefile b/linux/systemd/Makefile index 3522d8ca..e0cbbf60 100644 --- a/linux/systemd/Makefile +++ b/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 diff --git a/qubes/firewall.py b/qubes/firewall.py index 4ed16149..99b56868 100644 --- a/qubes/firewall.py +++ b/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 diff --git a/qubes/tests/firewall.py b/qubes/tests/firewall.py index 3875cafe..ad02afc4 100644 --- a/qubes/tests/firewall.py +++ b/qubes/tests/firewall.py @@ -21,6 +21,7 @@ import datetime import os +import asyncio import lxml.etree import unittest @@ -583,3 +584,24 @@ class TC_10_Firewall(qubes.tests.QubesTestCase): '0003': 'action=accept specialtarget=dns', } self.assertEqual(fw.qdb_entries(), expected_qdb_entries) + + def test_006_auto_expire_rules(self): + fw = qubes.firewall.Firewall(self.vm, True) + rules = [ + qubes.firewall.Rule(None, action='drop', proto='icmp'), + qubes.firewall.Rule(None, action='drop', proto='tcp', dstports=80), + qubes.firewall.Rule(None, action='accept', proto='udp', + dstports=67, expire=self.loop.time() + 5), + qubes.firewall.Rule(None, action='accept', specialtarget='dns'), + ] + fw.rules = rules + fw.save() + self.assertEqual(fw.rules, rules) + self.loop.run_until_complete(asyncio.sleep(3)) + # still old rules should be there + self.assertEqual(fw.rules, rules) + + rules.pop(2) + self.loop.run_until_complete(asyncio.sleep(3)) + # expect new rules + self.assertEqual(fw.rules, rules) diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index 9638352c..4f3e99b4 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -400,8 +400,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