1{
2 pkgs ? import <nixpkgs> {},
3 externalDomain ? "myawesomecluster.cluster.yourdomain.net",
4 serviceClusterIp ? "10.0.0.1",
5 kubelets,
6 ...
7}:
8let
9 runWithCFSSL = name: cmd:
10 let secrets = pkgs.runCommand "${name}-cfss.json" {
11 buildInputs = [ pkgs.cfssl pkgs.jq ];
12 outputs = [ "out" "cert" "key" "csr" ];
13 }
14 ''
15 (
16 echo "${cmd}"
17 cfssl ${cmd} > tmp
18 cat tmp | jq -r .key > $key
19 cat tmp | jq -r .cert > $cert
20 cat tmp | jq -r .csr > $csr
21
22 touch $out
23 ) 2>&1 | fold -w 80 -s
24 '';
25 in {
26 key = secrets.key;
27 cert = secrets.cert;
28 csr = secrets.csr;
29 };
30
31 writeCFSSL = content:
32 pkgs.runCommand content.name {
33 buildInputs = [ pkgs.cfssl pkgs.jq ];
34 } ''
35 mkdir -p $out
36 cd $out
37
38 json=${pkgs.lib.escapeShellArg (builtins.toJSON content)}
39
40 # for a given $field in the $json, treat the associated value as a
41 # file path and substitute the contents thereof into the $json
42 # object.
43 expandFileField() {
44 local field=$1
45 if jq -e --arg field "$field" 'has($field)'; then
46 local path="$(echo "$json" | jq -r ".$field")"
47 json="$(echo "$json" | jq --arg val "$(cat "$path")" ".$field = \$val")"
48 fi
49 }
50
51 expandFileField key
52 expandFileField ca
53 expandFileField cert
54
55 echo "$json" | cfssljson -bare ${content.name}
56 '';
57
58 noCSR = content: pkgs.lib.filterAttrs (n: v: n != "csr") content;
59 noKey = content: pkgs.lib.filterAttrs (n: v: n != "key") content;
60
61 writeFile = content:
62 if pkgs.lib.isDerivation content
63 then content
64 else pkgs.writeText "content" (builtins.toJSON content);
65
66 createServingCertKey = { ca, cn, hosts? [], size ? 2048, name ? cn }:
67 noCSR (
68 (runWithCFSSL name "gencert -ca=${writeFile ca.cert} -ca-key=${writeFile ca.key} -profile=server -config=${writeFile ca.config} ${writeFile {
69 CN = cn;
70 hosts = hosts;
71 key = { algo = "rsa"; inherit size; };
72 }}") // { inherit name; }
73 );
74
75 createClientCertKey = { ca, cn, groups ? [], size ? 2048, name ? cn }:
76 noCSR (
77 (runWithCFSSL name "gencert -ca=${writeFile ca.cert} -ca-key=${writeFile ca.key} -profile=client -config=${writeFile ca.config} ${writeFile {
78 CN = cn;
79 names = map (group: {O = group;}) groups;
80 hosts = [""];
81 key = { algo = "rsa"; inherit size; };
82 }}") // { inherit name; }
83 );
84
85 createSigningCertKey = { C ? "xx", ST ? "x", L ? "x", O ? "x", OU ? "x", CN ? "ca", emailAddress ? "x", expiry ? "43800h", size ? 2048, name ? CN }:
86 (noCSR (runWithCFSSL CN "genkey -initca ${writeFile {
87 key = { algo = "rsa"; inherit size; };
88 names = [{ inherit C ST L O OU CN emailAddress; }];
89 }}")) // {
90 inherit name;
91 config.signing = {
92 default.expiry = expiry;
93 profiles = {
94 server = {
95 inherit expiry;
96 usages = [
97 "signing"
98 "key encipherment"
99 "server auth"
100 ];
101 };
102 client = {
103 inherit expiry;
104 usages = [
105 "signing"
106 "key encipherment"
107 "client auth"
108 ];
109 };
110 peer = {
111 inherit expiry;
112 usages = [
113 "signing"
114 "key encipherment"
115 "server auth"
116 "client auth"
117 ];
118 };
119 };
120 };
121 };
122
123 ca = createSigningCertKey {};
124
125 kube-apiserver = createServingCertKey {
126 inherit ca;
127 cn = "kube-apiserver";
128 hosts = ["kubernetes.default" "kubernetes.default.svc" "localhost" "api.${externalDomain}" serviceClusterIp];
129 };
130
131 kubelet = createServingCertKey {
132 inherit ca;
133 cn = "kubelet";
134 hosts = ["*.${externalDomain}"];
135 };
136
137 service-accounts = createServingCertKey {
138 inherit ca;
139 cn = "kube-service-accounts";
140 };
141
142 etcd = createServingCertKey {
143 inherit ca;
144 cn = "etcd";
145 hosts = ["etcd.${externalDomain}"];
146 };
147
148 etcd-client = createClientCertKey {
149 inherit ca;
150 cn = "etcd-client";
151 };
152
153 kubelet-client = createClientCertKey {
154 inherit ca;
155 cn = "kubelet-client";
156 groups = ["system:masters"];
157 };
158
159 apiserver-client = {
160 kubelet = hostname: createClientCertKey {
161 inherit ca;
162 name = "apiserver-client-kubelet-${hostname}";
163 cn = "system:node:${hostname}.${externalDomain}";
164 groups = ["system:nodes"];
165 };
166
167 kube-proxy = createClientCertKey {
168 inherit ca;
169 name = "apiserver-client-kube-proxy";
170 cn = "system:kube-proxy";
171 groups = ["system:kube-proxy" "system:nodes"];
172 };
173
174 kube-controller-manager = createClientCertKey {
175 inherit ca;
176 name = "apiserver-client-kube-controller-manager";
177 cn = "system:kube-controller-manager";
178 groups = ["system:masters"];
179 };
180
181 kube-scheduler = createClientCertKey {
182 inherit ca;
183 name = "apiserver-client-kube-scheduler";
184 cn = "system:kube-scheduler";
185 groups = ["system:kube-scheduler"];
186 };
187
188 admin = createClientCertKey {
189 inherit ca;
190 cn = "admin";
191 groups = ["system:masters"];
192 };
193 };
194in {
195 master = pkgs.buildEnv {
196 name = "master-keys";
197 paths = [
198 (writeCFSSL (noKey ca))
199 (writeCFSSL kube-apiserver)
200 (writeCFSSL kubelet-client)
201 (writeCFSSL apiserver-client.kube-controller-manager)
202 (writeCFSSL apiserver-client.kube-scheduler)
203 (writeCFSSL service-accounts)
204 (writeCFSSL etcd)
205 ];
206 };
207
208 worker = pkgs.buildEnv {
209 name = "worker-keys";
210 paths = [
211 (writeCFSSL (noKey ca))
212 (writeCFSSL kubelet)
213 (writeCFSSL apiserver-client.kube-proxy)
214 (writeCFSSL etcd-client)
215 ] ++ map (hostname: writeCFSSL (apiserver-client.kubelet hostname)) kubelets;
216 };
217
218 admin = writeCFSSL apiserver-client.admin;
219}