Skip to content

Commit 53bd669

Browse files
Dmitriy Matrenichevdsseng
authored andcommitted
feat: support conditional start of IPv6 dns servers
This PR does those things: - Raise IPv6 listener on link-local address for dns (both TCP and UDP). - Update kubelet's `resolv.conf` IPv4/IPv6 endpoints. Closes #9384 Link: #9596 Signed-off-by: Dmitriy Matrenichev <dmitry.matrenichev@siderolabs.com>
1 parent b31d93e commit 53bd669

15 files changed

Lines changed: 350 additions & 159 deletions

File tree

api/resource/definitions/network/network.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ message HostDNSConfigSpec {
259259
repeated common.NetIPPort listen_addresses = 2;
260260
common.NetIP service_host_dns_address = 3;
261261
bool resolve_member_names = 4;
262+
common.NetIP service_host_dns_address_v6 = 5;
262263
}
263264

264265
// HostnameSpecSpec describes node hostname.

internal/app/machined/pkg/controllers/network/address_spec_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323

2424
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest"
2525
netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network"
26+
"github.com/siderolabs/talos/pkg/machinery/constants"
2627
"github.com/siderolabs/talos/pkg/machinery/nethelpers"
2728
"github.com/siderolabs/talos/pkg/machinery/resources/network"
2829
)
@@ -123,6 +124,40 @@ func (suite *AddressSpecSuite) TestLoopback() {
123124
suite.Destroy(loopback)
124125
}
125126

