Beispiel #1
0
def get_certificate_sans(bindings=None):
    """Get all possible IP addresses for certificate SANs.
    """
    _sans = [unit_get('private-address')]
    if bindings:
        # Add default API bindings to bindings list
        bindings = set(bindings + get_default_api_bindings())
    else:
        # Use default API bindings
        bindings = get_default_api_bindings()

    for binding in bindings:
        # Check for config override
        try:
            net_config = config(ADDRESS_MAP[binding]['config'])
        except KeyError:
            # There is no configuration network for this binding name
            net_config = None
        # Using resolve_address is likely redundant. Keeping it here in
        # case there is an edge case it handles.
        net_addr = resolve_address(endpoint_type=binding)
        ip = get_relation_ip(binding, cidr_network=net_config)
        _sans = _sans + [net_addr, ip]
        vip = get_vip_in_network(resolve_network_cidr(ip))
        if vip:
            _sans.append(vip)
    return set(_sans)
Beispiel #2
0
def create_ip_cert_links(ssl_dir, custom_hostname_link=None, bindings=None):
    """Create symlinks for SAN records

    :param ssl_dir: str Directory to create symlinks in
    :param custom_hostname_link: str Additional link to be created
    :param bindings: List of bindings to check in addition to default api
                     bindings.
    :type bindings: list of strings
    """

    if bindings:
        # Add default API bindings to bindings list
        bindings = list(bindings + get_default_api_bindings())
    else:
        # Use default API bindings
        bindings = get_default_api_bindings()

    # This includes the hostname cert and any specific bindng certs:
    # admin, internal, public
    req = get_certificate_request(json_encode=False, bindings=bindings)["cert_requests"]
    # Specific certs
    for cert_req in req.keys():
        requested_cert = os.path.join(
            ssl_dir,
            'cert_{}'.format(cert_req))
        requested_key = os.path.join(
            ssl_dir,
            'key_{}'.format(cert_req))
        for addr in req[cert_req]['sans']:
            cert = os.path.join(ssl_dir, 'cert_{}'.format(addr))
            key = os.path.join(ssl_dir, 'key_{}'.format(addr))
            if os.path.isfile(requested_cert) and not os.path.isfile(cert):
                os.symlink(requested_cert, cert)
                os.symlink(requested_key, key)

    # Handle custom hostnames
    hostname = get_hostname(local_address(unit_get_fallback='private-address'))
    hostname_cert = os.path.join(
        ssl_dir,
        'cert_{}'.format(hostname))
    hostname_key = os.path.join(
        ssl_dir,
        'key_{}'.format(hostname))
    if custom_hostname_link:
        custom_cert = os.path.join(
            ssl_dir,
            'cert_{}'.format(custom_hostname_link))
        custom_key = os.path.join(
            ssl_dir,
            'key_{}'.format(custom_hostname_link))
        if os.path.isfile(hostname_cert) and not os.path.isfile(custom_cert):
            os.symlink(hostname_cert, custom_cert)
            os.symlink(hostname_key, custom_key)
Beispiel #3
0
def get_certificate_request(json_encode=True, bindings=None):
    """Generate a certificate requests based on the network configuration

    :param json_encode: Encode request in JSON or not. Used for setting
                        directly on a relation.
    :type json_encode: boolean
    :param bindings: List of bindings to check in addition to default api
                     bindings.
    :type bindings: list of strings
    :returns: CertRequest request as dictionary or JSON string.
    :rtype: Union[dict, json]
    """
    if bindings:
        # Add default API bindings to bindings list
        bindings = set(bindings + get_default_api_bindings())
    else:
        # Use default API bindings
        bindings = get_default_api_bindings()
    req = CertRequest(json_encode=json_encode)
    req.add_hostname_cn()
    # Add os-hostname entries
    _sans = get_certificate_sans()

    # Handle specific hostnames per binding
    for binding in bindings:
        hostname_override = config(ADDRESS_MAP[binding]['override'])
        try:
            net_addr = resolve_address(endpoint_type=binding)
            ip = network_get_primary_address(ADDRESS_MAP[binding]['binding'])
            addresses = [net_addr, ip]
            vip = get_vip_in_network(resolve_network_cidr(ip))
            if vip:
                addresses.append(vip)
            # Add hostname certificate request
            if hostname_override:
                req.add_entry(binding, hostname_override, addresses)
            # Remove hostname specific addresses from _sans
            for addr in addresses:
                try:
                    _sans.remove(addr)
                except (ValueError, KeyError):
                    pass

        except NoNetworkBinding:
            log(
                "Skipping request for certificate for ip in {} space, no "
                "local address found".format(binding), WARNING)
    # Gurantee all SANs are covered
    # These are network addresses with no corresponding hostname.
    # Add the ips to the hostname cert to allow for this.
    req.add_hostname_cn_ip(_sans)
    return req.get_request()
Beispiel #4
0
def process_certificates(service_name,
                         relation_id,
                         unit,
                         custom_hostname_link=None,
                         user='******',
                         group='root',
                         bindings=None):
    """Process the certificates supplied down the relation

    :param service_name: str Name of service the certifcates are for.
    :param relation_id: str Relation id providing the certs
    :param unit: str Unit providing the certs
    :param custom_hostname_link: str Name of custom link to create
    :param user: (Optional) Owner of certificate files. Defaults to 'root'
    :type user: str
    :param group: (Optional) Group of certificate files. Defaults to 'root'
    :type group: str
    :param bindings: List of bindings to check in addition to default api
                     bindings.
    :type bindings: list of strings
    :returns: True if certificates processed for local unit or False
    :rtype: bool
    """
    if bindings:
        # Add default API bindings to bindings list
        bindings = list(bindings + get_default_api_bindings())
    else:
        # Use default API bindings
        bindings = get_default_api_bindings()

    data = relation_get(rid=relation_id, unit=unit)
    ssl_dir = os.path.join('/etc/apache2/ssl/', service_name)
    mkdir(path=ssl_dir)
    name = local_unit().replace('/', '_')
    certs = data.get('{}.processed_requests'.format(name))
    chain = data.get('chain')
    ca = data.get('ca')
    if certs:
        certs = json.loads(certs)
        _manage_ca_certs(ca, relation_id)
        install_certs(ssl_dir, certs, chain, user=user, group=group)
        create_ip_cert_links(ssl_dir,
                             custom_hostname_link=custom_hostname_link,
                             bindings=bindings)
        return True
    return False
Beispiel #5
0
def get_certificate_sans(bindings=None):
    """Get all possible IP addresses for certificate SANs.

    :param bindings: List of bindings to check in addition to default api
                     bindings.
    :type bindings: list of strings
    :returns: List of binding string names
    :rtype: List[str]
    """
    _sans = [local_address(unit_get_fallback='private-address')]
    if bindings:
        # Add default API bindings to bindings list
        bindings = list(bindings + get_default_api_bindings())
    else:
        # Use default API bindings
        bindings = get_default_api_bindings()

    for binding in bindings:
        # Check for config override
        try:
            net_config = config(ADDRESS_MAP[binding]['config'])
        except KeyError:
            # There is no configuration network for this binding name
            net_config = None
        # Using resolve_address is likely redundant. Keeping it here in
        # case there is an edge case it handles.
        try:
            net_addr = resolve_address(endpoint_type=binding)
        except KeyError:
            net_addr = None
        ip = get_relation_ip(binding, cidr_network=net_config)
        _sans = _sans + [net_addr, ip]
        vip = get_vip_in_network(resolve_network_cidr(ip))
        if vip:
            _sans.append(vip)
    # Clear any Nones and duplicates
    return list(set([i for i in _sans if i]))