diff options
-rw-r--r-- | debian/changelog | 2 | ||||
-rw-r--r-- | helpers/bitmask-root | 76 |
2 files changed, 69 insertions, 9 deletions
diff --git a/debian/changelog b/debian/changelog index 55e1f6b..cb996ed 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -riseup-vpn (0.21.2.5) groovy; urgency=medium +riseup-vpn (0.21.2.6) groovy; urgency=medium * Initial Release. diff --git a/helpers/bitmask-root b/helpers/bitmask-root index 6615d3b..f105bfc 100644 --- a/helpers/bitmask-root +++ b/helpers/bitmask-root @@ -43,6 +43,7 @@ The `openvpn start` action is special: it calls exec on openvpn and replaces the current process. If the `restart` parameter is passed, the firewall will not be teared down in the case of an error during launch. """ +import ipaddress import os import re import signal @@ -83,7 +84,7 @@ def get_no_group_name(): def tostr(s): return s.decode('utf-8') -VERSION = "12" +VERSION = "13" SCRIPT = "bitmask-root" NAMESERVER_TCP = "10.41.0.1" NAMESERVER_UDP = "10.42.0.1" @@ -275,6 +276,29 @@ def get_process_list(): return filter(None, res) +def getIPv4AllowAddresses(): + lines = [] + try: + with open("/etc/bitmask/ipv4.allow", 'r') as f: + lines = [l.strip() for l in f.readlines()] + except FileNotFoundError: + return lines + + lines = filter(lambda x: ipaddress.ip_address(x).version == 4, lines) + return list(filter(lambda x: ipaddress.ip_address(x).is_private, lines)) + +def getIPv6AllowAddresses(): + lines = [] + try: + with open("/etc/bitmask/ipv6.allow", 'r') as f: + lines = [l.strip() for l in f.readlines()] + except FileNotFoundError: + return lines + + lines = filter(lambda x: ipaddress.ip_address(x).version == 6, lines) + return list(filter(lambda x: ipaddress.ip_address(x).is_private, lines)) + + def run(command, *args, **options): """ Run an external command. @@ -655,6 +679,16 @@ def firewall_start(args): local_network_ipv6 = get_local_network_ipv6(default_device) gateways = get_gateways(args) + # allow local address in listed exception list + # this will allow all ports and both tcp and udp. + def allow4(ip): + ip4tables("--append", BITMASK_CHAIN, "--destination", ip, + "-o", default_device, "--jump", "ACCEPT") + + def allow6(ip): + ip6tables("--append", BITMASK_CHAIN, "--destination", ip, + "-o", default_device, "--jump", "ACCEPT") + # add custom chain "bitmask" to front of OUTPUT chain for both # the 'filter' and the 'nat' tables. if not ipv4_chain_exists(BITMASK_CHAIN): @@ -707,11 +741,14 @@ def firewall_start(args): "--protocol", "tcp", "--dport", "53", "--jump", "MASQUERADE") # allow local network traffic + + ipv4_exceptions = getIPv4AllowAddresses() if local_network_ipv4: - # allow local network destinations - ip4tables("--append", BITMASK_CHAIN, - "--destination", local_network_ipv4, "-o", default_device, - "--jump", "ACCEPT") + if len(ipv4_exceptions) == 0: + # allow all local network destinations if no explicit allow rules defined + ip4tables("--append", BITMASK_CHAIN, + "--destination", local_network_ipv4, "-o", default_device, + "--jump", "ACCEPT") # allow local network sources for DNS # (required to allow local network DNS that gets rewritten by NAT # to get passed through so that MASQUERADE can set correct source IP) @@ -731,10 +768,15 @@ def firewall_start(args): "--protocol", "udp", "--destination", "224.0.0.251", "--dport", "5353", "-o", default_device, "--jump", "RETURN") + + + ipv6_exceptions = getIPv6AllowAddresses() if local_network_ipv6: - ip6tables("--append", BITMASK_CHAIN, - "--destination", local_network_ipv6, "-o", default_device, - "--jump", "ACCEPT") + if len(ipv6_exceptions) == 0: + # allow all local network destinations if no explicit allow rules defined + ip6tables("--append", BITMASK_CHAIN, + "--destination", local_network_ipv6, "-o", default_device, + "--jump", "ACCEPT") # allow multicast Simple Service Discovery Protocol ip6tables("--append", BITMASK_CHAIN, "--protocol", "udp", @@ -751,12 +793,29 @@ def firewall_start(args): ip4tables("--append", BITMASK_CHAIN, "--destination", gateway, "-o", default_device, "--jump", "ACCEPT") + # TODO allow ipv6 traffic to gws too + # log rejected packets to syslog if DEBUG: iptables("--append", BITMASK_CHAIN, "-o", default_device, "--jump", "LOG", "--log-prefix", "iptables denied: ", "--log-level", "7") + # allow explicit private exceptions + if len(ipv4_exceptions) != 0: + for ip in ipv4_exceptions: + allow4(ip) + ip4tables("--append", BITMASK_CHAIN, + "--destination", local_network_ipv4, "-o", default_device, + "--jump", "REJECT") + + if len(ipv6_exceptions) != 0: + for ip in ipv6_exceptions: + allow6(ip) + ip6tables("--append", BITMASK_CHAIN, + "--destination", local_network_ipv6, "-o", default_device, + "--jump", "REJECT") + # for now, ensure all other ipv6 packets get rejected (regardless of # device). not sure why, but "-p any" doesn't work. ip6tables("--append", BITMASK_CHAIN, "-p", "tcp", "--jump", "REJECT") @@ -766,6 +825,7 @@ def firewall_start(args): ip4tables("--append", BITMASK_CHAIN, "-o", default_device, "--jump", "REJECT") + # On Qubes OS, add anti-leak rules for proxyVM qubes-firewall.service # Must stay on 'top' of chain! if QUBES_PROXY and QUBES_VER >= 3 and run("grep", "installed\ by\ " + |