Skip to content

Commit 83b4579

Browse files
author
Dmitriy Matrenichev
committed
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 Signed-off-by: Dmitriy Matrenichev <[email protected]>
1 parent 43fe380 commit 83b4579

File tree

14 files changed

+831
-649
lines changed

14 files changed

+831
-649
lines changed

api/resource/definitions/network/network.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ message HostDNSConfigSpec {
114114
repeated common.NetIPPort listen_addresses = 2;
115115
common.NetIP service_host_dns_address = 3;
116116
bool resolve_member_names = 4;
117+
common.NetIP service_host_dns_address_v6 = 5;
117118
}
118119

119120
// HostnameSpecSpec describes node hostname.

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"golang.org/x/sys/unix"
2929

3030
netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network"
31+
"github.com/siderolabs/talos/pkg/machinery/constants"
3132
"github.com/siderolabs/talos/pkg/machinery/nethelpers"
3233
"github.com/siderolabs/talos/pkg/machinery/resources/network"
3334
)
@@ -170,6 +171,47 @@ func (suite *AddressSpecSuite) TestLoopback() {
170171
suite.Require().NoError(suite.state.Destroy(suite.ctx, loopback.Metadata()))
171172
}
172173

174+
func (suite *AddressSpecSuite) TestIPV6ULA() {
175+
loopback := network.NewAddressSpec(network.NamespaceName, "lo/"+constants.HostDNSAddressV6+"/128")
176+
*loopback.TypedSpec() = network.AddressSpecSpec{
177+
Address: netip.MustParsePrefix(constants.HostDNSAddressV6 + "/128"),
178+
LinkName: "lo",
179+
Family: nethelpers.FamilyInet6,
180+
Scope: nethelpers.ScopeGlobal,
181+
ConfigLayer: network.ConfigDefault,
182+
Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent),
183+
}
184+
185+
for _, res := range []resource.Resource{loopback} {
186+
suite.Require().NoError(suite.state.Create(suite.ctx, res), "%v", res.Spec())
187+
}
188+
189+
suite.Assert().NoError(
190+
retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(
191+
func() error {
192+
return suite.assertLinkAddress("lo", constants.HostDNSAddressV6+"/128")
193+
},
194+
),
195+
)
196+
197+
// teardown the address
198+
for {
199+
ready, err := suite.state.Teardown(suite.ctx, loopback.Metadata())
200+
suite.Require().NoError(err)
201+
202+
if ready {
203+
break
204+
}
205+
206+
time.Sleep(100 * time.Millisecond)
207+
}
208+
209+
// torn down address should be removed immediately
210+
suite.Assert().NoError(suite.assertNoLinkAddress("lo", constants.HostDNSAddressV6+"/128"))
211+
212+
suite.Require().NoError(suite.state.Destroy(suite.ctx, loopback.Metadata()))
213+
}
214+
173215
func (suite *AddressSpecSuite) TestDummy() {
174216
dummyInterface := suite.uniqueDummyInterface()
175217

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
@@ -285,6 +285,7 @@ func getDynamicPort() (string, error) {
285285
func makeAddrs(port string) []netip.AddrPort {
286286
return []netip.AddrPort{
287287
netip.MustParseAddrPort("127.0.0.53:" + port),
288+
netip.MustParseAddrPort("[::1]:" + port),
288289
}
289290
}
290291

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

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

160160
if resolverStatus != nil && hostDNSCfg != nil {
161161
dnsServers := xslices.FilterInPlace(
162-
[]netip.Addr{hostDNSCfg.TypedSpec().ServiceHostDNSAddress},
162+
[]netip.Addr{hostDNSCfg.TypedSpec().ServiceHostDNSAddress, hostDNSCfg.TypedSpec().ServiceHostDNSAddressV6},
163163
netip.Addr.IsValid,
164164
)
165165

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,10 @@ func (suite *EtcFileConfigSuite) SetupTest() {
128128
suite.hostDNSConfig.TypedSpec().ListenAddresses = []netip.AddrPort{
129129
netip.MustParseAddrPort("127.0.0.53:53"),
130130
netip.MustParseAddrPort("169.254.116.108:53"),
131+
netip.MustParseAddrPort("[fd54:616c:6f73::204f:5320:444e:531]:53"),
131132
}
132133
suite.hostDNSConfig.TypedSpec().ServiceHostDNSAddress = netip.MustParseAddr("169.254.116.108")
134+
suite.hostDNSConfig.TypedSpec().ServiceHostDNSAddressV6 = netip.MustParseAddr("fd54:616c:6f73::204f:5320:444e:531")
133135
}
134136

135137
func (suite *EtcFileConfigSuite) startRuntime() {
@@ -228,7 +230,7 @@ func (suite *EtcFileConfigSuite) TestComplete() {
228230
etcFileContents{
229231
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
230232
resolvConf: "nameserver 127.0.0.53\n\nsearch example.com\n",
231-
resolvGlobalConf: "nameserver 169.254.116.108\n\nsearch example.com\n",
233+
resolvGlobalConf: "nameserver 169.254.116.108\nnameserver fd54:616c:6f73:0:204f:5320:444e:531\n\nsearch example.com\n",
232234
},
233235
)
234236
}
@@ -239,7 +241,7 @@ func (suite *EtcFileConfigSuite) TestNoExtraHosts() {
239241
etcFileContents{
240242
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",
241243
resolvConf: "nameserver 127.0.0.53\n\nsearch example.com\n",
242-
resolvGlobalConf: "nameserver 169.254.116.108\n\nsearch example.com\n",
244+
resolvGlobalConf: "nameserver 169.254.116.108\nnameserver fd54:616c:6f73:0:204f:5320:444e:531\n\nsearch example.com\n",
243245
},
244246
)
245247
}
@@ -262,7 +264,7 @@ func (suite *EtcFileConfigSuite) TestNoSearchDomain() {
262264
etcFileContents{
263265
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",
264266
resolvConf: "nameserver 127.0.0.53\n",
265-
resolvGlobalConf: "nameserver 169.254.116.108\n",
267+
resolvGlobalConf: "nameserver 169.254.116.108\nnameserver fd54:616c:6f73:0:204f:5320:444e:531\n",
266268
},
267269
)
268270
}
@@ -275,7 +277,7 @@ func (suite *EtcFileConfigSuite) TestNoDomainname() {
275277
etcFileContents{
276278
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",
277279
resolvConf: "nameserver 127.0.0.53\n",
278-
resolvGlobalConf: "nameserver 169.254.116.108\n",
280+
resolvGlobalConf: "nameserver 169.254.116.108\nnameserver fd54:616c:6f73:0:204f:5320:444e:531\n",
279281
},
280282
)
281283
}
@@ -286,7 +288,7 @@ func (suite *EtcFileConfigSuite) TestOnlyResolvers() {
286288
etcFileContents{
287289
hosts: "",
288290
resolvConf: "nameserver 127.0.0.53\n",
289-
resolvGlobalConf: "nameserver 169.254.116.108\n",
291+
resolvGlobalConf: "nameserver 169.254.116.108\nnameserver fd54:616c:6f73:0:204f:5320:444e:531\n",
290292
},
291293
)
292294
}

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

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

