rewrite firewall
This commit is contained in:
parent
43f2f6b881
commit
37d501c42b
@ -19,18 +19,24 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
import sys
|
import datetime
|
||||||
|
import ipaddress
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
import xml.etree.ElementTree
|
import xml.etree.ElementTree
|
||||||
|
|
||||||
from PyQt4.QtCore import *
|
from PyQt4.QtCore import *
|
||||||
from PyQt4.QtGui import *
|
from PyQt4.QtGui import *
|
||||||
import datetime
|
|
||||||
|
import qubesadmin.firewall
|
||||||
|
|
||||||
from . import ui_newfwruledlg
|
from . import ui_newfwruledlg
|
||||||
|
|
||||||
|
|
||||||
|
class FirewallModifiedOutsideError(ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
class QIPAddressValidator(QValidator):
|
class QIPAddressValidator(QValidator):
|
||||||
def __init__(self, parent = None):
|
def __init__(self, parent = None):
|
||||||
super (QIPAddressValidator, self).__init__(parent)
|
super (QIPAddressValidator, self).__init__(parent)
|
||||||
@ -39,10 +45,10 @@ class QIPAddressValidator(QValidator):
|
|||||||
hostname = str(input)
|
hostname = str(input)
|
||||||
|
|
||||||
if len(hostname) > 255 or len(hostname) == 0:
|
if len(hostname) > 255 or len(hostname) == 0:
|
||||||
return (QValidator.Intermediate, pos)
|
return (QValidator.Intermediate, input, pos)
|
||||||
|
|
||||||
if hostname == "*":
|
if hostname == "*":
|
||||||
return (QValidator.Acceptable, pos)
|
return (QValidator.Acceptable, input, pos)
|
||||||
|
|
||||||
unmask = hostname.split("/", 1)
|
unmask = hostname.split("/", 1)
|
||||||
if len(unmask) == 2:
|
if len(unmask) == 2:
|
||||||
@ -50,25 +56,25 @@ class QIPAddressValidator(QValidator):
|
|||||||
mask = unmask[1]
|
mask = unmask[1]
|
||||||
if mask.isdigit() or mask == "":
|
if mask.isdigit() or mask == "":
|
||||||
if re.match("^([0-9]{1,3}\.){3}[0-9]{1,3}$", hostname) is None:
|
if re.match("^([0-9]{1,3}\.){3}[0-9]{1,3}$", hostname) is None:
|
||||||
return (QValidator.Invalid, pos)
|
return (QValidator.Invalid, input, pos)
|
||||||
if mask != "":
|
if mask != "":
|
||||||
mask = int(unmask[1])
|
mask = int(unmask[1])
|
||||||
if mask < 0 or mask > 32:
|
if mask < 0 or mask > 32:
|
||||||
return (QValidator.Invalid, pos)
|
return (QValidator.Invalid, input, pos)
|
||||||
else:
|
else:
|
||||||
return (QValidator.Invalid, pos)
|
return (QValidator.Invalid, input, pos)
|
||||||
|
|
||||||
if hostname[-1:] == ".":
|
if hostname[-1:] == ".":
|
||||||
hostname = hostname[:-1]
|
hostname = hostname[:-1]
|
||||||
|
|
||||||
if hostname[-1:] == "-":
|
if hostname[-1:] == "-":
|
||||||
return (QValidator.Intermediate, pos)
|
return (QValidator.Intermediate, input, pos)
|
||||||
|
|
||||||
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
|
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
|
||||||
if all(allowed.match(x) for x in hostname.split(".")):
|
if all(allowed.match(x) for x in hostname.split(".")):
|
||||||
return (QValidator.Acceptable, pos)
|
return (QValidator.Acceptable, input, pos)
|
||||||
|
|
||||||
return (QValidator.Invalid, pos)
|
return (QValidator.Invalid, input, pos)
|
||||||
|
|
||||||
class NewFwRuleDlg (QDialog, ui_newfwruledlg.Ui_NewFwRuleDlg):
|
class NewFwRuleDlg (QDialog, ui_newfwruledlg.Ui_NewFwRuleDlg):
|
||||||
def __init__(self, parent = None):
|
def __init__(self, parent = None):
|
||||||
@ -194,12 +200,135 @@ class QubesFirewallRulesModel(QAbstractItemModel):
|
|||||||
def get_column_string(self, col, row):
|
def get_column_string(self, col, row):
|
||||||
return self.__columnValues[col](row)
|
return self.__columnValues[col](row)
|
||||||
|
|
||||||
|
|
||||||
|
def rule_to_dict(self, rule):
|
||||||
|
if rule.dsthost is None:
|
||||||
|
raise FirewallModifiedOutsideError('no dsthost')
|
||||||
|
|
||||||
|
d = {}
|
||||||
|
|
||||||
|
if not rule.proto:
|
||||||
|
d['proto'] = 'any'
|
||||||
|
d['portBegin'] = 'any'
|
||||||
|
d['portEnd'] = None
|
||||||
|
|
||||||
|
else:
|
||||||
|
d['proto'] = rule.proto
|
||||||
|
if rule.dstports is None:
|
||||||
|
raise FirewallModifiedOutsideError('no dstport')
|
||||||
|
d['portBegin'] = rule.dstports.range[0]
|
||||||
|
d['portEnd'] = rule.dstports.range[1] \
|
||||||
|
if rule.dstports.range[0] != rule.dstports.range[1] \
|
||||||
|
else None
|
||||||
|
|
||||||
|
if rule.dsthost.type == 'dsthost':
|
||||||
|
d['address'] = str(rule.dsthost)
|
||||||
|
d['netmask'] = 32
|
||||||
|
elif rule.dsthost.type == 'dst4':
|
||||||
|
network = ipaddress.IPv4Network(rule.dsthost)
|
||||||
|
d['address'] = str(network.network_address)
|
||||||
|
d['netmask'] = int(network.prefixlen)
|
||||||
|
else:
|
||||||
|
raise FirewallModifiedOutsideError(
|
||||||
|
'cannot map dsthost.type={!s}'.format(rule.dsthost))
|
||||||
|
|
||||||
|
if rule.expire is not None:
|
||||||
|
d['expire'] = int(rule.expire)
|
||||||
|
|
||||||
|
return d
|
||||||
|
|
||||||
|
def get_firewall_conf(self, vm):
|
||||||
|
conf = {
|
||||||
|
'allow': None,
|
||||||
|
'allowDns': False,
|
||||||
|
'allowIcmp': False,
|
||||||
|
'allowYumProxy': False,
|
||||||
|
'rules': [],
|
||||||
|
}
|
||||||
|
|
||||||
|
common_action = None
|
||||||
|
tentative_action = None
|
||||||
|
|
||||||
|
reversed_rules = list(reversed(vm.firewall.rules))
|
||||||
|
|
||||||
|
while reversed_rules:
|
||||||
|
rule = reversed_rules[0]
|
||||||
|
if rule.dsthost is not None or rule.proto is not None:
|
||||||
|
break
|
||||||
|
tentative_action = reversed_rules.pop(0).action
|
||||||
|
|
||||||
|
if not reversed_rules:
|
||||||
|
conf['allow'] = tentative_action == 'accept'
|
||||||
|
return conf
|
||||||
|
|
||||||
|
for rule in reversed_rules:
|
||||||
|
if rule.specialtarget == 'dns':
|
||||||
|
conf['allowDns'] = (rule.action == 'accept')
|
||||||
|
continue
|
||||||
|
|
||||||
|
if rule.proto == 'icmp':
|
||||||
|
if rule.icmptype is not None:
|
||||||
|
raise FirewallModifiedOutsideError(
|
||||||
|
'cannot map icmptype != None')
|
||||||
|
conf['allowIcmp'] = (rule.action == 'accept')
|
||||||
|
continue
|
||||||
|
|
||||||
|
if common_action is None:
|
||||||
|
common_action = rule.action
|
||||||
|
elif common_action != rule.action:
|
||||||
|
raise FirewallModifiedOutsideError('incoherent action')
|
||||||
|
|
||||||
|
conf['rules'].insert(0, self.rule_to_dict(rule))
|
||||||
|
|
||||||
|
if common_action is None or common_action != tentative_action:
|
||||||
|
# we've got only specialtarget and/or icmp
|
||||||
|
conf['allow'] = tentative_action == 'accept'
|
||||||
|
return conf
|
||||||
|
|
||||||
|
raise FirewallModifiedOutsideError('it does not add up')
|
||||||
|
|
||||||
|
def write_firewall_conf(self, vm, conf):
|
||||||
|
common_action = qubesadmin.firewall.Action(
|
||||||
|
'drop' if conf['allow'] else 'accept')
|
||||||
|
|
||||||
|
rules = []
|
||||||
|
|
||||||
|
for rule in conf['rules']:
|
||||||
|
kwargs = {}
|
||||||
|
if rule['proto'] != 'any':
|
||||||
|
kwargs['proto'] = rule['proto']
|
||||||
|
if rule['portBegin'] != 'any':
|
||||||
|
kwargs['dstports'] = '-'.join(map(str, filter((lambda x: x),
|
||||||
|
(rule['portBegin'], rule['portEnd']))))
|
||||||
|
|
||||||
|
netmask = str(rule['netmask']) if rule['netmask'] != 32 else None
|
||||||
|
|
||||||
|
rules.append(qubesadmin.firewall.Rule(None,
|
||||||
|
action=common_action,
|
||||||
|
dsthost='/'.join(map(str, filter((lambda x: x),
|
||||||
|
(rule['address'], netmask)))),
|
||||||
|
**kwargs))
|
||||||
|
|
||||||
|
if conf['allowDns']:
|
||||||
|
rules.append(qubesadmin.firewall.Rule(None,
|
||||||
|
action='accept', specialtarget='dns'))
|
||||||
|
|
||||||
|
if conf['allowIcmp']:
|
||||||
|
rules.append(qubesadmin.firewall.Rule(None,
|
||||||
|
action='accept', proto='icmp'))
|
||||||
|
|
||||||
|
if common_action == 'drop':
|
||||||
|
rules.append(qubesadmin.firewall.Rule(None,
|
||||||
|
action='accept'))
|
||||||
|
|
||||||
|
vm.firewall.rules = rules
|
||||||
|
|
||||||
def set_vm(self, vm):
|
def set_vm(self, vm):
|
||||||
self.__vm = vm
|
self.__vm = vm
|
||||||
|
|
||||||
self.clearChildren()
|
self.clearChildren()
|
||||||
|
|
||||||
conf = vm.get_firewall_conf()
|
conf = self.get_firewall_conf(vm)
|
||||||
|
|
||||||
self.allow = conf["allow"]
|
self.allow = conf["allow"]
|
||||||
self.allowDns = conf["allowDns"]
|
self.allowDns = conf["allowDns"]
|
||||||
@ -252,7 +381,7 @@ class QubesFirewallRulesModel(QAbstractItemModel):
|
|||||||
})
|
})
|
||||||
|
|
||||||
if self.fw_changed:
|
if self.fw_changed:
|
||||||
self.__vm.write_firewall_conf(conf)
|
self.write_firewall_conf(self.__vm, conf)
|
||||||
|
|
||||||
if self.__vm.is_running():
|
if self.__vm.is_running():
|
||||||
vm = self.__vm.netvm
|
vm = self.__vm.netvm
|
||||||
@ -287,15 +416,11 @@ class QubesFirewallRulesModel(QAbstractItemModel):
|
|||||||
if index.isValid() and role == Qt.DisplayRole:
|
if index.isValid() and role == Qt.DisplayRole:
|
||||||
return self.__columnValues[index.column()](index.row())
|
return self.__columnValues[index.column()](index.row())
|
||||||
|
|
||||||
return QVariant()
|
|
||||||
|
|
||||||
def headerData(self, section, orientation, role=Qt.DisplayRole):
|
def headerData(self, section, orientation, role=Qt.DisplayRole):
|
||||||
if section < len(self.__columnNames) \
|
if section < len(self.__columnNames) \
|
||||||
and orientation == Qt.Horizontal and role == Qt.DisplayRole:
|
and orientation == Qt.Horizontal and role == Qt.DisplayRole:
|
||||||
return self.__columnNames[section]
|
return self.__columnNames[section]
|
||||||
|
|
||||||
return QVariant()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def children(self):
|
def children(self):
|
||||||
return self.__children
|
return self.__children
|
||||||
|
Loading…
Reference in New Issue
Block a user