Build OpenSnitch v1.8.0 RPMs for Fedora with Per-VM Identity Patch (Fixing the "Identity Crisis")

Some time ago, I posted a workaround for running OpenSnitch across multiple VMs called Split-OpenSnitch with Per-VM Identity.

There is now a much better way to solve this natively! I have submitted a PR to the upstream OpenSnitch project that solves the node identity problem by using node_id as the canonical remote node identity. The PR is still pending, but I have successfully tested it on a Fedora 43 VM running the UI alongside 4 daemon nodes.

For anyone who wants to test this out on Qubes OS with Fedora templates, here is a complete guide to patching OpenSnitch v1.8.0 and building the RPM packages locally.

1. Install Build Dependencies

First, you need to install the system build tools and libraries:

sudo dnf install -y \
  git rpm-build rpmdevtools \
  golang protobuf-compiler golang-google-protobuf protoc-gen-go-grpc \
  clang llvm make gcc \
  libnetfilter_queue-devel libpcap-devel \
  python3-devel python3-setuptools python3-pip python3-packaging \
  qt5-linguist

For runtime and testing after installation, these packages are also highly recommended:

sudo dnf install -y \
  python3-pyqt6 python3-protobuf python3-grpcio \
  python3-inotify python3-slugify python3-grpcio python3-grpcio-tools
  • Important: The UI spec uses pyrcc5. If your system does not provide pyrcc5 natively but you have it in a micromamba environment, that is fine—just ensure the environment is activated when running rpmbuild.
  • You can verify you have the right Qt tools by running command -v pyrcc5 and checking for the linguist tool with command -v lrelease || command -v lrelease-qt5 || command -v lrelease6.

2. Save the #1577 and Fedora Build Fixes Patches

To successfully package v1.8.0 on Fedora, you will need a few minor build tweaks. Expand the section below, copy the patch text, and save it as 0002-v1.8.0-local-fedora-rpm-build-fixes.patch in your working directory.

Fedora Build Fixes Patch (0002-v1.8.0-local-fedora-rpm-build-fixes.patch)
From 6f756561842d4e090e8e9e51d2c6766d95dd904f Mon Sep 17 00:00:00 2001
From: nyymi <a01@truewall.eu>
Date: Fri, 3 Apr 2026 08:12:45 +0300
Subject: [PATCH] build: local Fedora RPM packaging fixes for v1.8.0

---
 ebpf_prog/Makefile                         |  3 ++-
 ui/i18n/generate_i18n.sh                   |  5 ++++-
 utils/packaging/daemon/rpm/opensnitch.spec | 13 ++++++-------
 utils/packaging/ui/rpm/opensnitch-ui.spec  |  8 ++++----
 4 files changed, 16 insertions(+), 13 deletions(-)

diff --git a/ebpf_prog/Makefile b/ebpf_prog/Makefile
index 558508fe..747cc74c 100644
--- a/ebpf_prog/Makefile
+++ b/ebpf_prog/Makefile
@@ -58,13 +58,14 @@ CFLAGS = -I. \
 	-Wno-address-of-packed-member \
 	-Wno-tautological-compare \
 	-Wno-unknown-warning-option  \
+	-fms-extensions \
 	-fno-stack-protector \
 	-g -O2 -emit-llvm
 
 all: $(BIN)
 
 %.bc: %.c
-	$(CC) $(CFLAGS) -c $<
+	$(CC) $(CFLAGS) -c $< -o $@
 
 %.o: %.bc
 	$(LLC) -march=bpf -mcpu=generic -filetype=obj -o $@ $<
diff --git a/ui/i18n/generate_i18n.sh b/ui/i18n/generate_i18n.sh
index 55622ea9..1babffc5 100755
--- a/ui/i18n/generate_i18n.sh
+++ b/ui/i18n/generate_i18n.sh
@@ -3,7 +3,10 @@
 app_name="opensnitch"
 langs_dir="./locales"
 lrelease_bin=""
-for lbin in $LRELEASE lrelease lrelease6 lrelease-qt6
+# On some distros the Qt linguist tools live under qt5/qt6 libexec paths,
+# but are not added to the user PATH.
+PATH=$PATH:/usr/lib/qt6/bin:/usr/lib64/qt6/bin:/usr/lib64/qt5/bin/
+for lbin in $LRELEASE lrelease lrelease6 lrelease-qt6 lrelease-qt5
 do
     lrelease_bin="$(command -v $lbin)"
     if [ -n "$lrelease_bin" ]; then
diff --git a/utils/packaging/daemon/rpm/opensnitch.spec b/utils/packaging/daemon/rpm/opensnitch.spec
index ecccfa51..01af46b0 100644
--- a/utils/packaging/daemon/rpm/opensnitch.spec
+++ b/utils/packaging/daemon/rpm/opensnitch.spec
@@ -1,6 +1,6 @@
 Name:           opensnitch
 Version:        1.8.0
-Release:        1%{?dist}
+Release:        1.qubes1%{?dist}
 Summary:        OpenSnitch is a GNU/Linux interactive application firewall
 
 License:        GPLv3+
@@ -31,12 +31,11 @@ rm -rf %{buildroot}
 %setup
 
 %build
-mkdir -p go/src/github.com/evilsocket
-ln -s $(pwd) go/src/github.com/evilsocket/opensnitch
-export GOPATH=$(pwd)/go
-cd go/src/github.com/evilsocket/opensnitch/
-make protocol
-cd go/src/github.com/evilsocket/opensnitch/daemon/
+mkdir -p daemon/ui/protocol
+protoc -Iproto proto/ui.proto --go_out=daemon/ui/protocol --go-grpc_out=daemon/ui/protocol --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative
+perl -0pi -e 's/SupportPackageIsVersion8/SupportPackageIsVersion7/g; s/cOpts := append\(\[\]grpc\.CallOption\{grpc\.StaticMethod\(\)\}, opts\.\.\.\)/cOpts := opts/g' daemon/ui/protocol/ui_grpc.pb.go
+make -C ebpf_prog KERNEL_VER="$(uname -r)"
+cd daemon/
 go mod vendor
 go build -o opensnitchd .
 
diff --git a/utils/packaging/ui/rpm/opensnitch-ui.spec b/utils/packaging/ui/rpm/opensnitch-ui.spec
index be325807..f73b7a8c 100644
--- a/utils/packaging/ui/rpm/opensnitch-ui.spec
+++ b/utils/packaging/ui/rpm/opensnitch-ui.spec
@@ -1,8 +1,7 @@
 %define name opensnitch-ui
 %define version 1.8.0
 %define unmangled_version 1.8.0
-%define release 1
-%define __python python3
+%define release 1.qubes1
 %define desktop_file opensnitch_ui.desktop
 
 Summary: Prompt service and UI for the OpenSnitch interactive application firewall.
@@ -75,13 +74,14 @@ fi
 
 
 %build
+export PATH=/usr/lib64/qt5/bin:/usr/lib64/qt6/bin:/usr/lib/qt6/bin:$PATH
 cd i18n; make; cd ..
 pyrcc5 -o opensnitch/resources_rc.py opensnitch/res/resources.qrc
 find opensnitch/proto/ -name 'ui_pb2_grpc.py' -exec sed -i 's/^import ui_pb2/from . import ui_pb2/' {} \;
-python3 setup.py build
+/usr/bin/python3 setup.py build
 
 %install
-python3 setup.py install --install-lib=/usr/lib/python3/dist-packages/ --single-version-externally-managed -O1 --root=$RPM_BUILD_ROOT --prefix=/usr --record=INSTALLED_FILES --install-scripts=/usr/bin
+/usr/bin/python3 setup.py install --install-lib=/usr/lib/python3/dist-packages/ --single-version-externally-managed -O1 --root=$RPM_BUILD_ROOT --prefix=/usr --record=INSTALLED_FILES --install-scripts=/usr/bin
 
 %clean
 rm -rf $RPM_BUILD_ROOT
-- 
2.53.0

Expand the section below, copy the patch text, and save it as 0001-v1.8.0-ui-use-node_id-as-canonical-remote-node-identity.patch in your working directory.

#1577 Patch (0001-v1.8.0-ui-use-node_id-as-canonical-remote-node-identity.patch)
From 65c270de28f7445ae1e23ea719de1b82a0c77b59 Mon Sep 17 00:00:00 2001
From: nyymi <a01@truewall.eu>
Date: Fri, 3 Apr 2026 08:01:47 +0300
Subject: [PATCH] ui: use node_id as canonical remote node identity

---
 daemon/ui/notifications.go    |   1 +
 proto/ui.proto                |   1 +
 ui/opensnitch/nodes.py        |  63 +++++--
 ui/opensnitch/proto/ui_pb2.py | 316 ++++++++++++++++++++++++++++++++--
 ui/opensnitch/service.py      |   9 +-
 5 files changed, 360 insertions(+), 30 deletions(-)

