Improved process detections by monitoring new processes execution.
It allow us to know the path of a process before a socket is opened.
Closes#617
Other improvements:
- If we fail to retrieve the path of a process, then we'll use the comm
name of the connection/process.
- Better kernel connections detection.
- If debugfs is not loaded, we'll try to mount it, to allow to use
eBPF monitor method.
Future work (help wanted):
- Extract command line arguments from the kernel (sys_execve, or mm
struct).
- Monitor other functions (execveat, clone*, fork, etc).
- Send these events to the server (GUI), and display all the commands
an application has executed.
Under certain conditions, when we dumped inodes via netlink, we were
linking network connections to wrong applications.
- To improve this situation:
1) Use netfilter's UID by default:
Sometimes the UID reported via netlink was different than the one
reported by libnetfilter. libnetfilter UID is always correct.
If you had a rule that filtered by UID, this problem could cause to
prompt you again to allow the connection.
2) Use the netlink entry that matches exactly the properties of an
outgoing connection:
There're some in-kernel sockets that doesn't match 1:1 outgoing
connections (daemon/netlink/socket.go#L22).
In order to identify the applications that initiate these network
connections we use a workaround. But under certain conditions
(source port reuse), we were associating connections to wrong
applications.
So in order to avoid this problem, if there's a 1:1 match use that
netlink entry. If not, fallback to the workaround.
- misc: added more logs to better debug these issues.
Some times, processes that establish connections to localhost are only
found in /proc/net/* files. So if we fail to get the PID of a
connection, fallback to legacy method to find it.
* Use ebpf program to find PID of new connections.
before running the branch you have to compile ebpf_prog/opensnitch.c
opensnitch.c is an eBPF program. Compilation requires getting kernel source.
cd opensnitch
wget https://github.com/torvalds/linux/archive/v5.8.tar.gz
tar -xf v5.8.tar.gz
patch linux-5.8/tools/lib/bpf/bpf_helpers.h < ebpf_prog/file.patch
cp ebpf_prog/opensnitch.c ebpf_prog/Makefile linux-5.8/samples/bpf
cd linux-5.8 && yes "" | make oldconfig && make prepare && make headers_install # (1 min)
cd samples/bpf && make
objdump -h opensnitch.o #you should see many section, number 1 should be called kprobe/tcp_v4_connect
llvm-strip -g opensnitch.o #remove debug info
sudo cp opensnitch.o /etc/opensnitchd
cd ../../../daemon
--opensnitchd expects to find opensnitch.o in /etc/opensnitchd/
--start opensnitchd with:
opensnitchd -rules-path /etc/opensnitchd/rules -process-monitor-method ebpf
Co-authored-by: themighty1 <you@example.com>
Co-authored-by: Gustavo Iñiguez Goia <gooffy1@gmail.com>
Sometimes when querying the kernel for a given connection, the inode of
the connection is 0, i.e.: invalid (or not yet valid).
In these cases we search for the connection in /proc/net/. It turns out
that some connections are found in netstat but the inode is still 0, and
we were accepting them erronously.
As a result, when looking for the inode under /proc we didn't find it,
so an "Unknown process" dialog was shown to the user.
Discarding this type of connections avoids unknown process dialogs when
using Epiphany in particular. It retries to establish the connection
several times, and finally we're able to find the PID of the process.
We were checking several times if a packet was IPv6.
Additionally we were itereating over all the layers of the packet, when
in reality we're only interested in network layer and transport layer.
This change brings down packets parsing from ~200µs to ~2µs.
In case we're connecting to an IP directly, or if an IP is not resolved,
leave the DstHost field empty and format it appropiately on the UIs.
Otherwise we can't know (easily) if the field DstHost of a connection is
an IP or a domain.
When a new connection is about to be established and the system performs
a dns resolution, we displayed it like this: 9.9.9.9 (www.opensnitch.io)
It added visibility of what was going on, but if you created a rule to
filter by destination host, you were prompted twice to allow firstly the
DNS query, and secondly the TCP connection, which was a bit annoying.
Some users (#5) also asked to display just the domain, so now we only
display the domain name.
(1/2)
We start receiving notifications from the UI, which allow us to change
configurations and perform actions on the daemon.
The concept of Node has also been introduced, which identifies every
daemon (client) connected to the UI (server).
These options has been added:
- Enable/Disable firewall interception (for all nodes)
- Change daemons (clients) configuration. globally or per node.
- Change prompt dialog options.
We have fixed some bugs along the way:
- Close audit client connection gracefully.
- Exclude our own connections from being intercepted.
- Better handling of client connection status with the UI.
We probably has also introduced some other bugs (not listed here).
- Dump connections from kernel querying by source port + protocol.
- Prioritize responses which match the outgoing connection.
- If we don't get any response, apply the default action configured in
/etc/opensnitchd/default-config.json
--
A connection can be considered unique if:
protocol + source port + source ip + destination ip + destination port
We can be quite sure that only one process has created the connection.
However, many times, querying the kernel for the connection details by
all these parameters results in no response.
A regular query and normal response would be:
query: TCP:47344:192.168.1.106 -> 151.101.65.140:443
response: 47344:192.168.1.106 -> 151.101.65.140:443, inode: 1234567, ...
But in another cases, the details of the outgoing connection differs
from the kernel response, or it even doesn't exist.
However, if we query by protocol+source port, we can get more entries, and
somewhat guess what program opened the outgoing connection.
Some examples of querying by outgoing connection and response from
kernel:
query: 8612:192.168.1.5 -> 192.168.1.255:8612
response: 8612:192.168.1.105 -> 0.0.0.0:0
query: 123:192.168.1.5 -> 217.144.138.234:123
response: 123:0.0.0.0 -> 0.0.0.0:0
query: 45015:127.0.0.1 -> 239.255.255.250:1900
response: 45015:127.0.0.1 -> 0.0.0.0:0
query: 50416:fe80::9fc2:ddcf:df22:aa50 -> fe80::1:53
response: 50416:254.128.0.0 -> 254.128.0.0:53
query: 51413:192.168.1.106 -> 103.224.182.250:1337
response: 51413:0.0.0.0 -> 0.0.0.0:0
With the current implementation, it's not possible to know what
process/pid has created an outgoing connection, but there's still
valuable information that the user may want to know, so:
- display outgoing connections even if the process name/path is unknown.
(src ip, dst ip, dst port, uid)
- get outgoing connection uid if kernel > 3.6.