1{ config, options, pkgs, lib, ... }:
2
3with lib;
4
5let
6 version = "1.10.1";
7 cfg = config.services.kubernetes.addons.dns;
8 ports = {
9 dns = 10053;
10 health = 10054;
11 metrics = 10055;
12 };
13in {
14 options.services.kubernetes.addons.dns = {
15 enable = mkEnableOption (lib.mdDoc "kubernetes dns addon");
16
17 clusterIp = mkOption {
18 description = lib.mdDoc "Dns addon clusterIP";
19
20 # this default is also what kubernetes users
21 default = (
22 concatStringsSep "." (
23 take 3 (splitString "." config.services.kubernetes.apiserver.serviceClusterIpRange
24 ))
25 ) + ".254";
26 defaultText = literalMD ''
27 The `x.y.z.254` IP of
28 `config.${options.services.kubernetes.apiserver.serviceClusterIpRange}`.
29 '';
30 type = types.str;
31 };
32
33 clusterDomain = mkOption {
34 description = lib.mdDoc "Dns cluster domain";
35 default = "cluster.local";
36 type = types.str;
37 };
38
39 replicas = mkOption {
40 description = lib.mdDoc "Number of DNS pod replicas to deploy in the cluster.";
41 default = 2;
42 type = types.int;
43 };
44
45 reconcileMode = mkOption {
46 description = lib.mdDoc ''
47 Controls the addon manager reconciliation mode for the DNS addon.
48
49 Setting reconcile mode to EnsureExists makes it possible to tailor DNS behavior by editing the coredns ConfigMap.
50
51 See: <https://github.com/kubernetes/kubernetes/blob/master/cluster/addons/addon-manager/README.md>.
52 '';
53 default = "Reconcile";
54 type = types.enum [ "Reconcile" "EnsureExists" ];
55 };
56
57 coredns = mkOption {
58 description = lib.mdDoc "Docker image to seed for the CoreDNS container.";
59 type = types.attrs;
60 default = {
61 imageName = "coredns/coredns";
62 imageDigest = "sha256:a0ead06651cf580044aeb0a0feba63591858fb2e43ade8c9dea45a6a89ae7e5e";
63 finalImageTag = version;
64 sha256 = "0wg696920smmal7552a2zdhfncndn5kfammfa8bk8l7dz9bhk0y1";
65 };
66 };
67
68 corefile = mkOption {
69 description = lib.mdDoc ''
70 Custom coredns corefile configuration.
71
72 See: <https://coredns.io/manual/toc/#configuration>.
73 '';
74 type = types.str;
75 default = ''
76 .:${toString ports.dns} {
77 errors
78 health :${toString ports.health}
79 kubernetes ${cfg.clusterDomain} in-addr.arpa ip6.arpa {
80 pods insecure
81 fallthrough in-addr.arpa ip6.arpa
82 }
83 prometheus :${toString ports.metrics}
84 forward . /etc/resolv.conf
85 cache 30
86 loop
87 reload
88 loadbalance
89 }'';
90 defaultText = literalExpression ''
91 '''
92 .:${toString ports.dns} {
93 errors
94 health :${toString ports.health}
95 kubernetes ''${config.services.kubernetes.addons.dns.clusterDomain} in-addr.arpa ip6.arpa {
96 pods insecure
97 fallthrough in-addr.arpa ip6.arpa
98 }
99 prometheus :${toString ports.metrics}
100 forward . /etc/resolv.conf
101 cache 30
102 loop
103 reload
104 loadbalance
105 }
106 '''
107 '';
108 };
109 };
110
111 config = mkIf cfg.enable {
112 services.kubernetes.kubelet.seedDockerImages =
113 singleton (pkgs.dockerTools.pullImage cfg.coredns);
114
115 services.kubernetes.addonManager.bootstrapAddons = {
116 coredns-cr = {
117 apiVersion = "rbac.authorization.k8s.io/v1";
118 kind = "ClusterRole";
119 metadata = {
120 labels = {
121 "addonmanager.kubernetes.io/mode" = "Reconcile";
122 k8s-app = "kube-dns";
123 "kubernetes.io/cluster-service" = "true";
124 "kubernetes.io/bootstrapping" = "rbac-defaults";
125 };
126 name = "system:coredns";
127 };
128 rules = [
129 {
130 apiGroups = [ "" ];
131 resources = [ "endpoints" "services" "pods" "namespaces" ];
132 verbs = [ "list" "watch" ];
133 }
134 {
135 apiGroups = [ "" ];
136 resources = [ "nodes" ];
137 verbs = [ "get" ];
138 }
139 {
140 apiGroups = [ "discovery.k8s.io" ];
141 resources = [ "endpointslices" ];
142 verbs = [ "list" "watch" ];
143 }
144 ];
145 };
146
147 coredns-crb = {
148 apiVersion = "rbac.authorization.k8s.io/v1";
149 kind = "ClusterRoleBinding";
150 metadata = {
151 annotations = {
152 "rbac.authorization.kubernetes.io/autoupdate" = "true";
153 };
154 labels = {
155 "addonmanager.kubernetes.io/mode" = "Reconcile";
156 k8s-app = "kube-dns";
157 "kubernetes.io/cluster-service" = "true";
158 "kubernetes.io/bootstrapping" = "rbac-defaults";
159 };
160 name = "system:coredns";
161 };
162 roleRef = {
163 apiGroup = "rbac.authorization.k8s.io";
164 kind = "ClusterRole";
165 name = "system:coredns";
166 };
167 subjects = [
168 {
169 kind = "ServiceAccount";
170 name = "coredns";
171 namespace = "kube-system";
172 }
173 ];
174 };
175 };
176
177 services.kubernetes.addonManager.addons = {
178 coredns-sa = {
179 apiVersion = "v1";
180 kind = "ServiceAccount";
181 metadata = {
182 labels = {
183 "addonmanager.kubernetes.io/mode" = "Reconcile";
184 k8s-app = "kube-dns";
185 "kubernetes.io/cluster-service" = "true";
186 };
187 name = "coredns";
188 namespace = "kube-system";
189 };
190 };
191
192 coredns-cm = {
193 apiVersion = "v1";
194 kind = "ConfigMap";
195 metadata = {
196 labels = {
197 "addonmanager.kubernetes.io/mode" = cfg.reconcileMode;
198 k8s-app = "kube-dns";
199 "kubernetes.io/cluster-service" = "true";
200 };
201 name = "coredns";
202 namespace = "kube-system";
203 };
204 data = {
205 Corefile = cfg.corefile;
206 };
207 };
208
209 coredns-deploy = {
210 apiVersion = "apps/v1";
211 kind = "Deployment";
212 metadata = {
213 labels = {
214 "addonmanager.kubernetes.io/mode" = cfg.reconcileMode;
215 k8s-app = "kube-dns";
216 "kubernetes.io/cluster-service" = "true";
217 "kubernetes.io/name" = "CoreDNS";
218 };
219 name = "coredns";
220 namespace = "kube-system";
221 };
222 spec = {
223 replicas = cfg.replicas;
224 selector = {
225 matchLabels = { k8s-app = "kube-dns"; };
226 };
227 strategy = {
228 rollingUpdate = { maxUnavailable = 1; };
229 type = "RollingUpdate";
230 };
231 template = {
232 metadata = {
233 labels = {
234 k8s-app = "kube-dns";
235 };
236 };
237 spec = {
238 containers = [
239 {
240 args = [ "-conf" "/etc/coredns/Corefile" ];
241 image = with cfg.coredns; "${imageName}:${finalImageTag}";
242 imagePullPolicy = "Never";
243 livenessProbe = {
244 failureThreshold = 5;
245 httpGet = {
246 path = "/health";
247 port = ports.health;
248 scheme = "HTTP";
249 };
250 initialDelaySeconds = 60;
251 successThreshold = 1;
252 timeoutSeconds = 5;
253 };
254 name = "coredns";
255 ports = [
256 {
257 containerPort = ports.dns;
258 name = "dns";
259 protocol = "UDP";
260 }
261 {
262 containerPort = ports.dns;
263 name = "dns-tcp";
264 protocol = "TCP";
265 }
266 {
267 containerPort = ports.metrics;
268 name = "metrics";
269 protocol = "TCP";
270 }
271 ];
272 resources = {
273 limits = {
274 memory = "170Mi";
275 };
276 requests = {
277 cpu = "100m";
278 memory = "70Mi";
279 };
280 };
281 securityContext = {
282 allowPrivilegeEscalation = false;
283 capabilities = {
284 drop = [ "all" ];
285 };
286 readOnlyRootFilesystem = true;
287 };
288 volumeMounts = [
289 {
290 mountPath = "/etc/coredns";
291 name = "config-volume";
292 readOnly = true;
293 }
294 ];
295 }
296 ];
297 dnsPolicy = "Default";
298 nodeSelector = {
299 "beta.kubernetes.io/os" = "linux";
300 };
301 serviceAccountName = "coredns";
302 tolerations = [
303 {
304 effect = "NoSchedule";
305 key = "node-role.kubernetes.io/master";
306 }
307 {
308 key = "CriticalAddonsOnly";
309 operator = "Exists";
310 }
311 ];
312 volumes = [
313 {
314 configMap = {
315 items = [
316 {
317 key = "Corefile";
318 path = "Corefile";
319 }
320 ];
321 name = "coredns";
322 };
323 name = "config-volume";
324 }
325 ];
326 };
327 };
328 };
329 };
330
331 coredns-svc = {
332 apiVersion = "v1";
333 kind = "Service";
334 metadata = {
335 annotations = {
336 "prometheus.io/port" = toString ports.metrics;
337 "prometheus.io/scrape" = "true";
338 };
339 labels = {
340 "addonmanager.kubernetes.io/mode" = "Reconcile";
341 k8s-app = "kube-dns";
342 "kubernetes.io/cluster-service" = "true";
343 "kubernetes.io/name" = "CoreDNS";
344 };
345 name = "kube-dns";
346 namespace = "kube-system";
347 };
348 spec = {
349 clusterIP = cfg.clusterIp;
350 ports = [
351 {
352 name = "dns";
353 port = 53;
354 targetPort = ports.dns;
355 protocol = "UDP";
356 }
357 {
358 name = "dns-tcp";
359 port = 53;
360 targetPort = ports.dns;
361 protocol = "TCP";
362 }
363 ];
364 selector = { k8s-app = "kube-dns"; };
365 };
366 };
367 };
368
369 services.kubernetes.kubelet.clusterDns = mkDefault cfg.clusterIp;
370 };
371
372 meta.buildDocsInSandbox = false;
373}