127+
func (suite *AddressSpecSuite) TestIPV6ULA() {
128+
loopback := network.NewAddressSpec(network.NamespaceName, "lo/"+constants.HostDNSAddressV6+"/128")
129+
*loopback.TypedSpec() = network.AddressSpecSpec{
130+
Address: netip.MustParsePrefix(constants.HostDNSAddressV6 + "/128"),
131+
LinkName: "lo",
132+
Family: nethelpers.FamilyInet6,
133+
Scope: nethelpers.ScopeGlobal,
134+
ConfigLayer: network.ConfigDefault,
135+
Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent),
136+
}
137+
138+
for _, res := range []resource.Resource{loopback} {
139+
suite.Create(res)
140+
}
141+
142+
suite.Assert().EventuallyWithT(func(collect *assert.CollectT) {
143+
assertLinkAddress(assert.New(collect), "lo", constants.HostDNSAddressV6+"/128")
144+
}, 3*time.Second, 10*time.Millisecond)
145+
146+
// teardown the address
147+
_, err := suite.State().Teardown(suite.Ctx(), loopback.Metadata())
148+
suite.Require().NoError(err)
149+
150+
_, err = suite.State().WatchFor(suite.Ctx(), loopback.Metadata(), state.WithFinalizerEmpty())
151+
suite.Require().NoError(err)
152+
153+
// torn down address should be removed immediately
154+
suite.Assert().EventuallyWithT(func(collect *assert.CollectT) {
155+
assertNoLinkAddress(assert.New(collect), "lo", constants.HostDNSAddressV6+"/128")
156+
}, 3*time.Second, 10*time.Millisecond)
157+
158+
suite.Destroy(loopback)
159+
}
160+
126161
func (suite *AddressSpecSuite) TestDummy() {
127162
dummyInterface := suite.uniqueDummyInterface()
128163

internal/app/machined/pkg/controllers/network/dns_resolve_cache.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ func (ctrl *DNSResolveCacheController) run(ctx context.Context, r controller.Run
117117
}
118118

119119
pairs := allAddressPairs(cfg.TypedSpec().ListenAddresses)
120-
forwardKubeDNSToHost := cfg.TypedSpec().ServiceHostDNSAddress.IsValid()
120+
forwardKubeDNSToHost := cfg.TypedSpec().ServiceHostDNSAddress.IsValid() || cfg.TypedSpec().ServiceHostDNSAddressV6.IsValid()
121121

122122
for runCfg, runErr := range ctrl.manager.RunAll(pairs, forwardKubeDNSToHost) {
123123
switch {

internal/app/machined/pkg/controllers/network/dns_resolve_cache_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ func getDynamicPort(t *testing.T) string {
286286
func makeAddrs(port string) []netip.AddrPort {
287287
return []netip.AddrPort{
288288
netip.MustParseAddrPort("127.0.0.53:" + port),
289+
netip.MustParseAddrPort("[::1]:" + port),
289290
}
290291
}
291292

internal/app/machined/pkg/controllers/network/etcfile.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ func (ctrl *EtcFileController) Run(ctx context.Context, r controller.Runtime, lo
165165

166166
if resolverStatus != nil && hostDNSCfg != nil {
167167
dnsServers := xslices.FilterInPlace(
168-
[]netip.Addr{hostDNSCfg.TypedSpec().ServiceHostDNSAddress},
168+
[]netip.Addr{hostDNSCfg.TypedSpec().ServiceHostDNSAddress, hostDNSCfg.TypedSpec().ServiceHostDNSAddressV6},
169169
netip.Addr.IsValid,
170170
)
171171

internal/app/machined/pkg/controllers/network/etcfile_test.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,10 @@ func (suite *EtcFileConfigSuite) ExtraSetup() {
121121
suite.hostDNSConfig.TypedSpec().ListenAddresses = []netip.AddrPort{
122122
netip.MustParseAddrPort("127.0.0.53:53"),
123123
netip.MustParseAddrPort("169.254.116.108:53"),
124+
netip.MustParseAddrPort("[fd54:616c:6f73::204f:5320:444e:531]:53"),
124125
}
125126
suite.hostDNSConfig.TypedSpec().ServiceHostDNSAddress = netip.MustParseAddr("169.254.116.108")
127+
suite.hostDNSConfig.TypedSpec().ServiceHostDNSAddressV6 = netip.MustParseAddr("fd54:616c:6f73::204f:5320:444e:531")
126128
}
127129

128130
type etcFileContents struct {
@@ -212,7 +214,7 @@ func (suite *EtcFileConfigSuite) TestComplete() {
212214
etcFileContents{
213215
hosts: "127.0.0.1 localhost\n33.11.22.44 foo.example.com foo\n::1 localhost ip6-localhost ip6-loopback\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n10.0.0.1 a b\n10.0.0.2 c d\n", //nolint:lll
214216
resolvConf: "nameserver 127.0.0.53\n\nsearch foo.example.com\n",
215-
resolvGlobalConf: "nameserver 169.254.116.108\n\nsearch foo.example.com\n",
217+
resolvGlobalConf: "nameserver 169.254.116.108\nnameserver fd54:616c:6f73:0:204f:5320:444e:531\n\nsearch foo.example.com\n",
216218
},
217219
)
218220
}
@@ -225,7 +227,7 @@ func (suite *EtcFileConfigSuite) TestExtraHostsNoHostname() {
225227
etcFileContents{
226228
hosts: "127.0.0.1 localhost\n::1 localhost ip6-localhost ip6-loopback\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n10.0.0.1 a b\n10.0.0.2 c d\n",
227229
resolvConf: "nameserver 127.0.0.53\n\nsearch foo.example.com\n",
228-
resolvGlobalConf: "nameserver 169.254.116.108\n\nsearch foo.example.com\n",
230+
resolvGlobalConf: "nameserver 169.254.116.108\nnameserver fd54:616c:6f73:0:204f:5320:444e:531\n\nsearch foo.example.com\n",
229231
},
230232
)
231233
}
@@ -238,7 +240,7 @@ func (suite *EtcFileConfigSuite) TestNoExtraHosts() {
238240
etcFileContents{
239241
hosts: "127.0.0.1 localhost\n33.11.22.44 foo.example.com foo\n::1 localhost ip6-localhost ip6-loopback\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n",
240242
resolvConf: "nameserver 127.0.0.53\n\nsearch foo.example.com\n",
241-
resolvGlobalConf: "nameserver 169.254.116.108\n\nsearch foo.example.com\n",
243+
resolvGlobalConf: "nameserver 169.254.116.108\nnameserver fd54:616c:6f73:0:204f:5320:444e:531\n\nsearch foo.example.com\n",
242244
},
243245
)
244246
}
@@ -261,7 +263,7 @@ func (suite *EtcFileConfigSuite) TestNoSearchDomainLegacy() {
261263
etcFileContents{
262264
hosts: "127.0.0.1 localhost\n33.11.22.44 foo.example.com foo\n::1 localhost ip6-localhost ip6-loopback\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n",
263265
resolvConf: "nameserver 127.0.0.53\n",
264-
resolvGlobalConf: "nameserver 169.254.116.108\n",
266+
resolvGlobalConf: "nameserver 169.254.116.108\nnameserver fd54:616c:6f73:0:204f:5320:444e:531\n",
265267
},
266268
)
267269
}
@@ -282,7 +284,7 @@ func (suite *EtcFileConfigSuite) TestNoSearchDomainNewStyle() {
282284
etcFileContents{
283285
hosts: "127.0.0.1 localhost\n33.11.22.44 foo.example.com foo\n::1 localhost ip6-localhost ip6-loopback\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n",
284286
resolvConf: "nameserver 127.0.0.53\n",
285-
resolvGlobalConf: "nameserver 169.254.116.108\n",
287+
resolvGlobalConf: "nameserver 169.254.116.108\nnameserver fd54:616c:6f73:0:204f:5320:444e:531\n",
286288
},
287289
)
288290
}
@@ -295,7 +297,7 @@ func (suite *EtcFileConfigSuite) TestNoDomainname() {
295297
etcFileContents{
296298
hosts: "127.0.0.1 localhost\n33.11.22.44 foo\n::1 localhost ip6-localhost ip6-loopback\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n",
297299
resolvConf: "nameserver 127.0.0.53\n",
298-
resolvGlobalConf: "nameserver 169.254.116.108\n",
300+
resolvGlobalConf: "nameserver 169.254.116.108\nnameserver fd54:616c:6f73:0:204f:5320:444e:531\n",
299301
},
300302
)
301303
}
@@ -306,7 +308,7 @@ func (suite *EtcFileConfigSuite) TestOnlyResolvers() {
306308
etcFileContents{
307309
hosts: "127.0.0.1 localhost\n::1 localhost ip6-localhost ip6-loopback\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n",
308310
resolvConf: "nameserver 127.0.0.53\n",
309-
resolvGlobalConf: "nameserver 169.254.116.108\n",
311+
resolvGlobalConf: "nameserver 169.254.116.108\nnameserver fd54:616c:6f73:0:204f:5320:444e:531\n",
310312
},
311313
)
312314
}

