#!/usr/bin/env python from builtins import object import datetime from io import BytesIO import json import logging import os import tempfile import subprocess import sys from cryptography import x509 from cryptography.hazmat.backends import default_backend import fabric from tools import secretstore import ca local_root = os.getenv('hscloud_root') if local_root is None: raise Exception("Please source env.sh") cluster = 'k0.hswaw.net' ss = secretstore.SecretStore( plain_root=os.path.join(local_root, 'cluster/secrets/plain'), cipher_root=os.path.join(local_root, 'cluster/secrets/cipher')) logger = logging.getLogger() logger.setLevel(logging.INFO) formatter = logging.Formatter('%(levelname)s - %(message)s') sh = logging.StreamHandler() sh.setFormatter(formatter) logger.addHandler(sh) def pki_config(key, fqdn): machine_name = fqdn.split('.')[0] raw = subprocess.check_output([ 'nix', 'eval', '--raw', '( ((import ' + local_root + '/cluster/nix/defs-cluster-k0.nix ) "' + machine_name + '").pki.' + key + '.json )', ]) return json.loads(raw) def _file_exists(c, filename): res = c.run('stat "{}"'.format(filename), warn=True, hide=True) return res.exited == 0 def configure_k8s(username, ca, cert, key): subprocess.check_call([ 'kubectl', 'config', 'set-cluster', 'admin.' + cluster, '--certificate-authority=' + ca, '--embed-certs=true', '--server=https://' + cluster + ':4001', ]) subprocess.check_call([ 'kubectl', 'config', 'set-credentials', username, '--client-certificate=' + cert, '--client-key=' + key, '--embed-certs=true', ]) subprocess.check_call([ 'kubectl', 'config', 'set-context', 'admin.' + cluster, '--cluster=' + 'admin.' + cluster, '--user=' + username, ]) subprocess.check_call([ 'kubectl', 'config', 'use-context', 'admin.' + cluster, ]) def admincreds(args): if len(args) != 1: sys.stderr.write("Usage: admincreds q3k\n") return 1 username = args[0] print("") print("WARNING WARNING WARNING WARNING WARNING WARNING") print("===============================================") print("") print("You are requesting ADMIN credentials.") print("") print("You likely shouldn't be doing this, and") print("instead should be using `prodaccess`.") print("") print("===============================================") print("WARNING WARNING WARNING WARNING WARNING WARNING") print("") ## Make kube certificates. certs_root = os.path.join(local_root, 'cluster/certs') ca_kube = ca.CA(ss, certs_root, 'kube', 'kubernetes main CA') local_key = os.path.join(local_root, '.kubectl/admin.key') local_crt = os.path.join(local_root, '.kubectl/admin.crt') kubectl = os.path.join(local_root, '.kubectl') if not os.path.exists(kubectl): os.mkdir(kubectl) generate_cert = False if not os.path.exists(local_key): generate_cert = True if os.path.exists(local_crt): with open(local_crt, 'rb') as f: b = f.read() cert = x509.load_pem_x509_certificate(b, default_backend()) delta = cert.not_valid_after - datetime.datetime.now() logger.info("admin: existing cert expiry: {}".format(delta)) if delta.total_seconds() < 3600 * 24: logger.info("admin: expires soon, regenerating") generate_cert = True else: generate_cert = True if not generate_cert: return configure_k8s(username, ca_kube._cert, local_crt, local_key) key, csr = ca_kube.gen_key(hosts=['admin', username], o='system:masters', ou='Kube Admin Account') crt = ca_kube.sign(csr) with open(local_key, 'w') as f: f.write(key) with open(local_crt, 'w') as f: f.write(crt) configure_k8s(username, ca_kube._cert, local_crt, local_key) def nodestrap(args, nocerts=False): if len(args) != 1: sys.stderr.write("Usage: nodestrap bc01n01.hswaw.net\n") return 1 fqdn = args[0] logger.info("Nodestrapping {}...".format(fqdn)) r = fabric.Connection('root@{}'.format(fqdn)) if not nocerts: cfg = dict((k, pki_config(k, fqdn)) for k in [ 'etcdPeer', 'etcd.server', 'etcd.kube' ]) certs_root = os.path.join(local_root, 'cluster/certs') # Make etcd peer certificate for node. ca_etcd_peer = ca.CA(ss, certs_root, 'etcdpeer', 'etcd peer ca') ca_etcd_peer.make_cert('etcdpeer-{}'.format(fqdn), hosts=[fqdn], ou='node etcd peer certificate') # Make etcd server certificate for node and client certificate for kube. ca_etcd = ca.CA(ss, certs_root, 'etcd', 'etcd ca') ca_etcd.make_cert('etcd-{}'.format(fqdn), hosts=[fqdn], ou='node etcd server certificate') ca_etcd.make_cert('etcd-kube', hosts=['kube'], ou='kube etcd client certificate') ca_etcd.make_cert('etcd-root', hosts=['root'], ou='root etcd client certificate') ca_etcd.make_cert('etcd-calico', hosts=['calico'], ou='root etcd client certificate') ## Make kube certificates. ca_kube = ca.CA(ss, certs_root, 'kube', 'kubernetes main CA') # Make prodvider intermediate CA. ca_kube.make_cert('ca-kube-prodvider', o='Warsaw Hackerspace', ou='kubernetes prodvider intermediate', hosts=['kubernetes prodvider intermediate CA'], profile='intermediate').ensure() # Make kubelet certificate (per node). ca_kube.make_cert('kube-kubelet-'+fqdn, o='system:nodes', ou='Kubelet', hosts=['system:node:'+fqdn, fqdn]) # Make apiserver certificate. ca_kube.make_cert('kube-apiserver', ou='Kubernetes API', hosts=[cluster, 'kubernetes.default.svc.'+cluster, '10.10.12.1']) # Make service accounts decryption key (as cert for consistency). ca_kube.make_cert('kube-serviceaccounts', ou='Kubernetes Service Accounts Signer', hosts=['serviceaccounts']) # Make kube component certificates. kube_components = ['controllermanager', 'scheduler', 'proxy'] cfg = dict((k, pki_config('kube.' + k, fqdn)) for k in kube_components) for k in kube_components: # meh if k == 'controllermanager': o = 'system:kube-controller-manager' else: o = 'system:kube-'+k ou = 'Kubernetes Component '+k c = ca_kube.make_cert('kube-'+k, ou=ou, o=o, hosts=[o,]) ## Make kubefront certificates. ca_kubefront = ca.CA(ss, certs_root, 'kubefront', 'kubernetes frontend CA') ca_kubefront.make_cert('kubefront-apiserver', ou='Kubernetes Frontend', hosts=['apiserver']) subprocess.check_call(["nix", "run", "-f", os.path.join(local_root, "cluster/nix/default.nix"), "provision", "-c", "provision-{}".format(fqdn.split('.')[0]), "switch"]) def usage(): sys.stderr.write("Usage: clustercfg \n") def main(): if len(sys.argv) < 2: usage() return 1 mode = sys.argv[1] if mode == "nodestrap": return nodestrap(sys.argv[2:]) elif mode == "nodestrap-nocerts": return nodestrap(sys.argv[2:], nocerts=True) elif mode == "admincreds": return admincreds(sys.argv[2:]) else: usage() return 1 if __name__ == '__main__': sys.exit(main() or 0)