rewrite firewall
This commit is contained in:
parent
43f2f6b881
commit
37d501c42b
@ -19,18 +19,24 @@
|
||||
#
|
||||
#
|
||||
|
||||
import sys
|
||||
import datetime
|
||||
import ipaddress
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import xml.etree.ElementTree
|
||||
|
||||
from PyQt4.QtCore import *
|
||||
from PyQt4.QtGui import *
|
||||
import datetime
|
||||
|
||||
import qubesadmin.firewall
|
||||
|
||||
from . import ui_newfwruledlg
|
||||
|
||||
|
||||
class FirewallModifiedOutsideError(ValueError):
|
||||
pass
|
||||
|
||||
class QIPAddressValidator(QValidator):
|
||||
def __init__(self, parent = None):
|
||||
super (QIPAddressValidator, self).__init__(parent)
|
||||
@ -39,10 +45,10 @@ class QIPAddressValidator(QValidator):
|
||||
hostname = str(input)
|
||||
|
||||
if len(hostname) > 255 or len(hostname) == 0:
|
||||
return (QValidator.Intermediate, pos)
|
||||
return (QValidator.Intermediate, input, pos)
|
||||
|
||||
if hostname == "*":
|
||||
return (QValidator.Acceptable, pos)
|
||||
return (QValidator.Acceptable, input, pos)
|
||||
|
||||
unmask = hostname.split("/", 1)
|
||||
if len(unmask) == 2:
|
||||
@ -50,25 +56,25 @@ class QIPAddressValidator(QValidator):
|
||||
mask = unmask[1]
|
||||
if mask.isdigit() or mask == "":
|
||||
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 != "":
|
||||
mask = int(unmask[1])
|
||||
if mask < 0 or mask > 32:
|
||||
return (QValidator.Invalid, pos)
|
||||
return (QValidator.Invalid, input, pos)
|
||||
else:
|
||||
return (QValidator.Invalid, pos)
|
||||
return (QValidator.Invalid, input, pos)
|
||||
|
||||
if hostname[-1:] == ".":
|
||||
hostname = hostname[:-1]
|
||||
|
||||
if hostname[-1:] == "-":
|
||||
return (QValidator.Intermediate, pos)
|
||||
return (QValidator.Intermediate, input, pos)
|
||||
|
||||
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
|
||||
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):
|
||||
def __init__(self, parent = None):
|
||||
@ -194,12 +200,135 @@ class QubesFirewallRulesModel(QAbstractItemModel):
|
||||
def get_column_string(self, 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):
|
||||
self.__vm = vm
|
||||
|
||||
self.clearChildren()
|
||||
|
||||
conf = vm.get_firewall_conf()
|
||||
conf = self.get_firewall_conf(vm)
|
||||
|
||||
self.allow = conf["allow"]
|
||||
self.allowDns = conf["allowDns"]
|
||||
@ -252,7 +381,7 @@ class QubesFirewallRulesModel(QAbstractItemModel):
|
||||
})
|
||||
|
||||
if self.fw_changed:
|
||||
self.__vm.write_firewall_conf(conf)
|
||||
self.write_firewall_conf(self.__vm, conf)
|
||||
|
||||
if self.__vm.is_running():
|
||||
vm = self.__vm.netvm
|
||||
@ -287,15 +416,11 @@ class QubesFirewallRulesModel(QAbstractItemModel):
|
||||
if index.isValid() and role == Qt.DisplayRole:
|
||||
return self.__columnValues[index.column()](index.row())
|
||||
|
||||
return QVariant()
|
||||
|
||||
def headerData(self, section, orientation, role=Qt.DisplayRole):
|
||||
if section < len(self.__columnNames) \
|
||||
and orientation == Qt.Horizontal and role == Qt.DisplayRole:
|
||||
return self.__columnNames[section]
|
||||
|
||||
return QVariant()
|
||||
|
||||
@property
|
||||
def children(self):
|
||||
return self.__children
|
||||
|
Loading…
Reference in New Issue
Block a user