internal/app/machined/pkg/controllers/network/hostdns_config.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ func (ctrl *HostDNSConfigController) Run(ctx context.Context, r controller.Runti
9494
}
9595

9696
res.TypedSpec().ServiceHostDNSAddress = netip.Addr{}
97+
res.TypedSpec().ServiceHostDNSAddressV6 = netip.Addr{}
9798

9899
if hostDNSConfig == nil {
99100
res.TypedSpec().Enabled = false
@@ -125,6 +126,17 @@ func (ctrl *HostDNSConfigController) Run(ctx context.Context, r controller.Runti
125126
res.TypedSpec().ServiceHostDNSAddress = parsed
126127
}
127128

129+
if slices.ContainsFunc(
130+
podCIDRs,
131+
func(cidr string) bool { return netip.MustParsePrefix(cidr).Addr().Is6() },
132+
) {
133+
parsed := netip.MustParseAddr(constants.HostDNSAddressV6)
134+
newServiceAddrs = append(newServiceAddrs, parsed)
135+
136+
res.TypedSpec().ListenAddresses = append(res.TypedSpec().ListenAddresses, netip.AddrPortFrom(parsed, 53))
137+
res.TypedSpec().ServiceHostDNSAddressV6 = parsed
138+
}
139+
128140
return nil
129141
}); err != nil {
130142
return fmt.Errorf("error writing host dns config: %w", err)

internal/app/machined/pkg/controllers/network/hostdns_config_test.go

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -119,36 +119,48 @@ func (suite *HostDNSConfigSuite) TestLegacyConfigForwardKubeDNSIPv4() {
119119
suite.Create(cfg)
120120

121121
hostDNSAddr := netip.MustParseAddr(constants.HostDNSAddress)
122+
hostDNSAddrV6 := netip.MustParseAddr(constants.HostDNSAddressV6)
122123

123124
ctest.AssertResource(suite, network.HostDNSConfigID, func(r *network.HostDNSConfig, asrt *assert.Assertions) {
124125
asrt.True(r.TypedSpec().Enabled)
125126
asrt.Equal(
126127
[]netip.AddrPort{
127128
netip.MustParseAddrPort("127.0.0.53:53"),
128129
netip.AddrPortFrom(hostDNSAddr, 53),
130+
netip.AddrPortFrom(hostDNSAddrV6, 53),
129131
},
130132
r.TypedSpec().ListenAddresses,
131133
)
132134
asrt.Equal(hostDNSAddr, r.TypedSpec().ServiceHostDNSAddress)
135+
asrt.Equal(hostDNSAddrV6, r.TypedSpec().ServiceHostDNSAddressV6)
133136
})
134137

135-
addrPrefix := netip.PrefixFrom(hostDNSAddr, hostDNSAddr.BitLen())
136-
137-
ctest.AssertResource(
138-
suite,
139-
network.LayeredID(network.ConfigOperator, network.AddressID("lo", addrPrefix)),
140-
func(r *network.AddressSpec, asrt *assert.Assertions) {
141-
spec := r.TypedSpec()
142-
143-
asrt.Equal(addrPrefix, spec.Address)
144-
asrt.Equal("lo", spec.LinkName)
145-
asrt.Equal(nethelpers.FamilyInet4, spec.Family)
146-
asrt.Equal(nethelpers.ScopeHost, spec.Scope)
147-
asrt.Equal(nethelpers.AddressFlags(nethelpers.AddressPermanent), spec.Flags)
148-
asrt.Equal(network.ConfigOperator, spec.ConfigLayer)
149-
},
150-
rtestutils.WithNamespace(network.ConfigNamespaceName),
151-
)
138+
for _, addrPrefix := range []netip.Prefix{
139+
netip.PrefixFrom(hostDNSAddr, hostDNSAddr.BitLen()),
140+
netip.PrefixFrom(hostDNSAddrV6, hostDNSAddrV6.BitLen()),
141+
} {
142+
ctest.AssertResource(
143+
suite,
144+
network.LayeredID(network.ConfigOperator, network.AddressID("lo", addrPrefix)),
145+
func(r *network.AddressSpec, asrt *assert.Assertions) {
146+
spec := r.TypedSpec()
147+
148+
if addrPrefix.Addr().Is6() {
149+
asrt.Equal(nethelpers.FamilyInet6, spec.Family)
150+
asrt.Equal(nethelpers.ScopeGlobal, spec.Scope)
151+
} else {
152+
asrt.Equal(nethelpers.FamilyInet4, spec.Family)
153+
asrt.Equal(nethelpers.ScopeHost, spec.Scope)
154+
}
155+
156+
asrt.Equal(addrPrefix, spec.Address)
157+
asrt.Equal("lo", spec.LinkName)
158+
asrt.Equal(nethelpers.AddressFlags(nethelpers.AddressPermanent), spec.Flags)
159+
asrt.Equal(network.ConfigOperator, spec.ConfigLayer)
160+
},
161+
rtestutils.WithNamespace(network.ConfigNamespaceName),
162+
)
163+
}
152164
}
153165

154166
func (suite *HostDNSConfigSuite) TestLegacyConfigForwardKubeDNSIPv6Only() {
@@ -184,10 +196,14 @@ func (suite *HostDNSConfigSuite) TestLegacyConfigForwardKubeDNSIPv6Only() {
184196
ctest.AssertResource(suite, network.HostDNSConfigID, func(r *network.HostDNSConfig, asrt *assert.Assertions) {
185197
asrt.True(r.TypedSpec().Enabled)
186198
asrt.Equal(
187-
[]netip.AddrPort{netip.MustParseAddrPort("127.0.0.53:53")},
199+
[]netip.AddrPort{
200+
netip.MustParseAddrPort("127.0.0.53:53"),
201+
netip.MustParseAddrPort("[" + constants.HostDNSAddressV6 + "]:53"),
202+
},
188203
r.TypedSpec().ListenAddresses,
189204
)
190205
asrt.Equal(netip.Addr{}, r.TypedSpec().ServiceHostDNSAddress)
206+
asrt.Equal(netip.MustParseAddr(constants.HostDNSAddressV6), r.TypedSpec().ServiceHostDNSAddressV6)
191207
})
192208

193209
ctest.AssertNoResource[*network.AddressSpec](

internal/app/machined/pkg/controllers/network/nftables_chain_config.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/siderolabs/gen/xslices"
1919
"go.uber.org/zap"
2020

21+
cfg "github.com/siderolabs/talos/pkg/machinery/config/config"
2122
"github.com/siderolabs/talos/pkg/machinery/constants"
2223
"github.com/siderolabs/talos/pkg/machinery/nethelpers"
2324
"github.com/siderolabs/talos/pkg/machinery/resources/config"
@@ -210,8 +211,6 @@ func (ctrl *NfTablesChainConfigController) buildIngressChain(cfg *config.Machine
210211

211212
if hostDNSConfig := cfg.Config().NetworkHostDNSConfig(); hostDNSConfig != nil {
212213
if hostDNSConfig.ForwardKubeDNSToHost() {
213-
hostDNSIP := netip.MustParseAddr(constants.HostDNSAddress)
214-
215214
// allow traffic to host DNS
216215
for _, protocol := range []nethelpers.Protocol{nethelpers.ProtocolUDP, nethelpers.ProtocolTCP} {
217216
spec.Rules = append(
@@ -227,7 +226,7 @@ func (ctrl *NfTablesChainConfigController) buildIngressChain(cfg *config.Machine
227226
),
228227
},
229228
MatchDestinationAddress: &network.NfTablesAddressMatch{
230-
IncludeSubnets: []netip.Prefix{netip.PrefixFrom(hostDNSIP, hostDNSIP.BitLen())},
229+
IncludeSubnets: hostDNSSubnets(cfg.Config().Cluster().Network()),
231230
},
232231
MatchLayer4: &network.NfTablesLayer4Match{
233232
Protocol: protocol,
@@ -433,3 +432,20 @@ func (ctrl *NfTablesChainConfigController) buildPreroutingChain(cfg *config.Mach
433432
return nil
434433
}
435434
}
435+
436+
func hostDNSSubnets(clusterNetwork cfg.ClusterNetwork) []netip.Prefix {
437+
result := []netip.Addr{hostDNSIPv4}
438+
439+
for _, podCIDR := range clusterNetwork.PodCIDRs() {
440+
if netip.MustParsePrefix(podCIDR).Addr().Is6() {
441+
result = append(result, hostDNSIPv6)
442+
}
443+
}
444+
445+
return xslices.Map(result, func(a netip.Addr) netip.Prefix { return netip.PrefixFrom(a, a.BitLen()) })
446+
}
447+
448+
var (
449+
hostDNSIPv4 = netip.MustParseAddr(constants.HostDNSAddress)
450+
hostDNSIPv6 = netip.MustParseAddr(constants.HostDNSAddressV6)
451+
)

internal/pkg/dns/dns_test.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func TestDNS(t *testing.T) {
8282
},
8383
}
8484

85-
for _, dnsAddr := range []string{"127.0.0.1:10700"} {
85+
for _, dnsAddr := range []string{"127.0.0.1:10700", "[::1]:10700"} {
8686
for _, test := range tests {
8787
t.Run(dnsAddr+"/"+test.name, func(t *testing.T) {
8888
stop := newManager(t, test.nameservers...)
@@ -123,6 +123,14 @@ func TestDNSEmptyDestinations(t *testing.T) {
123123
require.NoError(t, err)
124124
require.Equal(t, dnssrv.RcodeServerFailure, r.Rcode, r)
125125

126+
r, err = dnssrv.Exchange(createQuery("google.com"), "[::1]:10700")
127+
require.NoError(t, err)
128+
require.Equal(t, dnssrv.RcodeServerFailure, r.Rcode, r)
129+
130+
r, err = dnssrv.Exchange(createQuery("google.com"), "[::1]:10700")
131+
require.NoError(t, err)
132+
require.Equal(t, dnssrv.RcodeServerFailure, r.Rcode, r)
133+
126134
stop()
127135
}
128136

@@ -142,6 +150,8 @@ func Test_ServeBackground(t *testing.T) {
142150
for _, err := range m.RunAll(slices.Values([]dns.AddressPair{
143151
{Network: "udp", Addr: netip.MustParseAddrPort("127.0.0.1:10700")},
144152
{Network: "udp", Addr: netip.MustParseAddrPort("127.0.0.1:10701")},
153+
{Network: "udp", Addr: netip.MustParseAddrPort("[::1]:10700")},
154+
{Network: "udp", Addr: netip.MustParseAddrPort("[::1]:10701")},
145155
}), false) {
146156
require.NoError(t, err)
147157
}
@@ -189,6 +199,9 @@ func newManager(t *testing.T, nameservers ...string) func() {
189199
{Network: "udp", Addr: netip.MustParseAddrPort("127.0.0.1:10700")},
190200
{Network: "udp", Addr: netip.MustParseAddrPort("127.0.0.1:10701")},
191201
{Network: "tcp", Addr: netip.MustParseAddrPort("127.0.0.1:10700")},
202+
{Network: "udp", Addr: netip.MustParseAddrPort("[::1]:10700")},
203+
{Network: "tcp", Addr: netip.MustParseAddrPort("[::1]:10700")},
204+
{Network: "udp", Addr: netip.MustParseAddrPort("[::1]:10700")},
192205
}), false) {
193206
if err != nil && strings.Contains(err.Error(), "failed to set TCP_FASTOPEN") {
194207
continue
@@ -200,6 +213,8 @@ func newManager(t *testing.T, nameservers ...string) func() {
200213
for _, err := range m.RunAll(slices.Values([]dns.AddressPair{
201214
{Network: "udp", Addr: netip.MustParseAddrPort("127.0.0.1:10700")},
202215
{Network: "tcp", Addr: netip.MustParseAddrPort("127.0.0.1:10700")},
216+
{Network: "udp", Addr: netip.MustParseAddrPort("[::1]:10700")},
217+
{Network: "tcp", Addr: netip.MustParseAddrPort("[::1]:10700")},
203218
}), false) {
204219
if err != nil && strings.Contains(err.Error(), "failed to set TCP_FASTOPEN") {
205220
continue

0 commit comments

Comments
 (0)