Ejemplo n.º 1
0
def create_admin_kubeconfig(ca, ha_admin_token=None):
    """
    Create a kubeconfig file. The file in stored under credentials named after the admin

    :param ca: the ca
    :param ha_admin_token: the ha_cluster_token
    """
    if not ha_admin_token:
        token = get_token("admin", "basic_auth.csv")
        if not token:
            print(
                "Error, could not locate admin token. Joining cluster failed.")
            exit(2)
    else:
        token = ha_admin_token
    assert token is not None
    config_template = "{}/{}".format(snap_path, "client.config.template")
    config = "{}/credentials/client.config".format(snapdata_path)
    shutil.copyfile(config, "{}.backup".format(config))
    try_set_file_permissions("{}.backup".format(config))
    ca_line = ca_one_line(ca)
    with open(config_template, "r") as tfp:
        with open(config, "w+") as fp:
            for _, config_txt in enumerate(tfp):
                if config_txt.strip().startswith("username:"******"CADATA", ca_line)
                    config_txt = config_txt.replace("NAME", "admin")
                    config_txt = config_txt.replace("AUTHTYPE", "token")
                    config_txt = config_txt.replace("PASSWORD", token)
                    fp.write(config_txt)
        try_set_file_permissions(config)
Ejemplo n.º 2
0
def create_x509_kubeconfig(ca, master_ip, api_port, filename, user,
                           path_to_cert, path_to_cert_key):
    """
    Create a kubeconfig file. The file in stored under credentials named after the user

    :param ca: the ca
    :param master_ip: the master node IP
    :param api_port: the API server port
    :param filename: the name of the config file
    :param user: the user to use al login
    :param path_to_cert: path to certificate file
    :param path_to_cert_key: path to certificate key file
    """
    snap_path = os.environ.get("SNAP")
    config_template = "{}/{}".format(snap_path, "client-x509.config.template")
    config = "{}/credentials/{}".format(snapdata_path, filename)
    shutil.copyfile(config, "{}.backup".format(config))
    try_set_file_permissions("{}.backup".format(config))
    ca_line = ca_one_line(ca)
    with open(config_template, "r") as tfp:
        with open(config, "w+") as fp:
            config_txt = tfp.read()
            config_txt = config_txt.replace("CADATA", ca_line)
            config_txt = config_txt.replace("NAME", user)
            config_txt = config_txt.replace("PATHTOCERT", path_to_cert)
            config_txt = config_txt.replace("PATHTOKEYCERT", path_to_cert_key)
            config_txt = config_txt.replace("127.0.0.1", master_ip)
            config_txt = config_txt.replace("16443", api_port)
            fp.write(config_txt)
        try_set_file_permissions(config)
Ejemplo n.º 3
0
def get_etcd_client_cert(master_ip, master_port, token):
    """
    Get a signed cert to access etcd

    :param master_ip: master ip
    :param master_port: master port
    :param token: token to contact the master with
    """
    cer_req_file = "{}/certs/server.remote.csr".format(snapdata_path)
    cmd_cert = (
        "{snap}/usr/bin/openssl req -new -sha256 -key {snapdata}/certs/server.key -out {csr} "
        "-config {snapdata}/certs/csr.conf".format(snap=snap_path,
                                                   snapdata=snapdata_path,
                                                   csr=cer_req_file))
    subprocess.check_call(cmd_cert.split())
    with open(cer_req_file) as fp:
        csr = fp.read()
        req_data = {"token": token, "request": csr}
        # TODO: enable ssl verification
        signed = requests.post(
            "https://{}:{}/{}/sign-cert".format(master_ip, master_port,
                                                CLUSTER_API),
            json=req_data,
            verify=False,
        )
        if signed.status_code != 200:
            print("Failed to sign certificate. {}".format(
                signed.json()["error"]))
            exit(1)
        info = signed.json()
        with open(server_cert_file, "w") as cert_fp:
            cert_fp.write(info["certificate"])
        try_set_file_permissions(server_cert_file)
