def export_https(name, keyout, certout): keypath = os.path.join(configuration.get_project(), "https.%s.key.crypt" % name) certpath = os.path.join(configuration.get_project(), "https.%s.pem" % name) keycrypt.gpg_decrypt_file(keypath, keyout) util.copy(certpath, certout)
def export_https(name, keyout, certout): "decrypt and export the HTTPS keypair for a particular server" keypath = os.path.join(configuration.get_project(), "https.%s.key.crypt" % name) certpath = os.path.join(configuration.get_project(), "https.%s.pem" % name) keycrypt.gpg_decrypt_file(keypath, keyout) util.copy(certpath, certout)
def export_https(name, keyout, certout): if name != setup.REGISTRY_HOSTNAME: command.fail("unexpected https host: %s" % name) keypath = os.path.join(configuration.get_project(), "https.%s.key.crypt" % name) certpath = os.path.join(configuration.get_project(), "https.%s.pem" % name) keycrypt.gpg_decrypt_file(keypath, keyout) util.copy(certpath, certout)
def import_https(name, keyfile, certfile): check_pem_type(certfile, "CERTIFICATE") check_pem_type(keyfile, "RSA PRIVATE KEY") keypath = os.path.join(configuration.get_project(), "https.%s.key.crypt" % name) certpath = os.path.join(configuration.get_project(), "https.%s.pem" % name) keycrypt.gpg_encrypt_file(keyfile, keypath) util.copy(certfile, certpath)
def import_https(name, keyfile, certfile): if name != setup.REGISTRY_HOSTNAME: command.fail("unexpected https host: %s" % name) check_pem_type(certfile, "CERTIFICATE") check_pem_type(keyfile, "RSA PRIVATE KEY") keypath = os.path.join(configuration.get_project(), "https.%s.key.crypt" % name) certpath = os.path.join(configuration.get_project(), "https.%s.pem" % name) keycrypt.gpg_encrypt_file(keyfile, keypath) util.copy(certfile, certpath)
def setup_bootstrap_registry(ops: Operations) -> None: config = configuration.get_config() for node in config.nodes: if node.kind != "supervisor": continue keypath = os.path.join(configuration.get_project(), "https.%s.key.crypt" % REGISTRY_HOSTNAME) certpath = os.path.join(configuration.get_project(), "https.%s.pem" % REGISTRY_HOSTNAME) keydata = keycrypt.gpg_decrypt_to_memory(keypath) ops.ssh_mkdir("create ssl cert directory on @HOST", node, "/etc/homeworld/ssl") ops.ssh_upload_bytes("upload %s key to @HOST" % REGISTRY_HOSTNAME, node, keydata, "/etc/homeworld/ssl/%s.key" % REGISTRY_HOSTNAME) ops.ssh_upload_path("upload %s cert to @HOST" % REGISTRY_HOSTNAME, node, certpath, "/etc/homeworld/ssl/%s.pem" % REGISTRY_HOSTNAME) ops.ssh("unmask nginx on @HOST", node, "systemctl", "unmask", "nginx") ops.ssh("restart nginx on @HOST", node, "systemctl", "restart", "nginx")
def create_or_rotate_custom_ssh_key(interval=DEFAULT_ROTATE_INTERVAL, bits=DEFAULT_SHORTLIVED_RSA_BITS): project_dir = configuration.get_project() keypath = os.path.join(project_dir, "ssh-key") if needs_rotate(keypath, interval): if os.path.exists(keypath): os.remove(keypath) if os.path.exists(keypath + ".pub"): os.remove(keypath + ".pub") if os.path.exists(keypath + "-cert.pub"): os.remove(keypath + "-cert.pub") # 2048 bits is sufficient for a key only used for the duration of the certificate (probably four hours) subprocess.check_call([ "ssh-keygen", "-f", keypath, # output file "-t", "rsa", "-b", str(bits), # a <bits>-bit RSA key "-C", "autogen-homeworld", # generic comment "-N", "" ]) # no passphrase return keypath
def export_keytab(node, keytab_file): "decrypt and export the keytab for a particular server" keytab_source = os.path.join(configuration.get_project(), "keytab.%s.crypt" % node) if not os.path.exists(keytab_source): command.fail("no keytab for node %s" % node) keycrypt.gpg_decrypt_file(keytab_source, keytab_file)
def renew_ssh_cert() -> str: project_dir = configuration.get_project() keypath = os.path.join(project_dir, "ssh-key") # TODO: pull name of authority from worldconfig/apis.go refresh_cert(keypath, keypath + "-cert.pub", None, "ssh", "ssh-user", "ssh-user.pub") return keypath
def add_password_to_log(password): passwords = os.path.join(configuration.get_project(), "passwords") if not os.path.isdir(passwords): os.mkdir(passwords) passfile = os.path.join(passwords, "at-%s.gpg" % datetime.datetime.now().isoformat()) util.writefile(passfile, keycrypt.gpg_encrypt_in_memory(password))
def modify_keygateway(ops: command.Operations, overwrite_keytab: bool) -> None: config = configuration.get_config() if not config.is_kerberos_enabled(): print("keygateway disabled; skipping") return for node in config.nodes: if node.kind != "supervisor": continue # keytab is stored encrypted in the configuration folder keytab = os.path.join(configuration.get_project(), "keytab.%s.crypt" % node.hostname) decrypted = keycrypt.gpg_decrypt_to_memory(keytab) def safe_upload_keytab(node=node): if not overwrite_keytab: try: existing_keytab = ssh.check_ssh_output(node, "cat", KEYTAB_PATH) except subprocess.CalledProcessError as e_test: # if there is no existing keytab, cat will fail with error code 1 if e_test.returncode != 1: command.fail(e_test) print("no existing keytab found, uploading local keytab") else: if existing_keytab != decrypted: command.fail("existing keytab does not match local keytab") return # existing keytab matches local keytab, no action required ssh.upload_bytes(node, decrypted, KEYTAB_PATH) ops.add_operation("upload keytab for {}".format(node), safe_upload_keytab) ssh_cmd(ops, "enable keygateway on @HOST", node, "systemctl", "enable", "keygateway") ssh_cmd(ops, "restart keygateway on @HOST", node, "systemctl", "restart", "keygateway")
def import_keytab(node, keytab_file): "import and encrypt a keytab for a particular server" if not configuration.get_config().has_node(node): command.fail("no such node: %s" % node) keytab_target = os.path.join(configuration.get_project(), "keytab.%s.crypt" % node) keycrypt.gpg_encrypt_file(keytab_file, keytab_target)
def list_keytabs(keytab=None): keytabs = [ ".".join(kt.split(".")[1:-1]) for kt in os.listdir(configuration.get_project()) if kt.startswith("keytab.") and kt.endswith(".crypt") ] if keytab is not None: if keytab not in keytabs: command.fail("no keytab found for: %s" % keytab) keytabs = [keytab] with tempfile.TemporaryDirectory() as d: keytab_dest = os.path.join(d, "keytab.decrypt") for kt in keytabs: keytab_source = os.path.join(configuration.get_project(), "keytab.%s.crypt" % kt) keycrypt.gpg_decrypt_file(keytab_source, keytab_dest) print("== listing for %s ==" % kt) subprocess.check_call(["k5srvutil", "-f", keytab_dest, "list"]) os.remove(keytab_dest)
def setup_keygateway(ops: Operations, config: configuration.Config) -> None: for node in config.nodes: if node.kind != "supervisor": continue keytab = os.path.join(configuration.get_project(), "keytab.%s" % node.hostname) ops.ssh("confirm no existing keytab on @HOST", node, "test", "!", "-e", KEYTAB_PATH) ops.ssh_upload_path("upload keytab for @HOST", node, keytab, KEYTAB_PATH) ops.ssh("restart keygateway on @HOST", node, "systemctl", "restart", "keygateway")
def list_passphrases(): passwords = os.path.join(configuration.get_project(), "passwords") if not os.path.isdir(passwords): command.fail("no passwords stored") print("Passphrases:") for passfile in os.listdir(passwords): if passfile.startswith("at-") and passfile.endswith(".gpg"): date = passfile[3:-4] passph = keycrypt.gpg_decrypt_to_memory( os.path.join(passwords, passfile)).decode() print(" ", date, "=>", passph) print("End of list.")
def setup_keygateway(ops: Operations, config: configuration.Config) -> None: for node in config.nodes: if node.kind != "supervisor": continue # keytab is stored encrypted in the configuration folder keytab = os.path.join(configuration.get_project(), "keytab.%s.crypt" % node.hostname) decrypted = keycrypt.gpg_decrypt_to_memory(keytab) ops.ssh("confirm no existing keytab on @HOST", node, "test", "!", "-e", KEYTAB_PATH) ops.ssh_upload_bytes("upload keytab for @HOST", node, decrypted, KEYTAB_PATH) ops.ssh("restart keygateway on @HOST", node, "systemctl", "restart", "keygateway")
def keytab_op(node, op): if not configuration.Config.load_from_project().has_node(node): command.fail("no such node: %s" % node) keytab_source = os.path.join(configuration.get_project(), "keytab.%s.crypt" % node) keytab_target = os.path.join(configuration.get_project(), "keytab.%s.crypt.tmp" % node) with tempfile.TemporaryDirectory() as d: keytab_temp = os.path.join(d, "keytab.temp") keycrypt.gpg_decrypt_file(keytab_source, keytab_temp) if op == "rotate": operation = [ "k5srvutil", "-f", keytab_temp, "change", "-e", "aes256-cts:normal,aes128-cts:normal" ] elif op == "delold": operation = ["k5srvutil", "-f", keytab_temp, "delold"] else: command.fail("internal error: no such operation %s" % op) subprocess.check_call(operation) keycrypt.gpg_encrypt_file(keytab_temp, keytab_target) os.remove(keytab_source) os.rename(keytab_target, keytab_source)
def auto_install(ops: command.Operations, authorized_key=None, persistent: bool = False, cdrom_install: bool = False, debug_qemu: bool = False): "complete cluster installation and launch" if authorized_key is None: if "HOME" not in os.environ: command.fail( "expected $HOME to be set for authorized_key autodetect") authorized_key = os.path.join(os.getenv("HOME"), ".ssh/id_rsa.pub") project, config = configuration.get_project(), configuration.get_config() iso_path = os.path.join(project, "cluster-%d.iso" % os.getpid()) ops.add_operation("check nested virtualization", qemu_check_nested_virt) ops.add_operation("update known hosts", access.update_known_hosts) ops.add_operation("generate ISO", lambda: iso.gen_iso(iso_path, authorized_key, "serial")) with ops.context("networking", net_context()): with ops.context("termination", TerminationContext()) as tc: with ops.context("debug shell", DebugContext(persistent)): ops.add_subcommand(auto_install_supervisor, tc, config.keyserver, iso_path, cdrom_install=cdrom_install, debug_qemu=debug_qemu) ops.add_subcommand(auto_launch_supervisor, tc, config.keyserver, debug_qemu=debug_qemu) ops.add_subcommand(seq.sequence_supervisor) other_nodes = [ n for n in config.nodes if n != config.keyserver ] ops.add_subcommand(auto_install_nodes, tc, other_nodes, iso_path, cdrom_install=cdrom_install, debug_qemu=debug_qemu) ops.add_subcommand(auto_launch_nodes, tc, other_nodes, debug_qemu=debug_qemu) ops.add_subcommand(seq.sequence_cluster)
def call_etcdctl(params: list, return_result: bool): project_dir = configuration.get_project() endpoints = configuration.get_etcd_endpoints() etcd_key_path = os.path.join(project_dir, "etcd-access.key") etcd_cert_path = os.path.join(project_dir, "etcd-access.pem") etcd_ca_path = os.path.join(project_dir, "etcd-ca.pem") if needs_rotate(etcd_cert_path): print("rotating etcd certs...") call_keyreq("etcd-cert", etcd_key_path, etcd_cert_path, etcd_ca_path) args = ["etcdctl", "--cert-file", etcd_cert_path, "--key-file", etcd_key_path, "--ca-file", etcd_ca_path, "--endpoints", endpoints] + list(params) if return_result: return subprocess.check_output(args) else: subprocess.check_call(args)
def setup_bootstrap_registry(ops: Operations, config: configuration.Config) -> None: https_cert_dir = os.path.join(configuration.get_project(), "https-certs") for node in config.nodes: if node.kind != "supervisor": continue keypath = os.path.join(https_cert_dir, "%s.key" % REGISTRY_HOSTNAME) certpath = os.path.join(https_cert_dir, "%s.pem" % REGISTRY_HOSTNAME) ops.ssh_mkdir("create ssl cert directory on @HOST", node, "/etc/homeworld/ssl") ops.ssh_upload_path("upload %s key to @HOST" % REGISTRY_HOSTNAME, node, keypath, "/etc/homeworld/ssl/%s.key" % REGISTRY_HOSTNAME) ops.ssh_upload_path("upload %s cert to @HOST" % REGISTRY_HOSTNAME, node, certpath, "/etc/homeworld/ssl/%s.pem" % REGISTRY_HOSTNAME) ops.ssh("restart nginx on @HOST", node, "systemctl", "restart", "nginx")
def auto_cluster(ops: setup.Operations, authorized_key=None): if authorized_key is None: if "HOME" not in os.environ: command.fail( "expected $HOME to be set for authorized_key autodetect") authorized_key = os.path.join(os.getenv("HOME"), ".ssh/id_rsa.pub") project, config = configuration.get_project(), configuration.get_config() iso_path = os.path.join(project, "cluster-%d.iso" % os.getpid()) ops.add_operation("check nested virtualization", qemu_check_nested_virt) ops.add_operation("update known hosts", access.update_known_hosts) ops.add_operation("generate ISO", lambda: iso.gen_iso(iso_path, authorized_key, "serial")) with ops.context("networking", net_context()): with ops.context("termination", TerminationContext()) as tc: with ops.context("debug shell", DebugContext()): ops.add_subcommand(lambda ops: auto_supervisor( ops, tc, config.keyserver, iso_path)) for node in config.nodes: if node == config.keyserver: continue ops.add_subcommand( lambda ops, n=node: auto_node(ops, tc, n, iso_path)) ops.add_subcommand(seq.sequence_cluster)
def get_upstream_cert_paths(): # we don't encrypt user-grant-upstream.key, because it's not intended to be saved return os.path.join(configuration.get_project(), "user-grant-upstream.key"), \ os.path.join(configuration.get_project(), "user-grant-upstream.pem")
def get_local_grant_user_paths(): # we don't encrypt local-grant-user.key, because it's not intended to be saved return os.path.join(configuration.get_project(), "local-grant-user.key"), \ os.path.join(configuration.get_project(), "local-grant-user.pem")
def get_kube_cert_paths() -> (str, str, str): project_dir = configuration.get_project() return os.path.join(project_dir, "kube-access.key"),\ os.path.join(project_dir, "kube-access.pem"),\ os.path.join(project_dir, "kube-ca.pem")
def get_targz_path(check_exists=True): authorities = os.path.join(configuration.get_project(), "authorities.tgz") if check_exists and not os.path.exists(authorities): command.fail( "authorities.tgz does not exist (run spire authority gen?)") return authorities
def hd(self): return os.path.join(configuration.get_project(), "virt-local", "disk-%s.qcow2" % self.hostname)
def renew_ssh_cert() -> str: project_dir = configuration.get_project() keypath = os.path.join(project_dir, "ssh-key") refresh_cert(keypath, keypath + "-cert.pub", None, "ssh", "ssh_user_ca", "ssh_user_ca.pub") return keypath
def import_keytab(node, keytab_file): if not configuration.Config.load_from_project().has_node(node): command.fail("no such node: %s" % node) keytab_target = os.path.join(configuration.get_project(), "keytab.%s.crypt" % node) keycrypt.gpg_encrypt_file(keytab_file, keytab_target)
def decrypt_https(hostname): return keycrypt.gpg_decrypt_to_memory(os.path.join(configuration.get_project(), "https.%s.key.crypt" % hostname)), \ util.readfile(os.path.join(configuration.get_project(), "https.%s.pem" % hostname))
def add_password_to_log(password, creation_time): passwords = os.path.join(configuration.get_project(), "passwords") if not os.path.isdir(passwords): os.mkdir(passwords) passfile = os.path.join(passwords, "at-%s.gpg" % creation_time) util.writefile(passfile, keycrypt.gpg_encrypt_in_memory(password))