diff --git a/daemon/ui/notifications.go b/daemon/ui/notifications.go
index 16003bba..a1385d9d 100644
--- a/daemon/ui/notifications.go
+++ b/daemon/ui/notifications.go
@@ -51,6 +51,7 @@ func (c *Client) getClientConfig() *protocol.ClientConfig {
 	return &protocol.ClientConfig{
 		Id:                uint64(ts.UnixNano()),
 		Name:              nodeName,
+		NodeId:            nodeName,
 		Version:           nodeVersion,
 		IsFirewallRunning: firewall.IsRunning(),
 		Config:            strings.Replace(string(raw), "\n", "", -1),
diff --git a/proto/ui.proto b/proto/ui.proto
index f0c9640c..13b88604 100644
--- a/proto/ui.proto
+++ b/proto/ui.proto
@@ -258,6 +258,7 @@ message ClientConfig {
     uint32 logLevel = 6;
     repeated Rule rules = 7;
     SysFirewall systemFirewall = 8;
+    string node_id = 9;
 }
 
 /* Notification message is sent to the clients (daemons) from the GUI (server)
diff --git a/ui/opensnitch/nodes.py b/ui/opensnitch/nodes.py
index 466b78fe..3582b67b 100644
--- a/ui/opensnitch/nodes.py
+++ b/ui/opensnitch/nodes.py
@@ -32,6 +32,7 @@ class Nodes(QObject):
         self._db = Database.instance()
         self._rules = Rules()
         self._nodes = {}
+        self._peer_map = {}
         self._notifications_sent = {}
         self._interfaces = NetworkInterfaces()
 
@@ -40,18 +41,28 @@ class Nodes(QObject):
 
     def add(self, _peer, client_config=None):
         try:
-            proto, addr = self.get_addr(_peer)
-            peer = proto+":"+addr
+            peer = self._get_node_key(_peer, client_config)
+            now = datetime.now()
             if peer not in self._nodes:
                 self._nodes[peer] = {
+                        'session': {
+                            'peer': _peer,
+                            'last_seen': now
+                        },
                         'notifications': Queue(),
                         'online':        True,
-                        'last_seen':     datetime.now()
+                        'last_seen':     now
                         }
             else:
-                self._nodes[peer]['last_seen'] = datetime.now()
+                prev_peer = self._nodes[peer].get('session', {}).get('peer')
+                if prev_peer is not None and prev_peer != _peer:
+                    self._peer_map.pop(prev_peer, None)
+                self._nodes[peer]['last_seen'] = now
+                self._nodes[peer]['session']['peer'] = _peer
+                self._nodes[peer]['session']['last_seen'] = now
 
             self._nodes[peer]['online'] = True
+            self._peer_map[_peer] = peer
             self.add_data(peer, client_config)
             self.insert(peer)
 
@@ -143,20 +154,23 @@ class Nodes(QObject):
     def delete_all(self):
         self.send_notifications(None)
         self._nodes = {}
+        self._peer_map = {}
         self.nodesUpdated.emit(self.count())
 
     def delete(self, peer):
+        addr = self._resolve_node_key(peer)
         try:
-            proto, addr = self.get_addr(peer)
-            addr = "%s:%s" % (proto, addr)
             # Force the node to get one new item from queue,
             # in order to loop and exit.
             self._nodes[addr]['notifications'].put(None)
-        except:
-            addr = peer
+        except Exception:
+            pass
 
         if addr in self._nodes:
             del self._nodes[addr]
+            for mapped_peer, node_key in list(self._peer_map.items()):
+                if mapped_peer == peer or node_key == addr:
+                    del self._peer_map[mapped_peer]
             self.nodesUpdated.emit(self.count())
 
     def get(self):
@@ -164,6 +178,7 @@ class Nodes(QObject):
 
     def get_node(self, addr):
         try:
+            addr = self._resolve_node_key(addr)
             return self._nodes[addr]
         except:
             return None
@@ -173,6 +188,7 @@ class Nodes(QObject):
 
     def get_node_hostname(self, addr):
         try:
+            addr = self._resolve_node_key(addr)
             if addr not in self._nodes:
                 return ""
             return self._nodes[addr]['data'].name
@@ -182,6 +198,7 @@ class Nodes(QObject):
 
     def get_node_config(self, addr):
         try:
+            addr = self._resolve_node_key(addr)
             if addr not in self._nodes:
                 return None
             return self._nodes[addr]['data'].config
@@ -202,7 +219,7 @@ class Nodes(QObject):
 
     def get_addr(self, peer):
         try:
-            peer = peer.split(":")
+            peer = self._split_peer(self._resolve_node_key(peer))
             # WA for backward compatibility
             if peer[0] == "unix" and peer[1] == "":
                 peer[1] = "/local"
@@ -211,6 +228,25 @@ class Nodes(QObject):
             print(self.LOG_TAG, "get_addr() error getting addr:", peer)
             return peer
 
+    def _split_peer(self, peer):
+        return peer.split(":")
+
+    def _resolve_node_key(self, peer):
+        return self._peer_map.get(peer, peer)
+
+    def _get_node_key(self, peer, client_config=None):
+        proto, addr = self._split_peer(peer)[:2]
+        node_id = getattr(client_config, "node_id", "").strip() if client_config is not None else ""
+        node_name = getattr(client_config, "name", "").strip() if client_config is not None else ""
+
+        if proto in ("ipv4", "ipv6"):
+            if node_id != "":
+                return f"node:{node_id}"
+            if node_name != "" and self.is_local(f"{proto}:{addr}"):
+                return f"node:{node_name}"
+
+        return f"{proto}:{addr}"
+
     def is_connected(self, addr):
         try:
             nd = self.get_node(addr)
@@ -249,6 +285,7 @@ class Nodes(QObject):
 
     def save_node_config(self, addr, config):
         try:
+            addr = self._resolve_node_key(addr)
             self._nodes[addr]['data'].config = config
         except Exception as e:
             print(self.LOG_TAG + " exception saving node config: ", e, addr, config)
@@ -287,6 +324,7 @@ class Nodes(QObject):
 
     def send_notification(self, addr, notification, callback_signal=None):
         try:
+            addr = self._resolve_node_key(addr)
             notification.id = int(str(time.time()).replace(".", ""))
             if addr not in self._nodes:
                 # FIXME: the reply is sent before we return the notification id
@@ -341,6 +379,7 @@ class Nodes(QObject):
 
     def reply_notification(self, addr, reply):
         try:
+            addr = self._resolve_node_key(addr)
             if reply == None:
                 print(self.LOG_TAG, " reply notification None")
                 return
@@ -375,8 +414,7 @@ class Nodes(QObject):
 
     def insert(self, peer, status=ONLINE):
         try:
-            proto, addr = self.get_addr(peer)
-            naddr = "{0}:{1}".format(proto, addr)
+            naddr = self._resolve_node_key(peer)
             self._db.insert(
                 "nodes",
                 "(addr, status, hostname, daemon_version, daemon_uptime, " \
@@ -390,8 +428,7 @@ class Nodes(QObject):
 
     def update(self, peer, status=ONLINE):
         try:
-            proto, addr = self.get_addr(peer)
-            naddr = "{0}:{1}".format(proto, addr)
+            naddr = self._resolve_node_key(peer)
             self._db.update("nodes",
                     "hostname=?,version=?,last_connection=?,status=?",
                     (
diff --git a/ui/opensnitch/proto/ui_pb2.py b/ui/opensnitch/proto/ui_pb2.py
index a864f586..965c7f72 100644
--- a/ui/opensnitch/proto/ui_pb2.py
+++ b/ui/opensnitch/proto/ui_pb2.py
@@ -2,9 +2,11 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: ui.proto
 """Generated protocol buffer code."""
-from google.protobuf.internal import builder as _builder
+from google.protobuf.internal import enum_type_wrapper
 from google.protobuf import descriptor as _descriptor
 from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
 from google.protobuf import symbol_database as _symbol_database
 # @@protoc_insertion_point(imports)
 
@@ -13,10 +15,294 @@ _sym_db = _symbol_database.Default()
 
 
 
-DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x08ui.proto\x12\x08protocol\"\xcb\x04\n\x05\x41lert\x12\n\n\x02id\x18\x01 \x01(\x04\x12\"\n\x04type\x18\x02 \x01(\x0e\x32\x14.protocol.Alert.Type\x12&\n\x06\x61\x63tion\x18\x03 \x01(\x0e\x32\x16.protocol.Alert.Action\x12*\n\x08priority\x18\x04 \x01(\x0e\x32\x18.protocol.Alert.Priority\x12\"\n\x04what\x18\x05 \x01(\x0e\x32\x14.protocol.Alert.What\x12\x0e\n\x04text\x18\x06 \x01(\tH\x00\x12!\n\x04proc\x18\x08 \x01(\x0b\x32\x11.protocol.ProcessH\x00\x12$\n\x04\x63onn\x18\t \x01(\x0b\x32\x14.protocol.ConnectionH\x00\x12\x1e\n\x04rule\x18\n \x01(\x0b\x32\x0e.protocol.RuleH\x00\x12\"\n\x06\x66wrule\x18\x0b \x01(\x0b\x32\x10.protocol.FwRuleH\x00\")\n\x08Priority\x12\x07\n\x03LOW\x10\x00\x12\n\n\x06MEDIUM\x10\x01\x12\x08\n\x04HIGH\x10\x02\"(\n\x04Type\x12\t\n\x05\x45RROR\x10\x00\x12\x0b\n\x07WARNING\x10\x01\x12\x08\n\x04INFO\x10\x02\"2\n\x06\x41\x63tion\x12\x08\n\x04NONE\x10\x00\x12\x0e\n\nSHOW_ALERT\x10\x01\x12\x0e\n\nSAVE_TO_DB\x10\x02\"l\n\x04What\x12\x0b\n\x07GENERIC\x10\x00\x12\x10\n\x0cPROC_MONITOR\x10\x01\x12\x0c\n\x08\x46IREWALL\x10\x02\x12\x0e\n\nCONNECTION\x10\x03\x12\x08\n\x04RULE\x10\x04\x12\x0b\n\x07NETLINK\x10\x05\x12\x10\n\x0cKERNEL_EVENT\x10\x06\x42\x06\n\x04\x64\x61ta\"\x19\n\x0bMsgResponse\x12\n\n\x02id\x18\x01 \x01(\x04\"o\n\x05\x45vent\x12\x0c\n\x04time\x18\x01 \x01(\t\x12(\n\nconnection\x18\x02 \x01(\x0b\x32\x14.protocol.Connection\x12\x1c\n\x04rule\x18\x03 \x01(\x0b\x32\x0e.protocol.Rule\x12\x10\n\x08unixnano\x18\x04 \x01(\x03\"\xd3\x06\n\nStatistics\x12\x16\n\x0e\x64\x61\x65mon_version\x18\x01 \x01(\t\x12\r\n\x05rules\x18\x02 \x01(\x04\x12\x0e\n\x06uptime\x18\x03 \x01(\x04\x12\x15\n\rdns_responses\x18\x04 \x01(\x04\x12\x13\n\x0b\x63onnections\x18\x05 \x01(\x04\x12\x0f\n\x07ignored\x18\x06 \x01(\x04\x12\x10\n\x08\x61\x63\x63\x65pted\x18\x07 \x01(\x04\x12\x0f\n\x07\x64ropped\x18\x08 \x01(\x04\x12\x11\n\trule_hits\x18\t \x01(\x04\x12\x13\n\x0brule_misses\x18\n \x01(\x04\x12\x33\n\x08\x62y_proto\x18\x0b \x03(\x0b\x32!.protocol.Statistics.ByProtoEntry\x12\x37\n\nby_address\x18\x0c \x03(\x0b\x32#.protocol.Statistics.ByAddressEntry\x12\x31\n\x07\x62y_host\x18\r \x03(\x0b\x32 .protocol.Statistics.ByHostEntry\x12\x31\n\x07\x62y_port\x18\x0e \x03(\x0b\x32 .protocol.Statistics.ByPortEntry\x12/\n\x06\x62y_uid\x18\x0f \x03(\x0b\x32\x1f.protocol.Statistics.ByUidEntry\x12=\n\rby_executable\x18\x10 \x03(\x0b\x32&.protocol.Statistics.ByExecutableEntry\x12\x1f\n\x06\x65vents\x18\x11 \x03(\x0b\x32\x0f.protocol.Event\x1a.\n\x0c\x42yProtoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x30\n\x0e\x42yAddressEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yHostEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yPortEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a,\n\nByUidEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x33\n\x11\x42yExecutableEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\">\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x04\x12#\n\x05stats\x18\x02 \x01(\x0b\x32\x14.protocol.Statistics\"\x17\n\tPingReply\x12\n\n\x02id\x18\x01 \x01(\x04\"\'\n\tStringInt\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\r\"\x9b\x03\n\x07Process\x12\x0b\n\x03pid\x18\x01 \x01(\x04\x12\x0c\n\x04ppid\x18\x02 \x01(\x04\x12\x0b\n\x03uid\x18\x03 \x01(\x04\x12\x0c\n\x04\x63omm\x18\x04 \x01(\t\x12\x0c\n\x04path\x18\x05 \x01(\t\x12\x0c\n\x04\x61rgs\x18\x06 \x03(\t\x12\'\n\x03\x65nv\x18\x07 \x03(\x0b\x32\x1a.protocol.Process.EnvEntry\x12\x0b\n\x03\x63wd\x18\x08 \x01(\t\x12\x33\n\tchecksums\x18\t \x03(\x0b\x32 .protocol.Process.ChecksumsEntry\x12\x10\n\x08io_reads\x18\n \x01(\x04\x12\x11\n\tio_writes\x18\x0b \x01(\x04\x12\x11\n\tnet_reads\x18\x0c \x01(\x04\x12\x12\n\nnet_writes\x18\r \x01(\x04\x12)\n\x0cprocess_tree\x18\x0e \x03(\x0b\x32\x13.protocol.StringInt\x1a*\n\x08\x45nvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x30\n\x0e\x43hecksumsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xf3\x03\n\nConnection\x12\x10\n\x08protocol\x18\x01 \x01(\t\x12\x0e\n\x06src_ip\x18\x02 \x01(\t\x12\x10\n\x08src_port\x18\x03 \x01(\r\x12\x0e\n\x06\x64st_ip\x18\x04 \x01(\t\x12\x10\n\x08\x64st_host\x18\x05 \x01(\t\x12\x10\n\x08\x64st_port\x18\x06 \x01(\r\x12\x0f\n\x07user_id\x18\x07 \x01(\r\x12\x12\n\nprocess_id\x18\x08 \x01(\r\x12\x14\n\x0cprocess_path\x18\t \x01(\t\x12\x13\n\x0bprocess_cwd\x18\n \x01(\t\x12\x14\n\x0cprocess_args\x18\x0b \x03(\t\x12\x39\n\x0bprocess_env\x18\x0c \x03(\x0b\x32$.protocol.Connection.ProcessEnvEntry\x12\x45\n\x11process_checksums\x18\r \x03(\x0b\x32*.protocol.Connection.ProcessChecksumsEntry\x12)\n\x0cprocess_tree\x18\x0e \x03(\x0b\x32\x13.protocol.StringInt\x1a\x31\n\x0fProcessEnvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x37\n\x15ProcessChecksumsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"l\n\x08Operator\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0f\n\x07operand\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t\x12\x11\n\tsensitive\x18\x04 \x01(\x08\x12 \n\x04list\x18\x05 \x03(\x0b\x32\x12.protocol.Operator\"\xb6\x01\n\x04Rule\x12\x0f\n\x07\x63reated\x18\x01 \x01(\x03\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x04 \x01(\x08\x12\x12\n\nprecedence\x18\x05 \x01(\x08\x12\r\n\x05nolog\x18\x06 \x01(\x08\x12\x0e\n\x06\x61\x63tion\x18\x07 \x01(\t\x12\x10\n\x08\x64uration\x18\x08 \x01(\t\x12$\n\x08operator\x18\t \x01(\x0b\x32\x12.protocol.Operator\"-\n\x0fStatementValues\x12\x0b\n\x03Key\x18\x01 \x01(\t\x12\r\n\x05Value\x18\x02 \x01(\t\"P\n\tStatement\x12\n\n\x02Op\x18\x01 \x01(\t\x12\x0c\n\x04Name\x18\x02 \x01(\t\x12)\n\x06Values\x18\x03 \x03(\x0b\x32\x19.protocol.StatementValues\"5\n\x0b\x45xpressions\x12&\n\tStatement\x18\x01 \x01(\x0b\x32\x13.protocol.Statement\"\xd6\x01\n\x06\x46wRule\x12\r\n\x05Table\x18\x01 \x01(\t\x12\r\n\x05\x43hain\x18\x02 \x01(\t\x12\x0c\n\x04UUID\x18\x03 \x01(\t\x12\x0f\n\x07\x45nabled\x18\x04 \x01(\x08\x12\x10\n\x08Position\x18\x05 \x01(\x04\x12\x13\n\x0b\x44\x65scription\x18\x06 \x01(\t\x12\x12\n\nParameters\x18\x07 \x01(\t\x12*\n\x0b\x45xpressions\x18\x08 \x03(\x0b\x32\x15.protocol.Expressions\x12\x0e\n\x06Target\x18\t \x01(\t\x12\x18\n\x10TargetParameters\x18\n \x01(\t\"\x95\x01\n\x07\x46wChain\x12\x0c\n\x04Name\x18\x01 \x01(\t\x12\r\n\x05Table\x18\x02 \x01(\t\x12\x0e\n\x06\x46\x61mily\x18\x03 \x01(\t\x12\x10\n\x08Priority\x18\x04 \x01(\t\x12\x0c\n\x04Type\x18\x05 \x01(\t\x12\x0c\n\x04Hook\x18\x06 \x01(\t\x12\x0e\n\x06Policy\x18\x07 \x01(\t\x12\x1f\n\x05Rules\x18\x08 \x03(\x0b\x32\x10.protocol.FwRule\"M\n\x08\x46wChains\x12\x1e\n\x04Rule\x18\x01 \x01(\x0b\x32\x10.protocol.FwRule\x12!\n\x06\x43hains\x18\x02 \x03(\x0b\x32\x11.protocol.FwChain\"X\n\x0bSysFirewall\x12\x0f\n\x07\x45nabled\x18\x01 \x01(\x08\x12\x0f\n\x07Version\x18\x02 \x01(\r\x12\'\n\x0bSystemRules\x18\x03 \x03(\x0b\x32\x12.protocol.FwChains\"\xc4\x01\n\x0c\x43lientConfig\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x19\n\x11isFirewallRunning\x18\x04 \x01(\x08\x12\x0e\n\x06\x63onfig\x18\x05 \x01(\t\x12\x10\n\x08logLevel\x18\x06 \x01(\r\x12\x1d\n\x05rules\x18\x07 \x03(\x0b\x32\x0e.protocol.Rule\x12-\n\x0esystemFirewall\x18\x08 \x01(\x0b\x32\x15.protocol.SysFirewall\"\xbb\x01\n\x0cNotification\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x12\n\nclientName\x18\x02 \x01(\t\x12\x12\n\nserverName\x18\x03 \x01(\t\x12\x1e\n\x04type\x18\x04 \x01(\x0e\x32\x10.protocol.Action\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\t\x12\x1d\n\x05rules\x18\x06 \x03(\x0b\x32\x0e.protocol.Rule\x12*\n\x0bsysFirewall\x18\x07 \x01(\x0b\x32\x15.protocol.SysFirewall\"\\\n\x11NotificationReply\x12\n\n\x02id\x18\x01 \x01(\x04\x12-\n\x04\x63ode\x18\x02 \x01(\x0e\x32\x1f.protocol.NotificationReplyCode\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t*\x95\x02\n\x06\x41\x63tion\x12\x08\n\x04NONE\x10\x00\x12\x17\n\x13\x45NABLE_INTERCEPTION\x10\x01\x12\x18\n\x14\x44ISABLE_INTERCEPTION\x10\x02\x12\x13\n\x0f\x45NABLE_FIREWALL\x10\x03\x12\x14\n\x10\x44ISABLE_FIREWALL\x10\x04\x12\x13\n\x0fRELOAD_FW_RULES\x10\x05\x12\x11\n\rCHANGE_CONFIG\x10\x06\x12\x0f\n\x0b\x45NABLE_RULE\x10\x07\x12\x10\n\x0c\x44ISABLE_RULE\x10\x08\x12\x0f\n\x0b\x44\x45LETE_RULE\x10\t\x12\x0f\n\x0b\x43HANGE_RULE\x10\n\x12\r\n\tLOG_LEVEL\x10\x0b\x12\x08\n\x04STOP\x10\x0c\x12\x0e\n\nTASK_START\x10\r\x12\r\n\tTASK_STOP\x10\x0e**\n\x15NotificationReplyCode\x12\x06\n\x02OK\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x32\xaf\x02\n\x02UI\x12\x34\n\x04Ping\x12\x15.protocol.PingRequest\x1a\x13.protocol.PingReply\"\x00\x12\x31\n\x07\x41skRule\x12\x14.protocol.Connection\x1a\x0e.protocol.Rule\"\x00\x12=\n\tSubscribe\x12\x16.protocol.ClientConfig\x1a\x16.protocol.ClientConfig\"\x00\x12J\n\rNotifications\x12\x1b.protocol.NotificationReply\x1a\x16.protocol.Notification\"\x00(\x01\x30\x01\x12\x35\n\tPostAlert\x12\x0f.protocol.Alert\x1a\x15.protocol.MsgResponse\"\x00\x42\x35Z3github.com/evilsocket/opensnitch/daemon/ui/protocolb\x06proto3')
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x08ui.proto\x12\x08protocol\"\xcb\x04\n\x05\x41lert\x12\n\n\x02id\x18\x01 \x01(\x04\x12\"\n\x04type\x18\x02 \x01(\x0e\x32\x14.protocol.Alert.Type\x12&\n\x06\x61\x63tion\x18\x03 \x01(\x0e\x32\x16.protocol.Alert.Action\x12*\n\x08priority\x18\x04 \x01(\x0e\x32\x18.protocol.Alert.Priority\x12\"\n\x04what\x18\x05 \x01(\x0e\x32\x14.protocol.Alert.What\x12\x0e\n\x04text\x18\x06 \x01(\tH\x00\x12!\n\x04proc\x18\x08 \x01(\x0b\x32\x11.protocol.ProcessH\x00\x12$\n\x04\x63onn\x18\t \x01(\x0b\x32\x14.protocol.ConnectionH\x00\x12\x1e\n\x04rule\x18\n \x01(\x0b\x32\x0e.protocol.RuleH\x00\x12\"\n\x06\x66wrule\x18\x0b \x01(\x0b\x32\x10.protocol.FwRuleH\x00\")\n\x08Priority\x12\x07\n\x03LOW\x10\x00\x12\n\n\x06MEDIUM\x10\x01\x12\x08\n\x04HIGH\x10\x02\"(\n\x04Type\x12\t\n\x05\x45RROR\x10\x00\x12\x0b\n\x07WARNING\x10\x01\x12\x08\n\x04INFO\x10\x02\"2\n\x06\x41\x63tion\x12\x08\n\x04NONE\x10\x00\x12\x0e\n\nSHOW_ALERT\x10\x01\x12\x0e\n\nSAVE_TO_DB\x10\x02\"l\n\x04What\x12\x0b\n\x07GENERIC\x10\x00\x12\x10\n\x0cPROC_MONITOR\x10\x01\x12\x0c\n\x08\x46IREWALL\x10\x02\x12\x0e\n\nCONNECTION\x10\x03\x12\x08\n\x04RULE\x10\x04\x12\x0b\n\x07NETLINK\x10\x05\x12\x10\n\x0cKERNEL_EVENT\x10\x06\x42\x06\n\x04\x64\x61ta\"\x19\n\x0bMsgResponse\x12\n\n\x02id\x18\x01 \x01(\x04\"o\n\x05\x45vent\x12\x0c\n\x04time\x18\x01 \x01(\t\x12(\n\nconnection\x18\x02 \x01(\x0b\x32\x14.protocol.Connection\x12\x1c\n\x04rule\x18\x03 \x01(\x0b\x32\x0e.protocol.Rule\x12\x10\n\x08unixnano\x18\x04 \x01(\x03\"\xd3\x06\n\nStatistics\x12\x16\n\x0e\x64\x61\x65mon_version\x18\x01 \x01(\t\x12\r\n\x05rules\x18\x02 \x01(\x04\x12\x0e\n\x06uptime\x18\x03 \x01(\x04\x12\x15\n\rdns_responses\x18\x04 \x01(\x04\x12\x13\n\x0b\x63onnections\x18\x05 \x01(\x04\x12\x0f\n\x07ignored\x18\x06 \x01(\x04\x12\x10\n\x08\x61\x63\x63\x65pted\x18\x07 \x01(\x04\x12\x0f\n\x07\x64ropped\x18\x08 \x01(\x04\x12\x11\n\trule_hits\x18\t \x01(\x04\x12\x13\n\x0brule_misses\x18\n \x01(\x04\x12\x33\n\x08\x62y_proto\x18\x0b \x03(\x0b\x32!.protocol.Statistics.ByProtoEntry\x12\x37\n\nby_address\x18\x0c \x03(\x0b\x32#.protocol.Statistics.ByAddressEntry\x12\x31\n\x07\x62y_host\x18\r \x03(\x0b\x32 .protocol.Statistics.ByHostEntry\x12\x31\n\x07\x62y_port\x18\x0e \x03(\x0b\x32 .protocol.Statistics.ByPortEntry\x12/\n\x06\x62y_uid\x18\x0f \x03(\x0b\x32\x1f.protocol.Statistics.ByUidEntry\x12=\n\rby_executable\x18\x10 \x03(\x0b\x32&.protocol.Statistics.ByExecutableEntry\x12\x1f\n\x06\x65vents\x18\x11 \x03(\x0b\x32\x0f.protocol.Event\x1a.\n\x0c\x42yProtoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x30\n\x0e\x42yAddressEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yHostEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yPortEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a,\n\nByUidEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x33\n\x11\x42yExecutableEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\">\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x04\x12#\n\x05stats\x18\x02 \x01(\x0b\x32\x14.protocol.Statistics\"\x17\n\tPingReply\x12\n\n\x02id\x18\x01 \x01(\x04\"\'\n\tStringInt\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\r\"\x9b\x03\n\x07Process\x12\x0b\n\x03pid\x18\x01 \x01(\x04\x12\x0c\n\x04ppid\x18\x02 \x01(\x04\x12\x0b\n\x03uid\x18\x03 \x01(\x04\x12\x0c\n\x04\x63omm\x18\x04 \x01(\t\x12\x0c\n\x04path\x18\x05 \x01(\t\x12\x0c\n\x04\x61rgs\x18\x06 \x03(\t\x12\'\n\x03\x65nv\x18\x07 \x03(\x0b\x32\x1a.protocol.Process.EnvEntry\x12\x0b\n\x03\x63wd\x18\x08 \x01(\t\x12\x33\n\tchecksums\x18\t \x03(\x0b\x32 .protocol.Process.ChecksumsEntry\x12\x10\n\x08io_reads\x18\n \x01(\x04\x12\x11\n\tio_writes\x18\x0b \x01(\x04\x12\x11\n\tnet_reads\x18\x0c \x01(\x04\x12\x12\n\nnet_writes\x18\r \x01(\x04\x12)\n\x0cprocess_tree\x18\x0e \x03(\x0b\x32\x13.protocol.StringInt\x1a*\n\x08\x45nvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x30\n\x0e\x43hecksumsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xf3\x03\n\nConnection\x12\x10\n\x08protocol\x18\x01 \x01(\t\x12\x0e\n\x06src_ip\x18\x02 \x01(\t\x12\x10\n\x08src_port\x18\x03 \x01(\r\x12\x0e\n\x06\x64st_ip\x18\x04 \x01(\t\x12\x10\n\x08\x64st_host\x18\x05 \x01(\t\x12\x10\n\x08\x64st_port\x18\x06 \x01(\r\x12\x0f\n\x07user_id\x18\x07 \x01(\r\x12\x12\n\nprocess_id\x18\x08 \x01(\r\x12\x14\n\x0cprocess_path\x18\t \x01(\t\x12\x13\n\x0bprocess_cwd\x18\n \x01(\t\x12\x14\n\x0cprocess_args\x18\x0b \x03(\t\x12\x39\n\x0bprocess_env\x18\x0c \x03(\x0b\x32$.protocol.Connection.ProcessEnvEntry\x12\x45\n\x11process_checksums\x18\r \x03(\x0b\x32*.protocol.Connection.ProcessChecksumsEntry\x12)\n\x0cprocess_tree\x18\x0e \x03(\x0b\x32\x13.protocol.StringInt\x1a\x31\n\x0fProcessEnvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x37\n\x15ProcessChecksumsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"l\n\x08Operator\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0f\n\x07operand\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t\x12\x11\n\tsensitive\x18\x04 \x01(\x08\x12 \n\x04list\x18\x05 \x03(\x0b\x32\x12.protocol.Operator\"\xb6\x01\n\x04Rule\x12\x0f\n\x07\x63reated\x18\x01 \x01(\x03\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x04 \x01(\x08\x12\x12\n\nprecedence\x18\x05 \x01(\x08\x12\r\n\x05nolog\x18\x06 \x01(\x08\x12\x0e\n\x06\x61\x63tion\x18\x07 \x01(\t\x12\x10\n\x08\x64uration\x18\x08 \x01(\t\x12$\n\x08operator\x18\t \x01(\x0b\x32\x12.protocol.Operator\"-\n\x0fStatementValues\x12\x0b\n\x03Key\x18\x01 \x01(\t\x12\r\n\x05Value\x18\x02 \x01(\t\"P\n\tStatement\x12\n\n\x02Op\x18\x01 \x01(\t\x12\x0c\n\x04Name\x18\x02 \x01(\t\x12)\n\x06Values\x18\x03 \x03(\x0b\x32\x19.protocol.StatementValues\"5\n\x0b\x45xpressions\x12&\n\tStatement\x18\x01 \x01(\x0b\x32\x13.protocol.Statement\"\xd6\x01\n\x06\x46wRule\x12\r\n\x05Table\x18\x01 \x01(\t\x12\r\n\x05\x43hain\x18\x02 \x01(\t\x12\x0c\n\x04UUID\x18\x03 \x01(\t\x12\x0f\n\x07\x45nabled\x18\x04 \x01(\x08\x12\x10\n\x08Position\x18\x05 \x01(\x04\x12\x13\n\x0b\x44\x65scription\x18\x06 \x01(\t\x12\x12\n\nParameters\x18\x07 \x01(\t\x12*\n\x0b\x45xpressions\x18\x08 \x03(\x0b\x32\x15.protocol.Expressions\x12\x0e\n\x06Target\x18\t \x01(\t\x12\x18\n\x10TargetParameters\x18\n \x01(\t\"\x95\x01\n\x07\x46wChain\x12\x0c\n\x04Name\x18\x01 \x01(\t\x12\r\n\x05Table\x18\x02 \x01(\t\x12\x0e\n\x06\x46\x61mily\x18\x03 \x01(\t\x12\x10\n\x08Priority\x18\x04 \x01(\t\x12\x0c\n\x04Type\x18\x05 \x01(\t\x12\x0c\n\x04Hook\x18\x06 \x01(\t\x12\x0e\n\x06Policy\x18\x07 \x01(\t\x12\x1f\n\x05Rules\x18\x08 \x03(\x0b\x32\x10.protocol.FwRule\"M\n\x08\x46wChains\x12\x1e\n\x04Rule\x18\x01 \x01(\x0b\x32\x10.protocol.FwRule\x12!\n\x06\x43hains\x18\x02 \x03(\x0b\x32\x11.protocol.FwChain\"X\n\x0bSysFirewall\x12\x0f\n\x07\x45nabled\x18\x01 \x01(\x08\x12\x0f\n\x07Version\x18\x02 \x01(\r\x12\'\n\x0bSystemRules\x18\x03 \x03(\x0b\x32\x12.protocol.FwChains\"\xd5\x01\n\x0c\x43lientConfig\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x19\n\x11isFirewallRunning\x18\x04 \x01(\x08\x12\x0e\n\x06\x63onfig\x18\x05 \x01(\t\x12\x10\n\x08logLevel\x18\x06 \x01(\r\x12\x1d\n\x05rules\x18\x07 \x03(\x0b\x32\x0e.protocol.Rule\x12-\n\x0esystemFirewall\x18\x08 \x01(\x0b\x32\x15.protocol.SysFirewall\x12\x0f\n\x07node_id\x18\t \x01(\t\"\xbb\x01\n\x0cNotification\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x12\n\nclientName\x18\x02 \x01(\t\x12\x12\n\nserverName\x18\x03 \x01(\t\x12\x1e\n\x04type\x18\x04 \x01(\x0e\x32\x10.protocol.Action\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\t\x12\x1d\n\x05rules\x18\x06 \x03(\x0b\x32\x0e.protocol.Rule\x12*\n\x0bsysFirewall\x18\x07 \x01(\x0b\x32\x15.protocol.SysFirewall\"\\\n\x11NotificationReply\x12\n\n\x02id\x18\x01 \x01(\x04\x12-\n\x04\x63ode\x18\x02 \x01(\x0e\x32\x1f.protocol.NotificationReplyCode\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t*\x95\x02\n\x06\x41\x63tion\x12\x08\n\x04NONE\x10\x00\x12\x17\n\x13\x45NABLE_INTERCEPTION\x10\x01\x12\x18\n\x14\x44ISABLE_INTERCEPTION\x10\x02\x12\x13\n\x0f\x45NABLE_FIREWALL\x10\x03\x12\x14\n\x10\x44ISABLE_FIREWALL\x10\x04\x12\x13\n\x0fRELOAD_FW_RULES\x10\x05\x12\x11\n\rCHANGE_CONFIG\x10\x06\x12\x0f\n\x0b\x45NABLE_RULE\x10\x07\x12\x10\n\x0c\x44ISABLE_RULE\x10\x08\x12\x0f\n\x0b\x44\x45LETE_RULE\x10\t\x12\x0f\n\x0b\x43HANGE_RULE\x10\n\x12\r\n\tLOG_LEVEL\x10\x0b\x12\x08\n\x04STOP\x10\x0c\x12\x0e\n\nTASK_START\x10\r\x12\r\n\tTASK_STOP\x10\x0e**\n\x15NotificationReplyCode\x12\x06\n\x02OK\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x32\xaf\x02\n\x02UI\x12\x34\n\x04Ping\x12\x15.protocol.PingRequest\x1a\x13.protocol.PingReply\"\x00\x12\x31\n\x07\x41skRule\x12\x14.protocol.Connection\x1a\x0e.protocol.Rule\"\x00\x12=\n\tSubscribe\x12\x16.protocol.ClientConfig\x1a\x16.protocol.ClientConfig\"\x00\x12J\n\rNotifications\x12\x1b.protocol.NotificationReply\x1a\x16.protocol.Notification\"\x00(\x01\x30\x01\x12\x35\n\tPostAlert\x12\x0f.protocol.Alert\x1a\x15.protocol.MsgResponse\"\x00\x42\x35Z3github.com/evilsocket/opensnitch/daemon/ui/protocolb\x06proto3')
 
-_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
-_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ui_pb2', globals())
+_ACTION = DESCRIPTOR.enum_types_by_name['Action']
+Action = enum_type_wrapper.EnumTypeWrapper(_ACTION)
+_NOTIFICATIONREPLYCODE = DESCRIPTOR.enum_types_by_name['NotificationReplyCode']
+NotificationReplyCode = enum_type_wrapper.EnumTypeWrapper(_NOTIFICATIONREPLYCODE)
+NONE = 0
+ENABLE_INTERCEPTION = 1
+DISABLE_INTERCEPTION = 2
+ENABLE_FIREWALL = 3
+DISABLE_FIREWALL = 4
+RELOAD_FW_RULES = 5
+CHANGE_CONFIG = 6
+ENABLE_RULE = 7
+DISABLE_RULE = 8
+DELETE_RULE = 9
+CHANGE_RULE = 10
+LOG_LEVEL = 11
+STOP = 12
+TASK_START = 13
+TASK_STOP = 14
+OK = 0
+ERROR = 1
+
+
+_ALERT = DESCRIPTOR.message_types_by_name['Alert']
+_MSGRESPONSE = DESCRIPTOR.message_types_by_name['MsgResponse']
+_EVENT = DESCRIPTOR.message_types_by_name['Event']
+_STATISTICS = DESCRIPTOR.message_types_by_name['Statistics']
+_STATISTICS_BYPROTOENTRY = _STATISTICS.nested_types_by_name['ByProtoEntry']
+_STATISTICS_BYADDRESSENTRY = _STATISTICS.nested_types_by_name['ByAddressEntry']
+_STATISTICS_BYHOSTENTRY = _STATISTICS.nested_types_by_name['ByHostEntry']
+_STATISTICS_BYPORTENTRY = _STATISTICS.nested_types_by_name['ByPortEntry']
+_STATISTICS_BYUIDENTRY = _STATISTICS.nested_types_by_name['ByUidEntry']
+_STATISTICS_BYEXECUTABLEENTRY = _STATISTICS.nested_types_by_name['ByExecutableEntry']
+_PINGREQUEST = DESCRIPTOR.message_types_by_name['PingRequest']
+_PINGREPLY = DESCRIPTOR.message_types_by_name['PingReply']
+_STRINGINT = DESCRIPTOR.message_types_by_name['StringInt']
+_PROCESS = DESCRIPTOR.message_types_by_name['Process']
+_PROCESS_ENVENTRY = _PROCESS.nested_types_by_name['EnvEntry']
+_PROCESS_CHECKSUMSENTRY = _PROCESS.nested_types_by_name['ChecksumsEntry']
+_CONNECTION = DESCRIPTOR.message_types_by_name['Connection']
+_CONNECTION_PROCESSENVENTRY = _CONNECTION.nested_types_by_name['ProcessEnvEntry']
+_CONNECTION_PROCESSCHECKSUMSENTRY = _CONNECTION.nested_types_by_name['ProcessChecksumsEntry']
+_OPERATOR = DESCRIPTOR.message_types_by_name['Operator']
+_RULE = DESCRIPTOR.message_types_by_name['Rule']
+_STATEMENTVALUES = DESCRIPTOR.message_types_by_name['StatementValues']
+_STATEMENT = DESCRIPTOR.message_types_by_name['Statement']
+_EXPRESSIONS = DESCRIPTOR.message_types_by_name['Expressions']
+_FWRULE = DESCRIPTOR.message_types_by_name['FwRule']
+_FWCHAIN = DESCRIPTOR.message_types_by_name['FwChain']
+_FWCHAINS = DESCRIPTOR.message_types_by_name['FwChains']
+_SYSFIREWALL = DESCRIPTOR.message_types_by_name['SysFirewall']
+_CLIENTCONFIG = DESCRIPTOR.message_types_by_name['ClientConfig']
+_NOTIFICATION = DESCRIPTOR.message_types_by_name['Notification']
+_NOTIFICATIONREPLY = DESCRIPTOR.message_types_by_name['NotificationReply']
+_ALERT_PRIORITY = _ALERT.enum_types_by_name['Priority']
+_ALERT_TYPE = _ALERT.enum_types_by_name['Type']
+_ALERT_ACTION = _ALERT.enum_types_by_name['Action']
+_ALERT_WHAT = _ALERT.enum_types_by_name['What']
+Alert = _reflection.GeneratedProtocolMessageType('Alert', (_message.Message,), {
+  'DESCRIPTOR' : _ALERT,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.Alert)
+  })
+_sym_db.RegisterMessage(Alert)
+
+MsgResponse = _reflection.GeneratedProtocolMessageType('MsgResponse', (_message.Message,), {
+  'DESCRIPTOR' : _MSGRESPONSE,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.MsgResponse)
+  })
+_sym_db.RegisterMessage(MsgResponse)
+
+Event = _reflection.GeneratedProtocolMessageType('Event', (_message.Message,), {
+  'DESCRIPTOR' : _EVENT,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.Event)
+  })
+_sym_db.RegisterMessage(Event)
+
+Statistics = _reflection.GeneratedProtocolMessageType('Statistics', (_message.Message,), {
+
+  'ByProtoEntry' : _reflection.GeneratedProtocolMessageType('ByProtoEntry', (_message.Message,), {
+    'DESCRIPTOR' : _STATISTICS_BYPROTOENTRY,
+    '__module__' : 'ui_pb2'
+    # @@protoc_insertion_point(class_scope:protocol.Statistics.ByProtoEntry)
+    })
+  ,
+
+  'ByAddressEntry' : _reflection.GeneratedProtocolMessageType('ByAddressEntry', (_message.Message,), {
+    'DESCRIPTOR' : _STATISTICS_BYADDRESSENTRY,
+    '__module__' : 'ui_pb2'
+    # @@protoc_insertion_point(class_scope:protocol.Statistics.ByAddressEntry)
+    })
+  ,
+
+  'ByHostEntry' : _reflection.GeneratedProtocolMessageType('ByHostEntry', (_message.Message,), {
+    'DESCRIPTOR' : _STATISTICS_BYHOSTENTRY,
+    '__module__' : 'ui_pb2'
+    # @@protoc_insertion_point(class_scope:protocol.Statistics.ByHostEntry)
+    })
+  ,
+
+  'ByPortEntry' : _reflection.GeneratedProtocolMessageType('ByPortEntry', (_message.Message,), {
+    'DESCRIPTOR' : _STATISTICS_BYPORTENTRY,
+    '__module__' : 'ui_pb2'
+    # @@protoc_insertion_point(class_scope:protocol.Statistics.ByPortEntry)
+    })
+  ,
+
+  'ByUidEntry' : _reflection.GeneratedProtocolMessageType('ByUidEntry', (_message.Message,), {
+    'DESCRIPTOR' : _STATISTICS_BYUIDENTRY,
+    '__module__' : 'ui_pb2'
+    # @@protoc_insertion_point(class_scope:protocol.Statistics.ByUidEntry)
+    })
+  ,
+
+  'ByExecutableEntry' : _reflection.GeneratedProtocolMessageType('ByExecutableEntry', (_message.Message,), {
+    'DESCRIPTOR' : _STATISTICS_BYEXECUTABLEENTRY,
+    '__module__' : 'ui_pb2'
+    # @@protoc_insertion_point(class_scope:protocol.Statistics.ByExecutableEntry)
+    })
+  ,
+  'DESCRIPTOR' : _STATISTICS,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.Statistics)
+  })
+_sym_db.RegisterMessage(Statistics)
+_sym_db.RegisterMessage(Statistics.ByProtoEntry)
+_sym_db.RegisterMessage(Statistics.ByAddressEntry)
+_sym_db.RegisterMessage(Statistics.ByHostEntry)
+_sym_db.RegisterMessage(Statistics.ByPortEntry)
+_sym_db.RegisterMessage(Statistics.ByUidEntry)
+_sym_db.RegisterMessage(Statistics.ByExecutableEntry)
+
+PingRequest = _reflection.GeneratedProtocolMessageType('PingRequest', (_message.Message,), {
+  'DESCRIPTOR' : _PINGREQUEST,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.PingRequest)
+  })
+_sym_db.RegisterMessage(PingRequest)
+
+PingReply = _reflection.GeneratedProtocolMessageType('PingReply', (_message.Message,), {
+  'DESCRIPTOR' : _PINGREPLY,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.PingReply)
+  })
+_sym_db.RegisterMessage(PingReply)
+
+StringInt = _reflection.GeneratedProtocolMessageType('StringInt', (_message.Message,), {
+  'DESCRIPTOR' : _STRINGINT,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.StringInt)
+  })
+_sym_db.RegisterMessage(StringInt)
+
+Process = _reflection.GeneratedProtocolMessageType('Process', (_message.Message,), {
+
+  'EnvEntry' : _reflection.GeneratedProtocolMessageType('EnvEntry', (_message.Message,), {
+    'DESCRIPTOR' : _PROCESS_ENVENTRY,
+    '__module__' : 'ui_pb2'
+    # @@protoc_insertion_point(class_scope:protocol.Process.EnvEntry)
+    })
+  ,
+
+  'ChecksumsEntry' : _reflection.GeneratedProtocolMessageType('ChecksumsEntry', (_message.Message,), {
+    'DESCRIPTOR' : _PROCESS_CHECKSUMSENTRY,
+    '__module__' : 'ui_pb2'
+    # @@protoc_insertion_point(class_scope:protocol.Process.ChecksumsEntry)
+    })
+  ,
+  'DESCRIPTOR' : _PROCESS,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.Process)
+  })
+_sym_db.RegisterMessage(Process)
+_sym_db.RegisterMessage(Process.EnvEntry)
+_sym_db.RegisterMessage(Process.ChecksumsEntry)
+
+Connection = _reflection.GeneratedProtocolMessageType('Connection', (_message.Message,), {
+
+  'ProcessEnvEntry' : _reflection.GeneratedProtocolMessageType('ProcessEnvEntry', (_message.Message,), {
+    'DESCRIPTOR' : _CONNECTION_PROCESSENVENTRY,
+    '__module__' : 'ui_pb2'
+    # @@protoc_insertion_point(class_scope:protocol.Connection.ProcessEnvEntry)
+    })
+  ,
+
+  'ProcessChecksumsEntry' : _reflection.GeneratedProtocolMessageType('ProcessChecksumsEntry', (_message.Message,), {
+    'DESCRIPTOR' : _CONNECTION_PROCESSCHECKSUMSENTRY,
+    '__module__' : 'ui_pb2'
+    # @@protoc_insertion_point(class_scope:protocol.Connection.ProcessChecksumsEntry)
+    })
+  ,
+  'DESCRIPTOR' : _CONNECTION,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.Connection)
+  })
+_sym_db.RegisterMessage(Connection)
+_sym_db.RegisterMessage(Connection.ProcessEnvEntry)
+_sym_db.RegisterMessage(Connection.ProcessChecksumsEntry)
+
+Operator = _reflection.GeneratedProtocolMessageType('Operator', (_message.Message,), {
+  'DESCRIPTOR' : _OPERATOR,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.Operator)
+  })
+_sym_db.RegisterMessage(Operator)
+
+Rule = _reflection.GeneratedProtocolMessageType('Rule', (_message.Message,), {
+  'DESCRIPTOR' : _RULE,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.Rule)
+  })
+_sym_db.RegisterMessage(Rule)
+
+StatementValues = _reflection.GeneratedProtocolMessageType('StatementValues', (_message.Message,), {
+  'DESCRIPTOR' : _STATEMENTVALUES,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.StatementValues)
+  })
+_sym_db.RegisterMessage(StatementValues)
+
+Statement = _reflection.GeneratedProtocolMessageType('Statement', (_message.Message,), {
+  'DESCRIPTOR' : _STATEMENT,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.Statement)
+  })
+_sym_db.RegisterMessage(Statement)
+
+Expressions = _reflection.GeneratedProtocolMessageType('Expressions', (_message.Message,), {
+  'DESCRIPTOR' : _EXPRESSIONS,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.Expressions)
+  })
+_sym_db.RegisterMessage(Expressions)
+
+FwRule = _reflection.GeneratedProtocolMessageType('FwRule', (_message.Message,), {
+  'DESCRIPTOR' : _FWRULE,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.FwRule)
+  })
+_sym_db.RegisterMessage(FwRule)
+
+FwChain = _reflection.GeneratedProtocolMessageType('FwChain', (_message.Message,), {
+  'DESCRIPTOR' : _FWCHAIN,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.FwChain)
+  })
+_sym_db.RegisterMessage(FwChain)
+
+FwChains = _reflection.GeneratedProtocolMessageType('FwChains', (_message.Message,), {
+  'DESCRIPTOR' : _FWCHAINS,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.FwChains)
+  })
+_sym_db.RegisterMessage(FwChains)
+
+SysFirewall = _reflection.GeneratedProtocolMessageType('SysFirewall', (_message.Message,), {
+  'DESCRIPTOR' : _SYSFIREWALL,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.SysFirewall)
+  })
+_sym_db.RegisterMessage(SysFirewall)
+
+ClientConfig = _reflection.GeneratedProtocolMessageType('ClientConfig', (_message.Message,), {
+  'DESCRIPTOR' : _CLIENTCONFIG,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.ClientConfig)
+  })
+_sym_db.RegisterMessage(ClientConfig)
+
+Notification = _reflection.GeneratedProtocolMessageType('Notification', (_message.Message,), {
+  'DESCRIPTOR' : _NOTIFICATION,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.Notification)
+  })
+_sym_db.RegisterMessage(Notification)
+
+NotificationReply = _reflection.GeneratedProtocolMessageType('NotificationReply', (_message.Message,), {
+  'DESCRIPTOR' : _NOTIFICATIONREPLY,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.NotificationReply)
+  })
+_sym_db.RegisterMessage(NotificationReply)
+
+_UI = DESCRIPTOR.services_by_name['UI']
 if _descriptor._USE_C_DESCRIPTORS == False:
 
   DESCRIPTOR._options = None
@@ -41,10 +327,10 @@ if _descriptor._USE_C_DESCRIPTORS == False:
   _CONNECTION_PROCESSENVENTRY._serialized_options = b'8\001'
   _CONNECTION_PROCESSCHECKSUMSENTRY._options = None
   _CONNECTION_PROCESSCHECKSUMSENTRY._serialized_options = b'8\001'
-  _ACTION._serialized_start=4153
-  _ACTION._serialized_end=4430
-  _NOTIFICATIONREPLYCODE._serialized_start=4432
-  _NOTIFICATIONREPLYCODE._serialized_end=4474
+  _ACTION._serialized_start=4170
+  _ACTION._serialized_end=4447
+  _NOTIFICATIONREPLYCODE._serialized_start=4449
+  _NOTIFICATIONREPLYCODE._serialized_end=4491
   _ALERT._serialized_start=23
   _ALERT._serialized_end=610
   _ALERT_PRIORITY._serialized_start=357
@@ -110,11 +396,11 @@ if _descriptor._USE_C_DESCRIPTORS == False:
   _SYSFIREWALL._serialized_start=3579
   _SYSFIREWALL._serialized_end=3667
   _CLIENTCONFIG._serialized_start=3670
-  _CLIENTCONFIG._serialized_end=3866
-  _NOTIFICATION._serialized_start=3869
-  _NOTIFICATION._serialized_end=4056
-  _NOTIFICATIONREPLY._serialized_start=4058
-  _NOTIFICATIONREPLY._serialized_end=4150
-  _UI._serialized_start=4477
-  _UI._serialized_end=4780
+  _CLIENTCONFIG._serialized_end=3883
+  _NOTIFICATION._serialized_start=3886
+  _NOTIFICATION._serialized_end=4073
+  _NOTIFICATIONREPLY._serialized_start=4075
+  _NOTIFICATIONREPLY._serialized_end=4167
+  _UI._serialized_start=4494
+  _UI._serialized_end=4797
 # @@protoc_insertion_point(module_scope)
diff --git a/ui/opensnitch/service.py b/ui/opensnitch/service.py
index eb3b53ab..9d91a58c 100644
--- a/ui/opensnitch/service.py
+++ b/ui/opensnitch/service.py
@@ -831,7 +831,8 @@ class UIService(ui_pb2_grpc.UIServicer, QtWidgets.QGraphicsObject):
                 ost.start()
 
         elif kwargs['action'] == self.DELETE_RULE:
-            self._db.delete_rule(kwargs['name'], kwargs['addr'])
+            proto, addr = self._get_peer(kwargs['addr'])
+            self._db.delete_rule(kwargs['name'], f"{proto}:{addr}")
 
         elif kwargs['action'] == self.NODE_DELETE:
             self._delete_node(kwargs['peer'])
@@ -980,6 +981,10 @@ class UIService(ui_pb2_grpc.UIServicer, QtWidgets.QGraphicsObject):
 
         stop_event = Event()
         def _on_client_closed():
+            prev_node = self._nodes.get_node(node_addr)
+            if prev_node is not None and prev_node.get('session', {}).get('peer') != local_peer:
+                return
+
             stop_event.set()
             print(datetime.now(), "[Notifications] client closed", context.peer())
             self._nodes.stop_notifications(node_addr)
@@ -1022,7 +1027,7 @@ class UIService(ui_pb2_grpc.UIServicer, QtWidgets.QGraphicsObject):
                     if in_message == None:
                         continue
 
-                    self._nodes.reply_notification(addr, in_message)
+                    self._nodes.reply_notification(node_addr, in_message)
                 except StopIteration:
                     print("[Notifications] Node {0} exited".format(addr))
                     break
-- 
2.53.0

3. Clone and Patch the Repository

We’ll clone the official v1.8.0 repository and apply our patches.

# Clone the repository
git clone https://github.com/evilsocket/opensnitch.git
cd opensnitch

# Switch to the v1.8.0 branch
git checkout v1.8.0

# apply PR #1578
git am ../0001-v1.8.0-ui-use-node_id-as-canonical-remote-node-identity.patch

# apply the Fedora fix
git am ../0002-v1.8.0-local-fedora-rpm-build-fixes.patch

4. Setup the Build Directory and Archive Sources

Set up the standard RPM build directories, then package up the patched source code.

# Create RPM directories
mkdir -p ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}

# Archive the daemon source
git archive \
  --format=tar.gz \
  --prefix=opensnitch-1.8.0/ \
  HEAD \
  > ~/rpmbuild/SOURCES/opensnitch_1.8.0.orig.tar.gz

# Archive the UI source
git archive \
  --format=tar.gz \
  --prefix=opensnitch-ui-1.8.0/ \
  HEAD:ui \
  > ~/rpmbuild/SOURCES/opensnitch-ui-1.8.0.tar.gz

# Copy the patched spec files
cp utils/packaging/daemon/rpm/opensnitch.spec ~/rpmbuild/SPECS/
cp utils/packaging/ui/rpm/opensnitch-ui.spec ~/rpmbuild/SPECS/

5. Build the RPMs

Now, build your RPMs.

# Build the daemon RPM
rpmbuild -ba --define "_topdir $HOME/rpmbuild" ~/rpmbuild/SPECS/opensnitch.spec

# Build the UI RPM
rpmbuild -ba --define "_topdir $HOME/rpmbuild" ~/rpmbuild/SPECS/opensnitch-ui.spec

Once finished, your compiled RPMs will be waiting in ~/rpmbuild/RPMS/. Install them in your respective templates/AppVMs, configure your nodes, and you’re good to go!

2 Likes

Attempting to follow these instructions with fedora-43-minimal, but the template does not include any of the following: python3-pyside6-tools, python3-qt5, or libmamba. Since PyQt5 is deprecated and opensnitch migrated to PyQt6 in v1.8.0, I’m wondering which of these packages would be the preferred way forward? Is pyside6-rcc workable or even preferred?

I looked into this a bit more, and my current conclusion is:

pyside6-rcc is probably not the right direction here.

OpenSnitch 1.8.0 UI has already migrated to PyQt6, not PySide6, so switching the resource build step to a PySide6 tool would be mixing bindings. Also, Fedora’s package name is pyside6-tools, not python3-pyside6-tools, in case anyone goes looking for it.

More importantly, I now suspect the pyrcc5 step in my instructions is just stale packaging baggage from the older PyQt5 era. The upstream UI Makefile no longer actively runs that step, and I was able to do a local python3 setup.py build for the 1.8.0 UI without generating resources_rc.py first.

So for fedora-43-minimal, my best recommendation is:

  • keep using python3-pyqt6 for the UI runtime/build environment
  • keep qt5-linguist it’s needed for lrelease
  • do not switch the build over to pyside6-rcc
  • instead, remove the pyrcc5 step from the local RPM build instructions/spec for v1.8.0

Line 79 opensnitch-ui.spec: pyrcc5 -o opensnitch/resources_rc.py opensnitch/res/resources.qrc

If someone wants the lowest-risk workaround without editing the spec, using pyrcc5 from a micromamba env should still work as a temporary compatibility hack. But the cleaner fix seems to be: don’t require it at all.

I usually do this kind of thing in disposable VM so that I can install what’s needed and not leave any bloat. Just remember to copy resulting RPMs safety before destroying the VM.

E: To install pyrcc5 with micromamba on fedora-43-minimal:

Start fedora-43-minimal (disposable?).
Use qvm-run -u root [VM-name] xfce4-terminal to install bzip2 (dnf install bzip2)

With user:
Install micromamba curl -Ls https://micro.mamba.pm/api/micromamba/linux-64/latest | tar -xvj bin/micromamba
Create environment micromamba create -y -n opensnitch python=3.11
Prepare environment eval "$(micromamba shell hook --shell bash)"
Enable environment micromamba activate opensnitch
Install pyrcc5 with pip install PyQt5

Whonix Workstation 18 (whonix-workstation-18-dvm) version, might work on other Debian based VMs too:

:warning: Disclaimer: The eBPF part of this build was the least reliable part of the process and was only tested as a local workaround on my setup. In this recipe, the OpenSnitch eBPF objects are compiled against Debian kernel headers available in the build VM, which may not exactly match the kernel that a Qubes/Whonix VM actually runs. Because of that, the daemon may fail to load the eBPF programs on some systems even if the package itself builds successfully. If that happens, use the proc process monitor method instead of ebpf, or rebuild the eBPF objects against the actual target kernel environment.

Install Build Dependencies

sudo apt-get update
sudo apt-get install -y \
  git build-essential devscripts debhelper dh-python dh-golang \
  golang protobuf-compiler protoc-gen-go protoc-gen-go-grpc \
  clang llvm make gcc libnetfilter-queue-dev libpcap-dev \
  python3-dev python3-setuptools python3-pip python3-packaging \
  python3-pyqt5 pyqt5-dev-tools qttools5-dev-tools \
  linux-headers-amd64 python3-grpc-tools python3-grpcio \
  python3-protobuf libmnl-dev pyqt6-dev-tools python3-all

For runtime and testing after installation, the Debian package equivalents:

sudo apt-get install -y \
  python3-pyqt6 python3-protobuf python3-grpcio \
  python3-pyinotify python3-slugify python3-grpc-tools python3-notify2

You only need the 0001-v1.8.0-ui-use-node_id-as-canonical-remote-node-identity.patch.

Expand the section below, copy the patch text, and save it as 0001-v1.8.0-ui-use-node_id-as-canonical-remote-node-identity.patch in your home directory.

#1577 Patch (0001-v1.8.0-ui-use-node_id-as-canonical-remote-node-identity.patch)
From 65c270de28f7445ae1e23ea719de1b82a0c77b59 Mon Sep 17 00:00:00 2001
From: nyymi <a01@truewall.eu>
Date: Fri, 3 Apr 2026 08:01:47 +0300
Subject: [PATCH] ui: use node_id as canonical remote node identity

---
 daemon/ui/notifications.go    |   1 +
 proto/ui.proto                |   1 +
 ui/opensnitch/nodes.py        |  63 +++++--
 ui/opensnitch/proto/ui_pb2.py | 316 ++++++++++++++++++++++++++++++++--
 ui/opensnitch/service.py      |   9 +-
 5 files changed, 360 insertions(+), 30 deletions(-)

diff --git a/daemon/ui/notifications.go b/daemon/ui/notifications.go
index 16003bba..a1385d9d 100644
--- a/daemon/ui/notifications.go
+++ b/daemon/ui/notifications.go
@@ -51,6 +51,7 @@ func (c *Client) getClientConfig() *protocol.ClientConfig {
 	return &protocol.ClientConfig{
 		Id:                uint64(ts.UnixNano()),
 		Name:              nodeName,
+		NodeId:            nodeName,
 		Version:           nodeVersion,
 		IsFirewallRunning: firewall.IsRunning(),
 		Config:            strings.Replace(string(raw), "\n", "", -1),
diff --git a/proto/ui.proto b/proto/ui.proto
index f0c9640c..13b88604 100644
--- a/proto/ui.proto
+++ b/proto/ui.proto
@@ -258,6 +258,7 @@ message ClientConfig {
     uint32 logLevel = 6;
     repeated Rule rules = 7;
     SysFirewall systemFirewall = 8;
+    string node_id = 9;
 }
 
 /* Notification message is sent to the clients (daemons) from the GUI (server)
diff --git a/ui/opensnitch/nodes.py b/ui/opensnitch/nodes.py
index 466b78fe..3582b67b 100644
--- a/ui/opensnitch/nodes.py
+++ b/ui/opensnitch/nodes.py
@@ -32,6 +32,7 @@ class Nodes(QObject):
         self._db = Database.instance()
         self._rules = Rules()
         self._nodes = {}
+        self._peer_map = {}
         self._notifications_sent = {}
         self._interfaces = NetworkInterfaces()
 
@@ -40,18 +41,28 @@ class Nodes(QObject):
 
     def add(self, _peer, client_config=None):
         try:
-            proto, addr = self.get_addr(_peer)
-            peer = proto+":"+addr
+            peer = self._get_node_key(_peer, client_config)
+            now = datetime.now()
             if peer not in self._nodes:
                 self._nodes[peer] = {
+                        'session': {
+                            'peer': _peer,
+                            'last_seen': now
+                        },
                         'notifications': Queue(),
                         'online':        True,
-                        'last_seen':     datetime.now()
+                        'last_seen':     now
                         }
             else:
-                self._nodes[peer]['last_seen'] = datetime.now()
+                prev_peer = self._nodes[peer].get('session', {}).get('peer')
+                if prev_peer is not None and prev_peer != _peer:
+                    self._peer_map.pop(prev_peer, None)
+                self._nodes[peer]['last_seen'] = now
+                self._nodes[peer]['session']['peer'] = _peer
+                self._nodes[peer]['session']['last_seen'] = now
 
             self._nodes[peer]['online'] = True
+            self._peer_map[_peer] = peer
             self.add_data(peer, client_config)
             self.insert(peer)
 
@@ -143,20 +154,23 @@ class Nodes(QObject):
     def delete_all(self):
         self.send_notifications(None)
         self._nodes = {}
+        self._peer_map = {}
         self.nodesUpdated.emit(self.count())
 
     def delete(self, peer):
+        addr = self._resolve_node_key(peer)
         try:
-            proto, addr = self.get_addr(peer)
-            addr = "%s:%s" % (proto, addr)
             # Force the node to get one new item from queue,
             # in order to loop and exit.
             self._nodes[addr]['notifications'].put(None)
-        except:
-            addr = peer
+        except Exception:
+            pass
 
         if addr in self._nodes:
             del self._nodes[addr]
+            for mapped_peer, node_key in list(self._peer_map.items()):
+                if mapped_peer == peer or node_key == addr:
+                    del self._peer_map[mapped_peer]
             self.nodesUpdated.emit(self.count())
 
     def get(self):
@@ -164,6 +178,7 @@ class Nodes(QObject):
 
     def get_node(self, addr):
         try:
+            addr = self._resolve_node_key(addr)
             return self._nodes[addr]
         except:
             return None
@@ -173,6 +188,7 @@ class Nodes(QObject):
 
     def get_node_hostname(self, addr):
         try:
+            addr = self._resolve_node_key(addr)
             if addr not in self._nodes:
                 return ""
             return self._nodes[addr]['data'].name
@@ -182,6 +198,7 @@ class Nodes(QObject):
 
     def get_node_config(self, addr):
         try:
+            addr = self._resolve_node_key(addr)
             if addr not in self._nodes:
                 return None
             return self._nodes[addr]['data'].config
@@ -202,7 +219,7 @@ class Nodes(QObject):
 
     def get_addr(self, peer):
         try:
-            peer = peer.split(":")
+            peer = self._split_peer(self._resolve_node_key(peer))
             # WA for backward compatibility
             if peer[0] == "unix" and peer[1] == "":
                 peer[1] = "/local"
@@ -211,6 +228,25 @@ class Nodes(QObject):
             print(self.LOG_TAG, "get_addr() error getting addr:", peer)
             return peer
 
+    def _split_peer(self, peer):
+        return peer.split(":")
+
+    def _resolve_node_key(self, peer):
+        return self._peer_map.get(peer, peer)
+
+    def _get_node_key(self, peer, client_config=None):
+        proto, addr = self._split_peer(peer)[:2]
+        node_id = getattr(client_config, "node_id", "").strip() if client_config is not None else ""
+        node_name = getattr(client_config, "name", "").strip() if client_config is not None else ""
+
+        if proto in ("ipv4", "ipv6"):
+            if node_id != "":
+                return f"node:{node_id}"
+            if node_name != "" and self.is_local(f"{proto}:{addr}"):
+                return f"node:{node_name}"
+
+        return f"{proto}:{addr}"
+
     def is_connected(self, addr):
         try:
             nd = self.get_node(addr)
@@ -249,6 +285,7 @@ class Nodes(QObject):
 
     def save_node_config(self, addr, config):
         try:
+            addr = self._resolve_node_key(addr)
             self._nodes[addr]['data'].config = config
         except Exception as e:
             print(self.LOG_TAG + " exception saving node config: ", e, addr, config)
@@ -287,6 +324,7 @@ class Nodes(QObject):
 
     def send_notification(self, addr, notification, callback_signal=None):
         try:
+            addr = self._resolve_node_key(addr)
             notification.id = int(str(time.time()).replace(".", ""))
             if addr not in self._nodes:
                 # FIXME: the reply is sent before we return the notification id
@@ -341,6 +379,7 @@ class Nodes(QObject):
 
     def reply_notification(self, addr, reply):
         try:
+            addr = self._resolve_node_key(addr)
             if reply == None:
                 print(self.LOG_TAG, " reply notification None")
                 return
@@ -375,8 +414,7 @@ class Nodes(QObject):
 
     def insert(self, peer, status=ONLINE):
         try:
-            proto, addr = self.get_addr(peer)
-            naddr = "{0}:{1}".format(proto, addr)
+            naddr = self._resolve_node_key(peer)
             self._db.insert(
                 "nodes",
                 "(addr, status, hostname, daemon_version, daemon_uptime, " \
@@ -390,8 +428,7 @@ class Nodes(QObject):
 
     def update(self, peer, status=ONLINE):
         try:
-            proto, addr = self.get_addr(peer)
-            naddr = "{0}:{1}".format(proto, addr)
+            naddr = self._resolve_node_key(peer)
             self._db.update("nodes",
                     "hostname=?,version=?,last_connection=?,status=?",
                     (
diff --git a/ui/opensnitch/proto/ui_pb2.py b/ui/opensnitch/proto/ui_pb2.py
index a864f586..965c7f72 100644
--- a/ui/opensnitch/proto/ui_pb2.py
+++ b/ui/opensnitch/proto/ui_pb2.py
@@ -2,9 +2,11 @@
 # Generated by the protocol buffer compiler.  DO NOT EDIT!
 # source: ui.proto
 """Generated protocol buffer code."""
-from google.protobuf.internal import builder as _builder
+from google.protobuf.internal import enum_type_wrapper
 from google.protobuf import descriptor as _descriptor
 from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import message as _message
+from google.protobuf import reflection as _reflection
 from google.protobuf import symbol_database as _symbol_database
 # @@protoc_insertion_point(imports)
 
@@ -13,10 +15,294 @@ _sym_db = _symbol_database.Default()
 
 
 
-DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x08ui.proto\x12\x08protocol\"\xcb\x04\n\x05\x41lert\x12\n\n\x02id\x18\x01 \x01(\x04\x12\"\n\x04type\x18\x02 \x01(\x0e\x32\x14.protocol.Alert.Type\x12&\n\x06\x61\x63tion\x18\x03 \x01(\x0e\x32\x16.protocol.Alert.Action\x12*\n\x08priority\x18\x04 \x01(\x0e\x32\x18.protocol.Alert.Priority\x12\"\n\x04what\x18\x05 \x01(\x0e\x32\x14.protocol.Alert.What\x12\x0e\n\x04text\x18\x06 \x01(\tH\x00\x12!\n\x04proc\x18\x08 \x01(\x0b\x32\x11.protocol.ProcessH\x00\x12$\n\x04\x63onn\x18\t \x01(\x0b\x32\x14.protocol.ConnectionH\x00\x12\x1e\n\x04rule\x18\n \x01(\x0b\x32\x0e.protocol.RuleH\x00\x12\"\n\x06\x66wrule\x18\x0b \x01(\x0b\x32\x10.protocol.FwRuleH\x00\")\n\x08Priority\x12\x07\n\x03LOW\x10\x00\x12\n\n\x06MEDIUM\x10\x01\x12\x08\n\x04HIGH\x10\x02\"(\n\x04Type\x12\t\n\x05\x45RROR\x10\x00\x12\x0b\n\x07WARNING\x10\x01\x12\x08\n\x04INFO\x10\x02\"2\n\x06\x41\x63tion\x12\x08\n\x04NONE\x10\x00\x12\x0e\n\nSHOW_ALERT\x10\x01\x12\x0e\n\nSAVE_TO_DB\x10\x02\"l\n\x04What\x12\x0b\n\x07GENERIC\x10\x00\x12\x10\n\x0cPROC_MONITOR\x10\x01\x12\x0c\n\x08\x46IREWALL\x10\x02\x12\x0e\n\nCONNECTION\x10\x03\x12\x08\n\x04RULE\x10\x04\x12\x0b\n\x07NETLINK\x10\x05\x12\x10\n\x0cKERNEL_EVENT\x10\x06\x42\x06\n\x04\x64\x61ta\"\x19\n\x0bMsgResponse\x12\n\n\x02id\x18\x01 \x01(\x04\"o\n\x05\x45vent\x12\x0c\n\x04time\x18\x01 \x01(\t\x12(\n\nconnection\x18\x02 \x01(\x0b\x32\x14.protocol.Connection\x12\x1c\n\x04rule\x18\x03 \x01(\x0b\x32\x0e.protocol.Rule\x12\x10\n\x08unixnano\x18\x04 \x01(\x03\"\xd3\x06\n\nStatistics\x12\x16\n\x0e\x64\x61\x65mon_version\x18\x01 \x01(\t\x12\r\n\x05rules\x18\x02 \x01(\x04\x12\x0e\n\x06uptime\x18\x03 \x01(\x04\x12\x15\n\rdns_responses\x18\x04 \x01(\x04\x12\x13\n\x0b\x63onnections\x18\x05 \x01(\x04\x12\x0f\n\x07ignored\x18\x06 \x01(\x04\x12\x10\n\x08\x61\x63\x63\x65pted\x18\x07 \x01(\x04\x12\x0f\n\x07\x64ropped\x18\x08 \x01(\x04\x12\x11\n\trule_hits\x18\t \x01(\x04\x12\x13\n\x0brule_misses\x18\n \x01(\x04\x12\x33\n\x08\x62y_proto\x18\x0b \x03(\x0b\x32!.protocol.Statistics.ByProtoEntry\x12\x37\n\nby_address\x18\x0c \x03(\x0b\x32#.protocol.Statistics.ByAddressEntry\x12\x31\n\x07\x62y_host\x18\r \x03(\x0b\x32 .protocol.Statistics.ByHostEntry\x12\x31\n\x07\x62y_port\x18\x0e \x03(\x0b\x32 .protocol.Statistics.ByPortEntry\x12/\n\x06\x62y_uid\x18\x0f \x03(\x0b\x32\x1f.protocol.Statistics.ByUidEntry\x12=\n\rby_executable\x18\x10 \x03(\x0b\x32&.protocol.Statistics.ByExecutableEntry\x12\x1f\n\x06\x65vents\x18\x11 \x03(\x0b\x32\x0f.protocol.Event\x1a.\n\x0c\x42yProtoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x30\n\x0e\x42yAddressEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yHostEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yPortEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a,\n\nByUidEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x33\n\x11\x42yExecutableEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\">\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x04\x12#\n\x05stats\x18\x02 \x01(\x0b\x32\x14.protocol.Statistics\"\x17\n\tPingReply\x12\n\n\x02id\x18\x01 \x01(\x04\"\'\n\tStringInt\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\r\"\x9b\x03\n\x07Process\x12\x0b\n\x03pid\x18\x01 \x01(\x04\x12\x0c\n\x04ppid\x18\x02 \x01(\x04\x12\x0b\n\x03uid\x18\x03 \x01(\x04\x12\x0c\n\x04\x63omm\x18\x04 \x01(\t\x12\x0c\n\x04path\x18\x05 \x01(\t\x12\x0c\n\x04\x61rgs\x18\x06 \x03(\t\x12\'\n\x03\x65nv\x18\x07 \x03(\x0b\x32\x1a.protocol.Process.EnvEntry\x12\x0b\n\x03\x63wd\x18\x08 \x01(\t\x12\x33\n\tchecksums\x18\t \x03(\x0b\x32 .protocol.Process.ChecksumsEntry\x12\x10\n\x08io_reads\x18\n \x01(\x04\x12\x11\n\tio_writes\x18\x0b \x01(\x04\x12\x11\n\tnet_reads\x18\x0c \x01(\x04\x12\x12\n\nnet_writes\x18\r \x01(\x04\x12)\n\x0cprocess_tree\x18\x0e \x03(\x0b\x32\x13.protocol.StringInt\x1a*\n\x08\x45nvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x30\n\x0e\x43hecksumsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xf3\x03\n\nConnection\x12\x10\n\x08protocol\x18\x01 \x01(\t\x12\x0e\n\x06src_ip\x18\x02 \x01(\t\x12\x10\n\x08src_port\x18\x03 \x01(\r\x12\x0e\n\x06\x64st_ip\x18\x04 \x01(\t\x12\x10\n\x08\x64st_host\x18\x05 \x01(\t\x12\x10\n\x08\x64st_port\x18\x06 \x01(\r\x12\x0f\n\x07user_id\x18\x07 \x01(\r\x12\x12\n\nprocess_id\x18\x08 \x01(\r\x12\x14\n\x0cprocess_path\x18\t \x01(\t\x12\x13\n\x0bprocess_cwd\x18\n \x01(\t\x12\x14\n\x0cprocess_args\x18\x0b \x03(\t\x12\x39\n\x0bprocess_env\x18\x0c \x03(\x0b\x32$.protocol.Connection.ProcessEnvEntry\x12\x45\n\x11process_checksums\x18\r \x03(\x0b\x32*.protocol.Connection.ProcessChecksumsEntry\x12)\n\x0cprocess_tree\x18\x0e \x03(\x0b\x32\x13.protocol.StringInt\x1a\x31\n\x0fProcessEnvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x37\n\x15ProcessChecksumsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"l\n\x08Operator\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0f\n\x07operand\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t\x12\x11\n\tsensitive\x18\x04 \x01(\x08\x12 \n\x04list\x18\x05 \x03(\x0b\x32\x12.protocol.Operator\"\xb6\x01\n\x04Rule\x12\x0f\n\x07\x63reated\x18\x01 \x01(\x03\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x04 \x01(\x08\x12\x12\n\nprecedence\x18\x05 \x01(\x08\x12\r\n\x05nolog\x18\x06 \x01(\x08\x12\x0e\n\x06\x61\x63tion\x18\x07 \x01(\t\x12\x10\n\x08\x64uration\x18\x08 \x01(\t\x12$\n\x08operator\x18\t \x01(\x0b\x32\x12.protocol.Operator\"-\n\x0fStatementValues\x12\x0b\n\x03Key\x18\x01 \x01(\t\x12\r\n\x05Value\x18\x02 \x01(\t\"P\n\tStatement\x12\n\n\x02Op\x18\x01 \x01(\t\x12\x0c\n\x04Name\x18\x02 \x01(\t\x12)\n\x06Values\x18\x03 \x03(\x0b\x32\x19.protocol.StatementValues\"5\n\x0b\x45xpressions\x12&\n\tStatement\x18\x01 \x01(\x0b\x32\x13.protocol.Statement\"\xd6\x01\n\x06\x46wRule\x12\r\n\x05Table\x18\x01 \x01(\t\x12\r\n\x05\x43hain\x18\x02 \x01(\t\x12\x0c\n\x04UUID\x18\x03 \x01(\t\x12\x0f\n\x07\x45nabled\x18\x04 \x01(\x08\x12\x10\n\x08Position\x18\x05 \x01(\x04\x12\x13\n\x0b\x44\x65scription\x18\x06 \x01(\t\x12\x12\n\nParameters\x18\x07 \x01(\t\x12*\n\x0b\x45xpressions\x18\x08 \x03(\x0b\x32\x15.protocol.Expressions\x12\x0e\n\x06Target\x18\t \x01(\t\x12\x18\n\x10TargetParameters\x18\n \x01(\t\"\x95\x01\n\x07\x46wChain\x12\x0c\n\x04Name\x18\x01 \x01(\t\x12\r\n\x05Table\x18\x02 \x01(\t\x12\x0e\n\x06\x46\x61mily\x18\x03 \x01(\t\x12\x10\n\x08Priority\x18\x04 \x01(\t\x12\x0c\n\x04Type\x18\x05 \x01(\t\x12\x0c\n\x04Hook\x18\x06 \x01(\t\x12\x0e\n\x06Policy\x18\x07 \x01(\t\x12\x1f\n\x05Rules\x18\x08 \x03(\x0b\x32\x10.protocol.FwRule\"M\n\x08\x46wChains\x12\x1e\n\x04Rule\x18\x01 \x01(\x0b\x32\x10.protocol.FwRule\x12!\n\x06\x43hains\x18\x02 \x03(\x0b\x32\x11.protocol.FwChain\"X\n\x0bSysFirewall\x12\x0f\n\x07\x45nabled\x18\x01 \x01(\x08\x12\x0f\n\x07Version\x18\x02 \x01(\r\x12\'\n\x0bSystemRules\x18\x03 \x03(\x0b\x32\x12.protocol.FwChains\"\xc4\x01\n\x0c\x43lientConfig\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x19\n\x11isFirewallRunning\x18\x04 \x01(\x08\x12\x0e\n\x06\x63onfig\x18\x05 \x01(\t\x12\x10\n\x08logLevel\x18\x06 \x01(\r\x12\x1d\n\x05rules\x18\x07 \x03(\x0b\x32\x0e.protocol.Rule\x12-\n\x0esystemFirewall\x18\x08 \x01(\x0b\x32\x15.protocol.SysFirewall\"\xbb\x01\n\x0cNotification\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x12\n\nclientName\x18\x02 \x01(\t\x12\x12\n\nserverName\x18\x03 \x01(\t\x12\x1e\n\x04type\x18\x04 \x01(\x0e\x32\x10.protocol.Action\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\t\x12\x1d\n\x05rules\x18\x06 \x03(\x0b\x32\x0e.protocol.Rule\x12*\n\x0bsysFirewall\x18\x07 \x01(\x0b\x32\x15.protocol.SysFirewall\"\\\n\x11NotificationReply\x12\n\n\x02id\x18\x01 \x01(\x04\x12-\n\x04\x63ode\x18\x02 \x01(\x0e\x32\x1f.protocol.NotificationReplyCode\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t*\x95\x02\n\x06\x41\x63tion\x12\x08\n\x04NONE\x10\x00\x12\x17\n\x13\x45NABLE_INTERCEPTION\x10\x01\x12\x18\n\x14\x44ISABLE_INTERCEPTION\x10\x02\x12\x13\n\x0f\x45NABLE_FIREWALL\x10\x03\x12\x14\n\x10\x44ISABLE_FIREWALL\x10\x04\x12\x13\n\x0fRELOAD_FW_RULES\x10\x05\x12\x11\n\rCHANGE_CONFIG\x10\x06\x12\x0f\n\x0b\x45NABLE_RULE\x10\x07\x12\x10\n\x0c\x44ISABLE_RULE\x10\x08\x12\x0f\n\x0b\x44\x45LETE_RULE\x10\t\x12\x0f\n\x0b\x43HANGE_RULE\x10\n\x12\r\n\tLOG_LEVEL\x10\x0b\x12\x08\n\x04STOP\x10\x0c\x12\x0e\n\nTASK_START\x10\r\x12\r\n\tTASK_STOP\x10\x0e**\n\x15NotificationReplyCode\x12\x06\n\x02OK\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x32\xaf\x02\n\x02UI\x12\x34\n\x04Ping\x12\x15.protocol.PingRequest\x1a\x13.protocol.PingReply\"\x00\x12\x31\n\x07\x41skRule\x12\x14.protocol.Connection\x1a\x0e.protocol.Rule\"\x00\x12=\n\tSubscribe\x12\x16.protocol.ClientConfig\x1a\x16.protocol.ClientConfig\"\x00\x12J\n\rNotifications\x12\x1b.protocol.NotificationReply\x1a\x16.protocol.Notification\"\x00(\x01\x30\x01\x12\x35\n\tPostAlert\x12\x0f.protocol.Alert\x1a\x15.protocol.MsgResponse\"\x00\x42\x35Z3github.com/evilsocket/opensnitch/daemon/ui/protocolb\x06proto3')
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x08ui.proto\x12\x08protocol\"\xcb\x04\n\x05\x41lert\x12\n\n\x02id\x18\x01 \x01(\x04\x12\"\n\x04type\x18\x02 \x01(\x0e\x32\x14.protocol.Alert.Type\x12&\n\x06\x61\x63tion\x18\x03 \x01(\x0e\x32\x16.protocol.Alert.Action\x12*\n\x08priority\x18\x04 \x01(\x0e\x32\x18.protocol.Alert.Priority\x12\"\n\x04what\x18\x05 \x01(\x0e\x32\x14.protocol.Alert.What\x12\x0e\n\x04text\x18\x06 \x01(\tH\x00\x12!\n\x04proc\x18\x08 \x01(\x0b\x32\x11.protocol.ProcessH\x00\x12$\n\x04\x63onn\x18\t \x01(\x0b\x32\x14.protocol.ConnectionH\x00\x12\x1e\n\x04rule\x18\n \x01(\x0b\x32\x0e.protocol.RuleH\x00\x12\"\n\x06\x66wrule\x18\x0b \x01(\x0b\x32\x10.protocol.FwRuleH\x00\")\n\x08Priority\x12\x07\n\x03LOW\x10\x00\x12\n\n\x06MEDIUM\x10\x01\x12\x08\n\x04HIGH\x10\x02\"(\n\x04Type\x12\t\n\x05\x45RROR\x10\x00\x12\x0b\n\x07WARNING\x10\x01\x12\x08\n\x04INFO\x10\x02\"2\n\x06\x41\x63tion\x12\x08\n\x04NONE\x10\x00\x12\x0e\n\nSHOW_ALERT\x10\x01\x12\x0e\n\nSAVE_TO_DB\x10\x02\"l\n\x04What\x12\x0b\n\x07GENERIC\x10\x00\x12\x10\n\x0cPROC_MONITOR\x10\x01\x12\x0c\n\x08\x46IREWALL\x10\x02\x12\x0e\n\nCONNECTION\x10\x03\x12\x08\n\x04RULE\x10\x04\x12\x0b\n\x07NETLINK\x10\x05\x12\x10\n\x0cKERNEL_EVENT\x10\x06\x42\x06\n\x04\x64\x61ta\"\x19\n\x0bMsgResponse\x12\n\n\x02id\x18\x01 \x01(\x04\"o\n\x05\x45vent\x12\x0c\n\x04time\x18\x01 \x01(\t\x12(\n\nconnection\x18\x02 \x01(\x0b\x32\x14.protocol.Connection\x12\x1c\n\x04rule\x18\x03 \x01(\x0b\x32\x0e.protocol.Rule\x12\x10\n\x08unixnano\x18\x04 \x01(\x03\"\xd3\x06\n\nStatistics\x12\x16\n\x0e\x64\x61\x65mon_version\x18\x01 \x01(\t\x12\r\n\x05rules\x18\x02 \x01(\x04\x12\x0e\n\x06uptime\x18\x03 \x01(\x04\x12\x15\n\rdns_responses\x18\x04 \x01(\x04\x12\x13\n\x0b\x63onnections\x18\x05 \x01(\x04\x12\x0f\n\x07ignored\x18\x06 \x01(\x04\x12\x10\n\x08\x61\x63\x63\x65pted\x18\x07 \x01(\x04\x12\x0f\n\x07\x64ropped\x18\x08 \x01(\x04\x12\x11\n\trule_hits\x18\t \x01(\x04\x12\x13\n\x0brule_misses\x18\n \x01(\x04\x12\x33\n\x08\x62y_proto\x18\x0b \x03(\x0b\x32!.protocol.Statistics.ByProtoEntry\x12\x37\n\nby_address\x18\x0c \x03(\x0b\x32#.protocol.Statistics.ByAddressEntry\x12\x31\n\x07\x62y_host\x18\r \x03(\x0b\x32 .protocol.Statistics.ByHostEntry\x12\x31\n\x07\x62y_port\x18\x0e \x03(\x0b\x32 .protocol.Statistics.ByPortEntry\x12/\n\x06\x62y_uid\x18\x0f \x03(\x0b\x32\x1f.protocol.Statistics.ByUidEntry\x12=\n\rby_executable\x18\x10 \x03(\x0b\x32&.protocol.Statistics.ByExecutableEntry\x12\x1f\n\x06\x65vents\x18\x11 \x03(\x0b\x32\x0f.protocol.Event\x1a.\n\x0c\x42yProtoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x30\n\x0e\x42yAddressEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yHostEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a-\n\x0b\x42yPortEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a,\n\nByUidEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\x1a\x33\n\x11\x42yExecutableEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x04:\x02\x38\x01\">\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x04\x12#\n\x05stats\x18\x02 \x01(\x0b\x32\x14.protocol.Statistics\"\x17\n\tPingReply\x12\n\n\x02id\x18\x01 \x01(\x04\"\'\n\tStringInt\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\r\"\x9b\x03\n\x07Process\x12\x0b\n\x03pid\x18\x01 \x01(\x04\x12\x0c\n\x04ppid\x18\x02 \x01(\x04\x12\x0b\n\x03uid\x18\x03 \x01(\x04\x12\x0c\n\x04\x63omm\x18\x04 \x01(\t\x12\x0c\n\x04path\x18\x05 \x01(\t\x12\x0c\n\x04\x61rgs\x18\x06 \x03(\t\x12\'\n\x03\x65nv\x18\x07 \x03(\x0b\x32\x1a.protocol.Process.EnvEntry\x12\x0b\n\x03\x63wd\x18\x08 \x01(\t\x12\x33\n\tchecksums\x18\t \x03(\x0b\x32 .protocol.Process.ChecksumsEntry\x12\x10\n\x08io_reads\x18\n \x01(\x04\x12\x11\n\tio_writes\x18\x0b \x01(\x04\x12\x11\n\tnet_reads\x18\x0c \x01(\x04\x12\x12\n\nnet_writes\x18\r \x01(\x04\x12)\n\x0cprocess_tree\x18\x0e \x03(\x0b\x32\x13.protocol.StringInt\x1a*\n\x08\x45nvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x30\n\x0e\x43hecksumsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xf3\x03\n\nConnection\x12\x10\n\x08protocol\x18\x01 \x01(\t\x12\x0e\n\x06src_ip\x18\x02 \x01(\t\x12\x10\n\x08src_port\x18\x03 \x01(\r\x12\x0e\n\x06\x64st_ip\x18\x04 \x01(\t\x12\x10\n\x08\x64st_host\x18\x05 \x01(\t\x12\x10\n\x08\x64st_port\x18\x06 \x01(\r\x12\x0f\n\x07user_id\x18\x07 \x01(\r\x12\x12\n\nprocess_id\x18\x08 \x01(\r\x12\x14\n\x0cprocess_path\x18\t \x01(\t\x12\x13\n\x0bprocess_cwd\x18\n \x01(\t\x12\x14\n\x0cprocess_args\x18\x0b \x03(\t\x12\x39\n\x0bprocess_env\x18\x0c \x03(\x0b\x32$.protocol.Connection.ProcessEnvEntry\x12\x45\n\x11process_checksums\x18\r \x03(\x0b\x32*.protocol.Connection.ProcessChecksumsEntry\x12)\n\x0cprocess_tree\x18\x0e \x03(\x0b\x32\x13.protocol.StringInt\x1a\x31\n\x0fProcessEnvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x37\n\x15ProcessChecksumsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"l\n\x08Operator\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0f\n\x07operand\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t\x12\x11\n\tsensitive\x18\x04 \x01(\x08\x12 \n\x04list\x18\x05 \x03(\x0b\x32\x12.protocol.Operator\"\xb6\x01\n\x04Rule\x12\x0f\n\x07\x63reated\x18\x01 \x01(\x03\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x04 \x01(\x08\x12\x12\n\nprecedence\x18\x05 \x01(\x08\x12\r\n\x05nolog\x18\x06 \x01(\x08\x12\x0e\n\x06\x61\x63tion\x18\x07 \x01(\t\x12\x10\n\x08\x64uration\x18\x08 \x01(\t\x12$\n\x08operator\x18\t \x01(\x0b\x32\x12.protocol.Operator\"-\n\x0fStatementValues\x12\x0b\n\x03Key\x18\x01 \x01(\t\x12\r\n\x05Value\x18\x02 \x01(\t\"P\n\tStatement\x12\n\n\x02Op\x18\x01 \x01(\t\x12\x0c\n\x04Name\x18\x02 \x01(\t\x12)\n\x06Values\x18\x03 \x03(\x0b\x32\x19.protocol.StatementValues\"5\n\x0b\x45xpressions\x12&\n\tStatement\x18\x01 \x01(\x0b\x32\x13.protocol.Statement\"\xd6\x01\n\x06\x46wRule\x12\r\n\x05Table\x18\x01 \x01(\t\x12\r\n\x05\x43hain\x18\x02 \x01(\t\x12\x0c\n\x04UUID\x18\x03 \x01(\t\x12\x0f\n\x07\x45nabled\x18\x04 \x01(\x08\x12\x10\n\x08Position\x18\x05 \x01(\x04\x12\x13\n\x0b\x44\x65scription\x18\x06 \x01(\t\x12\x12\n\nParameters\x18\x07 \x01(\t\x12*\n\x0b\x45xpressions\x18\x08 \x03(\x0b\x32\x15.protocol.Expressions\x12\x0e\n\x06Target\x18\t \x01(\t\x12\x18\n\x10TargetParameters\x18\n \x01(\t\"\x95\x01\n\x07\x46wChain\x12\x0c\n\x04Name\x18\x01 \x01(\t\x12\r\n\x05Table\x18\x02 \x01(\t\x12\x0e\n\x06\x46\x61mily\x18\x03 \x01(\t\x12\x10\n\x08Priority\x18\x04 \x01(\t\x12\x0c\n\x04Type\x18\x05 \x01(\t\x12\x0c\n\x04Hook\x18\x06 \x01(\t\x12\x0e\n\x06Policy\x18\x07 \x01(\t\x12\x1f\n\x05Rules\x18\x08 \x03(\x0b\x32\x10.protocol.FwRule\"M\n\x08\x46wChains\x12\x1e\n\x04Rule\x18\x01 \x01(\x0b\x32\x10.protocol.FwRule\x12!\n\x06\x43hains\x18\x02 \x03(\x0b\x32\x11.protocol.FwChain\"X\n\x0bSysFirewall\x12\x0f\n\x07\x45nabled\x18\x01 \x01(\x08\x12\x0f\n\x07Version\x18\x02 \x01(\r\x12\'\n\x0bSystemRules\x18\x03 \x03(\x0b\x32\x12.protocol.FwChains\"\xd5\x01\n\x0c\x43lientConfig\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x19\n\x11isFirewallRunning\x18\x04 \x01(\x08\x12\x0e\n\x06\x63onfig\x18\x05 \x01(\t\x12\x10\n\x08logLevel\x18\x06 \x01(\r\x12\x1d\n\x05rules\x18\x07 \x03(\x0b\x32\x0e.protocol.Rule\x12-\n\x0esystemFirewall\x18\x08 \x01(\x0b\x32\x15.protocol.SysFirewall\x12\x0f\n\x07node_id\x18\t \x01(\t\"\xbb\x01\n\x0cNotification\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x12\n\nclientName\x18\x02 \x01(\t\x12\x12\n\nserverName\x18\x03 \x01(\t\x12\x1e\n\x04type\x18\x04 \x01(\x0e\x32\x10.protocol.Action\x12\x0c\n\x04\x64\x61ta\x18\x05 \x01(\t\x12\x1d\n\x05rules\x18\x06 \x03(\x0b\x32\x0e.protocol.Rule\x12*\n\x0bsysFirewall\x18\x07 \x01(\x0b\x32\x15.protocol.SysFirewall\"\\\n\x11NotificationReply\x12\n\n\x02id\x18\x01 \x01(\x04\x12-\n\x04\x63ode\x18\x02 \x01(\x0e\x32\x1f.protocol.NotificationReplyCode\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t*\x95\x02\n\x06\x41\x63tion\x12\x08\n\x04NONE\x10\x00\x12\x17\n\x13\x45NABLE_INTERCEPTION\x10\x01\x12\x18\n\x14\x44ISABLE_INTERCEPTION\x10\x02\x12\x13\n\x0f\x45NABLE_FIREWALL\x10\x03\x12\x14\n\x10\x44ISABLE_FIREWALL\x10\x04\x12\x13\n\x0fRELOAD_FW_RULES\x10\x05\x12\x11\n\rCHANGE_CONFIG\x10\x06\x12\x0f\n\x0b\x45NABLE_RULE\x10\x07\x12\x10\n\x0c\x44ISABLE_RULE\x10\x08\x12\x0f\n\x0b\x44\x45LETE_RULE\x10\t\x12\x0f\n\x0b\x43HANGE_RULE\x10\n\x12\r\n\tLOG_LEVEL\x10\x0b\x12\x08\n\x04STOP\x10\x0c\x12\x0e\n\nTASK_START\x10\r\x12\r\n\tTASK_STOP\x10\x0e**\n\x15NotificationReplyCode\x12\x06\n\x02OK\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x32\xaf\x02\n\x02UI\x12\x34\n\x04Ping\x12\x15.protocol.PingRequest\x1a\x13.protocol.PingReply\"\x00\x12\x31\n\x07\x41skRule\x12\x14.protocol.Connection\x1a\x0e.protocol.Rule\"\x00\x12=\n\tSubscribe\x12\x16.protocol.ClientConfig\x1a\x16.protocol.ClientConfig\"\x00\x12J\n\rNotifications\x12\x1b.protocol.NotificationReply\x1a\x16.protocol.Notification\"\x00(\x01\x30\x01\x12\x35\n\tPostAlert\x12\x0f.protocol.Alert\x1a\x15.protocol.MsgResponse\"\x00\x42\x35Z3github.com/evilsocket/opensnitch/daemon/ui/protocolb\x06proto3')
 
-_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
-_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ui_pb2', globals())
+_ACTION = DESCRIPTOR.enum_types_by_name['Action']
+Action = enum_type_wrapper.EnumTypeWrapper(_ACTION)
+_NOTIFICATIONREPLYCODE = DESCRIPTOR.enum_types_by_name['NotificationReplyCode']
+NotificationReplyCode = enum_type_wrapper.EnumTypeWrapper(_NOTIFICATIONREPLYCODE)
+NONE = 0
+ENABLE_INTERCEPTION = 1
+DISABLE_INTERCEPTION = 2
+ENABLE_FIREWALL = 3
+DISABLE_FIREWALL = 4
+RELOAD_FW_RULES = 5
+CHANGE_CONFIG = 6
+ENABLE_RULE = 7
+DISABLE_RULE = 8
+DELETE_RULE = 9
+CHANGE_RULE = 10
+LOG_LEVEL = 11
+STOP = 12
+TASK_START = 13
+TASK_STOP = 14
+OK = 0
+ERROR = 1
+
+
+_ALERT = DESCRIPTOR.message_types_by_name['Alert']
+_MSGRESPONSE = DESCRIPTOR.message_types_by_name['MsgResponse']
+_EVENT = DESCRIPTOR.message_types_by_name['Event']
+_STATISTICS = DESCRIPTOR.message_types_by_name['Statistics']
+_STATISTICS_BYPROTOENTRY = _STATISTICS.nested_types_by_name['ByProtoEntry']
+_STATISTICS_BYADDRESSENTRY = _STATISTICS.nested_types_by_name['ByAddressEntry']
+_STATISTICS_BYHOSTENTRY = _STATISTICS.nested_types_by_name['ByHostEntry']
+_STATISTICS_BYPORTENTRY = _STATISTICS.nested_types_by_name['ByPortEntry']
+_STATISTICS_BYUIDENTRY = _STATISTICS.nested_types_by_name['ByUidEntry']
+_STATISTICS_BYEXECUTABLEENTRY = _STATISTICS.nested_types_by_name['ByExecutableEntry']
+_PINGREQUEST = DESCRIPTOR.message_types_by_name['PingRequest']
+_PINGREPLY = DESCRIPTOR.message_types_by_name['PingReply']
+_STRINGINT = DESCRIPTOR.message_types_by_name['StringInt']
+_PROCESS = DESCRIPTOR.message_types_by_name['Process']
+_PROCESS_ENVENTRY = _PROCESS.nested_types_by_name['EnvEntry']
+_PROCESS_CHECKSUMSENTRY = _PROCESS.nested_types_by_name['ChecksumsEntry']
+_CONNECTION = DESCRIPTOR.message_types_by_name['Connection']
+_CONNECTION_PROCESSENVENTRY = _CONNECTION.nested_types_by_name['ProcessEnvEntry']
+_CONNECTION_PROCESSCHECKSUMSENTRY = _CONNECTION.nested_types_by_name['ProcessChecksumsEntry']
+_OPERATOR = DESCRIPTOR.message_types_by_name['Operator']
+_RULE = DESCRIPTOR.message_types_by_name['Rule']
+_STATEMENTVALUES = DESCRIPTOR.message_types_by_name['StatementValues']
+_STATEMENT = DESCRIPTOR.message_types_by_name['Statement']
+_EXPRESSIONS = DESCRIPTOR.message_types_by_name['Expressions']
+_FWRULE = DESCRIPTOR.message_types_by_name['FwRule']
+_FWCHAIN = DESCRIPTOR.message_types_by_name['FwChain']
+_FWCHAINS = DESCRIPTOR.message_types_by_name['FwChains']
+_SYSFIREWALL = DESCRIPTOR.message_types_by_name['SysFirewall']
+_CLIENTCONFIG = DESCRIPTOR.message_types_by_name['ClientConfig']
+_NOTIFICATION = DESCRIPTOR.message_types_by_name['Notification']
+_NOTIFICATIONREPLY = DESCRIPTOR.message_types_by_name['NotificationReply']
+_ALERT_PRIORITY = _ALERT.enum_types_by_name['Priority']
+_ALERT_TYPE = _ALERT.enum_types_by_name['Type']
+_ALERT_ACTION = _ALERT.enum_types_by_name['Action']
+_ALERT_WHAT = _ALERT.enum_types_by_name['What']
+Alert = _reflection.GeneratedProtocolMessageType('Alert', (_message.Message,), {
+  'DESCRIPTOR' : _ALERT,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.Alert)
+  })
+_sym_db.RegisterMessage(Alert)
+
+MsgResponse = _reflection.GeneratedProtocolMessageType('MsgResponse', (_message.Message,), {
+  'DESCRIPTOR' : _MSGRESPONSE,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.MsgResponse)
+  })
+_sym_db.RegisterMessage(MsgResponse)
+
+Event = _reflection.GeneratedProtocolMessageType('Event', (_message.Message,), {
+  'DESCRIPTOR' : _EVENT,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.Event)
+  })
+_sym_db.RegisterMessage(Event)
+
+Statistics = _reflection.GeneratedProtocolMessageType('Statistics', (_message.Message,), {
+
+  'ByProtoEntry' : _reflection.GeneratedProtocolMessageType('ByProtoEntry', (_message.Message,), {
+    'DESCRIPTOR' : _STATISTICS_BYPROTOENTRY,
+    '__module__' : 'ui_pb2'
+    # @@protoc_insertion_point(class_scope:protocol.Statistics.ByProtoEntry)
+    })
+  ,
+
+  'ByAddressEntry' : _reflection.GeneratedProtocolMessageType('ByAddressEntry', (_message.Message,), {
+    'DESCRIPTOR' : _STATISTICS_BYADDRESSENTRY,
+    '__module__' : 'ui_pb2'
+    # @@protoc_insertion_point(class_scope:protocol.Statistics.ByAddressEntry)
+    })
+  ,
+
+  'ByHostEntry' : _reflection.GeneratedProtocolMessageType('ByHostEntry', (_message.Message,), {
+    'DESCRIPTOR' : _STATISTICS_BYHOSTENTRY,
+    '__module__' : 'ui_pb2'
+    # @@protoc_insertion_point(class_scope:protocol.Statistics.ByHostEntry)
+    })
+  ,
+
+  'ByPortEntry' : _reflection.GeneratedProtocolMessageType('ByPortEntry', (_message.Message,), {
+    'DESCRIPTOR' : _STATISTICS_BYPORTENTRY,
+    '__module__' : 'ui_pb2'
+    # @@protoc_insertion_point(class_scope:protocol.Statistics.ByPortEntry)
+    })
+  ,
+
+  'ByUidEntry' : _reflection.GeneratedProtocolMessageType('ByUidEntry', (_message.Message,), {
+    'DESCRIPTOR' : _STATISTICS_BYUIDENTRY,
+    '__module__' : 'ui_pb2'
+    # @@protoc_insertion_point(class_scope:protocol.Statistics.ByUidEntry)
+    })
+  ,
+
+  'ByExecutableEntry' : _reflection.GeneratedProtocolMessageType('ByExecutableEntry', (_message.Message,), {
+    'DESCRIPTOR' : _STATISTICS_BYEXECUTABLEENTRY,
+    '__module__' : 'ui_pb2'
+    # @@protoc_insertion_point(class_scope:protocol.Statistics.ByExecutableEntry)
+    })
+  ,
+  'DESCRIPTOR' : _STATISTICS,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.Statistics)
+  })
+_sym_db.RegisterMessage(Statistics)
+_sym_db.RegisterMessage(Statistics.ByProtoEntry)
+_sym_db.RegisterMessage(Statistics.ByAddressEntry)
+_sym_db.RegisterMessage(Statistics.ByHostEntry)
+_sym_db.RegisterMessage(Statistics.ByPortEntry)
+_sym_db.RegisterMessage(Statistics.ByUidEntry)
+_sym_db.RegisterMessage(Statistics.ByExecutableEntry)
+
+PingRequest = _reflection.GeneratedProtocolMessageType('PingRequest', (_message.Message,), {
+  'DESCRIPTOR' : _PINGREQUEST,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.PingRequest)
+  })
+_sym_db.RegisterMessage(PingRequest)
+
+PingReply = _reflection.GeneratedProtocolMessageType('PingReply', (_message.Message,), {
+  'DESCRIPTOR' : _PINGREPLY,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.PingReply)
+  })
+_sym_db.RegisterMessage(PingReply)
+
+StringInt = _reflection.GeneratedProtocolMessageType('StringInt', (_message.Message,), {
+  'DESCRIPTOR' : _STRINGINT,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.StringInt)
+  })
+_sym_db.RegisterMessage(StringInt)
+
+Process = _reflection.GeneratedProtocolMessageType('Process', (_message.Message,), {
+
+  'EnvEntry' : _reflection.GeneratedProtocolMessageType('EnvEntry', (_message.Message,), {
+    'DESCRIPTOR' : _PROCESS_ENVENTRY,
+    '__module__' : 'ui_pb2'
+    # @@protoc_insertion_point(class_scope:protocol.Process.EnvEntry)
+    })
+  ,
+
+  'ChecksumsEntry' : _reflection.GeneratedProtocolMessageType('ChecksumsEntry', (_message.Message,), {
+    'DESCRIPTOR' : _PROCESS_CHECKSUMSENTRY,
+    '__module__' : 'ui_pb2'
+    # @@protoc_insertion_point(class_scope:protocol.Process.ChecksumsEntry)
+    })
+  ,
+  'DESCRIPTOR' : _PROCESS,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.Process)
+  })
+_sym_db.RegisterMessage(Process)
+_sym_db.RegisterMessage(Process.EnvEntry)
+_sym_db.RegisterMessage(Process.ChecksumsEntry)
+
+Connection = _reflection.GeneratedProtocolMessageType('Connection', (_message.Message,), {
+
+  'ProcessEnvEntry' : _reflection.GeneratedProtocolMessageType('ProcessEnvEntry', (_message.Message,), {
+    'DESCRIPTOR' : _CONNECTION_PROCESSENVENTRY,
+    '__module__' : 'ui_pb2'
+    # @@protoc_insertion_point(class_scope:protocol.Connection.ProcessEnvEntry)
+    })
+  ,
+
+  'ProcessChecksumsEntry' : _reflection.GeneratedProtocolMessageType('ProcessChecksumsEntry', (_message.Message,), {
+    'DESCRIPTOR' : _CONNECTION_PROCESSCHECKSUMSENTRY,
+    '__module__' : 'ui_pb2'
+    # @@protoc_insertion_point(class_scope:protocol.Connection.ProcessChecksumsEntry)
+    })
+  ,
+  'DESCRIPTOR' : _CONNECTION,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.Connection)
+  })
+_sym_db.RegisterMessage(Connection)
+_sym_db.RegisterMessage(Connection.ProcessEnvEntry)
+_sym_db.RegisterMessage(Connection.ProcessChecksumsEntry)
+
+Operator = _reflection.GeneratedProtocolMessageType('Operator', (_message.Message,), {
+  'DESCRIPTOR' : _OPERATOR,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.Operator)
+  })
+_sym_db.RegisterMessage(Operator)
+
+Rule = _reflection.GeneratedProtocolMessageType('Rule', (_message.Message,), {
+  'DESCRIPTOR' : _RULE,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.Rule)
+  })
+_sym_db.RegisterMessage(Rule)
+
+StatementValues = _reflection.GeneratedProtocolMessageType('StatementValues', (_message.Message,), {
+  'DESCRIPTOR' : _STATEMENTVALUES,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.StatementValues)
+  })
+_sym_db.RegisterMessage(StatementValues)
+
+Statement = _reflection.GeneratedProtocolMessageType('Statement', (_message.Message,), {
+  'DESCRIPTOR' : _STATEMENT,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.Statement)
+  })
+_sym_db.RegisterMessage(Statement)
+
+Expressions = _reflection.GeneratedProtocolMessageType('Expressions', (_message.Message,), {
+  'DESCRIPTOR' : _EXPRESSIONS,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.Expressions)
+  })
+_sym_db.RegisterMessage(Expressions)
+
+FwRule = _reflection.GeneratedProtocolMessageType('FwRule', (_message.Message,), {
+  'DESCRIPTOR' : _FWRULE,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.FwRule)
+  })
+_sym_db.RegisterMessage(FwRule)
+
+FwChain = _reflection.GeneratedProtocolMessageType('FwChain', (_message.Message,), {
+  'DESCRIPTOR' : _FWCHAIN,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.FwChain)
+  })
+_sym_db.RegisterMessage(FwChain)
+
+FwChains = _reflection.GeneratedProtocolMessageType('FwChains', (_message.Message,), {
+  'DESCRIPTOR' : _FWCHAINS,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.FwChains)
+  })
+_sym_db.RegisterMessage(FwChains)
+
+SysFirewall = _reflection.GeneratedProtocolMessageType('SysFirewall', (_message.Message,), {
+  'DESCRIPTOR' : _SYSFIREWALL,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.SysFirewall)
+  })
+_sym_db.RegisterMessage(SysFirewall)
+
+ClientConfig = _reflection.GeneratedProtocolMessageType('ClientConfig', (_message.Message,), {
+  'DESCRIPTOR' : _CLIENTCONFIG,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.ClientConfig)
+  })
+_sym_db.RegisterMessage(ClientConfig)
+
+Notification = _reflection.GeneratedProtocolMessageType('Notification', (_message.Message,), {
+  'DESCRIPTOR' : _NOTIFICATION,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.Notification)
+  })
+_sym_db.RegisterMessage(Notification)
+
+NotificationReply = _reflection.GeneratedProtocolMessageType('NotificationReply', (_message.Message,), {
+  'DESCRIPTOR' : _NOTIFICATIONREPLY,
+  '__module__' : 'ui_pb2'
+  # @@protoc_insertion_point(class_scope:protocol.NotificationReply)
+  })
+_sym_db.RegisterMessage(NotificationReply)
+
+_UI = DESCRIPTOR.services_by_name['UI']
 if _descriptor._USE_C_DESCRIPTORS == False:
 
   DESCRIPTOR._options = None
@@ -41,10 +327,10 @@ if _descriptor._USE_C_DESCRIPTORS == False:
   _CONNECTION_PROCESSENVENTRY._serialized_options = b'8\001'
   _CONNECTION_PROCESSCHECKSUMSENTRY._options = None
   _CONNECTION_PROCESSCHECKSUMSENTRY._serialized_options = b'8\001'
-  _ACTION._serialized_start=4153
-  _ACTION._serialized_end=4430
-  _NOTIFICATIONREPLYCODE._serialized_start=4432
-  _NOTIFICATIONREPLYCODE._serialized_end=4474
+  _ACTION._serialized_start=4170
+  _ACTION._serialized_end=4447
+  _NOTIFICATIONREPLYCODE._serialized_start=4449
+  _NOTIFICATIONREPLYCODE._serialized_end=4491
   _ALERT._serialized_start=23
   _ALERT._serialized_end=610
   _ALERT_PRIORITY._serialized_start=357
@@ -110,11 +396,11 @@ if _descriptor._USE_C_DESCRIPTORS == False:
   _SYSFIREWALL._serialized_start=3579
   _SYSFIREWALL._serialized_end=3667
   _CLIENTCONFIG._serialized_start=3670
-  _CLIENTCONFIG._serialized_end=3866
-  _NOTIFICATION._serialized_start=3869
-  _NOTIFICATION._serialized_end=4056
-  _NOTIFICATIONREPLY._serialized_start=4058
-  _NOTIFICATIONREPLY._serialized_end=4150
-  _UI._serialized_start=4477
-  _UI._serialized_end=4780
+  _CLIENTCONFIG._serialized_end=3883
+  _NOTIFICATION._serialized_start=3886
+  _NOTIFICATION._serialized_end=4073
+  _NOTIFICATIONREPLY._serialized_start=4075
+  _NOTIFICATIONREPLY._serialized_end=4167
+  _UI._serialized_start=4494
+  _UI._serialized_end=4797
 # @@protoc_insertion_point(module_scope)
diff --git a/ui/opensnitch/service.py b/ui/opensnitch/service.py
index eb3b53ab..9d91a58c 100644
--- a/ui/opensnitch/service.py
+++ b/ui/opensnitch/service.py
@@ -831,7 +831,8 @@ class UIService(ui_pb2_grpc.UIServicer, QtWidgets.QGraphicsObject):
                 ost.start()
 
         elif kwargs['action'] == self.DELETE_RULE:
-            self._db.delete_rule(kwargs['name'], kwargs['addr'])
+            proto, addr = self._get_peer(kwargs['addr'])
+            self._db.delete_rule(kwargs['name'], f"{proto}:{addr}")
 
         elif kwargs['action'] == self.NODE_DELETE:
             self._delete_node(kwargs['peer'])
@@ -980,6 +981,10 @@ class UIService(ui_pb2_grpc.UIServicer, QtWidgets.QGraphicsObject):
 
         stop_event = Event()
         def _on_client_closed():
+            prev_node = self._nodes.get_node(node_addr)
+            if prev_node is not None and prev_node.get('session', {}).get('peer') != local_peer:
+                return
+
             stop_event.set()
             print(datetime.now(), "[Notifications] client closed", context.peer())
             self._nodes.stop_notifications(node_addr)
@@ -1022,7 +1027,7 @@ class UIService(ui_pb2_grpc.UIServicer, QtWidgets.QGraphicsObject):
                     if in_message == None:
                         continue
 
-                    self._nodes.reply_notification(addr, in_message)
+                    self._nodes.reply_notification(node_addr, in_message)
                 except StopIteration:
                     print("[Notifications] Node {0} exited".format(addr))
                     break
-- 
2.53.0
## Clone the repository
git clone https://github.com/evilsocket/opensnitch.git
cd opensnitch

## Switch to the v1.8.0 branch
git checkout v1.8.0

## Git requires these apparently to apply the patch
`git config --global user.email "asdf@dslfj.sk"`
`git config --global user.name "asdfasdf"`

## apply PR #1578
git am ../0001-v1.8.0-ui-use-node_id-as-canonical-remote-node-identity.patch

Build the DEB Packages

# 1. Prepare the protocol buffers
make protocol

# 2. Build the Daemon .deb package
cd daemon
go mod vendor
cd ..

# Apply the gRPC Compatibility Fix
perl -0pi -e 's/SupportPackageIsVersion8/SupportPackageIsVersion7/g; s/cOpts := append\(\[\]grpc\.CallOption\{grpc\.StaticMethod\(\)\}, opts\.\.\.\)/cOpts := opts/g' daemon/ui/protocol/ui_grpc.pb.go

# As a root: Compile eBPF kernel Headers
cd /home/user/opensnitch
export DEB_KERNEL=$(ls /lib/modules | grep -E '^[0-9]' | grep -v qubes | sort -V | tail -n 1)
make -C ebpf_prog KERNEL_VER="$DEB_KERNEL"

# Force Go to Use Vendor and build the Daemon
# Make sure the debian directory is copied to the root
cp -r utils/packaging/daemon/deb/debian ./debian

# Export the flag to skip automated tests
export DEB_BUILD_OPTIONS="nocheck"

# Force Go to use vendored dependencies
export GOFLAGS="-mod=vendor"

# Build the package (skipping dependency checks)
dpkg-buildpackage -b -uc -us -d

# Remove the folder so it doesn't conflict with the upcoming UI build
rm -rf ./debian

# 3. Build the UI .deb package
cd ui

# Copy the UI's debian folder into the ui/ directory
cp -r ../utils/packaging/ui/deb/debian ./debian

# Build the UI package
dpkg-buildpackage -b -uc -us -d

# Clean up and return to the root directory
rm -rf ./debian
cd ..

Install the Packages

# Navigate to the parent directory to install the daemon
cd
sudo apt install ./opensnitch_1.8.0-1_amd64.deb

# Navigate into the opensnitch directory to install the UI
cd opensnitch
sudo apt install ./python3-opensnitch-ui_1.8.0-1_all.deb

I successfully built the rpm files and installed in a template. Looking forward to setting up the nodes and putting this to the test! Here’s how I modified steps 3-5 above to complete the build without pyrcc5:

## modified steps 3-5
git clone https://github.com/evilsocket/opensnitch.git
cd opensnitch
##added-steps/
git config --global user.name "<name from patch files>"
git config --global user.email "<email from patch files>"
git stash
##/
git checkout v1.8.0
git am ../0001-v1.8.0-ui-use-node_id-as-canonical-remote-node-identity.patch
git am ../0002-v1.8.0-local-fedora-rpm-build-fixes.patch
mkdir -p ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
git archive   --format=tar.gz   --prefix=opensnitch-1.8.0/   HEAD   > ~/rpmbuild/SOURCES/opensnitch_1.8.0.orig.tar.gz
git archive   --format=tar.gz   --prefix=opensnitch-ui-1.8.0/   HEAD:ui   > ~/rpmbuild/SOURCES/opensnitch-ui-1.8.0.tar.gz
cp utils/packaging/daemon/rpm/opensnitch.spec ~/rpmbuild/SPECS/
cp utils/packaging/ui/rpm/opensnitch-ui.spec ~/rpmbuild/SPECS/
##comment out line 81 #opensnitch-ui.spec: pyrcc5 -o opensnitch/resources_rc.py opensnitch/res/resources.qrc 
##add-comment/
nano -l utils/packaging/ui/rpm/opensnitch-ui.spec
##/
rpmbuild -ba --define "_topdir $HOME/rpmbuild" ~/rpmbuild/SPECS/opensnitch.spec
rpmbuild -ba --define "_topdir $HOME/rpmbuild" ~/rpmbuild/SPECS/opensnitch-ui.spec

rpmbuild threw a few warnings that didn’t appear to be of concern.

After copying the contents of the RPMS directory to the fedora-43-minimal template (which has the step 1 packages installed) I ran the following, which required one additional package, info:

bash-5.3# rpm -i opensnitch-ui-1.8.0-1.qubes1.noarch.rpm
gtk-update-icon-cache: Cache file created successfully.
bash-5.3# rpm -i opensnitch-1.8.0-1.qubes1.fc43.x86_64.rpm
error: Failed dependencies:
	info is needed by opensnitch-1.8.0-1.qubes1.fc43.x86_64
bash-5.3# dnf install info
...
bash-5.3# rpm -i opensnitch-1.8.0-1.qubes1.fc43.x86_64.rpm
Created symlink '/etc/systemd/system/multi-user.target.wants/opensnitch.service' → '/usr/lib/systemd/system/opensnitch.service'.
bash-5.3# systemctl stop opensnitch && systemctl disable opensnitch

Since I installed opensnitch in a template instead of a standalone (so that I could potentially test different configurations) I took the additional step of disabling the opensnitch service, which I later reenabled in /rw/config/rc.local of the app qube.

1 Like

Nice! Here are a few things I ended up doing; hopefully, they’ll be helpful to someone else.

Setting "Address":"127.0.0.1:50051" to /etc/opensnitchd/default-config.json allows connecting to UI VM.

`/etc/qubes/policy.d/30-opensnitch.policy only needs one line now:

# OpenSnitch node connections
qubes.ConnectTCP +50051 @tag:snitch snitch-ui allow

I also apply both to the template. Changing the /etc/opensnitchd/rules directory to be a symbolic link to /rw/config/opensnitchd/rules allows the rules to be preserved.

Removing /etc/xdg/opensnitch.desktop (if I remember correctly) prevents the UI from opening in all VMs that use the template in question.

On disposable VMs, I have it set up like this in /rw/config/rc.local:

HOST=$(hostname)

if [ "$HOST" = "sys-net" ] || [ "$HOST" = "sys-usb" ]; then
 cp -a "/rw/config/opensnitchd/rules.$HOST/." /rw/config/opensnitchd/rules/
elif echo "$HOST" | grep -q '^disp[0-9][0-9]*$'; then
 cp -a /rw/config/opensnitchd/rules.disp/. /rw/config/opensnitchd/rules/
fi

qvm-connect-tcp :snitch-ui:50051 &
sleep 1
systemctl start opensnitch

Obviously, this still doesn’t allow saving rules directly from the UI on disposable VMs—those rules still need to be copied or saved to the DVM template. However, this does copy the correct rules for named templates if that’s what you’re looking for.

1 Like

How did you set the server address with this setup (Fedora)? I tried all of the following to no avail :

  • Starting the application with opensnitch-ui --socket "[::]:50051"
  • Modifying ~/.config/opensnitch/settings.conf to set [global] server_address=[::]:50051
  • Changing Preferences > Nodes > Address to [::]:50051 in the GUI.
  • All of the above with 127.0.0.1:50051 as well

The GUI does register Preferences > UI > Server > Address as [::]:50051 and the CLI includes the following feedback lines:

[server] addr: [::]:50051
[server] using address: [::]:50051 auth type: simple

but Preferences > Nodes > Address always reverts to the default unix:///tmp/osui.sock.

I’ve had this problem in the past with Debian’s (older) version of opensnitch and hacked my way out of it, but the helper scripts with qubes-opensnitch-piped fortunately made such hacks unecessary. Which has me wondering, are you also using a helper script with the new setup?

Either OpenSnitch UI needs to listen on network devices or you need to set it to work with sockets. I haven’t tested the socket configuration with this specifically, but listening on just IPv4 localhost should be enough here since that’s what we are connecting with—it doesn’t necessarily have to listen on all devices. The firewall should block it anyway, and your snitch VM probably shouldn’t have a net qube attached to it. Adding --socket "127.0.0.1:50051" to the opensnitch-ui command should do it. We don’t have to do unique config for each VM anymore with this.

I’m using ~/.config/autostart/opensnitch_ui.desktop on my snitch-ui VM to start the UI, which works well with AppVMs. The original starting method is having this file in /etc/xdg/autostart.

[Desktop Entry]
Type=Application
Name=OpenSnitch
Exec=opensnitch-ui --socket "127.0.0.1:50051"
Icon=opensnitch-ui
GenericName=OpenSnitch Firewall
GenericName[hu]=OpenSnitch-tűzfal
GenericName[nb]=OpenSnitch brannmur
Comment=Interactive application firewall
Comment[es]=Firewall de aplicaciones
Comment[hu]=Alkalmazási tűzfal
Comment[nb]=Interaktiv programbrannmur
Terminal=false
NoDisplay=false
Categories=System;Security;Monitor;Network;
Keywords=system;firewall;policies;security;polkit;policykit;
X-GNOME-Autostart-Delay=3
X-GNOME-Autostart-enabled=true

Setting Preferences > Nodes > Address sets how nodes connect to the UI, which is probably saved in /etc/opensnitchd/default-config.json. Since it’s on the root filesystem, changes are not permanent with an AppVM. You should probably configure your base template to have:

    "Server":
    {
        "Address":"127.0.0.1:50051",
    ...

If you want to use your UI to set these settings, you probably should make /etc/opensnitchd a symbolic link to /rw/config/opensnitchd so that each VM can have its own persistent settings. You do have to manually copy that directory to all the VMs that use opensnitch. I suppose there’s a bind-dirs way of doing that too, but I have never learned to do that.

Thanks, that did the trick!

Yeah, I’ve always employed bind-dirs for this purpose, but I’ll likely try custom-persist this time around.

This is from memory and I haven’t tested the steps now but setting OpenSnitch up should go with something like this:

1. TemplateVM Setup

Install opensnitch and opensnitch-ui normally. Then run the following to configure the daemon and prevent the UI from launching in every qube:

# Prevent UI from starting globally
sudo rm /etc/xdg/autostart/opensnitch_ui.desktop

# Enable the daemon to start automatically in all VMs
sudo systemctl enable opensnitch

Edit the daemon configuration at /etc/opensnitchd/default-config.json to point to the local port:

    "Server":
    {
        "Address":"127.0.0.1:50051",

Shut down the TemplateVM.

2. dom0 Policy

Allow all VMs (including disposables) to forward the UI connection to your central snitch-ui AppVM.

Create or edit /etc/qubes/policy.d/50-opensnitch.policy:

qubes.ConnectTCP +50051 @anyvm snitch-ui allow

3. UI AppVM (snitch-ui) Setup

Set up the UI to autostart and listen on the socket. Create ~/.config/autostart/opensnitch_ui.desktop and add:

[Desktop Entry]
Type=Application
Name=OpenSnitch
Exec=opensnitch-ui --socket "127.0.0.1:50051"
Icon=opensnitch-ui
Terminal=false
NoDisplay=false
Categories=System;Security;Monitor;Network;
X-GNOME-Autostart-enabled=true

4. Client AppVMs & Disposables Setup

For any VM that needs to connect to the UI, append this to /rw/config/rc.local to establish the connection on boot:

# Forward UI connection to snitch-ui VM
qvm-connect-tcp :snitch-ui:50051

5. Persistent Rules (Symlink Method)

Since /etc resets on reboot, any rule changes made via the UI will be lost unless you make the directory persistent. For the AppVMs where you actually want to save rules, add this block to /rw/config/rc.local (below the qvm-connect-tcp line).

This automatically handles the symlink on boot and restarts the daemon so it picks up the persistent directory:

# Apply persistent OpenSnitch config only if the directory exists
if [ -d "/rw/config/opensnitchd" ] && [ ! -L "/etc/opensnitchd" ]; then
    rm -rf /etc/opensnitchd
    ln -s /rw/config/opensnitchd /etc/opensnitchd
    systemctl restart opensnitch
fi

(Note: For disposables or VMs where you don’t care about persistent rules, just use the qvm-connect-tcp line in rc.local and skip the symlink block. The daemon will just run ephemerally).