Skip to content

Commit

Permalink
rules: allow to filter connections by source ip/net
Browse files Browse the repository at this point in the history
- Allow to filter connections by source IP/Network.
  (it also may be useful filter by source port)
- Removed AlwaysOnTop Hint from rules editor dialog.

Closes #922
  • Loading branch information
gustavo-iniguez-goya committed Apr 29, 2023
1 parent 7975486 commit 57739cc
Show file tree
Hide file tree
Showing 4 changed files with 246 additions and 64 deletions.
6 changes: 6 additions & 0 deletions daemon/rule/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@ const (
OpProcessEnvPrefix = Operand("process.env.")
OpProcessEnvPrefixLen = 12
OpUserID = Operand("user.id")
OpSrcIP = Operand("source.ip")
OpDstIP = Operand("dest.ip")
OpDstHost = Operand("dest.host")
OpDstPort = Operand("dest.port")
OpDstNetwork = Operand("dest.network")
OpSrcNetwork = Operand("source.network")
OpProto = Operand("protocol")
OpIfaceIn = Operand("iface.in")
OpIfaceOut = Operand("iface.out")
Expand Down Expand Up @@ -271,6 +273,8 @@ func (o *Operator) Match(con *conman.Connection) bool {
return o.cb(con.DstHost)
} else if o.Operand == OpDstIP {
return o.cb(con.DstIP.String())
} else if o.Operand == OpSrcIP {
return o.cb(con.SrcIP.String())
} else if o.Operand == OpDstPort {
return o.cb(fmt.Sprintf("%d", con.DstPort))
} else if o.Operand == OpUserID {
Expand All @@ -283,6 +287,8 @@ func (o *Operator) Match(con *conman.Connection) bool {
return o.cb(con.DstIP.String())
} else if o.Operand == OpDstNetwork {
return o.cb(con.DstIP)
} else if o.Operand == OpSrcNetwork {
return o.cb(con.SrcIP)
} else if o.Operand == OpNetLists {
return o.cb(con.DstIP)
} else if o.Operand == OpDomainsRegexpLists {
Expand Down
2 changes: 2 additions & 0 deletions ui/opensnitch/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ class Config:
OPERAND_USER_ID = "user.id"
OPERAND_IFACE_OUT = "iface.out"
OPERAND_IFACE_IN = "iface.in"
OPERAND_SOURCE_IP = "source.ip"
OPERAND_DEST_IP = "dest.ip"
OPERAND_DEST_HOST = "dest.host"
OPERAND_DEST_PORT = "dest.port"
OPERAND_DEST_NETWORK = "dest.network"
OPERAND_SOURCE_NETWORK = "source.network"
OPERAND_PROTOCOL = "protocol"
OPERAND_LIST_DOMAINS = "lists.domains"
OPERAND_LIST_DOMAINS_REGEXP = "lists.domains_regexp"
Expand Down
57 changes: 56 additions & 1 deletion ui/opensnitch/dialogs/ruleseditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ class RulesEditorDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):

def __init__(self, parent=None, _rule=None, appicon=None):
super(RulesEditorDialog, self).__init__(parent)
QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint)

self._notifications_sent = {}
self._nodes = Nodes.instance()
Expand All @@ -67,6 +66,7 @@ def __init__(self, parent=None, _rule=None, appicon=None):
self.dstPortCheck.toggled.connect(self._cb_dstport_check_toggled)
self.uidCheck.toggled.connect(self._cb_uid_check_toggled)
self.pidCheck.toggled.connect(self._cb_pid_check_toggled)
self.srcIPCheck.toggled.connect(self._cb_srcip_check_toggled)
self.dstIPCheck.toggled.connect(self._cb_dstip_check_toggled)
self.dstHostCheck.toggled.connect(self._cb_dsthost_check_toggled)
self.dstListsCheck.toggled.connect(self._cb_dstlists_check_toggled)
Expand Down Expand Up @@ -159,6 +159,9 @@ def _cb_uid_check_toggled(self, state):
def _cb_pid_check_toggled(self, state):
self.pidLine.setEnabled(state)

def _cb_srcip_check_toggled(self, state):
self.srcIPCombo.setEnabled(state)

def _cb_dstip_check_toggled(self, state):
self.dstIPCombo.setEnabled(state)

Expand Down Expand Up @@ -293,6 +296,7 @@ def _is_valid_regex(self, regex):
def set_fields_from_connection(self, records):
self.nodesCombo.setCurrentText(records.value(ConnFields.Node))
self.protoCombo.setCurrentText(records.value(ConnFields.Protocol).upper())
self.srcIPCombo.setCurrentText(records.value(ConnFields.SrcIP))
self.dstIPCombo.setCurrentText(records.value(ConnFields.DstIP))
self.dstHostLine.setText(records.value(ConnFields.DstHost))
self.dstPortLine.setText(records.value(ConnFields.DstPort))
Expand Down Expand Up @@ -337,6 +341,9 @@ def _reset_state(self):
self.dstPortCheck.setChecked(False)
self.dstPortLine.setText("")

self.srcIPCheck.setChecked(False)
self.srcIPCombo.setCurrentText("")

self.dstIPCheck.setChecked(False)
self.dstIPCombo.setCurrentText("")

Expand Down Expand Up @@ -432,6 +439,16 @@ def _load_rule_operator(self, operator):
self.dstPortLine.setEnabled(True)
self.dstPortLine.setText(operator.data)

if operator.operand == Config.OPERAND_SOURCE_IP or operator.operand == Config.OPERAND_SOURCE_NETWORK:
self.srcIPCheck.setChecked(True)
self.srcIPCombo.setEnabled(True)
if operator.data == self.LAN_RANGES:
self.srcIPCombo.setCurrentText(self.LAN_LABEL)
elif operator.data == self.MULTICAST_RANGE:
self.srcIPCombo.setCurrentText(self.MULTICAST_LABEL)
else:
self.srcIPCombo.setCurrentText(operator.data)

if operator.operand == Config.OPERAND_DEST_IP or operator.operand == Config.OPERAND_DEST_NETWORK:
self.dstIPCheck.setChecked(True)
self.dstIPCombo.setEnabled(True)
Expand Down Expand Up @@ -685,6 +702,44 @@ def _save_rule(self):
if self._is_valid_regex(self.dstHostLine.text()) == False:
return False, QC.translate("rules", "Dst host regexp error")

if self.srcIPCheck.isChecked():
if self.srcIPCombo.currentText() == "":
return False, QC.translate("rules", "Source IP/Network can not be empty")

srcIPtext = self.srcIPCombo.currentText()

if srcIPtext == self.LAN_LABEL:
self.rule.operator.operand = Config.OPERAND_SOURCE_IP
self.rule.operator.type = Config.RULE_TYPE_REGEXP
srcIPtext = self.LAN_RANGES
elif srcIPtext == self.MULTICAST_LABEL:
self.rule.operator.operand = Config.OPERAND_SOURCE_IP
self.rule.operator.type = Config.RULE_TYPE_REGEXP
srcIPtext = self.MULTICAST_RANGE
else:
try:
if type(ipaddress.ip_address(self.srcIPCombo.currentText())) == ipaddress.IPv4Address \
or type(ipaddress.ip_address(self.srcIPCombo.currentText())) == ipaddress.IPv6Address:
self.rule.operator.operand = Config.OPERAND_SOURCE_IP
self.rule.operator.type = Config.RULE_TYPE_SIMPLE
except Exception:
self.rule.operator.operand = Config.OPERAND_SOURCE_NETWORK
self.rule.operator.type = Config.RULE_TYPE_NETWORK

if self._is_regex(srcIPtext):
self.rule.operator.operand = Config.OPERAND_SOURCE_IP
self.rule.operator.type = Config.RULE_TYPE_REGEXP
if self._is_valid_regex(self.srcIPCombo.currentText()) == False:
return False, QC.translate("rules", "Source IP regexp error")

rule_data.append(
{
'type': self.rule.operator.type,
'operand': self.rule.operator.operand,
'data': srcIPtext,
"sensitive": self.sensitiveCheck.isChecked()
})

if self.dstIPCheck.isChecked():
if self.dstIPCombo.currentText() == "":
return False, QC.translate("rules", "Dest IP/Network can not be empty")
Expand Down
Loading

0 comments on commit 57739cc

Please sign in to comment.