Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | |
| 3 | from builtins import object |
| 4 | |
| 5 | import datetime |
| 6 | from io import BytesIO |
| 7 | import json |
| 8 | import logging |
| 9 | import os |
| 10 | import tempfile |
| 11 | import subprocess |
| 12 | import sys |
| 13 | |
| 14 | from cryptography import x509 |
| 15 | from cryptography.hazmat.backends import default_backend |
| 16 | import fabric |
| 17 | |
| 18 | from tools import secretstore |
Serge Bazanski | 0f8e5a2 | 2021-10-16 20:53:51 +0000 | [diff] [blame] | 19 | from tools.hscloud import lib as hscloud |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 20 | |
| 21 | import ca |
| 22 | |
| 23 | |
Serge Bazanski | 0f8e5a2 | 2021-10-16 20:53:51 +0000 | [diff] [blame] | 24 | local_root = hscloud.workspace_location() |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 25 | |
| 26 | |
| 27 | cluster = 'k0.hswaw.net' |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 28 | ss = 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 | |
| 33 | logger = logging.getLogger() |
| 34 | logger.setLevel(logging.INFO) |
| 35 | formatter = logging.Formatter('%(levelname)s - %(message)s') |
| 36 | sh = logging.StreamHandler() |
| 37 | sh.setFormatter(formatter) |
| 38 | logger.addHandler(sh) |
| 39 | |
| 40 | |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 41 | def configure_k8s(username, ca, cert, key): |
| 42 | subprocess.check_call([ |
| 43 | 'kubectl', 'config', |
Sergiusz Bazanski | b13b7ff | 2019-08-29 20:12:24 +0200 | [diff] [blame] | 44 | 'set-cluster', 'admin.' + cluster, |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 45 | '--certificate-authority=' + ca, |
| 46 | '--embed-certs=true', |
| 47 | '--server=https://' + cluster + ':4001', |
| 48 | ]) |
| 49 | subprocess.check_call([ |
| 50 | 'kubectl', 'config', |
| 51 | 'set-credentials', username, |
| 52 | '--client-certificate=' + cert, |
| 53 | '--client-key=' + key, |
| 54 | '--embed-certs=true', |
| 55 | ]) |
| 56 | subprocess.check_call([ |
| 57 | 'kubectl', 'config', |
Sergiusz Bazanski | b13b7ff | 2019-08-29 20:12:24 +0200 | [diff] [blame] | 58 | 'set-context', 'admin.' + cluster, |
| 59 | '--cluster=' + 'admin.' + cluster, |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 60 | '--user=' + username, |
| 61 | ]) |
| 62 | subprocess.check_call([ |
| 63 | 'kubectl', 'config', |
Sergiusz Bazanski | b13b7ff | 2019-08-29 20:12:24 +0200 | [diff] [blame] | 64 | 'use-context', 'admin.' + cluster, |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 65 | ]) |
| 66 | |
| 67 | |
| 68 | def admincreds(args): |
| 69 | if len(args) != 1: |
| 70 | sys.stderr.write("Usage: admincreds q3k\n") |
| 71 | return 1 |
| 72 | username = args[0] |
Sergiusz Bazanski | b13b7ff | 2019-08-29 20:12:24 +0200 | [diff] [blame] | 73 | print("") |
| 74 | print("WARNING WARNING WARNING WARNING WARNING WARNING") |
| 75 | print("===============================================") |
| 76 | print("") |
| 77 | print("You are requesting ADMIN credentials.") |
| 78 | print("") |
| 79 | print("You likely shouldn't be doing this, and") |
| 80 | print("instead should be using `prodaccess`.") |
| 81 | print("") |
| 82 | print("===============================================") |
| 83 | print("WARNING WARNING WARNING WARNING WARNING WARNING") |
| 84 | print("") |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 85 | |
| 86 | ## Make kube certificates. |
| 87 | certs_root = os.path.join(local_root, 'cluster/certs') |
| 88 | ca_kube = ca.CA(ss, certs_root, 'kube', 'kubernetes main CA') |
| 89 | |
| 90 | local_key = os.path.join(local_root, '.kubectl/admin.key') |
| 91 | local_crt = os.path.join(local_root, '.kubectl/admin.crt') |
| 92 | |
| 93 | kubectl = os.path.join(local_root, '.kubectl') |
| 94 | if not os.path.exists(kubectl): |
| 95 | os.mkdir(kubectl) |
| 96 | |
| 97 | generate_cert = False |
| 98 | if not os.path.exists(local_key): |
| 99 | generate_cert = True |
| 100 | |
| 101 | if os.path.exists(local_crt): |
| 102 | with open(local_crt, 'rb') as f: |
| 103 | b = f.read() |
| 104 | cert = x509.load_pem_x509_certificate(b, default_backend()) |
| 105 | delta = cert.not_valid_after - datetime.datetime.now() |
| 106 | logger.info("admin: existing cert expiry: {}".format(delta)) |
| 107 | if delta.total_seconds() < 3600 * 24: |
| 108 | logger.info("admin: expires soon, regenerating") |
| 109 | generate_cert = True |
| 110 | else: |
| 111 | generate_cert = True |
| 112 | |
| 113 | if not generate_cert: |
| 114 | return configure_k8s(username, ca_kube._cert, local_crt, local_key) |
| 115 | |
| 116 | key, csr = ca_kube.gen_key(hosts=['admin', username], o='system:masters', ou='Kube Admin Account') |
| 117 | crt = ca_kube.sign(csr) |
| 118 | |
| 119 | with open(local_key, 'w') as f: |
| 120 | f.write(key) |
| 121 | |
| 122 | with open(local_crt, 'w') as f: |
| 123 | f.write(crt) |
| 124 | |
| 125 | configure_k8s(username, ca_kube._cert, local_crt, local_key) |
| 126 | |
| 127 | |
Sergiusz Bazanski | c0fc3ee | 2019-06-20 16:11:38 +0200 | [diff] [blame] | 128 | def nodestrap(args, nocerts=False): |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 129 | if len(args) != 1: |
| 130 | sys.stderr.write("Usage: nodestrap bc01n01.hswaw.net\n") |
| 131 | return 1 |
| 132 | fqdn = args[0] |
| 133 | |
| 134 | logger.info("Nodestrapping {}...".format(fqdn)) |
| 135 | r = fabric.Connection('root@{}'.format(fqdn)) |
| 136 | |
Sergiusz Bazanski | c0fc3ee | 2019-06-20 16:11:38 +0200 | [diff] [blame] | 137 | if not nocerts: |
Sergiusz Bazanski | c0fc3ee | 2019-06-20 16:11:38 +0200 | [diff] [blame] | 138 | certs_root = os.path.join(local_root, 'cluster/certs') |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 139 | |
Sergiusz Bazanski | c0fc3ee | 2019-06-20 16:11:38 +0200 | [diff] [blame] | 140 | # Make etcd peer certificate for node. |
| 141 | ca_etcd_peer = ca.CA(ss, certs_root, 'etcdpeer', 'etcd peer ca') |
Sergiusz Bazanski | c78cc13 | 2020-02-02 22:31:53 +0100 | [diff] [blame] | 142 | ca_etcd_peer.make_cert('etcdpeer-{}'.format(fqdn), hosts=[fqdn], ou='node etcd peer certificate') |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 143 | |
Sergiusz Bazanski | c0fc3ee | 2019-06-20 16:11:38 +0200 | [diff] [blame] | 144 | # Make etcd server certificate for node and client certificate for kube. |
| 145 | ca_etcd = ca.CA(ss, certs_root, 'etcd', 'etcd ca') |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 146 | |
Sergiusz Bazanski | c78cc13 | 2020-02-02 22:31:53 +0100 | [diff] [blame] | 147 | ca_etcd.make_cert('etcd-{}'.format(fqdn), hosts=[fqdn], ou='node etcd server certificate') |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 148 | |
Sergiusz Bazanski | c78cc13 | 2020-02-02 22:31:53 +0100 | [diff] [blame] | 149 | ca_etcd.make_cert('etcd-kube', hosts=['kube'], ou='kube etcd client certificate') |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 150 | |
Sergiusz Bazanski | c0fc3ee | 2019-06-20 16:11:38 +0200 | [diff] [blame] | 151 | ca_etcd.make_cert('etcd-root', hosts=['root'], ou='root etcd client certificate') |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 152 | |
Sergiusz Bazanski | c0fc3ee | 2019-06-20 16:11:38 +0200 | [diff] [blame] | 153 | ca_etcd.make_cert('etcd-calico', hosts=['calico'], ou='root etcd client certificate') |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 154 | |
Sergiusz Bazanski | c0fc3ee | 2019-06-20 16:11:38 +0200 | [diff] [blame] | 155 | ## Make kube certificates. |
| 156 | ca_kube = ca.CA(ss, certs_root, 'kube', 'kubernetes main CA') |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 157 | |
Sergiusz Bazanski | b13b7ff | 2019-08-29 20:12:24 +0200 | [diff] [blame] | 158 | # Make prodvider intermediate CA. |
Sergiusz Bazanski | 0dcc702 | 2020-03-28 17:58:19 +0100 | [diff] [blame] | 159 | ca_kube.make_cert('ca-kube-prodvider', o='Warsaw Hackerspace', ou='kubernetes prodvider intermediate', hosts=['kubernetes prodvider intermediate CA'], profile='intermediate').ensure() |
Sergiusz Bazanski | b13b7ff | 2019-08-29 20:12:24 +0200 | [diff] [blame] | 160 | |
Sergiusz Bazanski | c0fc3ee | 2019-06-20 16:11:38 +0200 | [diff] [blame] | 161 | # Make kubelet certificate (per node). |
Sergiusz Bazanski | c78cc13 | 2020-02-02 22:31:53 +0100 | [diff] [blame] | 162 | ca_kube.make_cert('kube-kubelet-'+fqdn, o='system:nodes', ou='Kubelet', hosts=['system:node:'+fqdn, fqdn]) |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 163 | |
Sergiusz Bazanski | c0fc3ee | 2019-06-20 16:11:38 +0200 | [diff] [blame] | 164 | # Make apiserver certificate. |
Sergiusz Bazanski | c78cc13 | 2020-02-02 22:31:53 +0100 | [diff] [blame] | 165 | ca_kube.make_cert('kube-apiserver', ou='Kubernetes API', hosts=[cluster, 'kubernetes.default.svc.'+cluster, '10.10.12.1']) |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 166 | |
Sergiusz Bazanski | c0fc3ee | 2019-06-20 16:11:38 +0200 | [diff] [blame] | 167 | # Make service accounts decryption key (as cert for consistency). |
Sergiusz Bazanski | c78cc13 | 2020-02-02 22:31:53 +0100 | [diff] [blame] | 168 | ca_kube.make_cert('kube-serviceaccounts', ou='Kubernetes Service Accounts Signer', hosts=['serviceaccounts']) |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 169 | |
Sergiusz Bazanski | c0fc3ee | 2019-06-20 16:11:38 +0200 | [diff] [blame] | 170 | # Make kube component certificates. |
| 171 | kube_components = ['controllermanager', 'scheduler', 'proxy'] |
Sergiusz Bazanski | c0fc3ee | 2019-06-20 16:11:38 +0200 | [diff] [blame] | 172 | for k in kube_components: |
Sergiusz Bazanski | c0fc3ee | 2019-06-20 16:11:38 +0200 | [diff] [blame] | 173 | # meh |
| 174 | if k == 'controllermanager': |
| 175 | o = 'system:kube-controller-manager' |
| 176 | else: |
| 177 | o = 'system:kube-'+k |
| 178 | ou = 'Kubernetes Component '+k |
| 179 | c = ca_kube.make_cert('kube-'+k, ou=ou, o=o, hosts=[o,]) |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 180 | |
Sergiusz Bazanski | c0fc3ee | 2019-06-20 16:11:38 +0200 | [diff] [blame] | 181 | ## Make kubefront certificates. |
| 182 | ca_kubefront = ca.CA(ss, certs_root, 'kubefront', 'kubernetes frontend CA') |
Sergiusz Bazanski | c78cc13 | 2020-02-02 22:31:53 +0100 | [diff] [blame] | 183 | ca_kubefront.make_cert('kubefront-apiserver', ou='Kubernetes Frontend', hosts=['apiserver']) |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 184 | |
Serge Bazanski | 04604b2 | 2021-02-06 14:57:20 +0000 | [diff] [blame] | 185 | ## Make admitomatic (admission controller) certificates. |
| 186 | ca_admitomatic = ca.CA(ss, certs_root, 'admitomatic', 'admitomatic webhook CA') |
| 187 | ca_admitomatic.make_cert('admitomatic-webhook', ou='Admitomatic Webhook', hosts=['admitomatic.admitomatic.svc']) |
| 188 | |
Serge Bazanski | b3c6770 | 2021-09-10 22:27:24 +0000 | [diff] [blame] | 189 | toplevel = subprocess.check_output([ |
| 190 | "nix-build", |
| 191 | local_root, |
| 192 | "-A", "ops.machines.\"" + fqdn + "\".config.passthru.hscloud.provision", |
| 193 | ]).decode().strip() |
| 194 | subprocess.check_call([toplevel]) |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 195 | |
| 196 | |
| 197 | def usage(): |
Sergiusz Bazanski | c78cc13 | 2020-02-02 22:31:53 +0100 | [diff] [blame] | 198 | sys.stderr.write("Usage: clustercfg <nodestrap|admincreds>\n") |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 199 | |
| 200 | |
| 201 | def main(): |
| 202 | if len(sys.argv) < 2: |
| 203 | usage() |
| 204 | return 1 |
| 205 | |
| 206 | mode = sys.argv[1] |
| 207 | if mode == "nodestrap": |
| 208 | return nodestrap(sys.argv[2:]) |
Sergiusz Bazanski | c0fc3ee | 2019-06-20 16:11:38 +0200 | [diff] [blame] | 209 | elif mode == "nodestrap-nocerts": |
| 210 | return nodestrap(sys.argv[2:], nocerts=True) |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 211 | elif mode == "admincreds": |
| 212 | return admincreds(sys.argv[2:]) |
Serge Bazanski | e7fca3a | 2020-09-25 20:23:53 +0000 | [diff] [blame] | 213 | elif mode == "smoketest": |
| 214 | sys.stdout.write("Smoke test passed.") |
| 215 | return 0 |
Sergiusz Bazanski | 73cef11 | 2019-04-07 00:06:23 +0200 | [diff] [blame] | 216 | else: |
| 217 | usage() |
| 218 | return 1 |
| 219 | |
| 220 | if __name__ == '__main__': |
| 221 | sys.exit(main() or 0) |