1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 top = config.services.kubernetes;
7 cfg = top.addonManager;
8
9 isRBACEnabled = elem "RBAC" top.apiserver.authorizationMode;
10
11 addons = pkgs.runCommand "kubernetes-addons" { } ''
12 mkdir -p $out
13 # since we are mounting the addons to the addon manager, they need to be copied
14 ${concatMapStringsSep ";" (a: "cp -v ${a}/* $out/") (mapAttrsToList (name: addon:
15 pkgs.writeTextDir "${name}.json" (builtins.toJSON addon)
16 ) (cfg.addons))}
17 '';
18in
19{
20 ###### interface
21 options.services.kubernetes.addonManager = with lib.types; {
22
23 bootstrapAddons = mkOption {
24 description = lib.mdDoc ''
25 Bootstrap addons are like regular addons, but they are applied with cluster-admin rights.
26 They are applied at addon-manager startup only.
27 '';
28 default = { };
29 type = attrsOf attrs;
30 example = literalExpression ''
31 {
32 "my-service" = {
33 "apiVersion" = "v1";
34 "kind" = "Service";
35 "metadata" = {
36 "name" = "my-service";
37 "namespace" = "default";
38 };
39 "spec" = { ... };
40 };
41 }
42 '';
43 };
44
45 addons = mkOption {
46 description = lib.mdDoc "Kubernetes addons (any kind of Kubernetes resource can be an addon).";
47 default = { };
48 type = attrsOf (either attrs (listOf attrs));
49 example = literalExpression ''
50 {
51 "my-service" = {
52 "apiVersion" = "v1";
53 "kind" = "Service";
54 "metadata" = {
55 "name" = "my-service";
56 "namespace" = "default";
57 };
58 "spec" = { ... };
59 };
60 }
61 // import <nixpkgs/nixos/modules/services/cluster/kubernetes/dns.nix> { cfg = config.services.kubernetes; };
62 '';
63 };
64
65 enable = mkEnableOption (lib.mdDoc "Kubernetes addon manager");
66 };
67
68 ###### implementation
69 config = mkIf cfg.enable {
70 environment.etc."kubernetes/addons".source = "${addons}/";
71
72 systemd.services.kube-addon-manager = {
73 description = "Kubernetes addon manager";
74 wantedBy = [ "kubernetes.target" ];
75 after = [ "kube-apiserver.service" ];
76 environment.ADDON_PATH = "/etc/kubernetes/addons/";
77 path = [ pkgs.gawk ];
78 serviceConfig = {
79 Slice = "kubernetes.slice";
80 ExecStart = "${top.package}/bin/kube-addons";
81 WorkingDirectory = top.dataDir;
82 User = "kubernetes";
83 Group = "kubernetes";
84 Restart = "on-failure";
85 RestartSec = 10;
86 };
87 unitConfig = {
88 StartLimitIntervalSec = 0;
89 };
90 };
91
92 services.kubernetes.addonManager.bootstrapAddons = mkIf isRBACEnabled
93 (let
94 name = "system:kube-addon-manager";
95 namespace = "kube-system";
96 in
97 {
98
99 kube-addon-manager-r = {
100 apiVersion = "rbac.authorization.k8s.io/v1";
101 kind = "Role";
102 metadata = {
103 inherit name namespace;
104 };
105 rules = [{
106 apiGroups = ["*"];
107 resources = ["*"];
108 verbs = ["*"];
109 }];
110 };
111
112 kube-addon-manager-rb = {
113 apiVersion = "rbac.authorization.k8s.io/v1";
114 kind = "RoleBinding";
115 metadata = {
116 inherit name namespace;
117 };
118 roleRef = {
119 apiGroup = "rbac.authorization.k8s.io";
120 kind = "Role";
121 inherit name;
122 };
123 subjects = [{
124 apiGroup = "rbac.authorization.k8s.io";
125 kind = "User";
126 inherit name;
127 }];
128 };
129
130 kube-addon-manager-cluster-lister-cr = {
131 apiVersion = "rbac.authorization.k8s.io/v1";
132 kind = "ClusterRole";
133 metadata = {
134 name = "${name}:cluster-lister";
135 };
136 rules = [{
137 apiGroups = ["*"];
138 resources = ["*"];
139 verbs = ["list"];
140 }];
141 };
142
143 kube-addon-manager-cluster-lister-crb = {
144 apiVersion = "rbac.authorization.k8s.io/v1";
145 kind = "ClusterRoleBinding";
146 metadata = {
147 name = "${name}:cluster-lister";
148 };
149 roleRef = {
150 apiGroup = "rbac.authorization.k8s.io";
151 kind = "ClusterRole";
152 name = "${name}:cluster-lister";
153 };
154 subjects = [{
155 kind = "User";
156 inherit name;
157 }];
158 };
159 });
160
161 services.kubernetes.pki.certs = {
162 addonManager = top.lib.mkCert {
163 name = "kube-addon-manager";
164 CN = "system:kube-addon-manager";
165 action = "systemctl restart kube-addon-manager.service";
166 };
167 };
168 };
169
170 meta.buildDocsInSandbox = false;
171}