def get_certificate_from_arn(self, certificate_arn):
     """
     Get the PEM encoded certificate from the provided ARN.
     """
     with stats.timer('get_certificate_from_arn'):
         client = confidant.clients.get_boto_client('acm-pca')
         # When a certificate is issued, it may take a while before it's
         # available via get_certificate. We need to keep retrying until it's
         # fully issued.
         i = 0
         while True:
             try:
                 response = client.get_certificate(
                     CertificateAuthorityArn=self.settings['arn'],
                     CertificateArn=certificate_arn,
                 )
                 break
             except client.exceptions.RequestInProgressException:
                 # Sleep for a maximum of 10 seconds
                 if i >= 50:
                     raise
                 logging.debug(
                     'Sleeping in get_certificate_from_arn for {}'.format(
                         certificate_arn, ))
                 time.sleep(.200)
                 i = i + 1
         return {
             'certificate': response['Certificate'],
             'certificate_chain': response['CertificateChain'],
         }
Exemple #2
0
def _get_blind_credentials(credential_ids):
    credentials = []
    with stats.timer('service_batch_get_blind_credentials'):
        for cred in BlindCredential.batch_get(copy.deepcopy(credential_ids)):
            credentials.append({
                'id': cred.id,
                'data_type': 'blind-credential',
                'name': cred.name,
                'enabled': cred.enabled,
                'revision': cred.revision,
                'credential_pairs': cred.credential_pairs,
                'credential_keys': list(cred.credential_keys),
                'metadata': cred.metadata,
                'data_key': cred.data_key,
                'cipher_version': cred.cipher_version,
                'cipher_type': cred.cipher_type
            })
    return credentials
Exemple #3
0
def _get_blind_credentials(credential_ids):
    credentials = []
    with stats.timer('service_batch_get_blind_credentials'):
        for cred in BlindCredential.batch_get(copy.deepcopy(credential_ids)):
            credentials.append({
                'id': cred.id,
                'data_type': 'blind-credential',
                'name': cred.name,
                'enabled': cred.enabled,
                'revision': cred.revision,
                'credential_pairs': cred.credential_pairs,
                'credential_keys': list(cred.credential_keys),
                'metadata': cred.metadata,
                'data_key': cred.data_key,
                'cipher_version': cred.cipher_version,
                'cipher_type': cred.cipher_type
            })
    return credentials
 def _get_cached_certificate_with_key(self, cache_id):
     """
     For the cache id, get the cached response, or, if another thread is in
     the process of issuing the same certificate, wait for the other thread
     to populate the cache.
     """
     with stats.timer('get_cached_certificate_with_key'):
         item = self.cache.get(cache_id)
         # We're the first thread attempting to get this certificate
         if not item:
             return {}
         # A certificate hasn't been issued yet, but since the cache id
         # exists, another thread has requested the certificate.
         if not item.response and item.lock:
             raise CertificateNotReadyError()
         # If the other thread failed to get the certificate, we need to
         # ensure that this thread attempts to fetch a certificate.
         return item.response
 def issue_certificate_with_key(self, cn, validity, san=None):
     """
     Given the string common name, the validity length of the certificate (in
     number of days), and a list of subject alternative names, return a dict
     with the PEM encoded certificate, certificate chain, and private RSA
     key.
     """
     with stats.timer('issue_certificate_with_key'):
         cache_id = self.cache.get_cache_id(cn, validity, san)
         cached_response = self._get_cached_certificate_with_key(cache_id)
         if cached_response:
             stats.incr('get_cached_certificate_with_key.hit')
             logging.debug('Used cached response for {}'.format(cache_id))
             return cached_response
         stats.incr('get_cached_certificate_with_key.miss')
         key = self.generate_key()
         encoded_key = self.encode_key(key)
         if self.settings['self_sign']:
             cert = self.encode_certificate(
                 self.generate_self_signed_certificate(
                     key,
                     cn,
                     validity,
                     san,
                 ))
             return {
                 'certificate': cert,
                 'certificate_chain': cert,
                 'key': encoded_key,
             }
         csr = self.generate_csr(key, cn, san)
         try:
             # set a lock
             self.cache.lock(cache_id)
             arn = self.issue_certificate(self.encode_csr(csr), validity)
             response = self.get_certificate_from_arn(arn)
             response['key'] = encoded_key
             self.cache.set_response(cache_id, response)
         finally:
             # release the lock
             self.cache.release(cache_id)
         return response
