diff --git a/Dockerfile b/Dockerfile index 023054e1..debb098e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -89,7 +89,6 @@ ENV VPNSP=pia \ DNS_KEEP_NAMESERVER=off \ # Firewall FIREWALL=on \ - EXTRA_SUBNETS= \ FIREWALL_VPN_INPUT_PORTS= \ FIREWALL_INPUT_PORTS= \ FIREWALL_DEBUG=off \ diff --git a/README.md b/README.md index 5ded1917..30116375 100644 --- a/README.md +++ b/README.md @@ -69,8 +69,8 @@ iptables, DNS over TLS, ShadowSocks and Tinyproxy* Note that you can: - Change the many [environment variables](#environment-variables) available - - Use `-p 8888:8888/tcp` to access the HTTP web proxy (and put your LAN in `EXTRA_SUBNETS` environment variable, in example `192.168.1.0/24`) - - Use `-p 8388:8388/tcp -p 8388:8388/udp` to access the Shadowsocks proxy (and put your LAN in `EXTRA_SUBNETS` environment variable, in example `192.168.1.0/24`) + - Use `-p 8888:8888/tcp` to access the HTTP web proxy + - Use `-p 8388:8388/tcp -p 8388:8388/udp` to access the Shadowsocks proxy - Use `-p 8000:8000/tcp` to access the [HTTP control server](#HTTP-control-server) built-in **If you encounter an issue with the tun device not being available, see [the FAQ](https://github.com/qdm12/gluetun/blob/master/doc/faq.md#how-to-fix-openvpn-failing-to-start)** @@ -230,7 +230,6 @@ That one is important if you want to connect to the container from your LAN for | Variable | Default | Choices | Description | | --- | --- | --- | --- | | `FIREWALL` | `on` | `on` or `off` | Turn on or off the container built-in firewall. You should use it for **debugging purposes** only. | -| `EXTRA_SUBNETS` | | i.e. `192.168.1.0/24,192.168.10.121,10.0.0.5/28` | Comma separated subnets allowed in the container firewall | | `FIREWALL_VPN_INPUT_PORTS` | | i.e. `1000,8080` | Comma separated list of ports to allow from the VPN server side (useful for **vyprvpn** port forwarding) | | `FIREWALL_INPUT_PORTS` | | i.e. `1000,8000` | Comma separated list of ports to allow through the default interface. This seems needed for Kubernetes sidecars. | | `FIREWALL_DEBUG` | `off` | `on` or `off` | Prints every firewall related command. You should use it for **debugging purposes** only. | @@ -304,7 +303,6 @@ There are various ways to achieve this, depending on your use case. 1. Setup a HTTP proxy client, such as [SwitchyOmega for Chrome](https://chrome.google.com/webstore/detail/proxy-switchyomega/padekgcemlokbadohgkifijomclgjgif?hl=en) 1. Ensure the Gluetun container is launched with: - port `8888` published `-p 8888:8888/tcp` - - your LAN subnet, i.e. `192.168.1.0/24`, set as `-e EXTRA_SUBNETS=192.168.1.0/24` 1. With your HTTP proxy client, connect to the Docker host (i.e. `192.168.1.10`) on port `8888`. You need to enter your credentials if you set them with `TINYPROXY_USER` and `TINYPROXY_PASSWORD`. 1. If you set `TINYPROXY_LOG` to `Info`, more information will be logged in the Docker logs @@ -319,7 +317,6 @@ There are various ways to achieve this, depending on your use case. - Android: Shadowsocks by Max Lv 1. Ensure the Gluetun container is launched with: - port `8388` published `-p 8388:8388/tcp -p 8388:8388/udp` - - your LAN subnet, i.e. `192.168.1.0/24`, set as `-e EXTRA_SUBNETS=192.168.1.0/24` 1. With your Shadowsocks proxy client - Enter the Docker host (i.e. `192.168.1.10`) as the server IP - Enter port TCP (and UDP, if available) `8388` as the server port diff --git a/cmd/gluetun/main.go b/cmd/gluetun/main.go index 9c8b4432..825e0308 100644 --- a/cmd/gluetun/main.go +++ b/cmd/gluetun/main.go @@ -150,6 +150,16 @@ func _main(background context.Context, args []string) int { //nolint:gocognit,go firewallConf.SetNetworkInformation(defaultInterface, defaultGateway, localSubnet) + if err := routingConf.Setup(); err != nil { + logger.Error(err) + return 1 + } + defer func() { + if err := routingConf.TearDown(); err != nil { + logger.Error(err) + } + }() + if err := ovpnConf.CheckTUN(); err != nil { logger.Warn(err) err = ovpnConf.CreateTUN() @@ -173,12 +183,6 @@ func _main(background context.Context, args []string) int { //nolint:gocognit,go } } - err = firewallConf.SetAllowedSubnets(ctx, allSettings.Firewall.AllowedSubnets) - if err != nil { - logger.Error(err) - return 1 - } - for _, vpnPort := range allSettings.Firewall.VPNInputPorts { err = firewallConf.SetAllowedPort(ctx, vpnPort, string(constants.TUN)) if err != nil { @@ -193,7 +197,7 @@ func _main(background context.Context, args []string) int { //nolint:gocognit,go logger.Error(err) return 1 } - } + } // TODO move inside firewall? wg := &sync.WaitGroup{} diff --git a/docker-compose.yml b/docker-compose.yml index 440a8b9c..eee84d0f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -35,7 +35,4 @@ services: # Mullvad only - COUNTRY=Sweden - - # Allow for example your LAN, set to: 192.168.1.0/24 - - EXTRA_SUBNETS= restart: always diff --git a/internal/firewall/enable.go b/internal/firewall/enable.go index c87b5fc0..f888a43d 100644 --- a/internal/firewall/enable.go +++ b/internal/firewall/enable.go @@ -93,26 +93,12 @@ func (c *configurator) enable(ctx context.Context) (err error) { if err = c.acceptOutputThroughInterface(ctx, string(constants.TUN), remove); err != nil { return fmt.Errorf("cannot enable firewall: %w", err) } - if err := c.acceptInputFromSubnetToSubnet(ctx, "*", c.localSubnet, c.localSubnet, remove); err != nil { + + // Allows packets from any IP address to go through eth0 / local network + // to reach Gluetun. + if err := c.acceptInputToSubnet(ctx, c.defaultInterface, c.localSubnet, remove); err != nil { return fmt.Errorf("cannot enable firewall: %w", err) } - if err := c.acceptOutputFromSubnetToSubnet(ctx, "*", c.localSubnet, c.localSubnet, remove); err != nil { - return fmt.Errorf("cannot enable firewall: %w", err) - } - for _, subnet := range c.allowedSubnets { - if err := c.acceptInputFromSubnetToSubnet(ctx, c.defaultInterface, subnet, c.localSubnet, remove); err != nil { - return fmt.Errorf("cannot enable firewall: %w", err) - } - if err := c.acceptOutputFromSubnetToSubnet(ctx, c.defaultInterface, c.localSubnet, subnet, remove); err != nil { - return fmt.Errorf("cannot enable firewall: %w", err) - } - } - // Re-ensure all routes exist - for _, subnet := range c.allowedSubnets { - if err := c.routing.AddRouteVia(subnet, c.defaultGateway, c.defaultInterface); err != nil { - return fmt.Errorf("cannot enable firewall: %w", err) - } - } for port, intf := range c.allowedInputPorts { if err := c.acceptInputToPort(ctx, intf, port, remove); err != nil { diff --git a/internal/firewall/firewall.go b/internal/firewall/firewall.go index 9a173508..838042bf 100644 --- a/internal/firewall/firewall.go +++ b/internal/firewall/firewall.go @@ -17,7 +17,6 @@ type Configurator interface { Version(ctx context.Context) (string, error) SetEnabled(ctx context.Context, enabled bool) (err error) SetVPNConnection(ctx context.Context, connection models.OpenVPNConnection) (err error) - SetAllowedSubnets(ctx context.Context, subnets []net.IPNet) (err error) SetAllowedPort(ctx context.Context, port uint16, intf string) (err error) RemoveAllowedPort(ctx context.Context, port uint16) (err error) SetDebug() @@ -40,7 +39,6 @@ type configurator struct { //nolint:maligned // State enabled bool vpnConnection models.OpenVPNConnection - allowedSubnets []net.IPNet allowedInputPorts map[uint16]string // port to interface mapping stateMutex sync.Mutex } diff --git a/internal/firewall/iptables.go b/internal/firewall/iptables.go index 2efe389f..4f811c43 100644 --- a/internal/firewall/iptables.go +++ b/internal/firewall/iptables.go @@ -94,6 +94,16 @@ func (c *configurator) acceptInputThroughInterface(ctx context.Context, intf str )) } +func (c *configurator) acceptInputToSubnet(ctx context.Context, intf string, destination net.IPNet, remove bool) error { + interfaceFlag := "-i " + intf + if intf == "*" { // all interfaces + interfaceFlag = "" + } + return c.runIptablesInstruction(ctx, fmt.Sprintf( + "%s INPUT %s -d %s -j ACCEPT", appendOrDelete(remove), interfaceFlag, destination.String(), + )) +} + func (c *configurator) acceptOutputThroughInterface(ctx context.Context, intf string, remove bool) error { return c.runIptablesInstruction(ctx, fmt.Sprintf( "%s OUTPUT -o %s -j ACCEPT", appendOrDelete(remove), intf, @@ -114,31 +124,6 @@ func (c *configurator) acceptOutputTrafficToVPN(ctx context.Context, appendOrDelete(remove), connection.IP, defaultInterface, connection.Protocol, connection.Protocol, connection.Port)) } -func (c *configurator) acceptInputFromSubnetToSubnet(ctx context.Context, - intf string, sourceSubnet, destinationSubnet net.IPNet, remove bool) error { - interfaceFlag := "-i " + intf - if intf == "*" { // all interfaces - interfaceFlag = "" - } - return c.runIptablesInstruction(ctx, fmt.Sprintf( - "%s INPUT %s -s %s -d %s -j ACCEPT", - appendOrDelete(remove), interfaceFlag, sourceSubnet.String(), destinationSubnet.String(), - )) -} - -// Thanks to @npawelek. -func (c *configurator) acceptOutputFromSubnetToSubnet(ctx context.Context, - intf string, sourceSubnet, destinationSubnet net.IPNet, remove bool) error { - interfaceFlag := "-o " + intf - if intf == "*" { // all interfaces - interfaceFlag = "" - } - return c.runIptablesInstruction(ctx, fmt.Sprintf( - "%s OUTPUT %s -s %s -d %s -j ACCEPT", - appendOrDelete(remove), interfaceFlag, sourceSubnet.String(), destinationSubnet.String(), - )) -} - // Used for port forwarding, with intf set to tun. func (c *configurator) acceptInputToPort(ctx context.Context, intf string, port uint16, remove bool) error { interfaceFlag := "-i " + intf diff --git a/internal/firewall/subnets.go b/internal/firewall/subnets.go deleted file mode 100644 index ef0c2fab..00000000 --- a/internal/firewall/subnets.go +++ /dev/null @@ -1,144 +0,0 @@ -package firewall - -import ( - "context" - "fmt" - "net" -) - -func (c *configurator) SetAllowedSubnets(ctx context.Context, subnets []net.IPNet) (err error) { - c.stateMutex.Lock() - defer c.stateMutex.Unlock() - - if !c.enabled { - c.logger.Info("firewall disabled, only updating allowed subnets internal list and updating routes") - c.updateSubnetRoutes(c.allowedSubnets, subnets) - c.allowedSubnets = make([]net.IPNet, len(subnets)) - copy(c.allowedSubnets, subnets) - return nil - } - - c.logger.Info("setting allowed subnets through firewall...") - - subnetsToAdd := findSubnetsToAdd(c.allowedSubnets, subnets) - subnetsToRemove := findSubnetsToRemove(c.allowedSubnets, subnets) - if len(subnetsToAdd) == 0 && len(subnetsToRemove) == 0 { - return nil - } - - c.removeSubnets(ctx, subnetsToRemove, c.defaultInterface, c.localSubnet) - if err := c.addSubnets(ctx, subnetsToAdd, c.defaultInterface, c.defaultGateway, c.localSubnet); err != nil { - return fmt.Errorf("cannot set allowed subnets through firewall: %w", err) - } - - return nil -} - -func findSubnetsToAdd(oldSubnets, newSubnets []net.IPNet) (subnetsToAdd []net.IPNet) { - for _, newSubnet := range newSubnets { - found := false - for _, oldSubnet := range oldSubnets { - if subnetsAreEqual(oldSubnet, newSubnet) { - found = true - break - } - } - if !found { - subnetsToAdd = append(subnetsToAdd, newSubnet) - } - } - return subnetsToAdd -} - -func findSubnetsToRemove(oldSubnets, newSubnets []net.IPNet) (subnetsToRemove []net.IPNet) { - for _, oldSubnet := range oldSubnets { - found := false - for _, newSubnet := range newSubnets { - if subnetsAreEqual(oldSubnet, newSubnet) { - found = true - break - } - } - if !found { - subnetsToRemove = append(subnetsToRemove, oldSubnet) - } - } - return subnetsToRemove -} - -func subnetsAreEqual(a, b net.IPNet) bool { - return a.IP.Equal(b.IP) && a.Mask.String() == b.Mask.String() -} - -func removeSubnetFromSubnets(subnets []net.IPNet, subnet net.IPNet) []net.IPNet { - L := len(subnets) - for i := range subnets { - if subnetsAreEqual(subnet, subnets[i]) { - subnets[i] = subnets[L-1] - subnets = subnets[:L-1] - break - } - } - return subnets -} - -func (c *configurator) removeSubnets(ctx context.Context, subnets []net.IPNet, defaultInterface string, - localSubnet net.IPNet) { - const remove = true - for _, subnet := range subnets { - failed := false - if err := c.acceptInputFromSubnetToSubnet(ctx, defaultInterface, subnet, localSubnet, remove); err != nil { - failed = true - c.logger.Error("cannot remove outdated allowed subnet through firewall: %s", err) - } - if err := c.acceptOutputFromSubnetToSubnet(ctx, defaultInterface, subnet, localSubnet, remove); err != nil { - failed = true - c.logger.Error("cannot remove outdated allowed subnet through firewall: %s", err) - } - if err := c.routing.DeleteRouteVia(subnet); err != nil { - failed = true - c.logger.Error("cannot remove outdated allowed subnet route: %s", err) - } - if failed { - continue - } - c.allowedSubnets = removeSubnetFromSubnets(c.allowedSubnets, subnet) - } -} - -func (c *configurator) addSubnets(ctx context.Context, subnets []net.IPNet, defaultInterface string, - defaultGateway net.IP, localSubnet net.IPNet) error { - const remove = false - for _, subnet := range subnets { - if err := c.acceptInputFromSubnetToSubnet(ctx, defaultInterface, subnet, localSubnet, remove); err != nil { - return fmt.Errorf("cannot add allowed subnet through firewall: %w", err) - } - if err := c.acceptOutputFromSubnetToSubnet(ctx, defaultInterface, localSubnet, subnet, remove); err != nil { - return fmt.Errorf("cannot add allowed subnet through firewall: %w", err) - } - if err := c.routing.AddRouteVia(subnet, defaultGateway, defaultInterface); err != nil { - return fmt.Errorf("cannot add route for allowed subnet: %w", err) - } - c.allowedSubnets = append(c.allowedSubnets, subnet) - } - return nil -} - -// updateSubnetRoutes does not return an error in order to try to run as many route commands as possible. -func (c *configurator) updateSubnetRoutes(oldSubnets, newSubnets []net.IPNet) { - subnetsToAdd := findSubnetsToAdd(oldSubnets, newSubnets) - subnetsToRemove := findSubnetsToRemove(oldSubnets, newSubnets) - if len(subnetsToAdd) == 0 && len(subnetsToRemove) == 0 { - return - } - for _, subnet := range subnetsToRemove { - if err := c.routing.DeleteRouteVia(subnet); err != nil { - c.logger.Error("cannot remove outdated route for subnet: %s", err) - } - } - for _, subnet := range subnetsToAdd { - if err := c.routing.AddRouteVia(subnet, c.defaultGateway, c.defaultInterface); err != nil { - c.logger.Error("cannot add route for subnet: %s", err) - } - } -} diff --git a/internal/params/firewall.go b/internal/params/firewall.go index e41b1247..cf6e51e2 100644 --- a/internal/params/firewall.go +++ b/internal/params/firewall.go @@ -2,7 +2,6 @@ package params import ( "fmt" - "net" "strconv" "strings" @@ -14,28 +13,6 @@ func (r *reader) GetFirewall() (enabled bool, err error) { return r.envParams.GetOnOff("FIREWALL", libparams.Default("on")) } -// GetExtraSubnets obtains the CIDR subnets from the comma separated list of the -// environment variable EXTRA_SUBNETS. -func (r *reader) GetExtraSubnets() (extraSubnets []net.IPNet, err error) { - s, err := r.envParams.GetEnv("EXTRA_SUBNETS") - if err != nil { - return nil, err - } else if s == "" { - return nil, nil - } - subnets := strings.Split(s, ",") - for _, subnet := range subnets { - _, cidr, err := net.ParseCIDR(subnet) - if err != nil { - return nil, fmt.Errorf("could not parse subnet %q from environment variable with key EXTRA_SUBNETS: %w", subnet, err) - } else if cidr == nil { - return nil, fmt.Errorf("parsing subnet %q resulted in a nil CIDR", subnet) - } - extraSubnets = append(extraSubnets, *cidr) - } - return extraSubnets, nil -} - // GetAllowedVPNInputPorts obtains a list of input ports to allow from the // VPN server side in the firewall, from the environment variable FIREWALL_VPN_INPUT_PORTS. func (r *reader) GetVPNInputPorts() (ports []uint16, err error) { diff --git a/internal/params/params.go b/internal/params/params.go index 4b7d2b4a..9a96b32e 100644 --- a/internal/params/params.go +++ b/internal/params/params.go @@ -41,7 +41,6 @@ type Reader interface { // Firewall getters GetFirewall() (enabled bool, err error) - GetExtraSubnets() (extraSubnets []net.IPNet, err error) GetVPNInputPorts() (ports []uint16, err error) GetInputPorts() (ports []uint16, err error) GetFirewallDebug() (debug bool, err error) diff --git a/internal/routing/enable.go b/internal/routing/enable.go new file mode 100644 index 00000000..caf0f456 --- /dev/null +++ b/internal/routing/enable.go @@ -0,0 +1,62 @@ +package routing + +import ( + "fmt" + "net" +) + +var ( + ErrSetup = fmt.Errorf("cannot setup routing") + ErrTeardown = fmt.Errorf("cannot teardown routing") +) + +const ( + table = 200 + priority = 100 +) + +func (r *routing) Setup() (err error) { + defaultIP, err := r.defaultIP() + if err != nil { + return fmt.Errorf("%s: %w", ErrSetup, err) + } + defaultInterfaceName, defaultGateway, err := r.DefaultRoute() + if err != nil { + return fmt.Errorf("%s: %w", ErrSetup, err) + } + + defer func() { + if err == nil { + return + } + if err := r.TearDown(); err != nil { + r.logger.Error(err) + } + }() + if err := r.addIPRule(defaultIP, table, priority); err != nil { + return fmt.Errorf("%s: %w", ErrSetup, err) + } + if err := r.addRouteVia(net.IPNet{}, defaultGateway, defaultInterfaceName, table); err != nil { + return fmt.Errorf("%s: %w", ErrSetup, err) + } + return nil +} + +func (r *routing) TearDown() error { + defaultIP, err := r.defaultIP() + if err != nil { + return fmt.Errorf("%s: %w", ErrTeardown, err) + } + defaultInterfaceName, defaultGateway, err := r.DefaultRoute() + if err != nil { + return fmt.Errorf("%s: %w", ErrTeardown, err) + } + + if err := r.deleteRouteVia(net.IPNet{}, defaultGateway, defaultInterfaceName, table); err != nil { + return fmt.Errorf("%s: %w", ErrTeardown, err) + } + if err := r.deleteIPRule(defaultIP, table, priority); err != nil { + return fmt.Errorf("%s: %w", ErrTeardown, err) + } + return nil +} diff --git a/internal/routing/mutate.go b/internal/routing/mutate.go index 232d2350..16afac1a 100644 --- a/internal/routing/mutate.go +++ b/internal/routing/mutate.go @@ -1,17 +1,18 @@ package routing import ( + "bytes" "fmt" "net" "github.com/vishvananda/netlink" ) -func (r *routing) AddRouteVia(destination net.IPNet, gateway net.IP, iface string) error { +func (r *routing) addRouteVia(destination net.IPNet, gateway net.IP, iface string, table int) error { destinationStr := destination.String() r.logger.Info("adding route for %s", destinationStr) if r.debug { - fmt.Printf("ip route add %s via %s dev %s\n", destinationStr, gateway, iface) + fmt.Printf("ip route replace %s via %s dev %s table %d\n", destinationStr, gateway, iface, table) } link, err := netlink.LinkByName(iface) @@ -22,6 +23,7 @@ func (r *routing) AddRouteVia(destination net.IPNet, gateway net.IP, iface strin Dst: &destination, Gw: gateway, LinkIndex: link.Attrs().Index, + Table: table, } if err := netlink.RouteReplace(&route); err != nil { return fmt.Errorf("cannot add route for %s: %w", destinationStr, err) @@ -29,17 +31,80 @@ func (r *routing) AddRouteVia(destination net.IPNet, gateway net.IP, iface strin return nil } -func (r *routing) DeleteRouteVia(destination net.IPNet) (err error) { +func (r *routing) deleteRouteVia(destination net.IPNet, gateway net.IP, iface string, table int) (err error) { destinationStr := destination.String() r.logger.Info("deleting route for %s", destinationStr) if r.debug { - fmt.Printf("ip route del %s\n", destinationStr) + fmt.Printf("ip route delete %s via %s dev %s table %d\n", destinationStr, gateway, iface, table) + } + + link, err := netlink.LinkByName(iface) + if err != nil { + return fmt.Errorf("cannot delete route for %s: %w", destinationStr, err) } route := netlink.Route{ - Dst: &destination, + Dst: &destination, + Gw: gateway, + LinkIndex: link.Attrs().Index, + Table: table, } if err := netlink.RouteDel(&route); err != nil { return fmt.Errorf("cannot delete route for %s: %w", destinationStr, err) } return nil } + +func (r *routing) addIPRule(src net.IP, table, priority int) error { + if r.debug { + fmt.Printf("ip rule add from %s lookup %d pref %d\n", + src, table, priority) + } + + rule := netlink.NewRule() + rule.Src = netlink.NewIPNet(src) + rule.Priority = priority + rule.Table = table + + rules, err := netlink.RuleList(netlink.FAMILY_ALL) + if err != nil { + return fmt.Errorf("cannot add ip rule: %w", err) + } + for _, existingRule := range rules { + if existingRule.Src != nil && + existingRule.Src.IP.Equal(rule.Src.IP) && + bytes.Equal(existingRule.Src.Mask, rule.Src.Mask) && + existingRule.Priority == rule.Priority && + existingRule.Table == rule.Table { + return nil // already exists + } + } + + return netlink.RuleAdd(rule) +} + +func (r *routing) deleteIPRule(src net.IP, table, priority int) error { + if r.debug { + fmt.Printf("ip rule del from %s lookup %d pref %d\n", + src, table, priority) + } + + rule := netlink.NewRule() + rule.Src = netlink.NewIPNet(src) + rule.Priority = priority + rule.Table = table + + rules, err := netlink.RuleList(netlink.FAMILY_ALL) + if err != nil { + return fmt.Errorf("cannot add ip rule: %w", err) + } + for _, existingRule := range rules { + if existingRule.Src != nil && + existingRule.Src.IP.Equal(rule.Src.IP) && + bytes.Equal(existingRule.Src.Mask, rule.Src.Mask) && + existingRule.Priority == rule.Priority && + existingRule.Table == rule.Table { + return netlink.RuleDel(rule) + } + } + return nil +} diff --git a/internal/routing/reader.go b/internal/routing/reader.go index 3799db75..588af438 100644 --- a/internal/routing/reader.go +++ b/internal/routing/reader.go @@ -31,6 +31,30 @@ func (r *routing) DefaultRoute() (defaultInterface string, defaultGateway net.IP return "", nil, fmt.Errorf("cannot find default route in %d routes", len(routes)) } +func (r *routing) defaultIP() (ip net.IP, err error) { + routes, err := netlink.RouteList(nil, netlink.FAMILY_ALL) + if err != nil { + return nil, fmt.Errorf("cannot get default IP address: %w", err) + } + + defaultLinkName := "" + for _, route := range routes { + if route.Dst == nil { + linkIndex := route.LinkIndex + link, err := netlink.LinkByIndex(linkIndex) + if err != nil { + return nil, fmt.Errorf("cannot get default IP address: %w", err) + } + defaultLinkName = link.Attrs().Name + } + } + if len(defaultLinkName) == 0 { + return nil, fmt.Errorf("cannot find default link name in %d routes", len(routes)) + } + + return r.assignedIP(defaultLinkName) +} + func (r *routing) LocalSubnet() (defaultSubnet net.IPNet, err error) { routes, err := netlink.RouteList(nil, netlink.FAMILY_ALL) if err != nil { @@ -60,6 +84,26 @@ func (r *routing) LocalSubnet() (defaultSubnet net.IPNet, err error) { return defaultSubnet, fmt.Errorf("cannot find default subnet in %d routes", len(routes)) } +func (r *routing) assignedIP(interfaceName string) (ip net.IP, err error) { + iface, err := net.InterfaceByName(interfaceName) + if err != nil { + return nil, err + } + addresses, err := iface.Addrs() + if err != nil { + return nil, err + } + for _, address := range addresses { + switch value := address.(type) { + case *net.IPAddr: + return value.IP, nil + case *net.IPNet: + return value.IP, nil + } + } + return nil, fmt.Errorf("IP address not found in addresses of interface %s", interfaceName) +} + func (r *routing) VPNDestinationIP() (ip net.IP, err error) { routes, err := netlink.RouteList(nil, netlink.FAMILY_ALL) if err != nil { diff --git a/internal/routing/routing.go b/internal/routing/routing.go index 638f2dd1..2a10d761 100644 --- a/internal/routing/routing.go +++ b/internal/routing/routing.go @@ -7,8 +7,8 @@ import ( ) type Routing interface { - AddRouteVia(destination net.IPNet, gateway net.IP, iface string) error - DeleteRouteVia(destination net.IPNet) (err error) + Setup() (err error) + TearDown() error DefaultRoute() (defaultInterface string, defaultGateway net.IP, err error) LocalSubnet() (defaultSubnet net.IPNet, err error) VPNDestinationIP() (ip net.IP, err error) diff --git a/internal/settings/firewall.go b/internal/settings/firewall.go index 34bd21b2..3154d74c 100644 --- a/internal/settings/firewall.go +++ b/internal/settings/firewall.go @@ -2,7 +2,6 @@ package settings import ( "fmt" - "net" "strings" "github.com/qdm12/gluetun/internal/params" @@ -10,18 +9,13 @@ import ( // Firewall contains settings to customize the firewall operation. type Firewall struct { - AllowedSubnets []net.IPNet - VPNInputPorts []uint16 - InputPorts []uint16 - Enabled bool - Debug bool + VPNInputPorts []uint16 + InputPorts []uint16 + Enabled bool + Debug bool } func (f *Firewall) String() string { - allowedSubnets := make([]string, len(f.AllowedSubnets)) - for i := range f.AllowedSubnets { - allowedSubnets[i] = f.AllowedSubnets[i].String() - } if !f.Enabled { return "Firewall settings: disabled" } @@ -36,7 +30,6 @@ func (f *Firewall) String() string { settingsList := []string{ "Firewall settings:", - "Allowed subnets: " + strings.Join(allowedSubnets, ", "), "VPN input ports: " + strings.Join(vpnInputPorts, ", "), "Input ports: " + strings.Join(inputPorts, ", "), } @@ -48,10 +41,6 @@ func (f *Firewall) String() string { // GetFirewallSettings obtains firewall settings from environment variables using the params package. func GetFirewallSettings(paramsReader params.Reader) (settings Firewall, err error) { - settings.AllowedSubnets, err = paramsReader.GetExtraSubnets() - if err != nil { - return settings, err - } settings.VPNInputPorts, err = paramsReader.GetVPNInputPorts() if err != nil { return settings, err