From 8d1bc97a8e7bf6950b9f0fac17d7d6f553c96a06 Mon Sep 17 00:00:00 2001 From: Dmitriy Matrenichev Date: Tue, 14 May 2024 20:36:22 +0300 Subject: [PATCH] fix: increase host dns packet ttl for pods This PR fixes incorrect packet TTL if `forwardKubeDNSToHost` is enabled. Credits go to Julian Wiedmann. Closes #8698. Signed-off-by: Dmitriy Matrenichev --- .../controllers/network/dns_resolve_cache.go | 13 +- internal/pkg/dns/dns.go | 120 +++++++++--------- internal/pkg/dns/dns_test.go | 3 +- 3 files changed, 73 insertions(+), 63 deletions(-) diff --git a/internal/app/machined/pkg/controllers/network/dns_resolve_cache.go b/internal/app/machined/pkg/controllers/network/dns_resolve_cache.go index 57b0baf86ef..763b4007ab1 100644 --- a/internal/app/machined/pkg/controllers/network/dns_resolve_cache.go +++ b/internal/app/machined/pkg/controllers/network/dns_resolve_cache.go @@ -130,7 +130,7 @@ func (ctrl *DNSResolveCacheController) Run(ctx context.Context, r controller.Run runnerCfg := runnerConfig{net: netwk, addr: addr} if _, ok := ctrl.runners[runnerCfg]; !ok { - runner, rErr := newDNSRunner(runnerCfg, ctrl.cache, ctrl.Logger) + runner, rErr := newDNSRunner(runnerCfg, ctrl.cache, ctrl.Logger, cfg.TypedSpec().ServiceHostDNSAddress.IsValid()) if rErr != nil { return fmt.Errorf("error creating dns runner: %w", rErr) } @@ -256,7 +256,7 @@ type runnerConfig struct { addr netip.AddrPort } -func newDNSRunner(cfg runnerConfig, cache *dns.Cache, logger *zap.Logger) (*dns.Server, error) { +func newDNSRunner(cfg runnerConfig, cache *dns.Cache, logger *zap.Logger, forwardEnabled bool) (*dns.Server, error) { if cfg.addr.Addr().Is6() { cfg.net += "6" } @@ -265,9 +265,14 @@ func newDNSRunner(cfg runnerConfig, cache *dns.Cache, logger *zap.Logger) (*dns. var serverOpts dns.ServerOptions + controlFn, ctrlErr := dns.MakeControl(cfg.net, forwardEnabled) + if ctrlErr != nil { + return nil, fmt.Errorf("error creating %q control function: %w", cfg.net, ctrlErr) + } + switch cfg.net { case "udp", "udp6": - packetConn, err := dns.NewUDPPacketConn(cfg.net, cfg.addr.String()) + packetConn, err := dns.NewUDPPacketConn(cfg.net, cfg.addr.String(), controlFn) if err != nil { return nil, fmt.Errorf("error creating %q packet conn: %w", cfg.net, err) } @@ -279,7 +284,7 @@ func newDNSRunner(cfg runnerConfig, cache *dns.Cache, logger *zap.Logger) (*dns. } case "tcp", "tcp6": - listener, err := dns.NewTCPListener(cfg.net, cfg.addr.String()) + listener, err := dns.NewTCPListener(cfg.net, cfg.addr.String(), controlFn) if err != nil { return nil, fmt.Errorf("error creating %q listener: %w", cfg.net, err) } diff --git a/internal/pkg/dns/dns.go b/internal/pkg/dns/dns.go index 2eefb78be64..d2cc5bfd283 100644 --- a/internal/pkg/dns/dns.go +++ b/internal/pkg/dns/dns.go @@ -338,88 +338,76 @@ func (s *Server) Start(onDone func(err error)) (stop func(), stopped <-chan stru } // NewTCPListener creates a new TCP listener. -func NewTCPListener(network, addr string) (net.Listener, error) { - var opts []controlOptions - - switch network { - case "tcp", "tcp4": - network = "tcp4" - opts = tcpOptions - - case "tcp6": - opts = tcpOptionsV6 - - default: +func NewTCPListener(network, addr string, control ControlFn) (net.Listener, error) { + network, ok := networkNames[network] + if !ok { return nil, fmt.Errorf("unsupported network: %s", network) } - lc := net.ListenConfig{Control: makeControl(opts)} + lc := net.ListenConfig{Control: control} return lc.Listen(context.Background(), network, addr) } // NewUDPPacketConn creates a new UDP packet connection. -func NewUDPPacketConn(network, addr string) (net.PacketConn, error) { - var opts []controlOptions - - switch network { - case "udp", "udp4": - network = "udp4" - opts = udpOptions - - case "udp6": - opts = udpOptionsV6 - - default: +func NewUDPPacketConn(network, addr string, control ControlFn) (net.PacketConn, error) { + network, ok := networkNames[network] + if !ok { return nil, fmt.Errorf("unsupported network: %s", network) } - lc := net.ListenConfig{ - Control: makeControl(opts), - } + lc := net.ListenConfig{Control: control} return lc.ListenPacket(context.Background(), network, addr) } -var ( - tcpOptions = []controlOptions{ - {unix.IPPROTO_IP, unix.IP_RECVTTL, 1, "failed to set IP_RECVTTL"}, - {unix.IPPROTO_TCP, unix.TCP_FASTOPEN, 5, "failed to set TCP_FASTOPEN"}, // tcp specific stuff from systemd - {unix.IPPROTO_TCP, unix.TCP_NODELAY, 1, "failed to set TCP_NODELAY"}, // tcp specific stuff from systemd - {unix.IPPROTO_IP, unix.IP_TTL, 1, "failed to set IP_TTL"}, - } +// ControlFn is an alias to [net.ListenConfig.Control] function. +type ControlFn = func(string, string, syscall.RawConn) error - tcpOptionsV6 = []controlOptions{ - {unix.IPPROTO_IPV6, unix.IPV6_RECVHOPLIMIT, 1, "failed to set IPV6_RECVHOPLIMIT"}, - {unix.IPPROTO_TCP, unix.TCP_FASTOPEN, 5, "failed to set TCP_FASTOPEN"}, // tcp specific stuff from systemd - {unix.IPPROTO_TCP, unix.TCP_NODELAY, 1, "failed to set TCP_NODELAY"}, // tcp specific stuff from systemd - {unix.IPPROTO_IPV6, unix.IPV6_UNICAST_HOPS, 1, "failed to set IPV6_UNICAST_HOPS"}, - } +// MakeControl creates a control function for setting socket options. +func MakeControl(network string, forwardEnabled bool) (ControlFn, error) { + maxHops := 1 - udpOptions = []controlOptions{ - {unix.IPPROTO_IP, unix.IP_RECVTTL, 1, "failed to set IP_RECVTTL"}, - {unix.IPPROTO_IP, unix.IP_TTL, 1, "failed to set IP_TTL"}, + if forwardEnabled { + maxHops = 2 } - udpOptionsV6 = []controlOptions{ - {unix.IPPROTO_IPV6, unix.IPV6_RECVHOPLIMIT, 1, "failed to set IPV6_RECVHOPLIMIT"}, - {unix.IPPROTO_IPV6, unix.IPV6_UNICAST_HOPS, 1, "failed to set IPV6_UNICAST_HOPS"}, - } -) + var options []controlOptions -type controlOptions struct { - level int - opt int - val int - errorMessage string -} + switch network { + case "tcp", "tcp4": + options = []controlOptions{ + {unix.IPPROTO_IP, unix.IP_RECVTTL, maxHops, "failed to set IP_RECVTTL"}, + {unix.IPPROTO_TCP, unix.TCP_FASTOPEN, 5, "failed to set TCP_FASTOPEN"}, // tcp specific stuff from systemd + {unix.IPPROTO_TCP, unix.TCP_NODELAY, 1, "failed to set TCP_NODELAY"}, // tcp specific stuff from systemd + {unix.IPPROTO_IP, unix.IP_TTL, maxHops, "failed to set IP_TTL"}, + } + case "tcp6": + options = []controlOptions{ + {unix.IPPROTO_IPV6, unix.IPV6_RECVHOPLIMIT, maxHops, "failed to set IPV6_RECVHOPLIMIT"}, + {unix.IPPROTO_TCP, unix.TCP_FASTOPEN, 5, "failed to set TCP_FASTOPEN"}, // tcp specific stuff from systemd + {unix.IPPROTO_TCP, unix.TCP_NODELAY, 1, "failed to set TCP_NODELAY"}, // tcp specific stuff from systemd + {unix.IPPROTO_IPV6, unix.IPV6_UNICAST_HOPS, maxHops, "failed to set IPV6_UNICAST_HOPS"}, + } + case "udp", "udp4": + options = []controlOptions{ + {unix.IPPROTO_IP, unix.IP_RECVTTL, maxHops, "failed to set IP_RECVTTL"}, + {unix.IPPROTO_IP, unix.IP_TTL, maxHops, "failed to set IP_TTL"}, + } + case "udp6": + options = []controlOptions{ + {unix.IPPROTO_IPV6, unix.IPV6_RECVHOPLIMIT, maxHops, "failed to set IPV6_RECVHOPLIMIT"}, + {unix.IPPROTO_IPV6, unix.IPV6_UNICAST_HOPS, maxHops, "failed to set IPV6_UNICAST_HOPS"}, + } + default: + return nil, fmt.Errorf("unsupported network: %s", network) + } -func makeControl(opts []controlOptions) func(string, string, syscall.RawConn) error { return func(_ string, _ string, c syscall.RawConn) error { var resErr error err := c.Control(func(fd uintptr) { - for _, opt := range opts { + for _, opt := range options { opErr := unix.SetsockoptInt(int(fd), opt.level, opt.opt, opt.val) if opErr != nil { resErr = fmt.Errorf(opt.errorMessage+": %w", opErr) @@ -437,5 +425,21 @@ func makeControl(opts []controlOptions) func(string, string, syscall.RawConn) er } return nil - } + }, nil +} + +type controlOptions struct { + level int + opt int + val int + errorMessage string +} + +var networkNames = map[string]string{ + "tcp": "tcp4", + "tcp4": "tcp4", + "tcp6": "tcp6", + "udp": "udp4", + "udp4": "udp4", + "udp6": "udp6", } diff --git a/internal/pkg/dns/dns_test.go b/internal/pkg/dns/dns_test.go index 54e055218f3..61499fca854 100644 --- a/internal/pkg/dns/dns_test.go +++ b/internal/pkg/dns/dns_test.go @@ -13,6 +13,7 @@ import ( "github.com/coredns/coredns/plugin/pkg/proxy" dnssrv "github.com/miekg/dns" + "github.com/siderolabs/gen/ensure" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/gen/xtesting/check" "github.com/stretchr/testify/require" @@ -120,7 +121,7 @@ func newServer(t *testing.T, nameservers ...string) func() { handler.SetProxy(pxs) - pc, err := dns.NewUDPPacketConn("udp", "127.0.0.53:10700") + pc, err := dns.NewUDPPacketConn("udp", "127.0.0.53:10700", ensure.Value(dns.MakeControl("udp", false))) require.NoError(t, err) nodeHandler := dns.NewNodeHandler(handler, &testResolver{}, l)