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)
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)
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()
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
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]))