220 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			220 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
| {
 | |
|   pkgs ? import <nixpkgs> {},
 | |
|   externalDomain ? "myawesomecluster.cluster.yourdomain.net",
 | |
|   serviceClusterIp ? "10.0.0.1",
 | |
|   kubelets,
 | |
|   ...
 | |
| }:
 | |
| let
 | |
|    runWithCFSSL = name: cmd:
 | |
|      let secrets = pkgs.runCommand "${name}-cfss.json" {
 | |
|          buildInputs = [ pkgs.cfssl pkgs.jq ];
 | |
|          outputs = [ "out" "cert" "key" "csr" ];
 | |
|        }
 | |
|        ''
 | |
|          (
 | |
|            echo "${cmd}"
 | |
|            cfssl ${cmd} > tmp
 | |
|            cat tmp | jq -r .key > $key
 | |
|            cat tmp | jq -r .cert > $cert
 | |
|            cat tmp | jq -r .csr > $csr
 | |
| 
 | |
|            touch $out
 | |
|          ) 2>&1 | fold -w 80 -s
 | |
|        '';
 | |
|      in {
 | |
|        key = secrets.key;
 | |
|        cert = secrets.cert;
 | |
|        csr = secrets.csr;
 | |
|      };
 | |
| 
 | |
|    writeCFSSL = content:
 | |
|      pkgs.runCommand content.name {
 | |
|       buildInputs = [ pkgs.cfssl pkgs.jq ];
 | |
|      } ''
 | |
|        mkdir -p $out
 | |
|        cd $out
 | |
| 
 | |
|        json=${pkgs.lib.escapeShellArg (builtins.toJSON content)}
 | |
| 
 | |
|        # for a given $field in the $json, treat the associated value as a
 | |
|        # file path and substitute the contents thereof into the $json
 | |
|        # object.
 | |
|        expandFileField() {
 | |
|          local field=$1
 | |
|          if jq -e --arg field "$field" 'has($field)'; then
 | |
|            local path="$(echo "$json" | jq -r ".$field")"
 | |
|            json="$(echo "$json" | jq --arg val "$(cat "$path")" ".$field = \$val")"
 | |
|          fi
 | |
|        }
 | |
| 
 | |
|        expandFileField key
 | |
|        expandFileField ca
 | |
|        expandFileField cert
 | |
| 
 | |
|        echo "$json" | cfssljson -bare ${content.name}
 | |
|      '';
 | |
| 
 | |
|   noCSR = content: pkgs.lib.filterAttrs (n: v: n != "csr") content;
 | |
|   noKey = content: pkgs.lib.filterAttrs (n: v: n != "key") content;
 | |
| 
 | |
|   writeFile = content:
 | |
|     if pkgs.lib.isDerivation content
 | |
|     then content
 | |
|     else pkgs.writeText "content" (builtins.toJSON content);
 | |
| 
 | |
|   createServingCertKey = { ca, cn, hosts? [], size ? 2048, name ? cn }:
 | |
|     noCSR (
 | |
|       (runWithCFSSL name "gencert -ca=${writeFile ca.cert} -ca-key=${writeFile ca.key} -profile=server -config=${writeFile ca.config} ${writeFile {
 | |
|         CN = cn;
 | |
|         hosts = hosts;
 | |
|         key = { algo = "rsa"; inherit size; };
 | |
|       }}") // { inherit name; }
 | |
|     );
 | |
| 
 | |
|   createClientCertKey = { ca, cn, groups ? [], size ? 2048, name ? cn }:
 | |
|     noCSR (
 | |
|       (runWithCFSSL name "gencert -ca=${writeFile ca.cert} -ca-key=${writeFile ca.key} -profile=client -config=${writeFile ca.config} ${writeFile {
 | |
|         CN = cn;
 | |
|         names = map (group: {O = group;}) groups;
 | |
|         hosts = [""];
 | |
|         key = { algo = "rsa"; inherit size; };
 | |
|       }}") // { inherit name; }
 | |
|     );
 | |
| 
 | |
|   createSigningCertKey = { C ? "xx", ST ? "x", L ? "x", O ? "x", OU ? "x", CN ? "ca", emailAddress ? "x", expiry ? "43800h", size ? 2048, name ? CN }:
 | |
|     (noCSR (runWithCFSSL CN "genkey -initca ${writeFile {
 | |
|       key = { algo = "rsa"; inherit size; };
 | |
|       names = [{ inherit C ST L O OU CN emailAddress; }];
 | |
|     }}")) // {
 | |
|       inherit name;
 | |
