Skip to content

Commit c43cd05

Browse files
committed
Add dnsServiceIP override for cluster DNS settings
Support `clusterNetwork.dnsServiceIP` in API versions and conversions, validate the value, and use it for kubelet/OSM when NodeLocalDNS is disabled. Fall back to the default 10th service-subnet IP when unset. Signed-off-by: Artiom Diomin <artiom@kubermatic.com>
1 parent cadcf3f commit c43cd05

13 files changed

Lines changed: 123 additions & 7 deletions

File tree

addons/operating-system-manager/deployment-controller.yaml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,7 @@ spec:
4646
- -log-format=json # json or console
4747
- -health-probe-address=0.0.0.0:8085
4848
- -metrics-address=0.0.0.0:8080
49-
{{ if .Config.Features.NodeLocalDNS.Deploy -}}
50-
- -cluster-dns={{ .Resources.NodeLocalDNSVirtualIP }}
51-
{{ end -}}
49+
- -cluster-dns={{ .ClusterDNSIP }}
5250
- -namespace=kube-system
5351
- -container-runtime={{ .Config.ContainerRuntime }}
5452
- -pause-image={{ .InternalImages.Get "PauseImage" }}

docs/api_reference/v1beta2.en.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
+++
22
title = "v1beta2 API Reference"
3-
date = 2026-04-03T12:52:19+02:00
3+
date = 2026-04-09T11:02:18+03:00
44
weight = 11
55
+++
66
## v1beta2
@@ -236,6 +236,7 @@ ClusterNetworkConfig describes the cluster network
236236
| ipFamily | IPFamily allows specifying IP family of a cluster. Valid values are IPv4 \| IPv6 \| IPv4+IPv6 \| IPv6+IPv4. | IPFamily | false |
237237
| nodeCIDRMaskSizeIPv4 | NodeCIDRMaskSizeIPv4 is the mask size used to address the nodes within provided IPv4 Pods CIDR. It has to be larger than the provided IPv4 Pods CIDR. Defaults to 24. | *int | false |
238238
| nodeCIDRMaskSizeIPv6 | NodeCIDRMaskSizeIPv6 is the mask size used to address the nodes within provided IPv6 Pods CIDR. It has to be larger than the provided IPv6 Pods CIDR. Defaults to 64. | *int | false |
239+
| dnsServiceIP | DNSServiceIP is an optional override for the cluster DNS service IP. If not set, it defaults to the 10th IP of the serviceSubnet (e.g. 10.96.0.10 for 10.96.0.0/12). When nodeLocalDNS is enabled, OSM always uses the NodeLocalDNS virtual IP regardless of this field. | string | false |
239240

