python ui server

This commit is contained in:
evilsocket 2018-04-05 12:01:33 +02:00
parent a17b4c689c
commit 2bda290899
Failed to generate hash of commit
17 changed files with 678 additions and 367 deletions

View file

@ -1,4 +1,4 @@
all: protocol osd osgui
all: protocol osd
protocol:
@cd ui.proto && make
@ -6,12 +6,8 @@ protocol:
osd:
@cd daemon && make && mv daemon ../osd
osgui:
@cd ui.gtk && make && mv ui.gtk ../osgui
clean:
@cd rules && rm -rf user.rule*.json
@cd daemon && make clean
@cd ui.proto && make clean
@cd ui.gtk && make clean
@rm -rf osd osgui
@rm -rf osd

View file

@ -62,7 +62,7 @@ func FromReply(reply *protocol.RuleReply) *Rule {
Duration(reply.Duration),
Cmp{
What: OperandType(reply.What),
With: reply.With,
With: reply.Value,
},
)
}

1
ui.gtk/.gitignore vendored
View file

@ -1 +0,0 @@
ui.gtk

View file

@ -1,7 +0,0 @@
all: ui.gtk
ui.gtk:
@go build .
clean:
@rm -rf ui.gtk

View file

@ -1,178 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkListStore" id="durationStore">
<columns>
<!-- column-name gchararray1 -->
<column type="gchararray"/>
</columns>
<data>
<row>
<col id="0" translatable="yes">Forever
Until Restart
Once</col>
</row>
</data>
</object>
<object class="GtkWindow" id="askWindow">
<property name="can_focus">False</property>
<property name="resizable">False</property>
<property name="window_position">center-always</property>
<property name="urgency_hint">True</property>
<property name="deletable">False</property>
<child>
<object class="GtkBox" id="mainBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkGrid" id="headerGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">20</property>
<property name="margin_bottom">20</property>
<child>
<object class="GtkImage" id="appIcon">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="stock">gtk-dialog-question</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="appMainGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<child>
<object class="GtkLabel" id="appNameLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Application Name</property>
<attributes>
<attribute name="font-desc" value="&lt;Inserire il valore&gt; 13"/>
<attribute name="weight" value="bold"/>
<attribute name="gravity" value="west"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="descLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="valign">center</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="label" translatable="yes">Wants to connect to www.google.com on tcp port 443</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="detailsGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">10</property>
<property name="margin_bottom">10</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">15</property>
<property name="label" translatable="yes">More Details &gt;</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">1</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkGrid" id="footerGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">center</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkButton" id="allowButton">
<property name="label" translatable="yes">Allow</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="denyButton">
<property name="label" translatable="yes">Deny</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="durationCombo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">durationStore</property>
<property name="active">0</property>
<property name="id_column">0</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View file

