ui, rules: added description field

- Added ability to add a description to the rules.
- Display the description field on the Rules view, and remove the internal
  fields (operator, operator_data, etc).
- Added DB migrations.
- Improved rules' executable path field tooltip (#661).

Closes #652 #466
This commit is contained in:
Gustavo Iñiguez Goia 2022-05-12 13:38:23 +02:00
parent db4646dec8
commit 3c524c1942
10 changed files with 442 additions and 309 deletions

View file

@ -33,26 +33,28 @@ const (
// The fields match the ones saved as json to disk. // The fields match the ones saved as json to disk.
// If a .json rule file is modified on disk, it's reloaded automatically. // If a .json rule file is modified on disk, it's reloaded automatically.
type Rule struct { type Rule struct {
Created time.Time `json:"created"` Created time.Time `json:"created"`
Updated time.Time `json:"updated"` Updated time.Time `json:"updated"`
Name string `json:"name"` Name string `json:"name"`
Enabled bool `json:"enabled"` Description string `json:"description"`
Precedence bool `json:"precedence"` Enabled bool `json:"enabled"`
Action Action `json:"action"` Precedence bool `json:"precedence"`
Duration Duration `json:"duration"` Action Action `json:"action"`
Operator Operator `json:"operator"` Duration Duration `json:"duration"`
Operator Operator `json:"operator"`
} }
// Create creates a new rule object with the specified parameters. // Create creates a new rule object with the specified parameters.
func Create(name string, enabled bool, precedence bool, action Action, duration Duration, op *Operator) *Rule { func Create(name, description string, enabled bool, precedence bool, action Action, duration Duration, op *Operator) *Rule {
return &Rule{ return &Rule{
Created: time.Now(), Created: time.Now(),
Enabled: enabled, Enabled: enabled,
Precedence: precedence, Precedence: precedence,
Name: name, Name: name,
Action: action, Description: description,
Duration: duration, Action: action,
Operator: *op, Duration: duration,
Operator: *op,
} }
} }
@ -86,6 +88,7 @@ func Deserialize(reply *protocol.Rule) (*Rule, error) {
return Create( return Create(
reply.Name, reply.Name,
reply.Description,
reply.Enabled, reply.Enabled,
reply.Precedence, reply.Precedence,
Action(reply.Action), Action(reply.Action),
@ -100,11 +103,12 @@ func (r *Rule) Serialize() *protocol.Rule {
return nil return nil
} }
return &protocol.Rule{ return &protocol.Rule{
Name: string(r.Name), Name: string(r.Name),
Enabled: bool(r.Enabled), Description: string(r.Description),
Precedence: bool(r.Precedence), Enabled: bool(r.Enabled),
Action: string(r.Action), Precedence: bool(r.Precedence),
Duration: string(r.Duration), Action: string(r.Action),
Duration: string(r.Duration),
Operator: &protocol.Operator{ Operator: &protocol.Operator{
Type: string(r.Operator.Type), Type: string(r.Operator.Type),
Sensitive: bool(r.Operator.Sensitive), Sensitive: bool(r.Operator.Sensitive),

View file

@ -23,10 +23,10 @@ import (
var ( var (
configFile = "/etc/opensnitchd/default-config.json" configFile = "/etc/opensnitchd/default-config.json"
dummyOperator, _ = rule.NewOperator(rule.Simple, false, rule.OpTrue, "", make([]rule.Operator, 0)) dummyOperator, _ = rule.NewOperator(rule.Simple, false, rule.OpTrue, "", make([]rule.Operator, 0))
clientDisconnectedRule = rule.Create("ui.client.disconnected", true, false, rule.Allow, rule.Once, dummyOperator) clientDisconnectedRule = rule.Create("ui.client.disconnected", "", true, false, rule.Allow, rule.Once, dummyOperator)
// While the GUI is connected, deny by default everything until the user takes an action. // While the GUI is connected, deny by default everything until the user takes an action.
clientConnectedRule = rule.Create("ui.client.connected", true, false, rule.Deny, rule.Once, dummyOperator) clientConnectedRule = rule.Create("ui.client.connected", "", true, false, rule.Deny, rule.Once, dummyOperator)
clientErrorRule = rule.Create("ui.client.error", true, false, rule.Allow, rule.Once, dummyOperator) clientErrorRule = rule.Create("ui.client.error", "", true, false, rule.Allow, rule.Once, dummyOperator)
config Config config Config
) )

View file

@ -71,11 +71,12 @@ message Operator {
message Rule { message Rule {
string name = 1; string name = 1;
bool enabled = 2; string description = 2;
bool precedence = 3; bool enabled = 3;
string action = 4; bool precedence = 4;
string duration = 5; string action = 5;
Operator operator = 6; string duration = 6;
Operator operator = 7;
} }
enum Action { enum Action {

View file

@ -1,6 +1,7 @@
from PyQt5.QtSql import QSqlDatabase, QSqlQueryModel, QSqlQuery from PyQt5.QtSql import QSqlDatabase, QSqlQueryModel, QSqlQuery
import threading import threading
import sys import sys
import os
from datetime import datetime, timedelta from datetime import datetime, timedelta
class Database: class Database:
@ -10,6 +11,8 @@ class Database:
DB_TYPE_MEMORY = 0 DB_TYPE_MEMORY = 0
DB_TYPE_FILE = 1 DB_TYPE_FILE = 1
DB_VERSION = 1
@staticmethod @staticmethod
def instance(): def instance():
if Database.__instance == None: if Database.__instance == None:
@ -26,6 +29,8 @@ class Database:
if dbtype != Database.DB_TYPE_MEMORY: if dbtype != Database.DB_TYPE_MEMORY:
self.db_file = dbfile self.db_file = dbfile
is_new_file = not os.path.isfile(self.db_file)
self.db = QSqlDatabase.addDatabase("QSQLITE", self.db_name) self.db = QSqlDatabase.addDatabase("QSQLITE", self.db_name)
self.db.setDatabaseName(self.db_file) self.db.setDatabaseName(self.db_file)
if not self.db.open(): if not self.db.open():
@ -38,7 +43,13 @@ class Database:
print("db.initialize() error:", db_error) print("db.initialize() error:", db_error)
return False, db_error return False, db_error
if is_new_file:
print("is new file, or IN MEMORY, setting initial schema version")
self.set_schema_version(self.DB_VERSION)
self._create_tables() self._create_tables()
self._upgrade_db_schema()
return True, None return True, None
def close(self): def close(self):
@ -77,6 +88,7 @@ class Database:
def _create_tables(self): def _create_tables(self):
# https://www.sqlite.org/wal.html # https://www.sqlite.org/wal.html
if self.db_file == Database.DB_IN_MEMORY: if self.db_file == Database.DB_IN_MEMORY:
self.set_schema_version(self.DB_VERSION)
q = QSqlQuery("PRAGMA journal_mode = OFF", self.db) q = QSqlQuery("PRAGMA journal_mode = OFF", self.db)
q.exec_() q.exec_()
q = QSqlQuery("PRAGMA synchronous = OFF", self.db) q = QSqlQuery("PRAGMA synchronous = OFF", self.db)
@ -137,6 +149,7 @@ class Database:
"operator_sensitive text, " \ "operator_sensitive text, " \
"operator_operand text, " \ "operator_operand text, " \
"operator_data text, " \ "operator_data text, " \
"description text, " \
"UNIQUE(node, name)" "UNIQUE(node, name)"
")", self.db) ")", self.db)
q.exec_() q.exec_()
@ -168,6 +181,49 @@ class Database:
, self.db) , self.db)
q.exec_() q.exec_()
def get_schema_version(self):
q = QSqlQuery("PRAGMA user_version;", self.db)
q.exec_()
if q.next():
print("schema version:", q.value(0))
return int(q.value(0))
return 0
def set_schema_version(self, version):
print("setting schema version to:", version)
q = QSqlQuery("PRAGMA user_version = {0}".format(version), self.db)
q.exec_()
def _upgrade_db_schema(self):
migrations_path = os.path.dirname(os.path.realpath(__file__)) + "/migrations"
schema_version = self.get_schema_version()
if schema_version == self.DB_VERSION:
print("db schema is up to date")
return
while schema_version < self.DB_VERSION:
schema_version += 1
try:
print("applying schema upgrade:", schema_version)
self._apply_db_upgrade("{0}/upgrade_{1}.sql".format(migrations_path, schema_version))
except Exception:
print("Not applyging upgrade_{0}.sql".format(schema_version))
self.set_schema_version(schema_version)
def _apply_db_upgrade(self, file):
print("applying upgrade from:", file)
q = QSqlQuery(self.db)
with open(file) as f:
for line in f.readlines():
# skip comments
if line.startswith("--"):
continue
print("applying upgrade:", line, end="")
if q.exec(line) == False:
print("\tError:", q.lastError().text())
else:
print("\tOK")
def optimize(self): def optimize(self):
"""https://www.sqlite.org/pragma.html#pragma_optimize """https://www.sqlite.org/pragma.html#pragma_optimize
""" """
@ -430,9 +486,9 @@ class Database:
def insert_rule(self, rule, node_addr): def insert_rule(self, rule, node_addr):
self.insert("rules", self.insert("rules",
"(time, node, name, enabled, precedence, action, duration, operator_type, operator_sensitive, operator_operand, operator_data)", "(time, node, name, description, enabled, precedence, action, duration, operator_type, operator_sensitive, operator_operand, operator_data)",
(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), (datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
node_addr, rule.name, node_addr, rule.name, rule.description,
str(rule.enabled), str(rule.precedence), str(rule.enabled), str(rule.precedence),
rule.action, rule.duration, rule.operator.type, rule.action, rule.duration, rule.operator.type,
str(rule.operator.sensitive), rule.operator.operand, rule.operator.data), str(rule.operator.sensitive), rule.operator.operand, rule.operator.data),

View file

@ -0,0 +1,2 @@
--ALTER TABLE rules ADD COLUMN description;
create table x(id int);

View file

@ -269,6 +269,7 @@ class RulesEditorDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
rule.operator.sensitive = self._bool(records.value(8)) rule.operator.sensitive = self._bool(records.value(8))
rule.operator.operand = records.value(9) rule.operator.operand = records.value(9)
rule.operator.data = "" if records.value(10) == None else str(records.value(10)) rule.operator.data = "" if records.value(10) == None else str(records.value(10))
rule.description = records.value(11)
return rule return rule
@ -277,6 +278,7 @@ class RulesEditorDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
self.rule = None self.rule = None
self.ruleNameEdit.setText("") self.ruleNameEdit.setText("")
self.ruleDescEdit.setPlainText("")
self.statusLabel.setText("") self.statusLabel.setText("")
self.actionDenyRadio.setChecked(True) self.actionDenyRadio.setChecked(True)
@ -327,6 +329,7 @@ class RulesEditorDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
return False return False
self.ruleNameEdit.setText(rule.name) self.ruleNameEdit.setText(rule.name)
self.ruleDescEdit.setPlainText(rule.description)
self.enableCheck.setChecked(rule.enabled) self.enableCheck.setChecked(rule.enabled)
self.precedenceCheck.setChecked(rule.precedence) self.precedenceCheck.setChecked(rule.precedence)
if rule.action == Config.ACTION_DENY: if rule.action == Config.ACTION_DENY:
@ -449,9 +452,9 @@ class RulesEditorDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
def _insert_rule_to_db(self, node_addr): def _insert_rule_to_db(self, node_addr):
self._db.insert("rules", self._db.insert("rules",
"(time, node, name, enabled, precedence, action, duration, operator_type, operator_sensitive, operator_operand, operator_data)", "(time, node, name, description, enabled, precedence, action, duration, operator_type, operator_sensitive, operator_operand, operator_data)",
(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), (datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
node_addr, self.rule.name, node_addr, self.rule.name, self.rule.description,
str(self.rule.enabled), str(self.rule.precedence), str(self.rule.enabled), str(self.rule.precedence),
self.rule.action, self.rule.duration, self.rule.operator.type, self.rule.action, self.rule.duration, self.rule.operator.type,
str(self.rule.operator.sensitive), self.rule.operator.operand, self.rule.operator.data), str(self.rule.operator.sensitive), self.rule.operator.operand, self.rule.operator.data),
@ -509,6 +512,7 @@ class RulesEditorDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
""" """
self.rule = ui_pb2.Rule() self.rule = ui_pb2.Rule()
self.rule.name = self.ruleNameEdit.text() self.rule.name = self.ruleNameEdit.text()
self.rule.description = self.ruleDescEdit.toPlainText()
self.rule.enabled = self.enableCheck.isChecked() self.rule.enabled = self.enableCheck.isChecked()
self.rule.precedence = self.precedenceCheck.isChecked() self.rule.precedence = self.precedenceCheck.isChecked()
self.rule.operator.type = Config.RULE_TYPE_SIMPLE self.rule.operator.type = Config.RULE_TYPE_SIMPLE

View file

@ -200,7 +200,13 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
"filterLine": None, "filterLine": None,
"model": None, "model": None,
"delegate": commonDelegateConf, "delegate": commonDelegateConf,
"display_fields": "*", "display_fields": "time as Time," \
"node as Node," \
"name as Name," \
"enabled as Enabled," \
"action as Action," \
"duration as Duration," \
"description as Description",
"header_labels": [], "header_labels": [],
"last_order_by": "2", "last_order_by": "2",
"last_order_to": 0, "last_order_to": 0,
@ -336,6 +342,7 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
self.COL_STR_TIME = QC.translate("stats", "Time", "This is a word, without spaces and symbols.") self.COL_STR_TIME = QC.translate("stats", "Time", "This is a word, without spaces and symbols.")
self.COL_STR_ACTION = QC.translate("stats", "Action", "This is a word, without spaces and symbols.") self.COL_STR_ACTION = QC.translate("stats", "Action", "This is a word, without spaces and symbols.")
self.COL_STR_DURATION = QC.translate("stats", "Duration", "This is a word, without spaces and symbols.") self.COL_STR_DURATION = QC.translate("stats", "Duration", "This is a word, without spaces and symbols.")
self.COL_STR_DESCRIPTION = QC.translate("stats", "Description", "This is a word, without spaces and symbols.")
self.COL_STR_NODE = QC.translate("stats", "Node", "This is a word, without spaces and symbols.") self.COL_STR_NODE = QC.translate("stats", "Node", "This is a word, without spaces and symbols.")
self.COL_STR_ENABLED = QC.translate("stats", "Enabled", "This is a word, without spaces and symbols.") self.COL_STR_ENABLED = QC.translate("stats", "Enabled", "This is a word, without spaces and symbols.")
self.COL_STR_PRECEDENCE = QC.translate("stats", "Precedence", "This is a word, without spaces and symbols.") self.COL_STR_PRECEDENCE = QC.translate("stats", "Precedence", "This is a word, without spaces and symbols.")
@ -447,13 +454,9 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
self.COL_STR_NODE, self.COL_STR_NODE,
self.COL_STR_NAME, self.COL_STR_NAME,
self.COL_STR_ENABLED, self.COL_STR_ENABLED,
self.COL_STR_PRECEDENCE,
self.COL_STR_ACTION, self.COL_STR_ACTION,
self.COL_STR_DURATION, self.COL_STR_DURATION,
"operator_type", self.COL_STR_DESCRIPTION,
"operator_sensitive",
"operator_operand",
"operator_data",
] ]
stats_headers = [ stats_headers = [
@ -492,8 +495,8 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
verticalScrollBar=self.verticalScrollBar, verticalScrollBar=self.verticalScrollBar,
sort_direction=self.SORT_ORDER[1], sort_direction=self.SORT_ORDER[1],
delegate=self.TABLES[self.TAB_NODES]['delegate']) delegate=self.TABLES[self.TAB_NODES]['delegate'])
self.TABLES[self.TAB_RULES]['view'] = self._setup_table(QtWidgets.QTableView, self.TABLES[self.TAB_RULES]['view'] = self._setup_table(QtWidgets.QTableView, self.rulesTable, "rules",
self.rulesTable, "rules", fields=self.TABLES[self.TAB_RULES]['display_fields'],
model=GenericTableModel("rules", self.TABLES[self.TAB_RULES]['header_labels']), model=GenericTableModel("rules", self.TABLES[self.TAB_RULES]['header_labels']),
verticalScrollBar=self.rulesScrollBar, verticalScrollBar=self.rulesScrollBar,
delegate=self.TABLES[self.TAB_RULES]['delegate'], delegate=self.TABLES[self.TAB_RULES]['delegate'],
@ -688,6 +691,7 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
def _configure_buttons_icons(self): def _configure_buttons_icons(self):
self.iconStart = self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_MediaPlay")) self.iconStart = self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_MediaPlay"))
self.iconPause = self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_MediaPause")) self.iconPause = self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_MediaPause"))
fwIcon = self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_VistaShield"))
self.newRuleButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_FileIcon"))) self.newRuleButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_FileIcon")))
self.delRuleButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_TrashIcon"))) self.delRuleButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_TrashIcon")))
@ -695,6 +699,7 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
self.saveButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogSaveButton"))) self.saveButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogSaveButton")))
self.prefsButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_FileDialogDetailedView"))) self.prefsButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_FileDialogDetailedView")))
self.startButton.setIcon(self.iconStart) self.startButton.setIcon(self.iconStart)
self.fwButton.setIcon(fwIcon)
self.cmdProcDetails.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_FileDialogContentsView"))) self.cmdProcDetails.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_FileDialogContentsView")))
self.TABLES[self.TAB_MAIN]['cmdCleanStats'].setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogResetButton"))) self.TABLES[self.TAB_MAIN]['cmdCleanStats'].setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogResetButton")))
for idx in range(1,8): for idx in range(1,8):
@ -1869,7 +1874,12 @@ class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
what = what + " AND" what = what + " AND"
what = what + " r.name LIKE '%{0}%'".format(filter_text) what = what + " r.name LIKE '%{0}%'".format(filter_text)
model = self._get_active_table().model() model = self._get_active_table().model()
self.setQuery(model, "SELECT * FROM rules as r %s %s %s" % (what, self._get_order(), self._get_limit())) self.setQuery(model, "SELECT {0} FROM rules as r {1} {2} {3}".format(
self.TABLES[self.TAB_RULES]['display_fields'],
what,
self._get_order(),
self._get_limit()
))
self._restore_details_view_columns( self._restore_details_view_columns(
self.TABLES[self.TAB_RULES]['view'].horizontalHeader(), self.TABLES[self.TAB_RULES]['view'].horizontalHeader(),
"{0}{1}".format(Config.STATS_VIEW_COL_STATE, self.TAB_RULES) "{0}{1}".format(Config.STATS_VIEW_COL_STATE, self.TAB_RULES)

View file

@ -70,27 +70,27 @@ class Nodes(QObject):
def add_fw_rules(self, addr, fwconfig): def add_fw_rules(self, addr, fwconfig):
self._nodes[addr]['fwrules'] = fwconfig self._nodes[addr]['fwrules'] = fwconfig
def add_rule(self, time, node, name, enabled, precedence, action, duration, op_type, op_sensitive, op_operand, op_data): def add_rule(self, time, node, name, description, enabled, precedence, action, duration, op_type, op_sensitive, op_operand, op_data):
# don't add rule if the user has selected to exclude temporary # don't add rule if the user has selected to exclude temporary
# rules # rules
if duration in Config.RULES_DURATION_FILTER: if duration in Config.RULES_DURATION_FILTER:
return return
self._db.insert("rules", self._db.insert("rules",
"(time, node, name, enabled, precedence, action, duration, operator_type, operator_sensitive, operator_operand, operator_data)", "(time, node, name, description, enabled, precedence, action, duration, operator_type, operator_sensitive, operator_operand, operator_data)",
(time, node, name, enabled, precedence, action, duration, op_type, op_sensitive, op_operand, op_data), (time, node, name, description, enabled, precedence, action, duration, op_type, op_sensitive, op_operand, op_data),
action_on_conflict="REPLACE") action_on_conflict="REPLACE")
def add_rules(self, addr, rules): def add_rules(self, addr, rules):
try: try:
for _,r in enumerate(rules): for _,r in enumerate(rules):
self.add_rule(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), self.add_rule(datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
addr, addr,
r.name, str(r.enabled), str(r.precedence), r.action, r.duration, r.name, r.description, str(r.enabled),
r.operator.type, str(r.precedence), r.action, r.duration,
str(r.operator.sensitive), r.operator.type,
r.operator.operand, str(r.operator.sensitive),
r.operator.data) r.operator.operand, r.operator.data)
except Exception as e: except Exception as e:
print(self.LOG_TAG + " exception adding node to db: ", e) print(self.LOG_TAG + " exception adding node to db: ", e)

View file

@ -6,39 +6,202 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>540</width> <width>552</width>
<height>490</height> <height>528</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle"> <property name="windowTitle">
<string>Rule</string> <string>Rule</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_4"> <layout class="QGridLayout" name="gridLayout_4">
<item row="2" column="1"> <item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4"> <widget class="QGroupBox" name="enableGroup">
<item> <property name="sizePolicy">
<widget class="QCheckBox" name="enableCheck"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<property name="text"> <horstretch>0</horstretch>
<string>Enable</string> <verstretch>0</verstretch>
</property> </sizepolicy>
</widget> </property>
</item> <property name="flat">
<item> <bool>false</bool>
<spacer name="horizontalSpacer_7"> </property>
<property name="orientation"> <property name="checkable">
<enum>Qt::Horizontal</enum> <bool>false</bool>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="checked">
<size> <bool>false</bool>
<width>40</width> </property>
<height>20</height> <layout class="QGridLayout" name="gridLayout" columnstretch="0,0,0,0,0,0,0">
</size> <property name="sizeConstraint">
</property> <enum>QLayout::SetDefaultConstraint</enum>
</spacer> </property>
</item> <property name="topMargin">
</layout> <number>0</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<property name="verticalSpacing">
<number>12</number>
</property>
<item row="3" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Action</string>
</property>
</widget>
</item>
<item row="4" column="4">
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="4">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Duration</string>
</property>
</widget>
</item>
<item row="4" column="6">
<widget class="QComboBox" name="durationCombo">
<item>
<property name="text">
<string>once</string>
</property>
</item>
<item>
<property name="text">
<string>30s</string>
</property>
</item>
<item>
<property name="text">
<string>5m</string>
</property>
</item>
<item>
<property name="text">
<string>15m</string>
</property>
</item>
<item>
<property name="text">
<string>30m</string>
</property>
</item>
<item>
<property name="text">
<string>1h</string>
</property>
</item>
<item>
<property name="text">
<string>until reboot</string>
</property>
</item>
<item>
<property name="text">
<string>always</string>
</property>
</item>
</widget>
</item>
<item row="3" column="6">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QRadioButton" name="actionDenyRadio">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Deny will just discard the connection</string>
</property>
<property name="text">
<string>Deny</string>
</property>
<property name="icon">
<iconset theme="emblem-important">
<normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="actionRejectRadio">
<property name="toolTip">
<string>Reject will drop the connection, and kill the socket that initiated it</string>
</property>
<property name="text">
<string>Reject</string>
</property>
<property name="icon">
<iconset theme="window-close">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="actionAllowRadio">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Allow will allow the connection</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Allow</string>
</property>
<property name="icon">
<iconset theme="emblem-default">
<normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item> </item>
<item row="3" column="1"> <item row="4" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_5"> <layout class="QHBoxLayout" name="horizontalLayout_5">
<item> <item>
<widget class="QCheckBox" name="precedenceCheck"> <widget class="QCheckBox" name="precedenceCheck">
@ -80,7 +243,63 @@ You must name the rule in such manner that it'll be checked first, because they'
</item> </item>
</layout> </layout>
</item> </item>
<item row="8" column="1"> <item row="1" column="1">
<widget class="QPlainTextEdit" name="ruleDescEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>60</height>
</size>
</property>
<property name="placeholderText">
<string>Description...</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QCheckBox" name="enableCheck">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="9" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3"> <layout class="QHBoxLayout" name="horizontalLayout_3">
<item> <item>
<spacer name="horizontalSpacer_6"> <spacer name="horizontalSpacer_6">
@ -110,7 +329,53 @@ You must name the rule in such manner that it'll be checked first, because they'
</item> </item>
</layout> </layout>
</item> </item>
<item row="1" column="1"> <item row="7" column="1">
<widget class="QLabel" name="statusLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="ruleNameEdit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them.
000-allow-localhost
001-deny-broadcast
...</string>
</property>
<property name="placeholderText">
<string>leave blank to autocreate</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<widget class="QLabel" name="label_3"> <widget class="QLabel" name="label_3">
@ -144,7 +409,7 @@ You must name the rule in such manner that it'll be checked first, because they'
</item> </item>
</layout> </layout>
</item> </item>
<item row="5" column="1"> <item row="6" column="1">
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>0</number>
@ -155,9 +420,10 @@ You must name the rule in such manner that it'll be checked first, because they'
<property name="documentMode"> <property name="documentMode">
<bool>true</bool> <bool>true</bool>
</property> </property>
<widget class="QWidget" name="tabWidgetPage1" native="true"> <widget class="QWidget" name="tabWidgetPage1">
<attribute name="icon"> <attribute name="icon">
<iconset theme="system-run"/> <iconset theme="system-run">
<normaloff>.</normaloff>.</iconset>
</attribute> </attribute>
<attribute name="title"> <attribute name="title">
<string>Applications</string> <string>Applications</string>
@ -213,7 +479,7 @@ You must name the rule in such manner that it'll be checked first, because they'
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This field will only match the executable path. It is not modifiable by the user.&lt;br/&gt;&lt;/p&gt;&lt;p&gt;You can use regular expressions to deny executions from /tmp for example:&lt;br/&gt;&lt;/p&gt;&lt;p&gt;^/tmp/.*$&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The value of this field is always the absolute path to the executable: /path/to/binary&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Examples:&lt;/p&gt;&lt;p&gt;- Simple: /path/to/binary&lt;/p&gt;&lt;p&gt;- Multiple paths: ^/usr/lib(64|)/firefox/firefox$&lt;/p&gt;&lt;p&gt;- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ &lt;/p&gt;&lt;p&gt;- Deny/Allow executions from /tmp:&lt;/p&gt;&lt;p&gt;^/(var/|)tmp/.*$&lt;br/&gt;&lt;/p&gt;&lt;p&gt;For more examples visit the &lt;a href=&quot;https://github.com/evilsocket/opensnitch/wiki/Rules-examples&quot;&gt;wiki page&lt;/a&gt; or ask on the &lt;a href=&quot;https://github.com/evilsocket/opensnitch/discussions&quot;&gt;Discussion forums&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="placeholderText"> <property name="placeholderText">
<string notr="true">/path/to/executable, .*/bin/executable[0-9\.]+$, ...</string> <string notr="true">/path/to/executable, .*/bin/executable[0-9\.]+$, ...</string>
@ -279,9 +545,10 @@ You must name the rule in such manner that it'll be checked first, because they'
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tabWidgetPage2" native="true"> <widget class="QWidget" name="tabWidgetPage2">
<attribute name="icon"> <attribute name="icon">
<iconset theme="preferences-system-network"/> <iconset theme="preferences-system-network">
<normaloff>.</normaloff>.</iconset>
</attribute> </attribute>
<attribute name="title"> <attribute name="title">
<string>Network</string> <string>Network</string>
@ -528,9 +795,10 @@ Note: Commas or spaces are not allowed to separate IPs or networks.</string>
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tabWidgetPage3" native="true"> <widget class="QWidget" name="tabWidgetPage3">
<attribute name="icon"> <attribute name="icon">
<iconset theme="document-properties"/> <iconset theme="document-properties">
<normaloff>.</normaloff>.</iconset>
</attribute> </attribute>
<attribute name="title"> <attribute name="title">
<string>List of domains/IPs</string> <string>List of domains/IPs</string>
@ -561,7 +829,8 @@ Note: Commas or spaces are not allowed to separate IPs or networks.</string>
<string/> <string/>
</property> </property>
<property name="icon"> <property name="icon">
<iconset theme="system-search"/> <iconset theme="system-search">
<normaloff>.</normaloff>.</iconset>
</property> </property>
</widget> </widget>
</item> </item>
@ -595,7 +864,8 @@ Note: Commas or spaces are not allowed to separate IPs or networks.</string>
<string/> <string/>
</property> </property>
<property name="icon"> <property name="icon">
<iconset theme="system-search"/> <iconset theme="system-search">
<normaloff>.</normaloff>.</iconset>
</property> </property>
</widget> </widget>
</item> </item>
@ -664,7 +934,8 @@ Note: Commas or spaces are not allowed to separate IPs or networks.</string>
<string/> <string/>
</property> </property>
<property name="icon"> <property name="icon">
<iconset theme="system-search"/> <iconset theme="system-search">
<normaloff>.</normaloff>.</iconset>
</property> </property>
</widget> </widget>
</item> </item>
@ -684,222 +955,6 @@ Note: Commas or spaces are not allowed to separate IPs or networks.</string>
</widget> </widget>
</widget> </widget>
</item> </item>
<item row="6" column="1">
<widget class="QLabel" name="statusLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="ruleNameEdit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them.
000-allow-localhost
001-deny-broadcast
...</string>
</property>
<property name="placeholderText">
<string>leave blank to autocreate</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QGroupBox" name="enableGroup">
<property name="flat">
<bool>false</bool>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,0,0,0,0,0,0">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<property name="verticalSpacing">
<number>12</number>
</property>
<item row="3" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Action</string>
</property>
</widget>
</item>
<item row="4" column="4">
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="4">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Duration</string>
</property>
</widget>
</item>
<item row="4" column="6">
<widget class="QComboBox" name="durationCombo">
<item>
<property name="text">
<string>once</string>
</property>
</item>
<item>
<property name="text">
<string>30s</string>
</property>
</item>
<item>
<property name="text">
<string>5m</string>
</property>
</item>
<item>
<property name="text">
<string>15m</string>
</property>
</item>
<item>
<property name="text">
<string>30m</string>
</property>
</item>
<item>
<property name="text">
<string>1h</string>
</property>
</item>
<item>
<property name="text">
<string>until reboot</string>
</property>
</item>
<item>
<property name="text">
<string>always</string>
</property>
</item>
</widget>
</item>
<item row="3" column="6">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QRadioButton" name="actionDenyRadio">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Deny</string>
</property>
<property name="icon">
<iconset theme="emblem-important">
<normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="actionRejectRadio">
<property name="text">
<string>Reject</string>
</property>
<property name="icon">
<iconset theme="window-close">
<normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="actionAllowRadio">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Allow</string>
</property>
<property name="icon">
<iconset theme="emblem-default">
<normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

View file

@ -549,9 +549,10 @@ class UIService(ui_pb2_grpc.UIServicer, QtWidgets.QGraphicsObject):
proto, addr = self._get_peer(kwargs['peer']) proto, addr = self._get_peer(kwargs['peer'])
self._nodes.add_rule((datetime.now().strftime("%Y-%m-%d %H:%M:%S")), self._nodes.add_rule((datetime.now().strftime("%Y-%m-%d %H:%M:%S")),
"{0}:{1}".format(proto, addr), "{0}:{1}".format(proto, addr),
rule.name, str(rule.enabled), str(rule.precedence), rule.action, rule.duration, rule.name, rule.description, str(rule.enabled),
rule.operator.type, str(rule.operator.sensitive), rule.operator.operand, str(rule.precedence), rule.action, rule.duration,
rule.operator.data) rule.operator.type, str(rule.operator.sensitive), rule.operator.operand,
rule.operator.data)
elif kwargs['action'] == self.DELETE_RULE: elif kwargs['action'] == self.DELETE_RULE:
self._db.delete_rule(kwargs['name'], kwargs['addr']) self._db.delete_rule(kwargs['name'], kwargs['addr'])