diff --git a/doc/manpages/qvm-firewall.rst b/doc/manpages/qvm-firewall.rst index a1853fd..7484937 100644 --- a/doc/manpages/qvm-firewall.rst +++ b/doc/manpages/qvm-firewall.rst @@ -7,8 +7,11 @@ Synopsis -------- :command:`qvm-firewall` [-h] [--verbose] [--quiet] [--reload] *VMNAME* add *RULE* + :command:`qvm-firewall` [-h] [--verbose] [--quiet] [--reload] *VMNAME* del [--rule-no=*RULE_NUMBER*] [*RULE*] + :command:`qvm-firewall` [-h] [--verbose] [--quiet] [--reload] *VMNAME* list [--raw] + :command:`qvm-firewall` [-h] [--verbose] [--quiet] [--reload] *VMNAME* policy {accept,drop} Options @@ -42,8 +45,7 @@ Available actions: * add - add specified rule. See `Rule syntax` section below. -* del - delete specified rule. Can be selected either by rule number using - :option:`--rule-no`, or specifying rule itself. +* del - delete specified rule. Can be selected either by rule number using :option:`--rule-no`, or specifying rule itself. * list - list all the rules for a given VM. @@ -78,6 +80,10 @@ Supported matches: - ``specialtarget`` - predefined target. Currently the only supported value is ``dns``. This can be combined with other matches to narrow it down. + - ``expire`` - rule matches only until specified time and then is automatically + removed. The time can be given either as number of seconds since 1/1/1970, or + ``+seconds`` as a relative time (``+300`` means 5 minutes from now). + Authors ------- diff --git a/qubesadmin/tests/tools/qvm_firewall.py b/qubesadmin/tests/tools/qvm_firewall.py index 1d04434..a5cc957 100644 --- a/qubesadmin/tests/tools/qvm_firewall.py +++ b/qubesadmin/tests/tools/qvm_firewall.py @@ -21,6 +21,7 @@ # import argparse +import datetime import qubesadmin.firewall import qubesadmin.tests @@ -66,6 +67,25 @@ class TC_00_RuleAction(qubesadmin.tests.QubesTestCase): None, action='accept', dsthost='127.0.0.1/32', proto='tcp', dstports=443)) + def test_004_expire_absolute(self): + ns = argparse.Namespace() + self.action(None, ns, ['dsthost=127.0.0.1', 'action=accept', + 'expire=1525054180']) + self.assertEqual(ns.rule, + qubesadmin.firewall.Rule( + None, action='accept', dsthost='127.0.0.1/32', + expire=1525054180)) + + def test_005_expire_relative(self): + ns = argparse.Namespace() + now = int(datetime.datetime.now().strftime('%s')) + self.action(None, ns, ['dsthost=127.0.0.1', 'action=accept', + 'expire=+100']) + self.assertEqual(ns.rule, + qubesadmin.firewall.Rule( + None, action='accept', dsthost='127.0.0.1/32', + expire=now+100)) + class TC_10_qvm_firewall(qubesadmin.tests.QubesTestCase): def setUp(self): diff --git a/qubesadmin/tools/qvm_firewall.py b/qubesadmin/tools/qvm_firewall.py index 65d067f..e17eb5e 100644 --- a/qubesadmin/tools/qvm_firewall.py +++ b/qubesadmin/tools/qvm_firewall.py @@ -22,6 +22,7 @@ '''qvm-firewall tool''' import argparse +import datetime import sys import itertools @@ -44,7 +45,7 @@ class RuleAction(argparse.Action): setattr(namespace, self.dest, None) return assumed_order = ['action', 'dsthost', 'proto', 'dstports', 'icmptype'] - allowed_opts = assumed_order + ['specialtarget', 'comment'] + allowed_opts = assumed_order + ['specialtarget', 'comment', 'expire'] kwargs = {} for opt in values: opt_elements = opt.split('=') @@ -58,6 +59,10 @@ class RuleAction(argparse.Action): if key not in allowed_opts: raise argparse.ArgumentError(None, 'Invalid rule element: {}'.format(opt)) + if key == 'expire' and value.startswith('+'): + value = (datetime.datetime.now() + + datetime.timedelta(seconds=int(value[1:]))).\ + strftime('%s') kwargs[key] = value if key in assumed_order: assumed_order.remove(key) @@ -75,6 +80,7 @@ Rules can be given as positional arguments: And as keyword arguments: action= [specialtarget=dns] [dsthost=] [proto=] [dstports=] [icmptype=] + [expire=] Both formats, positional and keyword arguments, can be used interchangeably. @@ -91,6 +97,9 @@ Available rules: specialtarget only the value dns is currently supported, it matches the configured dns servers of a VM + expire a rule is automatically removed at given time, given as + seconds since 1/1/1970, or +seconds (e.g. +300 for rule + expire in 5 minutes) """ parser = qubesadmin.tools.QubesArgumentParser(vmname_nargs=1, epilog=epilog,