@ -1,138 +0,0 @@
package main
import (
"flag"
"net"
"os"
"os/signal"
"syscall"
"golang.org/x/net/context"
"github.com/evilsocket/opensnitch/daemon/core"
"github.com/evilsocket/opensnitch/daemon/log"
protocol "github.com/evilsocket/opensnitch/ui.proto"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
"github.com/gotk3/gotk3/glib"
"github.com/gotk3/gotk3/gtk"
)
var (
socketPath = "opensnitch-ui.sock"
uiBuilder = (*gtk.Builder)(nil)
askWindow = (*gtk.Window)(nil)
listener = (net.Listener)(nil)
server = (*grpc.Server)(nil)
err = (error)(nil)
sigChan = (chan os.Signal)(nil)
isClosing = (bool)(false)
)
type service struct{}
func (s *service) Ping(ctx context.Context, ping *protocol.PingRequest) (*protocol.PingReply, error) {
log.Debug("Got ping 0x%x", ping.Id)
return &protocol.PingReply{Id: ping.Id}, nil
}
func (s *service) AskRule(ctx context.Context, req *protocol.RuleRequest) (*protocol.RuleReply, error) {
log.Info("Got rule request: %v", req)
glib.IdleAdd(func() bool {
askWindow.Show()
return false
})
return &protocol.RuleReply{
Name: "user.choice",
Action: "allow",
Duration: "always",
What: "process.path",
With: req.ProcessPath,
}, nil
}
func setupSignals() {
sigChan = make(chan os.Signal, 1)
signal.Notify(sigChan,
syscall.SIGHUP,
syscall.SIGINT,
syscall.SIGTERM,
syscall.SIGQUIT)
go func() {
sig := <-sigChan
isClosing = true
log.Raw("\n")
log.Important("Got signal: %v", sig)
if listener != nil {
listener.Close()
}
os.Exit(0)
}()
}
// TODO: this will be loaded from compiled resources
const uiGladeFile = "ui.gtk/glade/ask_window.glade"
func setupGtk() {
gtk.Init(&os.Args)
if uiBuilder, err = gtk.BuilderNew(); err != nil {
log.Fatal("Error while creating GTK builder: %s", err)
} else if err = uiBuilder.AddFromFile(uiGladeFile); err != nil {
log.Fatal("Error while loading %s: %s", uiGladeFile, err)
}
obj, err := uiBuilder.GetObject("askWindow")
if err != nil {
log.Fatal("Error while getting window: %s", err)
}
var ok bool
askWindow, ok = obj.(*gtk.Window)
if !ok {
log.Fatal("Could not cast window object.")
}
askWindow.SetTitle("OpenSnitch v" + Version)
go func() {
gtk.Main()
}()
}
func init() {
flag.StringVar(&socketPath, "socket-path", socketPath, "UNIX socket for this gRPC service.")
}
func main() {
flag.Parse()
socketPath, err = core.ExpandPath(socketPath)
if err != nil {
log.Fatal("%s", err)
}
setupSignals()
setupGtk()
log.Important("Starting %s v%s on socket %s", Name, Version, socketPath)
listener, err = net.Listen("unix", socketPath)
if err != nil {
log.Fatal("%s", err)
}
server = grpc.NewServer()
protocol.RegisterUIServer(server, &service{})
reflection.Register(server)
if err := server.Serve(listener); err != nil {
if isClosing == false {
log.Fatal("Failed to start: %s", err)
}
}
}

View file

@ -1,8 +0,0 @@
package main
const (
Name = "opensnitch-test-ui"
Version = "1.0.0b"
Author = "Simone 'evilsocket' Margaritelli"
Website = "https://github.com/evilsocket/opensnitch"
)

1
ui.proto/.gitiignore Normal file
View file

@ -0,0 +1 @@
*.pyc

View file

@ -1,5 +1,7 @@
all:
protoc -I. ui.proto --go_out=plugins=grpc:.
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ui.proto
clean:
@rm -rf ui.pb.go
@rm -rf *.go
@rm -rf *.py

View file