9494
res.TypedSpec().ServiceHostDNSAddress = netip.Addr{}
95+
res.TypedSpec().ServiceHostDNSAddressV6 = netip.Addr{}
9596

9697
if cfgProvider == nil {
9798
res.TypedSpec().Enabled = false
@@ -117,6 +118,17 @@ func (ctrl *HostDNSConfigController) Run(ctx context.Context, r controller.Runti
117118
res.TypedSpec().ServiceHostDNSAddress = parsed
118119
}
119120

121+
if slices.ContainsFunc(
122+
cfgProvider.Cluster().Network().PodCIDRs(),
123+
func(cidr string) bool { return netip.MustParsePrefix(cidr).Addr().Is6() },
124+
) {
125+
parsed := netip.MustParseAddr(constants.HostDNSAddressV6)
126+
newServiceAddrs = append(newServiceAddrs, parsed)
127+
128+
res.TypedSpec().ListenAddresses = append(res.TypedSpec().ListenAddresses, netip.AddrPortFrom(parsed, 53))
129+
res.TypedSpec().ServiceHostDNSAddressV6 = parsed
130+
}
131+
120132
return nil
121133
}); err != nil {
122134
return fmt.Errorf("error writing host dns config: %w", err)

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

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

22+
cfg "github.com/siderolabs/talos/pkg/machinery/config/config"
2223
"github.com/siderolabs/talos/pkg/machinery/constants"
2324
"github.com/siderolabs/talos/pkg/machinery/nethelpers"
2425
"github.com/siderolabs/talos/pkg/machinery/resources/config"
@@ -154,8 +155,6 @@ func (ctrl *NfTablesChainConfigController) Run(ctx context.Context, r controller
154155

155156
if cfg.Config().Machine() != nil && cfg.Config().Cluster() != nil {
156157
if cfg.Config().Machine().Features().HostDNS().ForwardKubeDNSToHost() {
157-
hostDNSIP := netip.MustParseAddr(constants.HostDNSAddress)
158-
159158
// allow traffic to host DNS
160159
for _, protocol := range []nethelpers.Protocol{nethelpers.ProtocolUDP, nethelpers.ProtocolTCP} {
161160
spec.Rules = append(spec.Rules,
@@ -170,7 +169,7 @@ func (ctrl *NfTablesChainConfigController) Run(ctx context.Context, r controller
170169
),
171170
},
172171
MatchDestinationAddress: &network.NfTablesAddressMatch{
173-
IncludeSubnets: []netip.Prefix{netip.PrefixFrom(hostDNSIP, hostDNSIP.BitLen())},
172+
IncludeSubnets: hostDNSSubnets(cfg.Config().Cluster().Network()),
174173
},
175174
MatchLayer4: &network.NfTablesLayer4Match{
176175
Protocol: protocol,
@@ -256,3 +255,20 @@ func (ctrl *NfTablesChainConfigController) Run(ctx context.Context, r controller
256255
}
257256
}
258257
}
258+
259+
func hostDNSSubnets(clusterNetwork cfg.ClusterNetwork) []netip.Prefix {
260+
result := []netip.Addr{hostDNSIPv4}
261+
262+
for _, podCIDR := range clusterNetwork.PodCIDRs() {
263+
if netip.MustParsePrefix(podCIDR).Addr().Is6() {
264+
result = append(result, hostDNSIPv6)
265+
}
266+
}
267+
268+
return xslices.Map(result, func(a netip.Addr) netip.Prefix { return netip.PrefixFrom(a, a.BitLen()) })
269+
}
270+
271+
var (
272+
hostDNSIPv4 = netip.MustParseAddr(constants.HostDNSAddress)
273+
hostDNSIPv6 = netip.MustParseAddr(constants.HostDNSAddressV6)
274+
)

internal/pkg/dns/dns_test.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func TestDNS(t *testing.T) {
8080
},
8181
}
8282

83-
for _, dnsAddr := range []string{"127.0.0.1:10700"} {
83+
for _, dnsAddr := range []string{"127.0.0.1:10700", "[::1]:10700"} {
8484
for _, test := range tests {
8585
t.Run(dnsAddr+"/"+test.name, func(t *testing.T) {
8686
stop := newManager(t, test.nameservers...)
@@ -115,6 +115,14 @@ func TestDNSEmptyDestinations(t *testing.T) {
115115
require.NoError(t, err)
116116
require.Equal(t, dnssrv.RcodeServerFailure, r.Rcode, r)
117117

118+
r, err = dnssrv.Exchange(createQuery("google.com"), "[::1]:10700")
119+
require.NoError(t, err)
120+
require.Equal(t, dnssrv.RcodeServerFailure, r.Rcode, r)
121+
122+
r, err = dnssrv.Exchange(createQuery("google.com"), "[::1]:10700")
123+
require.NoError(t, err)
124+
require.Equal(t, dnssrv.RcodeServerFailure, r.Rcode, r)
125+
118126
stop()
119127
}
120128

@@ -135,6 +143,8 @@ func TestGC_NOGC(t *testing.T) {
135143
for _, err := range m.RunAll(slices.Values([]dns.AddressPair{
136144
{Network: "udp", Addr: netip.MustParseAddrPort("127.0.0.1:10700")},
137145
{Network: "udp", Addr: netip.MustParseAddrPort("127.0.0.1:10701")},
146+
{Network: "udp", Addr: netip.MustParseAddrPort("[::1]:10700")},
147+
{Network: "udp", Addr: netip.MustParseAddrPort("[::1]:10701")},
138148
}), false) {
139149
require.NoError(t, err)
140150
}
@@ -195,6 +205,8 @@ func newManager(t *testing.T, nameservers ...string) func() {
195205
for _, err := range m.RunAll(slices.Values([]dns.AddressPair{
196206
{Network: "udp", Addr: netip.MustParseAddrPort("127.0.0.1:10700")},
197207
{Network: "tcp", Addr: netip.MustParseAddrPort("127.0.0.1:10700")},
208+
{Network: "udp", Addr: netip.MustParseAddrPort("[::1]:10700")},
209+
{Network: "tcp", Addr: netip.MustParseAddrPort("[::1]:10700")},
198210
}), false) {
199211
if err != nil && strings.Contains(err.Error(), "failed to set TCP_FASTOPEN") {
200212
continue

0 commit comments

Comments
 (0)