Skip to content

Commit 92897a2

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 6299dc7 commit 92897a2

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-03T16:57:31+03:00
3+
date = 2026-04-15T10:59:17+03:00
44
weight = 11
55
+++
66
## v1beta2
@@ -242,6 +242,7 @@ ClusterNetworkConfig describes the cluster network
242242
| ipFamily | IPFamily allows specifying IP family of a cluster. Valid values are IPv4 \| IPv6 \| IPv4+IPv6 \| IPv6+IPv4. | IPFamily | false |
243243
| 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 |
244244
| 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 |
245+
| 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 |
245246

246247
[Back to Group](#v1beta2)
247248

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-03T16:57:31+03:00
3+
date = 2026-04-15T10:59:17+03:00
44
weight = 11
55
+++
66
## v1beta3
@@ -255,6 +255,7 @@ ClusterNetworkConfig describes the cluster network
255255
| ipFamily | IPFamily allows specifying IP family of a cluster. Valid values are IPv4 \| IPv6 \| IPv4+IPv6 \| IPv6+IPv4. | IPFamily | false |
256256
| 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 |
257257
| 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 |
258+
| 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 |
258259

259260
[Back to Group](#v1beta3)
260261

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
@@ -675,6 +675,11 @@ type ClusterNetworkConfig struct {
675675

676676
// 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.
677677
NodeCIDRMaskSizeIPv6 *int `json:"nodeCIDRMaskSizeIPv6,omitempty"`
678+
679+
// DNSServiceIP is an optional override for the cluster DNS service IP.
680+
// If not set, it defaults to the 10th IP of the serviceSubnet (e.g. 10.96.0.10 for 10.96.0.0/12).
681+
// When nodeLocalDNS is enabled, OSM always uses the NodeLocalDNS virtual IP regardless of this field.
682+
DNSServiceIP string `json:"dnsServiceIP,omitempty"`
678683
}
679684

680685
// 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
@@ -680,6 +680,11 @@ type ClusterNetworkConfig struct {
680680

681681
// 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.
682682
NodeCIDRMaskSizeIPv6 *int `json:"nodeCIDRMaskSizeIPv6,omitempty"`
683+
684+
// DNSServiceIP is an optional override for the cluster DNS service IP.
685+
// If not set, it defaults to the 10th IP of the serviceSubnet (e.g. 10.96.0.10 for 10.96.0.0/12).
686+
// When nodeLocalDNS is enabled, OSM always uses the NodeLocalDNS virtual IP regardless of this field.
687+
DNSServiceIP string `json:"dnsServiceIP,omitempty"`
683688
}
684689

685690
// 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
@@ -672,6 +672,11 @@ type ClusterNetworkConfig struct {
672672

673673
// 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.
674674
NodeCIDRMaskSizeIPv6 *int `json:"nodeCIDRMaskSizeIPv6,omitempty"`
675+
676+
// DNSServiceIP is an optional override for the cluster DNS service IP.
677+
// If not set, it defaults to the 10th IP of the serviceSubnet (e.g. 10.96.0.10 for 10.96.0.0/12).
678+
// When nodeLocalDNS is enabled, OSM always uses the NodeLocalDNS virtual IP regardless of this field.
679+
DNSServiceIP string `json:"dnsServiceIP,omitempty"`
675680
}
676681

677682
// 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)