Exemple #6
0
def _get_credentials(credential_ids):
    credentials = []
    with stats.timer('service_batch_get_credentials'):
        for cred in Credential.batch_get(copy.deepcopy(credential_ids)):
            data_key = keymanager.decrypt_datakey(
                cred.data_key, encryption_context={'id': cred.id})
            cipher_version = cred.cipher_version
            cipher = CipherManager(data_key, cipher_version)
            _credential_pairs = cipher.decrypt(cred.credential_pairs)
            _credential_pairs = json.loads(_credential_pairs)
            credentials.append({
                'id': cred.id,
                'data_type': 'credential',
                'name': cred.name,
                'enabled': cred.enabled,
                'revision': cred.revision,
                'credential_pairs': _credential_pairs,
                'metadata': cred.metadata
            })
    return credentials
Exemple #7
0
def _get_credentials(credential_ids):
    credentials = []
    with stats.timer('service_batch_get_credentials'):
        for cred in Credential.batch_get(copy.deepcopy(credential_ids)):
            data_key = keymanager.decrypt_datakey(
                cred.data_key,
                encryption_context={'id': cred.id}
            )
            cipher_version = cred.cipher_version
            cipher = CipherManager(data_key, cipher_version)
            _credential_pairs = cipher.decrypt(cred.credential_pairs)
            _credential_pairs = json.loads(_credential_pairs)
            credentials.append({
                'id': cred.id,
                'data_type': 'credential',
                'name': cred.name,
                'enabled': cred.enabled,
                'revision': cred.revision,
                'credential_pairs': _credential_pairs,
                'metadata': cred.metadata
            })
    return credentials
 def issue_certificate(self, csr, validity):
     """
     Given a PEM encoded csr, and a validity for the certificate (in number
     of days), issue a certificate from ACM Private CA, and return the ARN
     of the issued certificate.
     """
     csr = csr.encode(encoding='UTF-8')
     with stats.timer('issue_certificate'):
         client = confidant.clients.get_boto_client('acm-pca')
         response = client.issue_certificate(
             CertificateAuthorityArn=self.settings['arn'],
             Csr=csr,
             SigningAlgorithm=self.settings['signing_algorithm'],
             Validity={
                 'Value': min(validity, self.settings['max_validity_days']),
                 'Type': 'DAYS',
             },
             # Quick/easy idempotent token is just a hash of the csr itself.
             # The token must be 36 chars or less.
             IdempotencyToken=hashlib.sha256(csr).hexdigest()[:36],
         )
         return response['CertificateArn']
Exemple #9
0
    def decorated(*args, **kwargs):
        if not settings.USE_AUTH:
            return f(*args, **kwargs)

        # User suppplied basic auth info
        try:
            kms_auth_data = _get_kms_auth_data()
        except AuthenticationError:
            logging.exception('Failed to authenticate request.')
            return abort(403)
        if kms_auth_data:
            validator = _get_validator()
            _from = validator.extract_username_field(
                kms_auth_data['username'],
                'from',
            )
            _user_type = validator.extract_username_field(
                kms_auth_data['username'],
                'user_type',
            )
            try:
                if _user_type not in settings.KMS_AUTH_USER_TYPES:
                    msg = '{0} is not an allowed user type for KMS auth.'
                    msg = msg.format(_user_type)
                    logging.warning(msg)
                    return abort(403)
                with stats.timer('decrypt_token'):
                    token_data = validator.decrypt_token(
                        kms_auth_data['username'], kms_auth_data['token'])
                logging.debug('Auth request had the following token_data:'
                              ' {0}'.format(token_data))
                msg = 'Authenticated {0} with user_type {1} via kms auth'
                msg = msg.format(_from, _user_type)
                logging.debug(msg)
                g.user_type = _user_type
                g.auth_type = 'kms'
                g.account = account_for_key_alias(token_data['key_alias'])
                g.username = _from
                return f(*args, **kwargs)
            except kmsauth.TokenValidationError:
                logging.exception('Failed to decrypt authentication token.')
                msg = 'Access denied for {0}. Authentication Failed.'
                msg = msg.format(_from)
                logging.warning(msg)
                return abort(403)

        # If not using kms auth, require auth using the user_mod authn module.
        else:
            user_type = 'user'

            if user_mod.is_expired():
                return abort(401)

            if user_mod.is_authenticated():
                try:
                    user_mod.check_authorization()
                except NotAuthorized as e:
                    logging.warning('Not authorized -- {}'.format(e))
                    return abort(403)
                else:
                    # User took an action, extend the expiration time.
                    user_mod.set_expiration()
                    # auth-N and auth-Z are good, call the decorated function
                    g.user_type = user_type
                    g.auth_type = user_mod.auth_type
                    # ensure that the csrf cookie value is set
                    resp = make_response(f(*args, **kwargs))
                    user_mod.set_csrf_token(resp)
                    return resp

            # Not authenticated
            return abort(401)