@ -152,7 +152,7 @@ type RuleReply struct {
Action string `protobuf:"bytes,2,opt,name=action" json:"action,omitempty"`
Duration string `protobuf:"bytes,3,opt,name=duration" json:"duration,omitempty"`
What string `protobuf:"bytes,4,opt,name=what" json:"what,omitempty"`
With string `protobuf:"bytes,5,opt,name=with" json:"with,omitempty"`
Value string `protobuf:"bytes,5,opt,name=value" json:"value,omitempty"`
}
func (m *RuleReply) Reset() { *m = RuleReply{} }
@ -188,9 +188,9 @@ func (m *RuleReply) GetWhat() string {
return ""
}
func (m *RuleReply) GetWith() string {
func (m *RuleReply) GetValue() string {
if m != nil {
return m.With
return m.Value
}
return ""
}
@ -310,27 +310,27 @@ var _UI_serviceDesc = grpc.ServiceDesc{
func init() { proto.RegisterFile("ui.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 341 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x51, 0xcd, 0x4a, 0xeb, 0x40,
0x14, 0x6e, 0xd3, 0x34, 0x3f, 0xa7, 0xb7, 0xf7, 0xc2, 0xc0, 0x95, 0x58, 0x29, 0xd4, 0xac, 0x0a,
0x42, 0x17, 0xfa, 0x04, 0xdd, 0xd9, 0x5d, 0x09, 0xb8, 0x72, 0x51, 0x62, 0x26, 0x34, 0x83, 0xb1,
0x33, 0xce, 0x39, 0x41, 0x8a, 0xcf, 0xe0, 0x3b, 0xcb, 0xfc, 0xa4, 0x06, 0xdc, 0x9d, 0xf3, 0xfd,
0x31, 0xf3, 0x1d, 0x48, 0x3a, 0xb1, 0x51, 0x5a, 0x92, 0x64, 0x41, 0x27, 0xf2, 0x25, 0xcc, 0xf6,
0xe2, 0x74, 0x2c, 0xea, 0xf7, 0xae, 0x46, 0x62, 0x7f, 0x21, 0x10, 0x3c, 0x1b, 0xaf, 0xc6, 0xeb,
0xb0, 0x08, 0x04, 0xcf, 0x6f, 0x20, 0x75, 0xb4, 0x6a, 0xcf, 0xbf, 0xc8, 0xaf, 0x00, 0x66, 0x45,
0xd7, 0xd6, 0xbd, 0x79, 0x01, 0x89, 0x0d, 0xae, 0x64, 0x6b, 0x55, 0x69, 0x71, 0xd9, 0xd9, 0x7f,
0x88, 0x50, 0x57, 0x07, 0xa1, 0xb2, 0xc0, 0x32, 0x53, 0xd4, 0xd5, 0x4e, 0xb1, 0x6b, 0x48, 0x0c,
0xac, 0xa4, 0xa6, 0x6c, 0xb2, 0x1a, 0xaf, 0xe7, 0x45, 0x8c, 0xba, 0xda, 0x4b, 0x4d, 0xc6, 0xc1,
0x91, 0x8c, 0x23, 0x74, 0x0e, 0x8e, 0xe4, 0x1c, 0x06, 0x6e, 0x24, 0x52, 0x36, 0xb5, 0x44, 0xcc,
0x91, 0x1e, 0x25, 0x52, 0x4f, 0xd9, 0xb0, 0xc8, 0x85, 0x71, 0x24, 0x1b, 0xb6, 0x04, 0x50, 0x5a,
0x56, 0x35, 0xe2, 0x41, 0xf0, 0x2c, 0xb6, 0x64, 0xea, 0x91, 0x1d, 0x67, 0xb7, 0xf0, 0xa7, 0xa7,
0x55, 0x49, 0x4d, 0x96, 0xd8, 0xe0, 0x99, 0xc7, 0xf6, 0x25, 0x35, 0x43, 0x49, 0xa9, 0x8f, 0x98,
0xa5, 0xab, 0xc9, 0x40, 0xb2, 0xd5, 0x47, 0xcc, 0x3f, 0x21, 0x75, 0x75, 0x98, 0xb2, 0x18, 0x84,
0xa7, 0xf2, 0xad, 0xf6, 0x45, 0xd8, 0x99, 0x5d, 0x41, 0x54, 0x56, 0x24, 0xe4, 0xc9, 0x97, 0xe0,
0x37, 0x53, 0x1c, 0xef, 0x74, 0x69, 0x99, 0x89, 0x2b, 0xae, 0xdf, 0x4d, 0xce, 0x47, 0x53, 0x92,
0x2f, 0xc1, 0xce, 0x16, 0x13, 0xd4, 0xf8, 0xff, 0xdb, 0xf9, 0xfe, 0x19, 0x82, 0xa7, 0x1d, 0x5b,
0x43, 0x68, 0xee, 0xc5, 0xfe, 0x6d, 0x3a, 0xb1, 0x19, 0x1c, 0x76, 0x31, 0xff, 0x01, 0x54, 0x7b,
0xce, 0x47, 0xec, 0x0e, 0xe2, 0x2d, 0xbe, 0x9a, 0xf7, 0x3a, 0xf1, 0xe0, 0x90, 0x4e, 0x7c, 0xf9,
0x4a, 0x3e, 0x7a, 0x89, 0xec, 0x1d, 0x1f, 0xbe, 0x03, 0x00, 0x00, 0xff, 0xff, 0xd9, 0x36, 0xbf,
0xe7, 0x3c, 0x02, 0x00, 0x00,
// 343 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x51, 0xcb, 0x4a, 0xc3, 0x40,
0x14, 0x6d, 0xd3, 0x34, 0x8f, 0x5b, 0xab, 0x30, 0xa8, 0xc4, 0x4a, 0xa1, 0x66, 0x55, 0x10, 0xba,
0xd0, 0x2f, 0xe8, 0xce, 0xee, 0x4a, 0xc0, 0x95, 0x8b, 0x32, 0x66, 0x42, 0x33, 0x18, 0x3b, 0xe3,
0xdc, 0x89, 0xd2, 0x85, 0x9f, 0xe0, 0x3f, 0xcb, 0x3c, 0x52, 0x03, 0xee, 0xe6, 0x9e, 0x17, 0xc9,
0x39, 0x90, 0xb4, 0x7c, 0x25, 0x95, 0xd0, 0x82, 0x04, 0x2d, 0xcf, 0xe7, 0x30, 0xd9, 0xf2, 0xc3,
0xbe, 0xa8, 0x3e, 0xda, 0x0a, 0x35, 0x39, 0x87, 0x80, 0xb3, 0x6c, 0xb8, 0x18, 0x2e, 0xc3, 0x22,
0xe0, 0x2c, 0xbf, 0x85, 0xd4, 0xd1, 0xb2, 0x39, 0xfe, 0x23, 0x7f, 0x02, 0x98, 0x14, 0x6d, 0x53,
0x75, 0xe6, 0x19, 0x24, 0x36, 0xb8, 0x14, 0x8d, 0x55, 0xa5, 0xc5, 0xe9, 0x26, 0x57, 0x10, 0xa1,
0x2a, 0x77, 0x5c, 0x66, 0x81, 0x65, 0xc6, 0xa8, 0xca, 0x8d, 0x24, 0x37, 0x90, 0x18, 0x58, 0x0a,
0xa5, 0xb3, 0xd1, 0x62, 0xb8, 0x9c, 0x16, 0x31, 0xaa, 0x72, 0x2b, 0x94, 0x36, 0x0e, 0x86, 0xda,
0x38, 0x42, 0xe7, 0x60, 0xa8, 0x9d, 0xc3, 0xc0, 0xb5, 0x40, 0x9d, 0x8d, 0x2d, 0x11, 0x33, 0xd4,
0x4f, 0x02, 0x75, 0x47, 0xd9, 0xb0, 0xc8, 0x85, 0x31, 0xd4, 0x36, 0x6c, 0x0e, 0x20, 0x95, 0x28,
0x2b, 0xc4, 0x1d, 0x67, 0x59, 0x6c, 0xc9, 0xd4, 0x23, 0x1b, 0x46, 0xee, 0xe0, 0xac, 0xa3, 0x25,
0xd5, 0x75, 0x96, 0xd8, 0xe0, 0x89, 0xc7, 0xb6, 0x54, 0xd7, 0x7d, 0x09, 0x55, 0x7b, 0xcc, 0xd2,
0xc5, 0xa8, 0x27, 0x59, 0xab, 0x3d, 0xe6, 0xdf, 0x90, 0xba, 0x3a, 0x4c, 0x59, 0x04, 0xc2, 0x03,
0x7d, 0xaf, 0x7c, 0x11, 0xf6, 0x4d, 0xae, 0x21, 0xa2, 0xa5, 0xe6, 0xe2, 0xe0, 0x4b, 0xf0, 0x97,
0x29, 0x8e, 0xb5, 0x8a, 0x5a, 0x66, 0xe4, 0x8a, 0xeb, 0x6e, 0x93, 0xf3, 0x55, 0x53, 0xed, 0x4b,
0xb0, 0x6f, 0x72, 0x09, 0xe3, 0x4f, 0xda, 0xb4, 0x95, 0x2f, 0xc0, 0x1d, 0x0f, 0x2f, 0x10, 0x3c,
0x6f, 0xc8, 0x12, 0x42, 0xb3, 0x18, 0xb9, 0x58, 0xb5, 0x7c, 0xd5, 0x9b, 0x76, 0x36, 0xfd, 0x03,
0x64, 0x73, 0xcc, 0x07, 0xe4, 0x1e, 0xe2, 0x35, 0xbe, 0x99, 0x2f, 0x76, 0xe2, 0xde, 0x94, 0x4e,
0x7c, 0xfa, 0x99, 0x7c, 0xf0, 0x1a, 0xd9, 0x25, 0x1f, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x8d,
0xa3, 0x76, 0xb5, 0x3e, 0x02, 0x00, 0x00,
}

View file

@ -32,5 +32,5 @@ message RuleReply {
string action = 2;
string duration = 3;
string what = 4;
string with = 5;
string value = 5;
}

303
ui.proto/ui_pb2.py Normal file
View file

@ -0,0 +1,303 @@
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: ui.proto
import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
from google.protobuf import descriptor_pb2
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor(
name='ui.proto',
package='ui',
syntax='proto3',
serialized_pb=_b('\n\x08ui.proto\x12\x02ui\"\x19\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x04\"\x17\n\tPingReply\x12\n\n\x02id\x18\x01 \x01(\x04\"\xb5\x01\n\x0bRuleRequest\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\x12\n\nprocess_id\x18\x07 \x01(\r\x12\x14\n\x0cprocess_path\x18\x08 \x01(\t\x12\x14\n\x0cprocess_args\x18\t \x03(\t\"X\n\tRuleReply\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tion\x18\x02 \x01(\t\x12\x10\n\x08\x64uration\x18\x03 \x01(\t\x12\x0c\n\x04what\x18\x04 \x01(\t\x12\r\n\x05value\x18\x05 \x01(\t2[\n\x02UI\x12(\n\x04Ping\x12\x0f.ui.PingRequest\x1a\r.ui.PingReply\"\x00\x12+\n\x07\x41skRule\x12\x0f.ui.RuleRequest\x1a\r.ui.RuleReply\"\x00\x62\x06proto3')
)
_PINGREQUEST = _descriptor.Descriptor(
name='PingRequest',
full_name='ui.PingRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='id', full_name='ui.PingRequest.id', index=0,
number=1, type=4, cpp_type=4, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=16,
serialized_end=41,
)
_PINGREPLY = _descriptor.Descriptor(
name='PingReply',
full_name='ui.PingReply',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='id', full_name='ui.PingReply.id', index=0,
number=1, type=4, cpp_type=4, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=43,
serialized_end=66,
)
_RULEREQUEST = _descriptor.Descriptor(
name='RuleRequest',
full_name='ui.RuleRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='protocol', full_name='ui.RuleRequest.protocol', index=0,
number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='src_ip', full_name='ui.RuleRequest.src_ip', index=1,
number=2, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='src_port', full_name='ui.RuleRequest.src_port', index=2,
number=3, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='dst_ip', full_name='ui.RuleRequest.dst_ip', index=3,
number=4, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='dst_host', full_name='ui.RuleRequest.dst_host', index=4,
number=5, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='dst_port', full_name='ui.RuleRequest.dst_port', index=5,
number=6, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='process_id', full_name='ui.RuleRequest.process_id', index=6,
number=7, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='process_path', full_name='ui.RuleRequest.process_path', index=7,
number=8, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='process_args', full_name='ui.RuleRequest.process_args', index=8,
number=9, type=9, cpp_type=9, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=69,
serialized_end=250,
)
_RULEREPLY = _descriptor.Descriptor(
name='RuleReply',
full_name='ui.RuleReply',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='name', full_name='ui.RuleReply.name', index=0,
number=1, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='action', full_name='ui.RuleReply.action', index=1,
number=2, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='duration', full_name='ui.RuleReply.duration', index=2,
number=3, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='what', full_name='ui.RuleReply.what', index=3,
number=4, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
_descriptor.FieldDescriptor(
name='value', full_name='ui.RuleReply.value', index=4,
number=5, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None, file=DESCRIPTOR),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=252,
serialized_end=340,
)
DESCRIPTOR.message_types_by_name['PingRequest'] = _PINGREQUEST
DESCRIPTOR.message_types_by_name['PingReply'] = _PINGREPLY
DESCRIPTOR.message_types_by_name['RuleRequest'] = _RULEREQUEST
DESCRIPTOR.message_types_by_name['RuleReply'] = _RULEREPLY
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
PingRequest = _reflection.GeneratedProtocolMessageType('PingRequest', (_message.Message,), dict(
DESCRIPTOR = _PINGREQUEST,
__module__ = 'ui_pb2'
# @@protoc_insertion_point(class_scope:ui.PingRequest)
))
_sym_db.RegisterMessage(PingRequest)
PingReply = _reflection.GeneratedProtocolMessageType('PingReply', (_message.Message,), dict(
DESCRIPTOR = _PINGREPLY,
__module__ = 'ui_pb2'
# @@protoc_insertion_point(class_scope:ui.PingReply)
))
_sym_db.RegisterMessage(PingReply)
RuleRequest = _reflection.GeneratedProtocolMessageType('RuleRequest', (_message.Message,), dict(
DESCRIPTOR = _RULEREQUEST,
__module__ = 'ui_pb2'
# @@protoc_insertion_point(class_scope:ui.RuleRequest)
))
_sym_db.RegisterMessage(RuleRequest)
RuleReply = _reflection.GeneratedProtocolMessageType('RuleReply', (_message.Message,), dict(
DESCRIPTOR = _RULEREPLY,
__module__ = 'ui_pb2'
# @@protoc_insertion_point(class_scope:ui.RuleReply)
))
_sym_db.RegisterMessage(RuleReply)
_UI = _descriptor.ServiceDescriptor(
name='UI',
full_name='ui.UI',
file=DESCRIPTOR,
index=0,
options=None,
serialized_start=342,
serialized_end=433,
methods=[
_descriptor.MethodDescriptor(
name='Ping',
full_name='ui.UI.Ping',
index=0,
containing_service=None,
input_type=_PINGREQUEST,
output_type=_PINGREPLY,
options=None,
),
_descriptor.MethodDescriptor(
name='AskRule',
full_name='ui.UI.AskRule',
index=1,
containing_service=None,
input_type=_RULEREQUEST,
output_type=_RULEREPLY,
options=None,
),
])
_sym_db.RegisterServiceDescriptor(_UI)
DESCRIPTOR.services_by_name['UI'] = _UI
# @@protoc_insertion_point(module_scope)

63
ui.proto/ui_pb2_grpc.py Normal file
View file

@ -0,0 +1,63 @@
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
import grpc
import ui_pb2 as ui__pb2
class UIStub(object):
# missing associated documentation comment in .proto file
pass
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.Ping = channel.unary_unary(
'/ui.UI/Ping',
request_serializer=ui__pb2.PingRequest.SerializeToString,
response_deserializer=ui__pb2.PingReply.FromString,
)
self.AskRule = channel.unary_unary(
'/ui.UI/AskRule',
request_serializer=ui__pb2.RuleRequest.SerializeToString,
response_deserializer=ui__pb2.RuleReply.FromString,
)
class UIServicer(object):
# missing associated documentation comment in .proto file
pass
def Ping(self, request, context):
# missing associated documentation comment in .proto file
pass
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def AskRule(self, request, context):
# missing associated documentation comment in .proto file
pass
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_UIServicer_to_server(servicer, server):
rpc_method_handlers = {
'Ping': grpc.unary_unary_rpc_method_handler(
servicer.Ping,
request_deserializer=ui__pb2.PingRequest.FromString,
response_serializer=ui__pb2.PingReply.SerializeToString,
),
'AskRule': grpc.unary_unary_rpc_method_handler(
servicer.AskRule,
request_deserializer=ui__pb2.RuleRequest.FromString,
response_serializer=ui__pb2.RuleReply.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'ui.UI', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))

1
ui/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
*.pyc

40
ui/dialog.py Normal file
View file

@ -0,0 +1,40 @@
from PyQt5 import QtCore, QtGui, uic, QtWidgets
from PyQt5 import QtDBus
import threading
import logging
import queue
import sys
import os
DIALOG_UI_PATH = "%s/res/dialog.ui" % os.path.dirname(sys.modules[__name__].__file__)
class Dialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
def __init__(self, parent=None):
QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint)
self.setupUi(self)
self._done = threading.Event()
self._allow_button = self.findChild(QtWidgets.QPushButton, "allowButton")
self._allow_button.clicked.connect(self._on_allow_click)
self._block_button = self.findChild(QtWidgets.QPushButton, "denyButton")
self._block_button.clicked.connect(self._on_block_click)
self._duration_combo = self.findChild(QtWidgets.QComboBox, "actionComboBox")
self._duration_combo.currentIndexChanged[str].connect(self._on_duration_changed)
def promptUser(self, req):
self._done.clear()
self.show()
self._done.wait()
def _on_duration_changed(self):
s_option = self._duration_combo.currentText()
def _on_allow_click(self):
self.hide()
self._done.set()
def _on_block_click(self):
self.hide()
self._done.set()

53
ui/main.py Normal file
View file

@ -0,0 +1,53 @@
from PyQt5 import QtWidgets
import sys
import os
import time
import signal
path = os.path.abspath(os.path.dirname(__file__))
sys.path.append(path)
sys.path.append(path + "/../ui.proto/")
import grpc
from concurrent import futures
import ui_pb2
import ui_pb2_grpc
from dialog import Dialog
class UIServicer(ui_pb2_grpc.UIServicer):
def __init__(self):
self.dialog = Dialog()
def Ping(self, request, context):
# print "Got ping 0x%x" % request.id
return ui_pb2.PingReply(id=request.id)
def AskRule(self, request, context):
print "Got request: %s" % request
self.dialog.promptUser(request)
return ui_pb2.RuleReply(
name="user.choice",
action="allow",
duration="always",
what="process.path",
value=request.process_path)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
server = grpc.server(futures.ThreadPoolExecutor(max_workers=1))
ui_pb2_grpc.add_UIServicer_to_server(UIServicer(), server)
server.add_insecure_port("unix:./opensnitch-ui.sock")
# https://stackoverflow.com/questions/5160577/ctrl-c-doesnt-work-with-pyqt
signal.signal(signal.SIGINT, signal.SIG_DFL)
try:
server.start()
app.exec_()
except KeyboardInterrupt:
app.quit()
server.stop(0)

184
ui/res/dialog.ui Normal file
View file

@ -0,0 +1,184 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>653</width>
<height>316</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>37</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>490</width>
<height>220</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>1000</width>
<height>400</height>
</size>
</property>
<property name="font">
<font>
<kerning>true</kerning>
</font>
</property>
<property name="windowTitle">
<string>OpenSnitch</string>
</property>
<widget class="QLabel" name="appNameLabel">
<property name="geometry">
<rect>
<x>20</x>
<y>20</y>
<width>433</width>
<height>42</height>
</rect>
</property>
<property name="font">
<font>
<pointsize>15</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Chromium Web Browser</string>
</property>
</widget>
<widget class="Line" name="line">
<property name="geometry">
<rect>
<x>0</x>
<y>60</y>
<width>651</width>
<height>16</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<widget class="QLabel" name="messageLabel">
<property name="geometry">
<rect>
<x>20</x>
<y>90</y>
<width>611</width>
<height>81</height>
</rect>
</property>
<property name="text">
<string>Chromium Web Browser (/usr/lib/chromium-browser) wants to connect to host 127.0.0.1 on TCP port 443 (https)</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
<widget class="QPushButton" name="denyButton">
<property name="geometry">
<rect>
<x>220</x>
<y>260</y>
<width>191</width>
<height>50</height>
</rect>
</property>
<property name="text">
<string>Block</string>
</property>
</widget>
<widget class="QPushButton" name="allowButton">
<property name="geometry">
<rect>
<x>10</x>
<y>260</y>
<width>191</width>
<height>50</height>
</rect>
</property>
<property name="font">
<font>
<strikeout>false</strikeout>
</font>
</property>
<property name="mouseTracking">
<bool>false</bool>
</property>
<property name="text">
<string>Allow</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
<widget class="QComboBox" name="actionComboBox">
<property name="geometry">
<rect>
<x>450</x>
<y>260</y>
<width>191</width>
<height>50</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>97</width>
<height>26</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>204</width>
<height>50</height>
</size>
</property>
<item>
<property name="text">
<string>Once</string>
</property>
</item>
<item>
<property name="text">
<string>Until Quit</string>
</property>
</item>
<item>
<property name="text">
<string>Forever</string>
</property>
</item>
</widget>
<widget class="QLabel" name="iconLabel">
<property name="geometry">
<rect>
<x>590</x>
<y>5</y>
<width>60</width>
<height>60</height>
</rect>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</widget>
<resources/>
<connections/>
</ui>