From 885c4894d39d7875d72c092774ceee2d3ee4d19e 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 --- hack/cloud-image-uploader/go.sum | 2 - hack/structprotogen/go.sum | 2 - .../controllers/network/dns_resolve_cache.go | 13 +- internal/pkg/dns/dns.go | 120 +++++++++--------- internal/pkg/dns/dns_test.go | 3 +- 5 files changed, 73 insertions(+), 67 deletions(-) diff --git a/hack/cloud-image-uploader/go.sum b/hack/cloud-image-uploader/go.sum index b38dadd92de..0e2a5bbf730 100644 --- a/hack/cloud-image-uploader/go.sum +++ b/hack/cloud-image-uploader/go.sum @@ -45,8 +45,6 @@ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUM github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= -github.com/aws/aws-sdk-go v1.51.25 h1:DjTT8mtmsachhV6yrXR8+yhnG6120dazr720nopRsls= -github.com/aws/aws-sdk-go v1.51.25/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/aws/aws-sdk-go v1.52.4 h1:9VsBVJ2TKf8xPP3+yIPGSYcEBIEymXsJzQoFgQuyvA0= github.com/aws/aws-sdk-go v1.52.4/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= diff --git a/hack/structprotogen/go.sum b/hack/structprotogen/go.sum index fd8be797b65..b47b351687c 100644 --- a/hack/structprotogen/go.sum +++ b/hack/structprotogen/go.sum @@ -12,8 +12,6 @@ golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= -golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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)