aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar netblue30 <netblue30@protonmail.com>2022-01-18 14:14:44 -0500
committerLibravatar netblue30 <netblue30@protonmail.com>2022-01-18 14:14:44 -0500
commit4dd1e92ba1c0687d3f5860ccc58c80d28c8905b8 (patch)
tree49fc769149cbc3a343cee46838c6a19c9e4145f3
parentgitlab-ci: fix debian_ci build (dh_missing hostnames) (#4865) (diff)
downloadfirejail-4dd1e92ba1c0687d3f5860ccc58c80d28c8905b8.tar.gz
firejail-4dd1e92ba1c0687d3f5860ccc58c80d28c8905b8.tar.zst
firejail-4dd1e92ba1c0687d3f5860ccc58c80d28c8905b8.zip
nettrace fixes
-rw-r--r--src/firejail/main.c34
-rw-r--r--src/firejail/netfilter.c5
-rw-r--r--src/fnettrace/fnettrace.h9
-rw-r--r--src/fnettrace/hostnames2
-rw-r--r--src/fnettrace/main.c72
-rw-r--r--src/fnettrace/radix.c52
-rw-r--r--src/man/firejail.txt46
7 files changed, 177 insertions, 43 deletions
diff --git a/src/firejail/main.c b/src/firejail/main.c
index 21a289efb..d614ae1ac 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -409,9 +409,22 @@ static void run_cmd_and_exit(int i, int argc, char **argv) {
409 } 409 }
410#endif 410#endif
411#ifdef HAVE_NETWORK 411#ifdef HAVE_NETWORK
412 else if (strcmp(argv[i], "--nettrace") == 0) {
413 if (checkcfg(CFG_NETWORK)) {
414 netfilter_trace(0);
415 }
416 else
417 exit_err_feature("networking");
418 exit(0);
419 }
412 else if (strncmp(argv[i], "--nettrace=", 11) == 0) { 420 else if (strncmp(argv[i], "--nettrace=", 11) == 0) {
413 pid_t pid = require_pid(argv[i] + 11); 421 if (checkcfg(CFG_NETWORK)) {
414 netfilter_trace(pid); 422 pid_t pid = require_pid(argv[i] + 11);
423 netfilter_trace(pid);
424 }
425 else
426 exit_err_feature("networking");
427 exit(0);
415 } 428 }
416 else if (strncmp(argv[i], "--bandwidth=", 12) == 0) { 429 else if (strncmp(argv[i], "--bandwidth=", 12) == 0) {
417 if (checkcfg(CFG_NETWORK)) { 430 if (checkcfg(CFG_NETWORK)) {
@@ -2316,11 +2329,20 @@ int main(int argc, char **argv, char **envp) {
2316 continue; 2329 continue;
2317 } 2330 }
2318#ifdef HAVE_NETWORK 2331#ifdef HAVE_NETWORK
2319 else if (strcmp(argv[i], "--netlock") == 0) 2332 else if (strcmp(argv[i], "--netlock") == 0) {
2320 arg_netlock = 1; 2333 if (checkcfg(CFG_NETWORK))
2334 arg_netlock = 1;
2335 else
2336 exit_err_feature("networking");
2337 }
2321 else if (strncmp(argv[i], "--netlock=", 10) == 0) { 2338 else if (strncmp(argv[i], "--netlock=", 10) == 0) {
2322 pid_t pid = require_pid(argv[i] + 10); 2339 if (checkcfg(CFG_NETWORK)) {
2323 netfilter_netlock(pid); 2340 pid_t pid = require_pid(argv[i] + 10);
2341 netfilter_netlock(pid);
2342 }
2343 else
2344 exit_err_feature("networking");
2345 exit(0);
2324 } 2346 }
2325 else if (strncmp(argv[i], "--interface=", 12) == 0) { 2347 else if (strncmp(argv[i], "--interface=", 12) == 0) {
2326 if (checkcfg(CFG_NETWORK)) { 2348 if (checkcfg(CFG_NETWORK)) {
diff --git a/src/firejail/netfilter.c b/src/firejail/netfilter.c
index 61164d740..939ab29fa 100644
--- a/src/firejail/netfilter.c
+++ b/src/firejail/netfilter.c
@@ -93,7 +93,10 @@ void netfilter_netlock(pid_t pid) {
93void netfilter_trace(pid_t pid) { 93void netfilter_trace(pid_t pid) {
94 EUID_ASSERT(); 94 EUID_ASSERT();
95 95
96 enter_network_namespace(pid); 96 // a pid of 0 means the main system network namespace
97 if (pid)
98 enter_network_namespace(pid);
99
97 char *cmd; 100 char *cmd;
98 if (asprintf(&cmd, "%s/firejail/fnettrace", LIBDIR) == -1) 101 if (asprintf(&cmd, "%s/firejail/fnettrace", LIBDIR) == -1)
99 errExit("asprintf"); 102 errExit("asprintf");
diff --git a/src/fnettrace/fnettrace.h b/src/fnettrace/fnettrace.h
index 59b9618a9..66b7378da 100644
--- a/src/fnettrace/fnettrace.h
+++ b/src/fnettrace/fnettrace.h
@@ -29,11 +29,10 @@
29#include <stdarg.h> 29#include <stdarg.h>
30//#define DEBUG 1 30//#define DEBUG 1
31 31
32//#define NETLOCK_INTERVAL 15 32#define NETLOCK_INTERVAL 60 // seconds
33#define NETLOCK_INTERVAL 60 33#define DISPLAY_INTERVAL 2 // seconds
34#define DISPLAY_INTERVAL 2 34#define DISPLAY_TTL 4 // display intervals (4 * 2 seconds)
35#define DISPLAY_TTL 4 35#define DISPLAY_BW_UNITS 20 // length of the bandwidth bar
36#define DISPLAY_BW_UNITS 20
37 36
38 37
39static inline void ansi_topleft(void) { 38static inline void ansi_topleft(void) {
diff --git a/src/fnettrace/hostnames b/src/fnettrace/hostnames
index a808a3b09..e24ecf218 100644
--- a/src/fnettrace/hostnames
+++ b/src/fnettrace/hostnames
@@ -20,7 +20,7 @@
20# 20#
21# Static Internet Map 21# Static Internet Map
22# 22#
23# Unfortunately, we cannot do a hostname lookup. This will leak a lot of 23# Unfortunately we cannot do a hostname lookup. This will leak a lot of
24# information about what network resources we access. 24# information about what network resources we access.
25# A static map, helped out by geoip package available on all Linux distros, 25# A static map, helped out by geoip package available on all Linux distros,
26# will have to do it for now! 26# will have to do it for now!
diff --git a/src/fnettrace/main.c b/src/fnettrace/main.c
index d5772328c..331311ee8 100644
--- a/src/fnettrace/main.c
+++ b/src/fnettrace/main.c
@@ -26,7 +26,7 @@ static int arg_netfilter = 0;
26static char *arg_log = NULL; 26static char *arg_log = NULL;
27 27
28typedef struct hnode_t { 28typedef struct hnode_t {
29 struct hnode_t *hnext; // used for hash table 29 struct hnode_t *hnext; // used for hash table and unused linked list
30 struct hnode_t *dnext; // used to display stremas on the screen 30 struct hnode_t *dnext; // used to display stremas on the screen
31 uint32_t ip_src; 31 uint32_t ip_src;
32 uint32_t bytes; // number of bytes received in the last display interval 32 uint32_t bytes; // number of bytes received in the last display interval
@@ -45,6 +45,36 @@ HNode *htable[HMAX] = {NULL};
45// display linked list 45// display linked list
46HNode *dlist = NULL; 46HNode *dlist = NULL;
47 47
48
49// speed up malloc/free
50#define HNODE_MAX_MALLOC 16
51static HNode *hnode_unused = NULL;
52static int hnode_malloc_cnt = 0;
53HNode *hmalloc(void) {
54 if (hnode_unused == NULL) {
55 hnode_unused = malloc(sizeof(HNode) * HNODE_MAX_MALLOC);
56 if (!hnode_unused)
57 errExit("malloc");
58 memset(hnode_unused, 0, sizeof(HNode) * HNODE_MAX_MALLOC);
59 HNode *ptr = hnode_unused;
60 int i;
61 for ( i = 1; i < HNODE_MAX_MALLOC; i++, ptr++)
62 ptr->hnext = hnode_unused + i;
63 }
64
65 HNode *rv = hnode_unused;
66 hnode_unused = hnode_unused->hnext;
67 return rv;
68}
69
70void hfree(HNode *ptr) {
71 assert(ptr);
72 memset(ptr, 0, sizeof(HNode));
73 ptr->hnext = hnode_unused;
74 hnode_unused = ptr;
75}
76
77
48static void hnode_add(uint32_t ip_src, uint8_t protocol, uint16_t port_src, uint32_t bytes) { 78static void hnode_add(uint32_t ip_src, uint8_t protocol, uint16_t port_src, uint32_t bytes) {
49 uint8_t h = hash(ip_src); 79 uint8_t h = hash(ip_src);
50 80
@@ -65,9 +95,8 @@ static void hnode_add(uint32_t ip_src, uint8_t protocol, uint16_t port_src, uint
65#ifdef DEBUG 95#ifdef DEBUG
66 printf("malloc %d.%d.%d.%d\n", PRINT_IP(ip_src)); 96 printf("malloc %d.%d.%d.%d\n", PRINT_IP(ip_src));
67#endif 97#endif
68 HNode *hnew = malloc(sizeof(HNode)); 98 HNode *hnew = hmalloc();
69 if (!hnew) 99 assert(hnew);
70 errExit("malloc");
71 hnew->hostname = NULL; 100 hnew->hostname = NULL;
72 hnew->ip_src = ip_src; 101 hnew->ip_src = ip_src;
73 hnew->port_src = port_src; 102 hnew->port_src = port_src;
@@ -117,7 +146,7 @@ static void hnode_free(HNode *elem) {
117 htable[h] = elem->hnext; 146 htable[h] = elem->hnext;
118 else 147 else
119 prev->hnext = elem->hnext; 148 prev->hnext = elem->hnext;
120 free(elem); 149 hfree(elem);
121} 150}
122 151
123#ifdef DEBUG 152#ifdef DEBUG
@@ -194,14 +223,15 @@ static unsigned adjust_bandwidth(unsigned bw) {
194 223
195static void hnode_print(unsigned bw) { 224static void hnode_print(unsigned bw) {
196 assert(!arg_netfilter); 225 assert(!arg_netfilter);
197 ansi_clrscr(); 226 bw = (bw < 1024 * DISPLAY_INTERVAL)? 1024 * DISPLAY_INTERVAL: bw;
198
199#ifdef DEBUG 227#ifdef DEBUG
200 printf("*********************\n"); 228 printf("*********************\n");
201 debug_dlist(); 229 debug_dlist();
202 printf("-----------------------------\n"); 230 printf("-----------------------------\n");
203 debug_hnode(); 231 debug_hnode();
204 printf("*********************\n"); 232 printf("*********************\n");
233#else
234 ansi_clrscr();
205#endif 235#endif
206 236
207 // get terminal size 237 // get terminal size
@@ -291,6 +321,17 @@ static void hnode_print(unsigned bw) {
291 ptr = next; 321 ptr = next;
292 } 322 }
293 323
324#ifdef DEBUG
325 {
326 int cnt = 0;
327 HNode *ptr = hnode_unused;
328 while (ptr) {
329 cnt++;
330 ptr = ptr->hnext;
331 }
332 printf("hnode unused %d\n", cnt);
333 }
334#endif
294} 335}
295 336
296static void run_trace(void) { 337static void run_trace(void) {
@@ -343,9 +384,22 @@ static void run_trace(void) {
343 384
344 unsigned bytes = recvfrom(sock, buf, MAX_BUF_SIZE, 0, NULL, NULL); 385 unsigned bytes = recvfrom(sock, buf, MAX_BUF_SIZE, 0, NULL, NULL);
345 if (bytes >= 20) { // size of IP header 386 if (bytes >= 20) { // size of IP header
346 bw += bytes + 14; // assume a 14 byte Ethernet layer 387#ifdef DEBUG
388 {
389 uint32_t ip_src;
390 memcpy(&ip_src, buf + 12, 4);
391 ip_src = ntohl(ip_src);
392
393 uint32_t ip_dst;
394 memcpy(&ip_dst, buf + 16, 4);
395 ip_dst = ntohl(ip_dst);
396 printf("%d.%d.%d.%d -> %d.%d.%d.%d, %u bytes\n", PRINT_IP(ip_src), PRINT_IP(ip_dst), bytes);
397 }
398#endif
347 // filter out loopback traffic 399 // filter out loopback traffic
348 if (buf[12] != 127) { 400 if (buf[12] != 127 && buf[16] != 127) {
401 bw += bytes + 14; // assume a 14 byte Ethernet layer
402
349 uint32_t ip_src; 403 uint32_t ip_src;
350 memcpy(&ip_src, buf + 12, 4); 404 memcpy(&ip_src, buf + 12, 4);
351 ip_src = ntohl(ip_src); 405 ip_src = ntohl(ip_src);
diff --git a/src/fnettrace/radix.c b/src/fnettrace/radix.c
index b9104bd39..c9493717d 100644
--- a/src/fnettrace/radix.c
+++ b/src/fnettrace/radix.c
@@ -34,18 +34,44 @@ typedef struct rnode_t {
34RNode *head = 0; 34RNode *head = 0;
35int radix_nodes = 0; 35int radix_nodes = 0;
36 36
37// get rid of the malloc overhead
38#define RNODE_MAX_MALLOC 128
39static RNode *rnode_unused = NULL;
40static int rnode_malloc_cnt = 0;
41static RNode *rmalloc(void) {
42 if (rnode_unused == NULL || rnode_malloc_cnt >= RNODE_MAX_MALLOC) {
43 rnode_unused = malloc(sizeof(RNode) * RNODE_MAX_MALLOC);
44 if (!rnode_unused)
45 errExit("malloc");
46 memset(rnode_unused, 0, sizeof(RNode) * RNODE_MAX_MALLOC);
47 rnode_malloc_cnt = 0;
48 }
49
50 rnode_malloc_cnt++;
51 return rnode_unused + rnode_malloc_cnt - 1;
52}
53
54
55static inline char *duplicate_name(const char *name) {
56 assert(name);
57
58 if (strcmp(name, "United States") == 0)
59 return "United States";
60 else if (strcmp(name, "Amazon") == 0)
61 return "Amazon";
62 return strdup(name);
63}
64
37static inline RNode *addOne(RNode *ptr, char *name) { 65static inline RNode *addOne(RNode *ptr, char *name) {
38 assert(ptr); 66 assert(ptr);
39 if (ptr->one) 67 if (ptr->one)
40 return ptr->one; 68 return ptr->one;
41 RNode *node = malloc(sizeof(RNode)); 69 RNode *node = rmalloc();
42 if (!node) 70 assert(node);
43 errExit("malloc");
44 memset(node, 0, sizeof(RNode));
45 if (name) { 71 if (name) {
46 node->name = strdup(name); 72 node->name = duplicate_name(name);
47 if (!node->name) 73 if (!node->name)
48 errExit("strdup"); 74 errExit("duplicate name");
49 } 75 }
50 76
51 ptr->one = node; 77 ptr->one = node;
@@ -56,14 +82,12 @@ static inline RNode *addZero(RNode *ptr, char *name) {
56 assert(ptr); 82 assert(ptr);
57 if (ptr->zero) 83 if (ptr->zero)
58 return ptr->zero; 84 return ptr->zero;
59 RNode *node = malloc(sizeof(RNode)); 85 RNode *node = rmalloc();
60 if (!node) 86 assert(node);
61 errExit("malloc");
62 memset(node, 0, sizeof(RNode));
63 if (name) { 87 if (name) {
64 node->name = strdup(name); 88 node->name = duplicate_name(name);
65 if (!node->name) 89 if (!node->name)
66 errExit("strdup"); 90 errExit("duplicate name");
67 } 91 }
68 92
69 ptr->zero = node; 93 ptr->zero = node;
@@ -97,9 +121,9 @@ char *radix_add(uint32_t ip, uint32_t mask, char *name) {
97 } 121 }
98 assert(ptr); 122 assert(ptr);
99 if (!ptr->name) { 123 if (!ptr->name) {
100 ptr->name = strdup(name); 124 ptr->name = duplicate_name(name);
101 if (!ptr->name) 125 if (!ptr->name)
102 errExit("strdup"); 126 errExit("duplicate_name");
103 } 127 }
104 128
105 return ptr->name; 129 return ptr->name;
diff --git a/src/man/firejail.txt b/src/man/firejail.txt
index a5704e995..9e3bce643 100644
--- a/src/man/firejail.txt
+++ b/src/man/firejail.txt
@@ -1463,6 +1463,28 @@ $ firejail --name=browser --net=eth0 --netfilter firefox &
1463$ firejail --netfilter6.print=browser 1463$ firejail --netfilter6.print=browser
1464 1464
1465.TP 1465.TP
1466\fB\-\-netlock=name/pid
1467Several type of programs (email clients, multiplayer games etc.) talk to a very small
1468number of IP addresses. But the best example is tor browser. It only talks to a guard node,
1469and there are two or three more on standby in case the main one fails.
1470During startup, the browser contacts all of them, after that it keeps talking to the main
1471one... for weeks!
1472
1473Use the network locking feature to build and deploy a network firewall in your sandbox.
1474The firewall allows only the network traffic to the IP addresses detected during the program
1475startup. Traffic to any other address is quietly dropped. By default the startup monitoring
1476time is one minute. Example:
1477.br
1478
1479.br
1480$ firejail --net=eth0 --netlock \\
1481.br
1482--private=~/tor-browser_en-US ./start-tor-browser.desktop
1483.br
1484
1485.br
1486
1487.TP
1466\fB\-\-netmask=address 1488\fB\-\-netmask=address
1467Use this option when you want to assign an IP address in a new namespace and 1489Use this option when you want to assign an IP address in a new namespace and
1468the parent interface specified by --net is not configured. An IP address and 1490the parent interface specified by --net is not configured. An IP address and
@@ -1500,25 +1522,35 @@ PID User RX(KB/s) TX(KB/s) Command
1500.br 1522.br
15017383 netblue 9.045 0.112 firejail \-\-net=eth0 transmission 15237383 netblue 9.045 0.112 firejail \-\-net=eth0 transmission
1502.TP 1524.TP
1503\fB\-\-nettrace=name|pid 1525\fB\-\-nettrace[=name|pid]
1504Monitor TCP and UDP traffic coming into the sandbox specified by name or pid. Only networked sandboxes 1526Monitor TCP and UDP traffic coming into the sandbox specified by name or pid. Only networked sandboxes
1505created with \-\-net are supported. 1527created with \-\-net are supported.
1506.br 1528.br
1507 1529
1508.br 1530.br
1509$ firejail --nettrace=browser 1531Without a name/pid, Firejail will monitor the main system network namespace.
1532.br
1533
1534.br
1535 $ firejail --nettrace=browser
1536.br
1537
1538.br
1539 95 KB/s geoip 457, IP database 4436
1540.br
1541 52 KB/s *********** 64.222.84.207:443 United States
1510.br 1542.br
1511 86 KB/s ********* 64.222.84.207:443 United States 1543 33 KB/s ******* 89.147.74.105:63930 Hungary
1512.br 1544.br
1513 76 KB/s ******** 192.229.210.163:443 MCI 1545 0 B/s 45.90.28.0:443 NextDNS
1514.br 1546.br
1515 111 B/s 9.9.9.9:53 Quad9 DNS 1547 0 B/s 94.70.122.176:52309(UDP) Greece
1516.br 1548.br
1517 32 KB/s *** 142.250.179.182:443 Google 1549 339 B/s 104.26.7.35:443 Cloudflare
1518.br 1550.br
1519 1551
1520.br 1552.br
1521If /usr/bin/geoiplookup is installed (geoip-bin packet in Debian), 1553If /usr/bin/geoiplookup is installed (geoip-bin package in Debian),
1522the country the IP address originates from is added to the trace. 1554the country the IP address originates from is added to the trace.
1523We also use the static IP map in /etc/firejail/hostnames 1555We also use the static IP map in /etc/firejail/hostnames
1524to print the domain names for some of the more common websites and cloud platforms. 1556to print the domain names for some of the more common websites and cloud platforms.