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