blob: c5f5c6c983da3293d51076bd6d201070dcb45b59 [file] [log] [blame]
#!/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'])
## Make admitomatic (admission controller) certificates.
ca_admitomatic = ca.CA(ss, certs_root, 'admitomatic', 'admitomatic webhook CA')
ca_admitomatic.make_cert('admitomatic-webhook', ou='Admitomatic Webhook', hosts=['admitomatic.admitomatic.svc'])
subprocess.check_call(["nix", "run",
"-f", os.path.join(local_root, "cluster/nix/default.nix"),
"provision",
"-c", "provision-{}".format(fqdn.split('.')[0])])
def usage():
sys.stderr.write("Usage: clustercfg <nodestrap|admincreds>\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:])
elif mode == "smoketest":
sys.stdout.write("Smoke test passed.")
return 0
else:
usage()
return 1
if __name__ == '__main__':
sys.exit(main() or 0)