|
39 | 39 | """ |
40 | 40 |
|
41 | 41 |
|
| 42 | +def _get_default_gateway() -> str | None: |
| 43 | + """Returns the system's default gateway IP, or None if not found.""" |
| 44 | + try: |
| 45 | + result = subprocess.run( |
| 46 | + ["ip", "route"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True |
| 47 | + ) |
| 48 | + for line in result.stdout.split("\n"): |
| 49 | + if line.startswith("default via"): |
| 50 | + return line.split()[2] # Extract the gateway IP |
| 51 | + |
| 52 | + except Exception as e: |
| 53 | + print(f"❌ Error detecting gateway: {e}") |
| 54 | + |
| 55 | + return None |
| 56 | + |
| 57 | + |
42 | 58 | def _wrap_in_sandbox(cmd: list[str]) -> list[str]: |
43 | 59 | """Wrap the given command in Firejail. |
44 | 60 | This function modifies the command to include Firejail options |
@@ -66,8 +82,50 @@ def _wrap_in_sandbox(cmd: list[str]) -> list[str]: |
66 | 82 | return cmd |
67 | 83 |
|
68 | 84 | # Firejail is available |
| 85 | + gateway_ip: str | None = _get_default_gateway() |
| 86 | + if not gateway_ip: |
| 87 | + print("❌ No gateway detected. Blocking all egress traffic.") |
| 88 | + netfilter_rules = [ |
| 89 | + "--netfilter=reject 0.0.0.0/0" # Block all traffic if no gateway is detected |
| 90 | + ] |
| 91 | + else: |
| 92 | + print(f"🔥 Allowing egress only via gateway: {gateway_ip}") |
| 93 | + netfilter_rules = [ |
| 94 | + # Allow traffic only via the detected gateway: |
| 95 | + f"--netfilter=accept {gateway_ip}", |
| 96 | + # Block all private networks and localhost: |
| 97 | + "--netfilter=reject 10.0.0.0/8", |
| 98 | + "--netfilter=reject 172.16.0.0/12", |
| 99 | + "--netfilter=reject 192.168.0.0/16", |
| 100 | + "--netfilter=reject 127.0.0.0/8", |
| 101 | + "--netfilter=reject 169.254.0.0/16", |
| 102 | + "--netfilter=reject ::1/128", |
| 103 | + "--netfilter=reject fc00::/7", |
| 104 | + "--netfilter=reject fe80::/10", |
| 105 | + ] |
| 106 | + |
| 107 | + # Use Google DNS resolution. It is fine to use others but we just don't want to block DNS. |
| 108 | + dns_args = [ |
| 109 | + "--dns=8.8.8.8", |
| 110 | + "--dns=8.8.4.4", |
| 111 | + ] |
| 112 | + # Firejail Command with DNS resolution allowed |
| 113 | + firejail_args = ( |
| 114 | + [ |
| 115 | + "--private", # Isolates the filesystem (prevents access to user files) |
| 116 | + "--net=none", # Ensures restricted networking |
| 117 | + "--seccomp", # Enable seccomp-bpf syscall filtering |
| 118 | + "--blacklist=/var/run/" # Block system sockets |
| 119 | + "--private-tmp" # Creates an isolated temp directory for each process |
| 120 | + "--rlimit-cpu=120", # Limits CPU time to 1 second (prevents runaway processes) |
| 121 | + "--rlimit-fsize=10000000", # Limits max file size to 10 MB (prevents excessive disk writes) |
| 122 | + ] |
| 123 | + + netfilter_rules |
| 124 | + + dns_args |
| 125 | + ) |
| 126 | + |
69 | 127 | print("Firejail found. Running with Firejail sandboxing.") |
70 | | - return ["firejail", "--private", "--net=none"] + cmd |
| 128 | + return ["firejail"] + firejail_args + cmd |
71 | 129 |
|
72 | 130 |
|
73 | 131 | def sandboxed_run(): |
|
0 commit comments