Exemple #1
0
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)
Exemple #2
0
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)
Exemple #3
0
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
Exemple #4
0
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)
Exemple #5
0
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)
Exemple #6
0
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)
Exemple #7
0
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)