blob: ea15df8b4552a5dcbcf42c5aaa019d70113d32c9 [file] [log] [blame]
Sergiusz Bazanski73cef112019-04-07 00:06:23 +02001#!/usr/bin/env python
2
3from builtins import object
4
5import datetime
6from io import BytesIO
7import json
8import logging
9import os
10import tempfile
11import subprocess
12import sys
13
14from cryptography import x509
15from cryptography.hazmat.backends import default_backend
16import fabric
17
18from tools import secretstore
Serge Bazanski0f8e5a22021-10-16 20:53:51 +000019from tools.hscloud import lib as hscloud
Sergiusz Bazanski73cef112019-04-07 00:06:23 +020020
21import ca
22
23
Serge Bazanski0f8e5a22021-10-16 20:53:51 +000024local_root = hscloud.workspace_location()
Sergiusz Bazanski73cef112019-04-07 00:06:23 +020025
26
27cluster = 'k0.hswaw.net'
Sergiusz Bazanski73cef112019-04-07 00:06:23 +020028ss = secretstore.SecretStore(
29 plain_root=os.path.join(local_root, 'cluster/secrets/plain'),
30 cipher_root=os.path.join(local_root, 'cluster/secrets/cipher'))
31
32
33logger = logging.getLogger()
34logger.setLevel(logging.INFO)
35formatter = logging.Formatter('%(levelname)s - %(message)s')
36sh = logging.StreamHandler()
37sh.setFormatter(formatter)
38logger.addHandler(sh)
39
40
41
Sergiusz Bazanskic78cc132020-02-02 22:31:53 +010042def pki_config(key, fqdn):
43 machine_name = fqdn.split('.')[0]
Sergiusz Bazanski73cef112019-04-07 00:06:23 +020044 raw = subprocess.check_output([
45 'nix', 'eval', '--raw',
Sergiusz Bazanskic78cc132020-02-02 22:31:53 +010046 '( ((import ' + local_root + '/cluster/nix/defs-cluster-k0.nix ) "' + machine_name + '").pki.' + key + '.json )',
Sergiusz Bazanski73cef112019-04-07 00:06:23 +020047 ])
48 return json.loads(raw)
49
50
51def _file_exists(c, filename):
52 res = c.run('stat "{}"'.format(filename), warn=True, hide=True)
53 return res.exited == 0
54
55
56def configure_k8s(username, ca, cert, key):
57 subprocess.check_call([
58 'kubectl', 'config',
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020059 'set-cluster', 'admin.' + cluster,
Sergiusz Bazanski73cef112019-04-07 00:06:23 +020060 '--certificate-authority=' + ca,
61 '--embed-certs=true',
62 '--server=https://' + cluster + ':4001',
63 ])
64 subprocess.check_call([
65 'kubectl', 'config',
66 'set-credentials', username,
67 '--client-certificate=' + cert,
68 '--client-key=' + key,
69 '--embed-certs=true',
70 ])
71 subprocess.check_call([
72 'kubectl', 'config',
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020073 'set-context', 'admin.' + cluster,
74 '--cluster=' + 'admin.' + cluster,
Sergiusz Bazanski73cef112019-04-07 00:06:23 +020075 '--user=' + username,
76 ])
77 subprocess.check_call([
78 'kubectl', 'config',
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020079 'use-context', 'admin.' + cluster,
Sergiusz Bazanski73cef112019-04-07 00:06:23 +020080 ])
81
82
83def admincreds(args):
84 if len(args) != 1:
85 sys.stderr.write("Usage: admincreds q3k\n")
86 return 1
87 username = args[0]
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +020088 print("")
89 print("WARNING WARNING WARNING WARNING WARNING WARNING")
90 print("===============================================")
91 print("")
92 print("You are requesting ADMIN credentials.")
93 print("")
94 print("You likely shouldn't be doing this, and")
95 print("instead should be using `prodaccess`.")
96 print("")
97 print("===============================================")
98 print("WARNING WARNING WARNING WARNING WARNING WARNING")
99 print("")
Sergiusz Bazanski73cef112019-04-07 00:06:23 +0200100
101 ## Make kube certificates.
102 certs_root = os.path.join(local_root, 'cluster/certs')
103 ca_kube = ca.CA(ss, certs_root, 'kube', 'kubernetes main CA')
104
105 local_key = os.path.join(local_root, '.kubectl/admin.key')
106 local_crt = os.path.join(local_root, '.kubectl/admin.crt')
107
108 kubectl = os.path.join(local_root, '.kubectl')
109 if not os.path.exists(kubectl):
110 os.mkdir(kubectl)
111
112 generate_cert = False
113 if not os.path.exists(local_key):
114 generate_cert = True
115
116 if os.path.exists(local_crt):
117 with open(local_crt, 'rb') as f:
118 b = f.read()
119 cert = x509.load_pem_x509_certificate(b, default_backend())
120 delta = cert.not_valid_after - datetime.datetime.now()
121 logger.info("admin: existing cert expiry: {}".format(delta))
122 if delta.total_seconds() < 3600 * 24:
123 logger.info("admin: expires soon, regenerating")
124 generate_cert = True
125 else:
126 generate_cert = True
127
128 if not generate_cert:
129 return configure_k8s(username, ca_kube._cert, local_crt, local_key)
130
131 key, csr = ca_kube.gen_key(hosts=['admin', username], o='system:masters', ou='Kube Admin Account')
132 crt = ca_kube.sign(csr)
133
134 with open(local_key, 'w') as f:
135 f.write(key)
136
137 with open(local_crt, 'w') as f:
138 f.write(crt)
139
140 configure_k8s(username, ca_kube._cert, local_crt, local_key)
141
142
Sergiusz Bazanskic0fc3ee2019-06-20 16:11:38 +0200143def nodestrap(args, nocerts=False):
Sergiusz Bazanski73cef112019-04-07 00:06:23 +0200144 if len(args) != 1:
145 sys.stderr.write("Usage: nodestrap bc01n01.hswaw.net\n")
146 return 1
147 fqdn = args[0]
148
149 logger.info("Nodestrapping {}...".format(fqdn))
150 r = fabric.Connection('root@{}'.format(fqdn))
151
Sergiusz Bazanskic0fc3ee2019-06-20 16:11:38 +0200152 if not nocerts:
Sergiusz Bazanskic78cc132020-02-02 22:31:53 +0100153 cfg = dict((k, pki_config(k, fqdn)) for k in [
Sergiusz Bazanskic0fc3ee2019-06-20 16:11:38 +0200154 'etcdPeer', 'etcd.server', 'etcd.kube'
155 ])
156 certs_root = os.path.join(local_root, 'cluster/certs')
Sergiusz Bazanski73cef112019-04-07 00:06:23 +0200157
Sergiusz Bazanskic0fc3ee2019-06-20 16:11:38 +0200158 # Make etcd peer certificate for node.
159 ca_etcd_peer = ca.CA(ss, certs_root, 'etcdpeer', 'etcd peer ca')
Sergiusz Bazanskic78cc132020-02-02 22:31:53 +0100160 ca_etcd_peer.make_cert('etcdpeer-{}'.format(fqdn), hosts=[fqdn], ou='node etcd peer certificate')
Sergiusz Bazanski73cef112019-04-07 00:06:23 +0200161
Sergiusz Bazanskic0fc3ee2019-06-20 16:11:38 +0200162 # Make etcd server certificate for node and client certificate for kube.
163 ca_etcd = ca.CA(ss, certs_root, 'etcd', 'etcd ca')
Sergiusz Bazanski73cef112019-04-07 00:06:23 +0200164
Sergiusz Bazanskic78cc132020-02-02 22:31:53 +0100165 ca_etcd.make_cert('etcd-{}'.format(fqdn), hosts=[fqdn], ou='node etcd server certificate')
Sergiusz Bazanski73cef112019-04-07 00:06:23 +0200166
Sergiusz Bazanskic78cc132020-02-02 22:31:53 +0100167 ca_etcd.make_cert('etcd-kube', hosts=['kube'], ou='kube etcd client certificate')
Sergiusz Bazanski73cef112019-04-07 00:06:23 +0200168
Sergiusz Bazanskic0fc3ee2019-06-20 16:11:38 +0200169 ca_etcd.make_cert('etcd-root', hosts=['root'], ou='root etcd client certificate')
Sergiusz Bazanski73cef112019-04-07 00:06:23 +0200170
Sergiusz Bazanskic0fc3ee2019-06-20 16:11:38 +0200171 ca_etcd.make_cert('etcd-calico', hosts=['calico'], ou='root etcd client certificate')
Sergiusz Bazanski73cef112019-04-07 00:06:23 +0200172
Sergiusz Bazanskic0fc3ee2019-06-20 16:11:38 +0200173 ## Make kube certificates.
174 ca_kube = ca.CA(ss, certs_root, 'kube', 'kubernetes main CA')
Sergiusz Bazanski73cef112019-04-07 00:06:23 +0200175
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +0200176 # Make prodvider intermediate CA.
Sergiusz Bazanski0dcc7022020-03-28 17:58:19 +0100177 ca_kube.make_cert('ca-kube-prodvider', o='Warsaw Hackerspace', ou='kubernetes prodvider intermediate', hosts=['kubernetes prodvider intermediate CA'], profile='intermediate').ensure()
Sergiusz Bazanskib13b7ff2019-08-29 20:12:24 +0200178
Sergiusz Bazanskic0fc3ee2019-06-20 16:11:38 +0200179 # Make kubelet certificate (per node).
Sergiusz Bazanskic78cc132020-02-02 22:31:53 +0100180 ca_kube.make_cert('kube-kubelet-'+fqdn, o='system:nodes', ou='Kubelet', hosts=['system:node:'+fqdn, fqdn])
Sergiusz Bazanski73cef112019-04-07 00:06:23 +0200181
Sergiusz Bazanskic0fc3ee2019-06-20 16:11:38 +0200182 # Make apiserver certificate.
Sergiusz Bazanskic78cc132020-02-02 22:31:53 +0100183 ca_kube.make_cert('kube-apiserver', ou='Kubernetes API', hosts=[cluster, 'kubernetes.default.svc.'+cluster, '10.10.12.1'])
Sergiusz Bazanski73cef112019-04-07 00:06:23 +0200184
Sergiusz Bazanskic0fc3ee2019-06-20 16:11:38 +0200185 # Make service accounts decryption key (as cert for consistency).
Sergiusz Bazanskic78cc132020-02-02 22:31:53 +0100186 ca_kube.make_cert('kube-serviceaccounts', ou='Kubernetes Service Accounts Signer', hosts=['serviceaccounts'])
Sergiusz Bazanski73cef112019-04-07 00:06:23 +0200187
Sergiusz Bazanskic0fc3ee2019-06-20 16:11:38 +0200188 # Make kube component certificates.
189 kube_components = ['controllermanager', 'scheduler', 'proxy']
Sergiusz Bazanskic78cc132020-02-02 22:31:53 +0100190 cfg = dict((k, pki_config('kube.' + k, fqdn)) for k in kube_components)
Sergiusz Bazanskic0fc3ee2019-06-20 16:11:38 +0200191 for k in kube_components:
Sergiusz Bazanskic0fc3ee2019-06-20 16:11:38 +0200192 # meh
193 if k == 'controllermanager':
194 o = 'system:kube-controller-manager'
195 else:
196 o = 'system:kube-'+k
197 ou = 'Kubernetes Component '+k
198 c = ca_kube.make_cert('kube-'+k, ou=ou, o=o, hosts=[o,])
Sergiusz Bazanski73cef112019-04-07 00:06:23 +0200199
Sergiusz Bazanskic0fc3ee2019-06-20 16:11:38 +0200200 ## Make kubefront certificates.
201 ca_kubefront = ca.CA(ss, certs_root, 'kubefront', 'kubernetes frontend CA')
Sergiusz Bazanskic78cc132020-02-02 22:31:53 +0100202 ca_kubefront.make_cert('kubefront-apiserver', ou='Kubernetes Frontend', hosts=['apiserver'])
Sergiusz Bazanski73cef112019-04-07 00:06:23 +0200203
Serge Bazanski04604b22021-02-06 14:57:20 +0000204 ## Make admitomatic (admission controller) certificates.
205 ca_admitomatic = ca.CA(ss, certs_root, 'admitomatic', 'admitomatic webhook CA')
206 ca_admitomatic.make_cert('admitomatic-webhook', ou='Admitomatic Webhook', hosts=['admitomatic.admitomatic.svc'])
207
Serge Bazanskib3c67702021-09-10 22:27:24 +0000208 toplevel = subprocess.check_output([
209 "nix-build",
210 local_root,
211 "-A", "ops.machines.\"" + fqdn + "\".config.passthru.hscloud.provision",
212 ]).decode().strip()
213 subprocess.check_call([toplevel])
Sergiusz Bazanski73cef112019-04-07 00:06:23 +0200214
215
216def usage():
Sergiusz Bazanskic78cc132020-02-02 22:31:53 +0100217 sys.stderr.write("Usage: clustercfg <nodestrap|admincreds>\n")
Sergiusz Bazanski73cef112019-04-07 00:06:23 +0200218
219
220def main():
221 if len(sys.argv) < 2:
222 usage()
223 return 1
224
225 mode = sys.argv[1]
226 if mode == "nodestrap":
227 return nodestrap(sys.argv[2:])
Sergiusz Bazanskic0fc3ee2019-06-20 16:11:38 +0200228 elif mode == "nodestrap-nocerts":
229 return nodestrap(sys.argv[2:], nocerts=True)
Sergiusz Bazanski73cef112019-04-07 00:06:23 +0200230 elif mode == "admincreds":
231 return admincreds(sys.argv[2:])
Serge Bazanskie7fca3a2020-09-25 20:23:53 +0000232 elif mode == "smoketest":
233 sys.stdout.write("Smoke test passed.")
234 return 0
Sergiusz Bazanski73cef112019-04-07 00:06:23 +0200235 else:
236 usage()
237 return 1
238
239if __name__ == '__main__':
240 sys.exit(main() or 0)