def get_ca(ca): ''' Get the CA information for the provided ca. ''' try: ca_object = certificatemanager.get_ca(ca) except certificatemanager.CertificateAuthorityNotFoundError: return jsonify({'error': 'Provided CA not found.'}), 404 logged_in_user = authnz.get_logged_in_user() if not acl_module_check( resource_type='ca', action='get', resource_id=ca, ): msg = '{} does not have access to get ca {}'.format( authnz.get_logged_in_user(), ca, ) error_msg = {'error': msg, 'reference': ca} return jsonify(error_msg), 403 logging.info('get_ca called on id={} by user={}'.format( ca, logged_in_user, )) _ca = ca_object.get_certificate_authority_certificate() ca_response = CertificateAuthorityResponse( ca=_ca['ca'], certificate=_ca['certificate'], certificate_chain=_ca['certificate_chain'], tags=_ca['tags'], ) return certificate_authority_response_schema.dumps(ca_response)
def get_certificate_from_csr(ca): ''' Get a certificate from the ca provided in the url, using the CSR, validity and san provided in the POST body. ''' try: ca_object = certificatemanager.get_ca(ca) except certificatemanager.CertificateAuthorityNotFoundError: return jsonify({'error': 'Provided CA not found.'}), 404 data = request.get_json() if not data or not data.get('csr'): return jsonify( {'error': 'csr must be provided in the POST body.'}, ), 400 validity = data.get( 'validity', ca_object.settings['max_validity_days'], ) try: csr = ca_object.decode_csr(data['csr']) except Exception: logging.exception('Failed to decode PEM csr') return jsonify({'error': 'csr could not be decoded'}, ), 400 # Get the cn and san values from the csr object, so that we can use them # for the ACL check. cn = ca_object.get_csr_common_name(csr) san = ca_object.get_csr_san(csr) logged_in_user = authnz.get_logged_in_user() if not acl_module_check( resource_type='certificate', action='get', resource_id=cn, kwargs={ 'ca': ca, 'san': san, }, ): msg = ('{} does not have access to get certificate cn {} against' ' ca {}').format( authnz.get_logged_in_user(), cn, ca, ) error_msg = {'error': msg, 'reference': cn} return jsonify(error_msg), 403 logging.info('get_certificate called on id={} for ca={} by user={}'.format( cn, ca, logged_in_user, )) arn = ca_object.issue_certificate(data['csr'], validity) certificate = ca_object.get_certificate_from_arn(arn) certificate_response = CertificateResponse( certificate=certificate['certificate'], certificate_chain=certificate['certificate_chain'], ) return certificate_response_schema.dumps(certificate_response)
def default_acl(*args, **kwargs): """ Default ACLs for confidant: Allow access to all resource types and actions for users, except for certificate resource_type. Deny access to all resource types and actions for services, except: * resource_type: service actions: metadata, get resource_id: must match logged-in user's username * resource_type: certificate actions: get resource_id: must match against ACM_PRIVATE_CA_DOMAIN_REGEX setting for the CA for the CN in the CSR, and for all SAN values in the CSR, and the server_name named group in the regex must match the logged in user's username. kwargs (ca): CA used for this get kwargs (san): A list of subject alternative names in the CSR """ resource_type = kwargs.get('resource_type') action = kwargs.get('action') resource_id = kwargs.get('resource_id') resource_kwargs = kwargs.get('kwargs') if authnz.user_is_user_type('user'): if resource_type == 'certificate': return False elif resource_type == 'ca': return False return True elif authnz.user_is_user_type('service'): if resource_type == 'service' and action in ['metadata', 'get']: # Does the resource ID match the authenticated username? if authnz.user_is_service(resource_id): return True elif resource_type == 'ca' and action in ['list', 'get']: return True elif resource_type == 'certificate' and action in ['get']: ca_object = certificatemanager.get_ca(resource_kwargs.get('ca')) # Require a name pattern if not ca_object.settings['name_regex']: return False cert_pattern = re.compile(ca_object.settings['name_regex']) domains = [resource_id] domains.extend(resource_kwargs.get('san', [])) # Ensure the CN and every value in the SAN is allowed for this # user. for domain in domains: match = cert_pattern.match(domain) if not match: return False service_name = match.group('service_name') if not service_name: return False if not authnz.user_is_service(service_name): return False return True return False else: # This should never happen, but paranoia wins out return False
def get_certificate(ca, cn): ''' Get a certificate for the provided cn, using the provided CA. ''' try: ca_object = certificatemanager.get_ca(ca) except certificatemanager.CertificateAuthorityNotFoundError: return jsonify({'error': 'Provided CA not found.'}), 404 san = request.args.getlist('san') logged_in_user = authnz.get_logged_in_user() if not acl_module_check( resource_type='certificate', action='get', resource_id=cn, kwargs={ 'ca': ca, 'san': san, }, ): msg = ('{} does not have access to get certificate cn {} against' ' ca {}').format( authnz.get_logged_in_user(), cn, ca, ) error_msg = {'error': msg, 'reference': cn} return jsonify(error_msg), 403 logging.info('get_certificate called on id={} for ca={} by user={}'.format( cn, ca, logged_in_user, )) validity = request.args.get( 'validity', default=ca_object.settings['max_validity_days'], type=int, ) try: certificate = ca_object.issue_certificate_with_key( cn, validity, san, ) except certificatemanager.CertificateNotReadyError: # Ratelimit response for a locked certificate in the cache error_msg = 'Certificate being requested, please wait and try again.' response = jsonify(error_msg) response.retry_after = 2 return response, 429 certificate_response = CertificateResponse( certificate=certificate['certificate'], certificate_chain=certificate['certificate_chain'], key=certificate['key'], ) return certificate_expanded_response_schema.dumps(certificate_response)
def get_ca(ca): ''' Get the CA information for the provided ca. .. :quickref: Certificate Authorities; Get the detailed certificate authority information for the specified CA. **Example request**: .. sourcecode:: http GET /v1/cas/example-ca :param ca: The friendly name of the certificate authority to issue a certificate against. :type ca: str **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "example-ca": { "certificate": "---...BEGIN...", "certificate_chain": "---...BEGIN...", "tags": { "hello": "world" } } :resheader Content-Type: application/json :statuscode 200: Success :statuscode 403: Client does not have access to get the requested CA. ''' try: ca_object = certificatemanager.get_ca(ca) except certificatemanager.CertificateAuthorityNotFoundError: return jsonify({'error': 'Provided CA not found.'}), 404 logged_in_user = authnz.get_logged_in_user() if not acl_module_check( resource_type='ca', action='get', resource_id=ca, ): msg = '{} does not have access to get ca {}'.format( authnz.get_logged_in_user(), ca, ) error_msg = {'error': msg, 'reference': ca} return jsonify(error_msg), 403 logger.info('get_ca called on id={} by user={}'.format( ca, logged_in_user, )) _ca = ca_object.get_certificate_authority_certificate() ca_response = CertificateAuthorityResponse( ca=_ca['ca'], certificate=_ca['certificate'], certificate_chain=_ca['certificate_chain'], tags=_ca['tags'], ) return certificate_authority_response_schema.dumps(ca_response)
def get_certificate(ca, cn): ''' Get a certificate from the provided CA, for the provided CN. .. :quickref: Certificate; Get certificate from the provided CA, for the provided CN. **Example request**: .. sourcecode:: http GET /v1/certificates/example-ca/service.example.com :param ca: The friendly name of the certificate authority to issue a certificate against. :type ca: str :param cn: The canonical name attribute to use in the issued certificate. :type cn: str :query string san: A subject alternative name attribute to use in the issued certificate. This query parameter can be provided multiple times :query int validity: The length (in days) that the issued certificate should be valid for. If this value is longer than the server defined maximum validity length, the validity will be set to the maximum validity length. **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "certificate": "---...BEGIN...", "certificate_chain": "---...BEGIN...", "key": "---...BEGIN..." } :resheader Content-Type: application/json :statuscode 200: success :statuscode 403: client does not have access to generate the requested certificate. ''' try: ca_object = certificatemanager.get_ca(ca) except certificatemanager.CertificateAuthorityNotFoundError: return jsonify({'error': 'Provided CA not found.'}), 404 san = request.args.getlist('san') logged_in_user = authnz.get_logged_in_user() if not acl_module_check( resource_type='certificate', action='get', resource_id=cn, kwargs={ 'ca': ca, 'san': san, }, ): msg = ('{} does not have access to get certificate cn {} against' ' ca {}').format( authnz.get_logged_in_user(), cn, ca, ) error_msg = {'error': msg, 'reference': cn} return jsonify(error_msg), 403 logger.info('get_certificate called on id={} for ca={} by user={}'.format( cn, ca, logged_in_user, )) validity = request.args.get( 'validity', default=ca_object.settings['max_validity_days'], type=int, ) try: certificate = ca_object.issue_certificate_with_key( cn, validity, san, ) except certificatemanager.CertificateNotReadyError: # Ratelimit response for a locked certificate in the cache error_msg = 'Certificate being requested, please wait and try again.' response = jsonify(error_msg) response.retry_after = 2 return response, 429 certificate_response = CertificateResponse( certificate=certificate['certificate'], certificate_chain=certificate['certificate_chain'], key=certificate['key'], ) return certificate_expanded_response_schema.dumps(certificate_response)
def get_certificate_from_csr(ca): ''' Get a certificate from the ca provided in the url, using the CSR, validity and san provided in the POST body. .. :quickref: Certificate; Issue and get a certificate from the provided CA, using a CSR provided in the POST body. **Example request**: .. sourcecode:: http POST /v1/certificates/example-ca :<json string ca: The friendly name of the certificate authority to issue a certificate against. :<json List[string] san: a list of subject alternative name attributes to use in the issued certificate. This query parameter can be provided multiple times :<json int validity: The length (in days) that the issued certificate. should be valid for. If this value is longer than the server defined maximum validity length, the validity will be set to the maximum validity length. **Example response**: .. sourcecode:: http HTTP/1.1 200 OK Content-Type: application/json { "certificate": "---...BEGIN...", "certificate_chain": "---...BEGIN..." } :resheader Content-Type: application/json :statuscode 200: Success :statuscode 400: Invalid input; either the CSR was unsbale to be decoded, or was missing from the request. :statuscode 403: Client does not have access to generate the requested certificate. ''' try: ca_object = certificatemanager.get_ca(ca) except certificatemanager.CertificateAuthorityNotFoundError: return jsonify({'error': 'Provided CA not found.'}), 404 data = request.get_json() if not data or not data.get('csr'): return jsonify( {'error': 'csr must be provided in the POST body.'}, ), 400 validity = data.get( 'validity', ca_object.settings['max_validity_days'], ) try: csr = ca_object.decode_csr(data['csr']) except Exception: logger.exception('Failed to decode PEM csr') return jsonify({'error': 'csr could not be decoded'}, ), 400 # Get the cn and san values from the csr object, so that we can use them # for the ACL check. cn = ca_object.get_csr_common_name(csr) san = ca_object.get_csr_san(csr) logged_in_user = authnz.get_logged_in_user() if not acl_module_check( resource_type='certificate', action='get', resource_id=cn, kwargs={ 'ca': ca, 'san': san, }, ): msg = ('{} does not have access to get certificate cn {} against' ' ca {}').format( authnz.get_logged_in_user(), cn, ca, ) error_msg = {'error': msg, 'reference': cn} return jsonify(error_msg), 403 logger.info('get_certificate called on id={} for ca={} by user={}'.format( cn, ca, logged_in_user, )) arn = ca_object.issue_certificate(data['csr'], validity) certificate = ca_object.get_certificate_from_arn(arn) certificate_response = CertificateResponse( certificate=certificate['certificate'], certificate_chain=certificate['certificate_chain'], ) return certificate_response_schema.dumps(certificate_response)