240241
[Back to Group](#v1beta2)
241242

docs/api_reference/v1beta3.en.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
+++
22
title = "v1beta3 API Reference"
3-
date = 2026-04-03T12:52:19+02:00
3+
date = 2026-04-09T11:02:18+03:00
44
weight = 11
55
+++
66
## v1beta3
@@ -249,6 +249,7 @@ ClusterNetworkConfig describes the cluster network
249249
| ipFamily | IPFamily allows specifying IP family of a cluster. Valid values are IPv4 \| IPv6 \| IPv4+IPv6 \| IPv6+IPv4. | IPFamily | false |
250250
| nodeCIDRMaskSizeIPv4 | NodeCIDRMaskSizeIPv4 is the mask size used to address the nodes within provided IPv4 Pods CIDR. It has to be larger than the provided IPv4 Pods CIDR. Defaults to 24. | *int | false |
251251
| nodeCIDRMaskSizeIPv6 | NodeCIDRMaskSizeIPv6 is the mask size used to address the nodes within provided IPv6 Pods CIDR. It has to be larger than the provided IPv6 Pods CIDR. Defaults to 64. | *int | false |
252+
| dnsServiceIP | DNSServiceIP is an optional override for the cluster DNS service IP. If not set, it defaults to the 10th IP of the serviceSubnet (e.g. 10.96.0.10 for 10.96.0.0/12). When nodeLocalDNS is enabled, OSM always uses the NodeLocalDNS virtual IP regardless of this field. | string | false |
252253

253254
[Back to Group](#v1beta3)
254255

pkg/addons/applier.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ type templateData struct {
9090
InternalImages *internalImages
9191
Resources map[string]string
9292
Params map[string]string
93+
ClusterDNSIP string
9394
}
9495

9596
type registryCredentialsContainer struct {
@@ -185,6 +186,14 @@ func newAddonsApplier(s *state.State) (*applier, error) {
185186
}
186187
}
187188

189+
clusterDNSIP := resources.NodeLocalDNSVirtualIP
190+
if !s.Cluster.Features.NodeLocalDNS.Deploy {
191+
clusterDNSIP, err = s.Cluster.ClusterNetwork.EffectiveDNSServiceIP()
192+
if err != nil {
193+
return nil, fail.Runtime(err, "computing cluster DNS service IP")
194+
}
195+
}
196+
188197
data := templateData{
189198
Config: s.Cluster,
190199
Certificates: map[string]string{
@@ -211,8 +220,9 @@ func newAddonsApplier(s *state.State) (*applier, error) {
211220
pauseImage: s.PauseImage,
212221
resolver: s.Images.Get,
213222
},
214-
Resources: resources.All(),
215-
Params: map[string]string{},
223+
Resources: resources.All(),
224+
Params: map[string]string{},
225+
ClusterDNSIP: clusterDNSIP,
216226
}
217227

218228
if !s.LiveCluster.IsProvisioned() {

pkg/apis/kubeone/helpers.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ package kubeone
1818

1919
import (
2020
"bytes"
21+
"encoding/binary"
2122
"fmt"
2223
"math/rand"
24+
"net"
2325
"net/url"
2426
"path/filepath"
2527
"sort"
@@ -547,3 +549,31 @@ func (v VersionConfig) KubernetesMajorMinorVersion() string {
547549

548550
return fmt.Sprintf("v%d.%d", kubeSemVer.Major(), kubeSemVer.Minor())
549551
}
552+
553+
// EffectiveDNSServiceIP returns the cluster DNS service IP to use for kubelet and OSM.
554+
// If DNSServiceIP is explicitly set, it is returned as-is.
555+
// Otherwise the 10th IP in the ServiceSubnet is computed (e.g. 10.96.0.10 for 10.96.0.0/12),
556+
// matching the CoreDNS service IP assigned by kubeadm.
557+
func (c ClusterNetworkConfig) EffectiveDNSServiceIP() (string, error) {
558+
if c.DNSServiceIP != "" {
559+
return c.DNSServiceIP, nil
560+
}
561+
562+
return nthIPInSubnet(c.ServiceSubnet, 10)
563+
}
564+
565+
// nthIPInSubnet returns the n-th IP address within the given CIDR subnet.
566+
func nthIPInSubnet(cidr string, n uint32) (string, error) {
567+
_, network, err := net.ParseCIDR(cidr)
568+
if err != nil {
569+
return "", fmt.Errorf("parsing subnet %q: %w", cidr, err)
570+
}
571+
572+
ip := network.IP.To4()
573+
addr := binary.BigEndian.Uint32(ip)
574+
addr += n
575+
result := make(net.IP, 4)
576+
binary.BigEndian.PutUint32(result, addr)
577+
578+
return result.String(), nil
579+
}

pkg/apis/kubeone/types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,11 @@ type ClusterNetworkConfig struct {
611611

612612
// NodeCIDRMaskSizeIPv6 is the mask size used to address the nodes within provided IPv6 Pods CIDR. It has to be larger than the provided IPv6 Pods CIDR. Defaults to 64.
613613
NodeCIDRMaskSizeIPv6 *int `json:"nodeCIDRMaskSizeIPv6,omitempty"`
614+
615+
// DNSServiceIP is an optional override for the cluster DNS service IP.
616+
// If not set, it defaults to the 10th IP of the serviceSubnet (e.g. 10.96.0.10 for 10.96.0.0/12).
617+
// When nodeLocalDNS is enabled, OSM always uses the NodeLocalDNS virtual IP regardless of this field.
618+
DNSServiceIP string `json:"dnsServiceIP,omitempty"`
614619
}
615620

616621
// IPFamily allows specifying IP family of a cluster.

pkg/apis/kubeone/v1beta2/types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,11 @@ type ClusterNetworkConfig struct {
616616

617617
// NodeCIDRMaskSizeIPv6 is the mask size used to address the nodes within provided IPv6 Pods CIDR. It has to be larger than the provided IPv6 Pods CIDR. Defaults to 64.
618618
NodeCIDRMaskSizeIPv6 *int `json:"nodeCIDRMaskSizeIPv6,omitempty"`
619+
620+
// DNSServiceIP is an optional override for the cluster DNS service IP.
621+
// If not set, it defaults to the 10th IP of the serviceSubnet (e.g. 10.96.0.10 for 10.96.0.0/12).
622+
// When nodeLocalDNS is enabled, OSM always uses the NodeLocalDNS virtual IP regardless of this field.
623+
DNSServiceIP string `json:"dnsServiceIP,omitempty"`
619624
}
620625

621626
// IPFamily allows specifying IP family of a cluster.

pkg/apis/kubeone/v1beta2/zz_generated.conversion.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/kubeone/v1beta3/types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,11 @@ type ClusterNetworkConfig struct {
608608

609609
// NodeCIDRMaskSizeIPv6 is the mask size used to address the nodes within provided IPv6 Pods CIDR. It has to be larger than the provided IPv6 Pods CIDR. Defaults to 64.
610610
NodeCIDRMaskSizeIPv6 *int `json:"nodeCIDRMaskSizeIPv6,omitempty"`
611+
612+
// DNSServiceIP is an optional override for the cluster DNS service IP.
613+
// If not set, it defaults to the 10th IP of the serviceSubnet (e.g. 10.96.0.10 for 10.96.0.0/12).
614+
// When nodeLocalDNS is enabled, OSM always uses the NodeLocalDNS virtual IP regardless of this field.
615+
DNSServiceIP string `json:"dnsServiceIP,omitempty"`
611616
}
612617

613618
// IPFamily allows specifying IP family of a cluster.

pkg/apis/kubeone/v1beta3/zz_generated.conversion.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)