|       config.signing = {
 | |
|         default.expiry = expiry;
 | |
|         profiles = {
 | |
|           server = {
 | |
|             inherit expiry;
 | |
|             usages = [
 | |
|               "signing"
 | |
|               "key encipherment"
 | |
|               "server auth"
 | |
|             ];
 | |
|           };
 | |
|           client = {
 | |
|             inherit expiry;
 | |
|             usages = [
 | |
|               "signing"
 | |
|               "key encipherment"
 | |
|               "client auth"
 | |
|             ];
 | |
|           };
 | |
|           peer = {
 | |
|             inherit expiry;
 | |
|             usages = [
 | |
|               "signing"
 | |
|               "key encipherment"
 | |
|               "server auth"
 | |
|               "client auth"
 | |
|             ];
 | |
|           };
 | |
|         };
 | |
|       };
 | |
|     };
 | |
| 
 | |
|   ca = createSigningCertKey {};
 | |
| 
 | |
|   kube-apiserver = createServingCertKey {
 | |
|     inherit ca;
 | |
|     cn = "kube-apiserver";
 | |
|     hosts = ["kubernetes.default" "kubernetes.default.svc" "localhost" "api.${externalDomain}" serviceClusterIp];
 | |
|   };
 | |
| 
 | |
|   kubelet = createServingCertKey {
 | |
|     inherit ca;
 | |
|     cn = "kubelet";
 | |
|     hosts = ["*.${externalDomain}"];
 | |
|   };
 | |
| 
 | |
|   service-accounts = createServingCertKey {
 | |
|     inherit ca;
 | |
|     cn = "kube-service-accounts";
 | |
|   };
 | |
| 
 | |
|   etcd = createServingCertKey {
 | |
|     inherit ca;
 | |
|     cn = "etcd";
 | |
|     hosts = ["etcd.${externalDomain}"];
 | |
|   };
 | |
| 
 | |
|   etcd-client = createClientCertKey {
 | |
|     inherit ca;
 | |
|     cn = "etcd-client";
 | |
|   };
 | |
| 
 | |
|   kubelet-client = createClientCertKey {
 | |
|     inherit ca;
 | |
|     cn = "kubelet-client";
 | |
|     groups = ["system:masters"];
 | |
|   };
 | |
| 
 | |
|   apiserver-client = {
 | |
|     kubelet = hostname: createClientCertKey {
 | |
|       inherit ca;
 | |
|       name = "apiserver-client-kubelet-${hostname}";
 | |
|       cn = "system:node:${hostname}.${externalDomain}";
 | |
|       groups = ["system:nodes"];
 | |
|     };
 | |
| 
 | |
|     kube-proxy = createClientCertKey {
 | |
|       inherit ca;
 | |
|       name = "apiserver-client-kube-proxy";
 | |
|       cn = "system:kube-proxy";
 | |
|       groups = ["system:kube-proxy" "system:nodes"];
 | |
|     };
 | |
| 
 | |
|     kube-controller-manager = createClientCertKey {
 | |
|       inherit ca;
 | |
|       name = "apiserver-client-kube-controller-manager";
 | |
|       cn = "system:kube-controller-manager";
 | |
|       groups = ["system:masters"];
 | |
|     };
 | |
| 
 | |
|     kube-scheduler = createClientCertKey {
 | |
|       inherit ca;
 | |
|       name = "apiserver-client-kube-scheduler";
 | |
|       cn = "system:kube-scheduler";
 | |
|       groups = ["system:kube-scheduler"];
 | |
|     };
 | |
| 
 | |
|     admin = createClientCertKey {
 | |
|       inherit ca;
 | |
|       cn = "admin";
 | |
|       groups = ["system:masters"];
 | |
|     };
 | |
|   };
 | |
| in {
 | |
|   master = pkgs.buildEnv {
 | |
|     name = "master-keys";
 | |
|     paths = [
 | |
|       (writeCFSSL (noKey ca))
 | |
|       (writeCFSSL kube-apiserver)
 | |
|       (writeCFSSL kubelet-client)
 | |
|       (writeCFSSL apiserver-client.kube-controller-manager)
 | |
|       (writeCFSSL apiserver-client.kube-scheduler)
 | |
|       (writeCFSSL service-accounts)
 | |
|       (writeCFSSL etcd)
 | |
|     ];
 | |
|   };
 | |
| 
 | |
|   worker = pkgs.buildEnv {
 | |
|     name = "worker-keys";
 | |
|     paths = [
 | |
|       (writeCFSSL (noKey ca))
 | |
|       (writeCFSSL kubelet)
 | |
|       (writeCFSSL apiserver-client.kube-proxy)
 | |
|       (writeCFSSL etcd-client)
 | |
|     ] ++ map (hostname: writeCFSSL (apiserver-client.kubelet hostname)) kubelets;
 | |
|   };
 | |
| 
 | |
|   admin = writeCFSSL apiserver-client.admin;
 | |
| }
 | 