Ejemplo n.º 4
0
def create_kubeconfig(token, ca, master_ip, api_port, filename, user):
    """
    Create a kubeconfig file. The file in stored under credentials named after the user

    :param token: the token to be in the kubeconfig
    :param ca: the ca
    :param master_ip: the master node IP
    :param api_port: the API server port
    :param filename: the name of the config file
    :param user: the user to use al login
    """
    snap_path = os.environ.get("SNAP")
    config_template = "{}/{}".format(snap_path, "kubelet.config.template")
    config = "{}/credentials/{}".format(snapdata_path, filename)
    shutil.copyfile(config, "{}.backup".format(config))
    try_set_file_permissions("{}.backup".format(config))
    ca_line = ca_one_line(ca)
    with open(config_template, "r") as tfp:
        with open(config, "w+") as fp:
            config_txt = tfp.read()
            config_txt = config_txt.replace("CADATA", ca_line)
            config_txt = config_txt.replace("NAME", user)
            config_txt = config_txt.replace("TOKEN", token)
            config_txt = config_txt.replace("127.0.0.1", master_ip)
            config_txt = config_txt.replace("16443", api_port)
            fp.write(config_txt)
        try_set_file_permissions(config)
Ejemplo n.º 5
0
def rebuild_client_config():
    """
    Recreate the client config
    """
    token = get_token("admin")
    if not token:
        print("Error, could not locate admin token. Resetting the node failed.")
        exit(2)

    config_template = "{}/{}".format(snap_path, "client.config.template")
    config = "{}/credentials/client.config".format(snapdata_path)
    shutil.copyfile(config, "{}.backup".format(config))
    try_set_file_permissions("{}.backup".format(config))
    cert_file = "{}/certs/{}".format(snapdata_path, "ca.crt")
    with open(cert_file) as fp:
        ca = fp.read()
    ca_line = base64.b64encode(ca.encode("utf-8")).decode("utf-8")
    with open(config_template, "r") as tfp:
        with open(config, "w+") as fp:
            for _, config_txt in enumerate(tfp):
                if config_txt.strip().startswith("username:"******"CADATA", ca_line)
                    config_txt = config_txt.replace("NAME", "admin")
                    config_txt = config_txt.replace("AUTHTYPE", "token")
                    config_txt = config_txt.replace("PASSWORD", token)
                    fp.write(config_txt)
        try_set_file_permissions(config)
Ejemplo n.º 6
0
def update_apiserver_proxy(master_ip, api_port):
    """
    Update the apiserver-proxy configuration
    """
    lock_path = os.path.expandvars("${SNAP_DATA}/var/lock")
    lock = "{}/no-apiserver-proxy".format(lock_path)
    if os.path.exists(lock):
        os.remove(lock)

    # add the initial control plane endpoint
    addresses = [{"address": "{}:{}".format(master_ip, api_port)}]

    traefik_providers = os.path.expandvars(
        "${SNAP_DATA}/args/traefik/provider-template.yaml")
    traefik_providers_out = os.path.expandvars(
        "${SNAP_DATA}/args/traefik/provider.yaml")
    with open(traefik_providers) as f:
        p = yaml.safe_load(f)
        p["tcp"]["services"]["kube-apiserver"]["loadBalancer"][
            "servers"] = addresses
        with open(traefik_providers_out, "w") as out_file:
            yaml.dump(p, out_file)

    try_set_file_permissions(traefik_providers_out)
    service("restart", "apiserver-proxy")