Exemple #10
0
def decrypt_token(version, user_type, _from, token):
    '''
    Decrypt a token.
    '''
    if (version > app.config['KMS_MAXIMUM_TOKEN_VERSION']
            or version < app.config['KMS_MINIMUM_TOKEN_VERSION']):
        raise TokenDecryptionError('Unacceptable token version.')
    stats.incr('token_version_{0}'.format(version))
    try:
        token_key = '{0}{1}'.format(hashlib.sha256(token).hexdigest(), _from)
    except Exception:
        raise TokenDecryptionError('Authentication error.')
    if token_key not in TOKENS:
        try:
            token = base64.b64decode(token)
            context = {
                # This key is sent to us.
                'to': app.config['AUTH_CONTEXT'],
                # From a service.
                'from': _from
            }
            if version > 1:
                context['user_type'] = user_type
            with stats.timer('kms_decrypt_token'):
                data = kms_client.decrypt(CiphertextBlob=token,
                                          EncryptionContext=context)
            # Decrypt doesn't take KeyId as an argument. We need to verify the
            # correct key was used to do the decryption.
            # Annoyingly, the KeyId from the data is actually an arn.
            key_arn = data['KeyId']
            if user_type == 'service':
                if not valid_service_auth_key(key_arn):
                    raise TokenDecryptionError(
                        'Authentication error (wrong KMS key).')
            elif user_type == 'user':
                if key_arn != get_key_arn(app.config['USER_AUTH_KEY']):
                    raise TokenDecryptionError(
                        'Authentication error (wrong KMS key).')
            else:
                raise TokenDecryptionError(
                    'Authentication error. Unsupported user_type.')
            plaintext = data['Plaintext']
            payload = json.loads(plaintext)
            key_alias = get_key_alias_from_cache(key_arn)
            ret = {'payload': payload, 'key_alias': key_alias}
        except TokenDecryptionError:
            raise
        # We don't care what exception is thrown. For paranoia's sake, fail
        # here.
        except Exception:
            logging.exception('Failed to validate token.')
            raise TokenDecryptionError('Authentication error. General error.')
    else:
        ret = TOKENS[token_key]
    time_format = "%Y%m%dT%H%M%SZ"
    now = datetime.datetime.utcnow()
    try:
        not_before = datetime.datetime.strptime(ret['payload']['not_before'],
                                                time_format)
        not_after = datetime.datetime.strptime(ret['payload']['not_after'],
                                               time_format)
    except Exception:
        logging.exception(
            'Failed to get not_before and not_after from token payload.')
        raise TokenDecryptionError('Authentication error. Missing validity.')
    delta = (not_after - not_before).seconds / 60
    if delta > app.config['AUTH_TOKEN_MAX_LIFETIME']:
        logging.warning('Token used which exceeds max token lifetime.')
        raise TokenDecryptionError(
            'Authentication error. Token lifetime exceeded.')
    if (now < not_before) or (now > not_after):
        logging.warning('Invalid time validity for token.')
        raise TokenDecryptionError(
            'Authentication error. Invalid time validity for token.')
    TOKENS[token_key] = ret
    return TOKENS[token_key]
Exemple #11
0
def get_blind_credentials(credential_ids, metadata_only=False):
    with stats.timer('service_batch_get_blind_credentials'):
        _credential_ids = copy.deepcopy(credential_ids)
        return [cred for cred in BlindCredential.batch_get(_credential_ids)]
Exemple #12
0
def get_credentials(credential_ids):
    with stats.timer('service_batch_get_credentials'):
        _credential_ids = copy.deepcopy(credential_ids)
        return [cred for cred in Credential.batch_get(_credential_ids)]
