-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
452 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,62 +1,85 @@ | ||
.. program:: qvm-firewall | ||
|
||
======================================================= | ||
:program:`qvm-firewall` -- Qubes firewall configuration | ||
======================================================= | ||
:program:`qvm-firewall` -- Manage VM outbound firewall | ||
====================================================== | ||
|
||
Synopsis | ||
======== | ||
:command:`qvm-firewall` [-n] <*vm-name*> [*action*] [*rule spec*] | ||
-------- | ||
|
||
Rule specification can be one of: | ||
1. *address*\ |\ *hostname*\ [/*netmask*] tcp|udp *port*\ [-*port*] | ||
2. *address*\ |\ *hostname*\ [/*netmask*] tcp|udp *service_name* | ||
3. *address*\ |\ *hostname*\ [/*netmask*] any | ||
: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 | ||
======= | ||
------- | ||
|
||
.. option:: --help, -h | ||
|
||
Show this help message and exit | ||
show help message and exit | ||
|
||
.. option:: --list, -l | ||
.. option:: --verbose, -v | ||
|
||
List firewall settings (default action) | ||
increase verbosity | ||
|
||
.. option:: --add, -a | ||
.. option:: --quiet, -q | ||
|
||
Add rule | ||
decrease verbosity | ||
|
||
.. option:: --del, -d | ||
.. option:: --reload, -r | ||
|
||
Remove rule (given by number or by rule spec) | ||
force reloading rules even when unchanged | ||
|
||
.. option:: --policy=SET_POLICY, -P SET_POLICY | ||
.. option:: --raw | ||
|
||
Set firewall policy (allow/deny) | ||
Print raw rules when listing | ||
|
||
.. option:: --icmp=SET_ICMP, -i SET_ICMP | ||
|
||
Set ICMP access (allow/deny) | ||
Actions description | ||
------------------- | ||
|
||
.. option:: --dns=SET_DNS, -D SET_DNS | ||
Available actions: | ||
|
||
Set DNS access (allow/deny) | ||
* add - add specified rule. See `Rule syntax` section below. | ||
|
||
.. option:: --yum-proxy=SET_YUM_PROXY, -Y SET_YUM_PROXY | ||
* del - delete specified rule. Can be selected either by rule number using | ||
:option:`--rule-no`, or specifying rule itself. | ||
|
||
Set access to Qubes yum proxy (allow/deny). | ||
* list - list all the rules for a given VM. | ||
|
||
.. note:: | ||
if set to "deny", access will be rejected even if policy set to "allow" | ||
* policy - set default action if no rule matches. | ||
|
||
.. option:: --numeric, -n | ||
|
||
Display port numbers instead of services (makes sense only with :option:`--list`) | ||
Rule syntax | ||
----------- | ||
|
||
A single rule is built from: | ||
- action - either ``drop`` or ``accept`` | ||
- zero or more matches | ||
|
||
Selected action is applied on given packet when all specified matches do match, | ||
further rules are not evaluated. If none of the rules match, default action | ||
(``policy``) is applied. | ||
|
||
Supported matches: | ||
- ``dsthost`` - destination host or network. Can be either IP address in CIDR | ||
notation, or a host name. Both IPv4 and IPv6 are supported by the rule syntax. | ||
- ``proto`` - specific IP protocol. Supported values: ``tcp``, ``udp``, | ||
``icmp``. | ||
- ``dstports`` - destination port or ports range. Can be either a single port, | ||
or a range separated by ``-``. Valid only together with ``proto=udp`` or | ||
``proto=tcp``. | ||
- ``icmptype`` - ICMP message type, specified as numeric value. Valid only | ||
together with ``proto=icmp``. | ||
- ``specialtarget`` - predefined target. Currently the only supported value is | ||
``dns``. This can be combined with other matches to narrow it down. | ||
|
||
Authors | ||
======= | ||
------- | ||
|
||
| Joanna Rutkowska <joanna at invisiblethingslab dot com> | ||
| Rafal Wojtczuk <rafal at invisiblethingslab dot com> | ||
| Marek Marczykowski <marmarek at invisiblethingslab dot com> | ||
| Wojtek Porczyk <woju at invisiblethingslab dot com> | ||
.. vim: ts=3 sw=3 et tw=80 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
#!/usr/bin/python2 | ||
# -*- encoding: utf8 -*- | ||
# | ||
# The Qubes OS Project, http://www.qubes-os.org | ||
# | ||
# Copyright (C) 2016 Marek Marczykowski-Górecki | ||
# <[email protected]> | ||
# | ||
# This program is free software; you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation; either version 2 of the License, or | ||
# (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU General Public License along | ||
# with this program; if not, write to the Free Software Foundation, Inc., | ||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
|
||
import qubes.firewall | ||
import qubes.tests | ||
import qubes.tests.tools | ||
import qubes.tools.qvm_firewall | ||
import qubes.vm.appvm | ||
|
||
|
||
class TC_10_ArgParser(qubes.tests.SystemTestsMixin, qubes.tests.QubesTestCase): | ||
list_header = ['NO', 'ACTION', 'HOST', 'PROTOCOL', 'PORT(S)', | ||
'SPECIAL TARGET', 'ICMP TYPE'] | ||
|
||
def setUp(self): | ||
super(TC_10_ArgParser, self).setUp() | ||
self.init_default_template() | ||
self.vm = self.app.add_new_vm(qubes.vm.appvm.AppVM, None, | ||
name=self.make_vm_name('vm'), label='red') | ||
self.vm.create_on_disk() | ||
self.app.save() | ||
|
||
def test_000_list(self): | ||
with qubes.tests.tools.StdoutBuffer() as stdout: | ||
qubes.tools.qvm_firewall.main([self.vm.name, 'list']) | ||
self.assertEqual(stdout.getvalue(), | ||
' '.join(self.list_header) + '\n') | ||
|
||
def test_001_list(self): | ||
self.vm.firewall.rules.append( | ||
qubes.firewall.Rule(action='accept', dsthost='127.0.0.2', | ||
proto='tcp', dstports=80)) | ||
self.vm.firewall.rules.append( | ||
qubes.firewall.Rule(action='accept', dsthost='127.0.0.3', | ||
proto='icmp', icmptype=8)) | ||
self.vm.firewall.rules.append( | ||
qubes.firewall.Rule(action='accept', specialtarget='dns')) | ||
self.vm.firewall.save() | ||
expected_output = ( | ||
'NO ACTION HOST PROTOCOL PORT(S) SPECIAL TARGET ICMP ' | ||
'TYPE\n' | ||
'0 accept 127.0.0.2/32 tcp 80 ' | ||
' \n' | ||
'1 accept 127.0.0.3/32 icmp 8 ' | ||
' \n' | ||
'2 accept dns ' | ||
' \n' | ||
) | ||
with qubes.tests.tools.StdoutBuffer() as stdout: | ||
qubes.tools.qvm_firewall.main([self.vm.name, 'list']) | ||
self.assertEqual( | ||
'\n'.join(l.rstrip() for l in stdout.getvalue().splitlines()), | ||
'\n'.join(l.rstrip() for l in expected_output.splitlines())) | ||
|
||
def test_002_list_raw(self): | ||
self.vm.firewall.rules = [ | ||
qubes.firewall.Rule(action='accept', dsthost='127.0.0.2', | ||
proto='tcp', dstports=80), | ||
qubes.firewall.Rule(action='accept', dsthost='127.0.0.3', | ||
proto='icmp', icmptype=8), | ||
qubes.firewall.Rule(action='accept', specialtarget='dns'), | ||
] | ||
self.vm.firewall.save() | ||
expected_output = '\n'.join(rule.rule for rule in | ||
self.vm.firewall.rules) + '\n' | ||
with qubes.tests.tools.StdoutBuffer() as stdout: | ||
qubes.tools.qvm_firewall.main(['--raw', self.vm.name, 'list']) | ||
self.assertEqual(stdout.getvalue(), expected_output) | ||
|
||
def test_010_add(self): | ||
qubes.tools.qvm_firewall.main( | ||
[self.vm.name, 'add', 'accept', '1.2.3.0/24', 'tcp', '443']) | ||
self.assertEqual(self.vm.firewall.rules, | ||
[qubes.firewall.Rule(action='accept', dsthost='1.2.3.0/24', | ||
proto='tcp', dstports='443')]) | ||
|
||
def test_011_add_before(self): | ||
self.vm.firewall.rules = [ | ||
qubes.firewall.Rule(action='accept', dsthost='1.2.3.1'), | ||
qubes.firewall.Rule(action='accept', dsthost='1.2.3.2'), | ||
qubes.firewall.Rule(action='accept', dsthost='1.2.3.3'), | ||
] | ||
self.vm.firewall.save() | ||
qubes.tools.qvm_firewall.main( | ||
[self.vm.name, 'add', '--before', '2', | ||
'accept', '1.2.3.0/24', 'tcp', '443']) | ||
self.vm.firewall.load() | ||
self.assertEqual(self.vm.firewall.rules, | ||
[qubes.firewall.Rule(action='accept', dsthost='1.2.3.1'), | ||
qubes.firewall.Rule(action='accept', dsthost='1.2.3.2'), | ||
qubes.firewall.Rule(action='accept', dsthost='1.2.3.0/24', | ||
proto='tcp', dstports='443'), | ||
qubes.firewall.Rule(action='accept', dsthost='1.2.3.3'), | ||
]) | ||
|
||
def test_020_del(self): | ||
self.vm.firewall.rules = [ | ||
qubes.firewall.Rule(action='accept', dsthost='1.2.3.1'), | ||
qubes.firewall.Rule(action='accept', dsthost='1.2.3.2'), | ||
qubes.firewall.Rule(action='accept', dsthost='1.2.3.3'), | ||
] | ||
self.vm.firewall.save() | ||
qubes.tools.qvm_firewall.main( | ||
[self.vm.name, 'del', 'accept', '1.2.3.2']) | ||
self.vm.firewall.load() | ||
self.assertEqual(self.vm.firewall.rules, | ||
[qubes.firewall.Rule(action='accept', dsthost='1.2.3.1'), | ||
qubes.firewall.Rule(action='accept', dsthost='1.2.3.3'), | ||
]) | ||
|
||
def test_021_del_by_number(self): | ||
self.vm.firewall.rules = [ | ||
qubes.firewall.Rule(action='accept', dsthost='1.2.3.1'), | ||
qubes.firewall.Rule(action='accept', dsthost='1.2.3.2'), | ||
qubes.firewall.Rule(action='accept', dsthost='1.2.3.3'), | ||
] | ||
self.vm.firewall.save() | ||
qubes.tools.qvm_firewall.main( | ||
[self.vm.name, 'del', '--rule-no', '1']) | ||
self.vm.firewall.load() | ||
self.assertEqual(self.vm.firewall.rules, | ||
[qubes.firewall.Rule(action='accept', dsthost='1.2.3.1'), | ||
qubes.firewall.Rule(action='accept', dsthost='1.2.3.3'), | ||
]) | ||
|
||
def test_030_policy(self): | ||
with qubes.tests.tools.StdoutBuffer() as stdout: | ||
qubes.tools.qvm_firewall.main([self.vm.name, 'policy']) | ||
self.assertEqual(stdout.getvalue(), 'accept\n') | ||
self.vm.firewall.policy = 'drop' | ||
self.vm.firewall.save() | ||
with qubes.tests.tools.StdoutBuffer() as stdout: | ||
qubes.tools.qvm_firewall.main([self.vm.name, 'policy']) | ||
self.assertEqual(stdout.getvalue(), 'drop\n') | ||
|
||
def test_031_policy_set(self): | ||
qubes.tools.qvm_firewall.main([self.vm.name, 'policy', 'drop']) | ||
self.assertEqual(self.vm.firewall.policy, 'drop') | ||
qubes.tools.qvm_firewall.main([self.vm.name, 'policy', 'accept']) | ||
self.vm.firewall.load() | ||
self.assertEqual(self.vm.firewall.policy, 'accept') | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
#!/usr/bin/python2 | ||
# -*- encoding: utf8 -*- | ||
# | ||
# The Qubes OS Project, http://www.qubes-os.org | ||
# | ||
# Copyright (C) 2016 Marek Marczykowski-Górecki | ||
# <[email protected]> | ||
# | ||
# This program is free software; you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation; either version 2 of the License, or | ||
# (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU General Public License along | ||
# with this program; if not, write to the Free Software Foundation, Inc., | ||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
import argparse | ||
|
||
import qubes.firewall | ||
import qubes.tests | ||
import qubes.tests.firewall | ||
import qubes.tools.qvm_firewall | ||
|
||
|
||
class TC_00_RuleAction(qubes.tests.QubesTestCase): | ||
def setUp(self): | ||
super(TC_00_RuleAction, self).setUp() | ||
self.action = qubes.tools.qvm_firewall.RuleAction(None, dest='rule') | ||
|
||
def test_000_named_opts(self): | ||
ns = argparse.Namespace() | ||
self.action(None, ns, ['dsthost=127.0.0.1', 'action=accept']) | ||
self.assertEqual(ns.rule, | ||
qubes.firewall.Rule(None, action='accept', dsthost='127.0.0.1/32')) | ||
|
||
def test_001_unnamed_opts(self): | ||
ns = argparse.Namespace() | ||
self.action(None, ns, ['accept', '127.0.0.1', 'tcp', '80']) | ||
self.assertEqual(ns.rule, | ||
qubes.firewall.Rule(None, action='accept', dsthost='127.0.0.1/32', | ||
proto='tcp', dstports=80)) | ||
|
||
def test_002_unnamed_opts(self): | ||
ns = argparse.Namespace() | ||
self.action(None, ns, ['accept', '127.0.0.1', 'icmp', '8']) | ||
self.assertEqual(ns.rule, | ||
qubes.firewall.Rule(None, action='accept', dsthost='127.0.0.1/32', | ||
proto='icmp', icmptype=8)) | ||
|
||
def test_003_mixed_opts(self): | ||
ns = argparse.Namespace() | ||
self.action(None, ns, ['dsthost=127.0.0.1', 'accept', | ||
'dstports=443', 'tcp']) | ||
self.assertEqual(ns.rule, | ||
qubes.firewall.Rule(None, action='accept', dsthost='127.0.0.1/32', | ||
proto='tcp', dstports=443)) |
Oops, something went wrong.