| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- // Copyright (c) Tailscale Inc & AUTHORS
- // SPDX-License-Identifier: BSD-3-Clause
- //go:build linux
- package main
- import (
- "context"
- "fmt"
- "log"
- "net"
- "net/netip"
- "os"
- "path/filepath"
- "strings"
- "tailscale.com/util/linuxfw"
- )
- // ensureIPForwarding enables IPv4/IPv6 forwarding for the container.
- func ensureIPForwarding(root, clusterProxyTargetIP, tailnetTargetIP, tailnetTargetFQDN string, routes *string) error {
- var (
- v4Forwarding, v6Forwarding bool
- )
- if clusterProxyTargetIP != "" {
- proxyIP, err := netip.ParseAddr(clusterProxyTargetIP)
- if err != nil {
- return fmt.Errorf("invalid cluster destination IP: %v", err)
- }
- if proxyIP.Is4() {
- v4Forwarding = true
- } else {
- v6Forwarding = true
- }
- }
- if tailnetTargetIP != "" {
- proxyIP, err := netip.ParseAddr(tailnetTargetIP)
- if err != nil {
- return fmt.Errorf("invalid tailnet destination IP: %v", err)
- }
- if proxyIP.Is4() {
- v4Forwarding = true
- } else {
- v6Forwarding = true
- }
- }
- // Currently we only proxy traffic to the IPv4 address of the tailnet
- // target.
- if tailnetTargetFQDN != "" {
- v4Forwarding = true
- }
- if routes != nil && *routes != "" {
- for _, route := range strings.Split(*routes, ",") {
- cidr, err := netip.ParsePrefix(route)
- if err != nil {
- return fmt.Errorf("invalid subnet route: %v", err)
- }
- if cidr.Addr().Is4() {
- v4Forwarding = true
- } else {
- v6Forwarding = true
- }
- }
- }
- return enableIPForwarding(v4Forwarding, v6Forwarding, root)
- }
- func enableIPForwarding(v4Forwarding, v6Forwarding bool, root string) error {
- var paths []string
- if v4Forwarding {
- paths = append(paths, filepath.Join(root, "proc/sys/net/ipv4/ip_forward"))
- }
- if v6Forwarding {
- paths = append(paths, filepath.Join(root, "proc/sys/net/ipv6/conf/all/forwarding"))
- }
- // In some common configurations (e.g. default docker,
- // kubernetes), the container environment denies write access to
- // most sysctls, including IP forwarding controls. Check the
- // sysctl values before trying to change them, so that we
- // gracefully do nothing if the container's already been set up
- // properly by e.g. a k8s initContainer.
- for _, path := range paths {
- bs, err := os.ReadFile(path)
- if err != nil {
- return fmt.Errorf("reading %q: %w", path, err)
- }
- if v := strings.TrimSpace(string(bs)); v != "1" {
- if err := os.WriteFile(path, []byte("1"), 0644); err != nil {
- return fmt.Errorf("enabling %q: %w", path, err)
- }
- }
- }
- return nil
- }
- func installEgressForwardingRule(_ context.Context, dstStr string, tsIPs []netip.Prefix, nfr linuxfw.NetfilterRunner) error {
- dst, err := netip.ParseAddr(dstStr)
- if err != nil {
- return err
- }
- var local netip.Addr
- for _, pfx := range tsIPs {
- if !pfx.IsSingleIP() {
- continue
- }
- if pfx.Addr().Is4() != dst.Is4() {
- continue
- }
- local = pfx.Addr()
- break
- }
- if !local.IsValid() {
- return fmt.Errorf("no tailscale IP matching family of %s found in %v", dstStr, tsIPs)
- }
- if err := nfr.DNATNonTailscaleTraffic("tailscale0", dst); err != nil {
- return fmt.Errorf("installing egress proxy rules: %w", err)
- }
- if err := nfr.EnsureSNATForDst(local, dst); err != nil {
- return fmt.Errorf("installing egress proxy rules: %w", err)
- }
- if err := nfr.ClampMSSToPMTU("tailscale0", dst); err != nil {
- return fmt.Errorf("installing egress proxy rules: %w", err)
- }
- return nil
- }
- // installTSForwardingRuleForDestination accepts a destination address and a
- // list of node's tailnet addresses, sets up rules to forward traffic for
- // destination to the tailnet IP matching the destination IP family.
- // Destination can be Pod IP of this node.
- func installTSForwardingRuleForDestination(_ context.Context, dstFilter string, tsIPs []netip.Prefix, nfr linuxfw.NetfilterRunner) error {
- dst, err := netip.ParseAddr(dstFilter)
- if err != nil {
- return err
- }
- var local netip.Addr
- for _, pfx := range tsIPs {
- if !pfx.IsSingleIP() {
- continue
- }
- if pfx.Addr().Is4() != dst.Is4() {
- continue
- }
- local = pfx.Addr()
- break
- }
- if !local.IsValid() {
- return fmt.Errorf("no tailscale IP matching family of %s found in %v", dstFilter, tsIPs)
- }
- if err := nfr.AddDNATRule(dst, local); err != nil {
- return fmt.Errorf("installing rule for forwarding traffic to tailnet IP: %w", err)
- }
- return nil
- }
- func installIngressForwardingRule(_ context.Context, dstStr string, tsIPs []netip.Prefix, nfr linuxfw.NetfilterRunner) error {
- dst, err := netip.ParseAddr(dstStr)
- if err != nil {
- return err
- }
- var local netip.Addr
- proxyHasIPv4Address := false
- for _, pfx := range tsIPs {
- if !pfx.IsSingleIP() {
- continue
- }
- if pfx.Addr().Is4() {
- proxyHasIPv4Address = true
- }
- if pfx.Addr().Is4() != dst.Is4() {
- continue
- }
- local = pfx.Addr()
- break
- }
- if proxyHasIPv4Address && dst.Is6() {
- log.Printf("Warning: proxy backend ClusterIP is an IPv6 address and the proxy has a IPv4 tailnet address. You might need to disable IPv4 address allocation for the proxy for forwarding to work. See https://github.com/tailscale/tailscale/issues/12156")
- }
- if !local.IsValid() {
- return fmt.Errorf("no tailscale IP matching family of %s found in %v", dstStr, tsIPs)
- }
- if err := nfr.AddDNATRule(local, dst); err != nil {
- return fmt.Errorf("installing ingress proxy rules: %w", err)
- }
- if err := nfr.ClampMSSToPMTU("tailscale0", dst); err != nil {
- return fmt.Errorf("installing ingress proxy rules: %w", err)
- }
- return nil
- }
- func installIngressForwardingRuleForDNSTarget(_ context.Context, backendAddrs []net.IP, tsIPs []netip.Prefix, nfr linuxfw.NetfilterRunner) error {
- var (
- tsv4 netip.Addr
- tsv6 netip.Addr
- v4Backends []netip.Addr
- v6Backends []netip.Addr
- )
- for _, pfx := range tsIPs {
- if pfx.IsSingleIP() && pfx.Addr().Is4() {
- tsv4 = pfx.Addr()
- continue
- }
- if pfx.IsSingleIP() && pfx.Addr().Is6() {
- tsv6 = pfx.Addr()
- continue
- }
- }
- // TODO: log if more than one backend address is found and firewall is
- // in nftables mode that only the first IP will be used.
- for _, ip := range backendAddrs {
- if ip.To4() != nil {
- v4Backends = append(v4Backends, netip.AddrFrom4([4]byte(ip.To4())))
- }
- if ip.To16() != nil {
- v6Backends = append(v6Backends, netip.AddrFrom16([16]byte(ip.To16())))
- }
- }
- // Enable IP forwarding here as opposed to at the start of containerboot
- // as the IPv4/IPv6 requirements might have changed.
- // For Kubernetes operator proxies, forwarding for both IPv4 and IPv6 is
- // enabled by an init container, so in practice enabling forwarding here
- // is only needed if this proxy has been configured by manually setting
- // TS_EXPERIMENTAL_DEST_DNS_NAME env var for a containerboot instance.
- if err := enableIPForwarding(len(v4Backends) != 0, len(v6Backends) != 0, ""); err != nil {
- log.Printf("[unexpected] failed to ensure IP forwarding: %v", err)
- }
- updateFirewall := func(dst netip.Addr, backendTargets []netip.Addr) error {
- if err := nfr.DNATWithLoadBalancer(dst, backendTargets); err != nil {
- return fmt.Errorf("installing DNAT rules for ingress backends %+#v: %w", backendTargets, err)
- }
- // The backend might advertize MSS higher than that of the
- // tailscale interfaces. Clamp MSS of packets going out via
- // tailscale0 interface to its MTU to prevent broken connections
- // in environments where path MTU discovery is not working.
- if err := nfr.ClampMSSToPMTU("tailscale0", dst); err != nil {
- return fmt.Errorf("adding rule to clamp traffic via tailscale0: %v", err)
- }
- return nil
- }
- if len(v4Backends) != 0 {
- if !tsv4.IsValid() {
- log.Printf("backend targets %v contain at least one IPv4 address, but this node's Tailscale IPs do not contain a valid IPv4 address: %v", backendAddrs, tsIPs)
- } else if err := updateFirewall(tsv4, v4Backends); err != nil {
- return fmt.Errorf("Installing IPv4 firewall rules: %w", err)
- }
- }
- if len(v6Backends) != 0 && !tsv6.IsValid() {
- if !tsv6.IsValid() {
- log.Printf("backend targets %v contain at least one IPv6 address, but this node's Tailscale IPs do not contain a valid IPv6 address: %v", backendAddrs, tsIPs)
- } else if !nfr.HasIPV6NAT() {
- log.Printf("backend targets %v contain at least one IPv6 address, but the chosen firewall mode does not support IPv6 NAT", backendAddrs)
- } else if err := updateFirewall(tsv6, v6Backends); err != nil {
- return fmt.Errorf("Installing IPv6 firewall rules: %w", err)
- }
- }
- return nil
- }
|