Exemple #13
0
def decrypt_token(version, user_type, _from, token):
    '''
    Decrypt a token.
    '''
    if (version > app.config['KMS_MAXIMUM_TOKEN_VERSION'] or
            version < app.config['KMS_MINIMUM_TOKEN_VERSION']):
        raise TokenDecryptionError('Unacceptable token version.')
    stats.incr('token_version_{0}'.format(version))
    try:
        token_key = '{0}{1}'.format(
            hashlib.sha256(token).hexdigest(),
            _from
        )
    except Exception:
        raise TokenDecryptionError('Authentication error.')
    if token_key not in TOKENS:
        try:
            token = base64.b64decode(token)
            context = {
                # This key is sent to us.
                'to': app.config['AUTH_CONTEXT'],
                # From a service.
                'from': _from
            }
            if version > 1:
                context['user_type'] = user_type
            with stats.timer('kms_decrypt_token'):
                data = kms_client.decrypt(
                    CiphertextBlob=token,
                    EncryptionContext=context
                )
            # Decrypt doesn't take KeyId as an argument. We need to verify the
            # correct key was used to do the decryption.
            # Annoyingly, the KeyId from the data is actually an arn.
            key_arn = data['KeyId']
            if user_type == 'service':
                if not valid_service_auth_key(key_arn):
                    raise TokenDecryptionError(
                        'Authentication error (wrong KMS key).'
                    )
            elif user_type == 'user':
                if key_arn != get_key_arn(app.config['USER_AUTH_KEY']):
                    raise TokenDecryptionError(
                        'Authentication error (wrong KMS key).'
                    )
            else:
                raise TokenDecryptionError(
                    'Authentication error. Unsupported user_type.'
                )
            plaintext = data['Plaintext']
            payload = json.loads(plaintext)
            key_alias = get_key_alias_from_cache(key_arn)
            ret = {'payload': payload, 'key_alias': key_alias}
        except TokenDecryptionError:
            raise
        # We don't care what exception is thrown. For paranoia's sake, fail
        # here.
        except Exception:
            logging.exception('Failed to validate token.')
            raise TokenDecryptionError('Authentication error. General error.')
    else:
        ret = TOKENS[token_key]
    time_format = "%Y%m%dT%H%M%SZ"
    now = datetime.datetime.utcnow()
    try:
        not_before = datetime.datetime.strptime(
            ret['payload']['not_before'],
            time_format
        )
        not_after = datetime.datetime.strptime(
            ret['payload']['not_after'],
            time_format
        )
    except Exception:
        logging.exception(
            'Failed to get not_before and not_after from token payload.'
        )
        raise TokenDecryptionError('Authentication error. Missing validity.')
    delta = (not_after - not_before).seconds / 60
    if delta > app.config['AUTH_TOKEN_MAX_LIFETIME']:
        logging.warning('Token used which exceeds max token lifetime.')
        raise TokenDecryptionError(
            'Authentication error. Token lifetime exceeded.'
        )
    if (now < not_before) or (now > not_after):
        logging.warning('Invalid time validity for token.')
        raise TokenDecryptionError(
            'Authentication error. Invalid time validity for token.'
        )
    TOKENS[token_key] = ret
    return TOKENS[token_key]
Exemple #14
0
    def decorated(*args, **kwargs):
        if not app.config.get('USE_AUTH'):
            return f(*args, **kwargs)

        # User suppplied basic auth info
        try:
            kms_auth_data = _get_kms_auth_data()
        except TokenVersionError:
            logging.warning('Invalid token version used.')
            return abort(403)
        except AuthenticationError:
            logging.exception('Failed to authenticate request.')
            return abort(403)
        if kms_auth_data:
            try:
                if (kms_auth_data['user_type']
                        not in app.config['KMS_AUTH_USER_TYPES']):
                    msg = '{0} is not an allowed user type for KMS auth.'
                    msg = msg.format(kms_auth_data['user_type'])
                    logging.warning(msg)
                    return abort(403)
                with stats.timer('decrypt_token'):
                    token_data = keymanager.decrypt_token(
                        kms_auth_data['version'], kms_auth_data['user_type'],
                        kms_auth_data['from'], kms_auth_data['token'])
                logging.debug('Auth request had the following token_data:'
                              ' {0}'.format(token_data))
                msg = 'Authenticated {0} with user_type {1} via kms auth'
                msg = msg.format(kms_auth_data['from'],
                                 kms_auth_data['user_type'])
                logging.debug(msg)
                if user_type_has_privilege(kms_auth_data['user_type'],
                                           f.func_name):
                    g.user_type = kms_auth_data['user_type']
                    g.auth_type = 'kms'
                    g.account = account_for_key_alias(token_data['key_alias'])
                    g.username = kms_auth_data['from']
                    return f(*args, **kwargs)
                else:
                    msg = '{0} is not authorized to access {1}.'
                    msg = msg.format(kms_auth_data['from'], f.func_name)
                    logging.warning(msg)
                    return abort(403)
            except keymanager.TokenDecryptionError:
                logging.exception('Failed to decrypt authentication token.')
                msg = 'Access denied for {0}. Authentication Failed.'
                msg = msg.format(kms_auth_data['from'])
                logging.warning(msg)
                return abort(403)

        # If not using kms auth, require auth using the user_mod authn module.
        else:
            user_type = 'user'
            if not user_type_has_privilege(user_type, f.func_name):
                return abort(403)

            if user_mod.is_expired():
                return abort(401)

            if user_mod.is_authenticated():
                try:
                    user_mod.check_authorization()
                except NotAuthorized as e:
                    logging.warning('Not authorized -- ' + e.message)
                    return abort(403)
                else:
                    # User took an action, extend the expiration time.
                    user_mod.set_expiration()
                    # auth-N and auth-Z are good, call the decorated function
                    g.user_type = user_type
                    g.auth_type = user_mod.auth_type
                    # ensure that the csrf cookie value is set
                    resp = make_response(f(*args, **kwargs))
                    user_mod.set_csrf_token(resp)
                    return resp

            # Not authenticated
            return abort(401)

        logging.error('Ran out of authentication methods')
        return abort(403)