Ejemplo n.º 7
0
def set_arg(key, value, file):
    """
    Set an argument to a file

    :param key: argument name
    :param value: value
    :param file: the arguments file
    """
    filename = "{}/args/{}".format(snapdata_path, file)
    filename_remote = "{}/args/{}.remote".format(snapdata_path, file)
    done = False
    with open(filename_remote, "w+") as back_fp:
        with open(filename, "r+") as fp:
            for _, line in enumerate(fp):
                if line.startswith(key):
                    done = True
                    if value is not None:
                        back_fp.write("{}={}\n".format(key, value))
                else:
                    back_fp.write("{}".format(line))
        if not done and value is not None:
            back_fp.write("{}={}\n".format(key, value))

    shutil.copyfile(filename, "{}.backup".format(filename))
    try_set_file_permissions("{}.backup".format(filename))
    shutil.copyfile(filename_remote, filename)
    try_set_file_permissions(filename)
    os.remove(filename_remote)
Ejemplo n.º 8
0
def store_remote_ca(ca):
    """
    Store the remote ca

    :param ca: the CA
    """
    with open(ca_cert_file, "w+") as fp:
        fp.write(ca)
    try_set_file_permissions(ca_cert_file)
Ejemplo n.º 9
0
def store_base_kubelet_args(args_string):
    """
    Create a kubelet args file from the set of args provided

    :param args_string: the arguments provided
    """
    args_file = "{}/args/kubelet".format(snapdata_path)
    with open(args_file, "w") as fp:
        fp.write(args_string)
    try_set_file_permissions(args_file)
Ejemplo n.º 10
0
def store_callback_token(token):
    """
    Store the callback token

    :param token: the callback token
    """
    callback_token_file = "{}/credentials/callback-token.txt".format(
        snapdata_path)
    with open(callback_token_file, "w") as fp:
        fp.write(token)
    try_set_file_permissions(callback_token_file)
Ejemplo n.º 11
0
def store_cluster_certs(cluster_cert, cluster_key):
    """
    Store the dqlite cluster certs

    :param cluster_cert: the cluster certificate
    :param cluster_key: the cluster certificate key
    """
    with open(cluster_cert_file, "w+") as fp:
        fp.write(cluster_cert)
    try_set_file_permissions(cluster_cert_file)
    with open(cluster_key_file, "w+") as fp:
        fp.write(cluster_key)
    try_set_file_permissions(cluster_key_file)
Ejemplo n.º 12
0
def store_cert(filename, payload):
    """
    Store a certificate

    :param filename: where to store the certificate
    :param payload: certificate payload
    """
    file_with_path = "{}/certs/{}".format(snapdata_path, filename)
    backup_file_with_path = "{}.backup".format(file_with_path)
    shutil.copyfile(file_with_path, backup_file_with_path)
    try_set_file_permissions(backup_file_with_path)
    with open(file_with_path, "w+") as fp:
        fp.write(payload)
    try_set_file_permissions(file_with_path)
Ejemplo n.º 13
0
def generate_callback_token():
    """
    Generate a token and store it in the callback token file

    :return: the token
    """
    token = "".join(
        random.choice(string.ascii_uppercase + string.digits)
        for _ in range(64))
    with open(callback_token_file, "w") as fp:
        fp.write("{}\n".format(token))

    try_set_file_permissions(callback_token_file)
    return token
Ejemplo n.º 14
0
def remove_kubelet_token(node):
    """
    Remove a token for a node in the known tokens

    :param node: the name of the node
    """
    file = "{}/credentials/known_tokens.csv".format(snapdata_path)
    backup_file = "{}.backup".format(file)
    token = "system:node:{}".format(node)
    # That is a critical section. We need to protect it.
    with open(backup_file, "w") as back_fp:
        with open(file, "r") as fp:
            for _, line in enumerate(fp):
                if token in line:
                    continue
                back_fp.write("{}".format(line))

    try_set_file_permissions(backup_file)
    shutil.copyfile(backup_file, file)
