From 5e89b23288b9c2cfce0ee2948e019b98483c98e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Marczykowski-G=C3=B3recki?= Date: Wed, 7 Feb 2018 02:40:56 +0100 Subject: [PATCH] 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 --- linux/systemd/Makefile | 2 -- qubes/firewall.py | 28 ++++++++++++++++++++++------ rpm_spec/core-dom0.spec | 2 -- 3 files changed, 22 insertions(+), 10 deletions(-) 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/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index fbd74510..b1ffff2b 100644 --- a/rpm_spec/core-dom0.spec +++ b/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