From ed9b42d5b4138695e14ae59aa2b2b506f1c1593e Mon Sep 17 00:00:00 2001 From: Peter Gerber Date: Mon, 30 Apr 2018 16:26:54 +0200 Subject: [PATCH] tools/qvm-firewall: Show EXPIRE column in list output --- qubesadmin/firewall.py | 14 ++++++++++- qubesadmin/tests/tools/qvm_firewall.py | 34 ++++++++++++++++++++------ qubesadmin/tools/qvm_firewall.py | 8 +++--- 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/qubesadmin/firewall.py b/qubesadmin/firewall.py index a1067c9..3f6a6e5 100644 --- a/qubesadmin/firewall.py +++ b/qubesadmin/firewall.py @@ -34,6 +34,11 @@ class RuleOption(object): '''API representation of this rule element''' raise NotImplementedError + @property + def pretty_value(self): + '''Human readable representation''' + return str(self) + def __str__(self): return self._value @@ -212,9 +217,16 @@ class Expire(RuleOption): @property def expired(self): - '''Have this rule expired already?''' + '''Has this rule expired already?''' return self.datetime < datetime.datetime.utcnow() + @property + def pretty_value(self): + '''Human readable representation''' + now = datetime.datetime.utcnow() + duration = (self.datetime - now).total_seconds() + return "{:+.0f}s".format(duration) + class Comment(RuleOption): '''User comment''' diff --git a/qubesadmin/tests/tools/qvm_firewall.py b/qubesadmin/tests/tools/qvm_firewall.py index 24145c9..ae6f516 100644 --- a/qubesadmin/tests/tools/qvm_firewall.py +++ b/qubesadmin/tests/tools/qvm_firewall.py @@ -22,6 +22,8 @@ import argparse import datetime +import re +import time import qubesadmin.firewall import qubesadmin.tests @@ -103,11 +105,11 @@ class TC_10_qvm_firewall(qubesadmin.tests.QubesTestCase): self.assertEqual( [l.strip() for l in stdout.getvalue().splitlines()], ['NO ACTION HOST PROTOCOL PORT(S) SPECIAL ' - 'TARGET ICMP TYPE COMMENT', + 'TARGET ICMP TYPE EXPIRE COMMENT', '0 accept qubes-os.org - - - ' - ' - -', + ' - - -', '1 drop - icmp - - ' - ' - -', + ' - - -', ]) def test_001_list_default(self): @@ -122,15 +124,33 @@ class TC_10_qvm_firewall(qubesadmin.tests.QubesTestCase): self.assertEqual( [l.strip() for l in stdout.getvalue().splitlines()], ['NO ACTION HOST PROTOCOL PORT(S) SPECIAL ' - 'TARGET ICMP TYPE COMMENT', + 'TARGET ICMP TYPE EXPIRE COMMENT', '0 accept qubes-os.org tcp 443 - ' - ' - -', + ' - - -', '1 drop - icmp - - ' - ' 8 -', + ' 8 - -', '2 accept - - - dns ' - ' - Allow DNS', + ' - - Allow DNS', ]) + + def test_002_list_expire(self): + in_1h = int(time.time()) + 3600 + self.app.expected_calls[('test-vm', 'admin.vm.firewall.Get', + None, None)] = \ + '0\0action=accept dsthost=qubes-os.org proto=tcp ' \ + 'dstports=443-443 expire={}\n'.format(in_1h).encode() + with qubesadmin.tests.tools.StdoutBuffer() as stdout: + qubesadmin.tools.qvm_firewall.main(['test-vm'], app=self.app) + line = stdout.getvalue().splitlines()[-1] + match = re.match( + '0 accept qubes-os.org tcp 443 - ' + ' - \+(.{4})s -', + line) + self.assertTrue(match, "no match for: {!r}".format(line)) + duration = int(match.group(1)) + self.assertTrue(3590 < duration <= 3600) + def test_002_list_raw(self): self.app.expected_calls[('test-vm', 'admin.vm.firewall.Get', None, None)] = \ diff --git a/qubesadmin/tools/qvm_firewall.py b/qubesadmin/tools/qvm_firewall.py index fbb92cd..53b9596 100644 --- a/qubesadmin/tools/qvm_firewall.py +++ b/qubesadmin/tools/qvm_firewall.py @@ -135,20 +135,20 @@ def rules_list_table(vm): :return: None ''' header = ['NO', 'ACTION', 'HOST', 'PROTOCOL', 'PORT(S)', - 'SPECIAL TARGET', 'ICMP TYPE', 'COMMENT'] + 'SPECIAL TARGET', 'ICMP TYPE', 'EXPIRE', 'COMMENT'] rows = [] for (rule, rule_no) in zip(vm.firewall.rules, itertools.count()): - row = [str(x) if x is not None else '-' for x in [ - rule_no, + row = [x.pretty_value if x is not None else '-' for x in [ rule.action, rule.dsthost, rule.proto, rule.dstports, rule.specialtarget, rule.icmptype, + rule.expire, rule.comment, ]] - rows.append(row) + rows.append([str(rule_no)] + row) qubesadmin.tools.print_table([header] + rows)