Exemple #15
0
    def decorated(*args, **kwargs):
        if not app.config.get('USE_AUTH'):
            return f(*args, **kwargs)

        # User suppplied basic auth info
        try:
            kms_auth_data = _get_kms_auth_data()
        except TokenVersionError:
            logging.warning('Invalid token version used.')
            return abort(403)
        except AuthenticationError:
            logging.exception('Failed to authenticate request.')
            return abort(403)
        if kms_auth_data:
            try:
                if (kms_auth_data['user_type']
                        not in app.config['KMS_AUTH_USER_TYPES']):
                    msg = '{0} is not an allowed user type for KMS auth.'
                    msg = msg.format(kms_auth_data['user_type'])
                    logging.warning(msg)
                    return abort(403)
                with stats.timer('decrypt_token'):
                    token_data = keymanager.decrypt_token(
                        kms_auth_data['version'],
                        kms_auth_data['user_type'],
                        kms_auth_data['from'],
                        kms_auth_data['token']
                    )
                logging.debug('Auth request had the following token_data:'
                              ' {0}'.format(token_data))
                msg = 'Authenticated {0} with user_type {1} via kms auth'
                msg = msg.format(
                    kms_auth_data['from'],
                    kms_auth_data['user_type']
                )
                logging.debug(msg)
                if user_type_has_privilege(
                        kms_auth_data['user_type'],
                        f.func_name):
                    g.user_type = kms_auth_data['user_type']
                    g.auth_type = 'kms'
                    g.account = account_for_key_alias(token_data['key_alias'])
                    g.username = kms_auth_data['from']
                    return f(*args, **kwargs)
                else:
                    msg = '{0} is not authorized to access {1}.'
                    msg = msg.format(kms_auth_data['from'], f.func_name)
                    logging.warning(msg)
                    return abort(403)
            except keymanager.TokenDecryptionError:
                logging.exception('Failed to decrypt authentication token.')
                msg = 'Access denied for {0}. Authentication Failed.'
                msg = msg.format(kms_auth_data['from'])
                logging.warning(msg)
                return abort(403)

        # If not using kms auth, require auth using the user_mod authn module.
        else:
            user_type = 'user'
            if not user_type_has_privilege(user_type, f.func_name):
                return abort(403)

            if user_mod.is_expired():
                return abort(401)

            if user_mod.is_authenticated():
                try:
                    user_mod.check_authorization()
                except NotAuthorized as e:
                    logging.warning('Not authorized -- ' + e.message)
                    return abort(403)
                else:
                    # User took an action, extend the expiration time.
                    user_mod.set_expiration()
                    # auth-N and auth-Z are good, call the decorated function
                    g.user_type = user_type
                    g.auth_type = user_mod.auth_type
                    # ensure that the csrf cookie value is set
                    resp = make_response(f(*args, **kwargs))
                    user_mod.set_csrf_token(resp)
                    return resp

            # Not authenticated
            return abort(401)

        logging.error('Ran out of authentication methods')
        return abort(403)