Ejemplo n.º 15
0
def replace_admin_token(token):
    """
    Replaces the admin token in the known tokens

    :param token: the admin token
    """
    file = "{}/credentials/known_tokens.csv".format(snapdata_path)
    backup_file = "{}.backup".format(file)
    # That is a critical section. We need to protect it.
    with open(backup_file, "w") as back_fp:
        with open(file, "r") as fp:
            for _, line in enumerate(fp):
                if 'admin,admin,"system:masters"' in line:
                    continue
                back_fp.write("{}".format(line))
            back_fp.write('{},admin,admin,"system:masters"\n'.format(token))

    try_set_file_permissions(backup_file)
    shutil.copyfile(backup_file, file)
Ejemplo n.º 16
0
def remove_callback_token(node):
    """
    Remove a callback token

    :param node: the node
    """
    tmp_file = "{}.tmp".format(callback_tokens_file)
    if not os.path.isfile(callback_tokens_file):
        open(callback_tokens_file, "a+")
        os.chmod(callback_tokens_file, 0o600)
    with open(tmp_file, "w") as backup_fp:
        os.chmod(tmp_file, 0o600)
        with open(callback_tokens_file, "r+") as callback_fp:
            # Entries are of the format: 'node_hostname:agent_port token'
            # We need to get the node_hostname part
            for line in callback_fp:
                parts = line.split(":")
                if parts[0] == node:
                    continue
                else:
                    backup_fp.write(line)

    try_set_file_permissions(tmp_file)
    shutil.move(tmp_file, callback_tokens_file)
Ejemplo n.º 17
0
def get_client_cert(master_ip,
                    master_port,
                    fname,
                    token,
                    username,
                    group=None):
    """
    Get a signed cert.
    See https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs

    :param master_ip: master ip
    :param master_port: master port
    :param fname: file name prefix for the certificate
    :param token: token to contact the master with
    :param username: the username of the cert's owner
    :param group: the group the owner belongs to
    """
    info = "/CN={}".format(username)
    if group:
        info = "{}/O={}".format(info, group)
    cer_req_file = "{}/certs/{}.csr".format(snapdata_path, fname)
    cer_key_file = "{}/certs/{}.key".format(snapdata_path, fname)
    cer_file = "{}/certs/{}.crt".format(snapdata_path, fname)
    if not os.path.exists(cer_key_file):
        cmd_gen_cert_key = "{snap}/usr/bin/openssl genrsa -out {key} 2048".format(
            snap=snap_path, key=cer_key_file)
        subprocess.check_call(cmd_gen_cert_key.split(),
                              stdout=subprocess.DEVNULL,
                              stderr=subprocess.DEVNULL)
        try_set_file_permissions(cer_key_file)

    cmd_cert = "{snap}/usr/bin/openssl req -new -sha256 -key {key} -out {csr} -subj {info}".format(
        snap=snap_path,
        key=cer_key_file,
        csr=cer_req_file,
        info=info,
    )
    subprocess.check_call(cmd_cert.split(),
                          stdout=subprocess.DEVNULL,
                          stderr=subprocess.DEVNULL)
    with open(cer_req_file) as fp:
        csr = fp.read()
        req_data = {"token": token, "request": csr}
        # TODO: enable ssl verification
        signed = requests.post(
            "https://{}:{}/{}/sign-cert".format(master_ip, master_port,
                                                CLUSTER_API),
            json=req_data,
            verify=False,
        )
        if signed.status_code != 200:
            error = "Failed to sign {} certificate ({}).".format(
                fname, signed.status_code)
            try:
                if "error" in signed.json():
                    error = "{} {}".format(error,
                                           format(signed.json()["error"]))
            except ValueError:
                print(
                    "Make sure the cluster you connect to supports joining worker nodes."
                )
            print(error)
            exit(1)
        info = signed.json()
        with open(cer_file, "w") as cert_fp:
            cert_fp.write(info["certificate"])
        try_set_file_permissions(cer_file)

        return {
            "certificate_location": cer_file,
            "certificate_key_location": cer_key_file,
        }