diff options
author | 2022-01-18 14:14:44 -0500 | |
---|---|---|
committer | 2022-01-18 14:14:44 -0500 | |
commit | 4dd1e92ba1c0687d3f5860ccc58c80d28c8905b8 (patch) | |
tree | 49fc769149cbc3a343cee46838c6a19c9e4145f3 /src | |
parent | gitlab-ci: fix debian_ci build (dh_missing hostnames) (#4865) (diff) | |
download | firejail-4dd1e92ba1c0687d3f5860ccc58c80d28c8905b8.tar.gz firejail-4dd1e92ba1c0687d3f5860ccc58c80d28c8905b8.tar.zst firejail-4dd1e92ba1c0687d3f5860ccc58c80d28c8905b8.zip |
nettrace fixes
Diffstat (limited to 'src')
-rw-r--r-- | src/firejail/main.c | 34 | ||||
-rw-r--r-- | src/firejail/netfilter.c | 5 | ||||
-rw-r--r-- | src/fnettrace/fnettrace.h | 9 | ||||
-rw-r--r-- | src/fnettrace/hostnames | 2 | ||||
-rw-r--r-- | src/fnettrace/main.c | 72 | ||||
-rw-r--r-- | src/fnettrace/radix.c | 52 | ||||
-rw-r--r-- | src/man/firejail.txt | 46 |
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) { | |||
93 | void netfilter_trace(pid_t pid) { | 93 | void 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 | ||
39 | static inline void ansi_topleft(void) { | 38 | static 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; | |||
26 | static char *arg_log = NULL; | 26 | static char *arg_log = NULL; |
27 | 27 | ||
28 | typedef struct hnode_t { | 28 | typedef 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 |
46 | HNode *dlist = NULL; | 46 | HNode *dlist = NULL; |
47 | 47 | ||
48 | |||
49 | // speed up malloc/free | ||
50 | #define HNODE_MAX_MALLOC 16 | ||
51 | static HNode *hnode_unused = NULL; | ||
52 | static int hnode_malloc_cnt = 0; | ||
53 | HNode *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 | |||
70 | void hfree(HNode *ptr) { | ||
71 | assert(ptr); | ||
72 | memset(ptr, 0, sizeof(HNode)); | ||
73 | ptr->hnext = hnode_unused; | ||
74 | hnode_unused = ptr; | ||
75 | } | ||
76 | |||
77 | |||
48 | static void hnode_add(uint32_t ip_src, uint8_t protocol, uint16_t port_src, uint32_t bytes) { | 78 | static 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 | ||
195 | static void hnode_print(unsigned bw) { | 224 | static 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 | ||
296 | static void run_trace(void) { | 337 | static 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 { | |||
34 | RNode *head = 0; | 34 | RNode *head = 0; |
35 | int radix_nodes = 0; | 35 | int radix_nodes = 0; |
36 | 36 | ||
37 | // get rid of the malloc overhead | ||
38 | #define RNODE_MAX_MALLOC 128 | ||
39 | static RNode *rnode_unused = NULL; | ||
40 | static int rnode_malloc_cnt = 0; | ||
41 | static 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 | |||
55 | static 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 | |||
37 | static inline RNode *addOne(RNode *ptr, char *name) { | 65 | static 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 | ||
1467 | Several type of programs (email clients, multiplayer games etc.) talk to a very small | ||
1468 | number of IP addresses. But the best example is tor browser. It only talks to a guard node, | ||
1469 | and there are two or three more on standby in case the main one fails. | ||
1470 | During startup, the browser contacts all of them, after that it keeps talking to the main | ||
1471 | one... for weeks! | ||
1472 | |||
1473 | Use the network locking feature to build and deploy a network firewall in your sandbox. | ||
1474 | The firewall allows only the network traffic to the IP addresses detected during the program | ||
1475 | startup. Traffic to any other address is quietly dropped. By default the startup monitoring | ||
1476 | time 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 |
1467 | Use this option when you want to assign an IP address in a new namespace and | 1489 | Use this option when you want to assign an IP address in a new namespace and |
1468 | the parent interface specified by --net is not configured. An IP address and | 1490 | the 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 |
1501 | 7383 netblue 9.045 0.112 firejail \-\-net=eth0 transmission | 1523 | 7383 netblue 9.045 0.112 firejail \-\-net=eth0 transmission |
1502 | .TP | 1524 | .TP |
1503 | \fB\-\-nettrace=name|pid | 1525 | \fB\-\-nettrace[=name|pid] |
1504 | Monitor TCP and UDP traffic coming into the sandbox specified by name or pid. Only networked sandboxes | 1526 | Monitor TCP and UDP traffic coming into the sandbox specified by name or pid. Only networked sandboxes |
1505 | created with \-\-net are supported. | 1527 | created with \-\-net are supported. |
1506 | .br | 1528 | .br |
1507 | 1529 | ||
1508 | .br | 1530 | .br |
1509 | $ firejail --nettrace=browser | 1531 | Without 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 |
1521 | If /usr/bin/geoiplookup is installed (geoip-bin packet in Debian), | 1553 | If /usr/bin/geoiplookup is installed (geoip-bin package in Debian), |
1522 | the country the IP address originates from is added to the trace. | 1554 | the country the IP address originates from is added to the trace. |
1523 | We also use the static IP map in /etc/firejail/hostnames | 1555 | We also use the static IP map in /etc/firejail/hostnames |
1524 | to print the domain names for some of the more common websites and cloud platforms. | 1556 | to print the domain names for some of the more common websites and cloud platforms. |