예제 #1
0
    def _second_payload(self, server_payload):
        """Return the second and final SASL payload."""
        if not server_payload:
            raise PyMongoAuthAwsError(
                'MONGODB-AWS failed: server payload empty')

        server_payload = self.bson_decode(server_payload)
        server_nonce = server_payload['s']
        if len(server_nonce) != 64 or not server_nonce.startswith(
                self._client_nonce):
            raise PyMongoAuthAwsError("Server returned an invalid nonce.")

        sts_host = server_payload['h']
        payload = _aws_auth_header(self._credentials, server_nonce, sts_host)
        return self.binary_type()(self.bson_encode(payload))
예제 #2
0
def _get_region(sts_host):
    """Return the AWS region to use for the given host."""
    # Drivers must also validate that the host is greater than 0 and
    # less than or equal to 255 bytes per RFC 1035.
    if not sts_host or len(sts_host) > 255:
        raise PyMongoAuthAwsError(
            "Server returned an invalid sts host: %s" % (sts_host,))

    parts = sts_host.split('.')
    if len(parts) == 1 or sts_host == 'sts.amazonaws.com':
        return 'us-east-1'  # Default

    # Check for empty labels (eg "invalid..host" or ".invalid.host").
    if not all(parts):
        raise PyMongoAuthAwsError(
            "Server returned an invalid sts host: %s" % (sts_host,))

    return parts[1]
예제 #3
0
    def step(self, server_payload):
        """Step through the SASL conversation.

        :Parameters:
          - `server_payload`: The server payload (SASL challenge). Must be a
            bytes-like object.

        :Returns:
          The response payload for the next SASL step.

        :Raises:
          :class:`~pymongo_auth_aws.PyMongoAuthAwsError` on error.
        """
        self._step += 1
        if self._step == 1:
            return self._first_payload()
        elif self._step == 2:
            return self._second_payload(server_payload)
        else:
            raise PyMongoAuthAwsError('MONGODB-AWS failed: too many steps')
        pass
예제 #4
0
def _aws_temp_credentials():
    """Construct temporary MONGODB-AWS credentials."""
    access_key = os.environ.get('AWS_ACCESS_KEY_ID')
    secret_key = os.environ.get('AWS_SECRET_ACCESS_KEY')
    if access_key and secret_key:
        return AwsCredential(
            access_key, secret_key, os.environ.get('AWS_SESSION_TOKEN'))
    # If the environment variable
    # AWS_CONTAINER_CREDENTIALS_RELATIVE_URI is set then drivers MUST
    # assume that it was set by an AWS ECS agent and use the URI
    # http://169.254.170.2/$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI to
    # obtain temporary credentials.
    relative_uri = os.environ.get('AWS_CONTAINER_CREDENTIALS_RELATIVE_URI')
    if relative_uri is not None:
        try:
            res = requests.get(_AWS_REL_URI+relative_uri,
                               timeout=_AWS_HTTP_TIMEOUT)
            res_json = res.json()
        except (ValueError, requests.exceptions.RequestException) as exc:
            raise PyMongoAuthAwsError(
                'temporary MONGODB-AWS credentials could not be obtained, '
                'error: %s' % (exc,))
    else:
        # If the environment variable AWS_CONTAINER_CREDENTIALS_RELATIVE_URI is
        # not set drivers MUST assume we are on an EC2 instance and use the
        # endpoint
        # http://169.254.169.254/latest/meta-data/iam/security-credentials
        # /<role-name>
        # whereas role-name can be obtained from querying the URI
        # http://169.254.169.254/latest/meta-data/iam/security-credentials/.
        try:
            # Get token
            headers = {'X-aws-ec2-metadata-token-ttl-seconds': "30"}
            res = requests.put(_AWS_EC2_URI+'latest/api/token',
                               headers=headers, timeout=_AWS_HTTP_TIMEOUT)
            token = res.content
            # Get role name
            headers = {'X-aws-ec2-metadata-token': token}
            res = requests.get(_AWS_EC2_URI+_AWS_EC2_PATH, headers=headers,
                               timeout=_AWS_HTTP_TIMEOUT)
            role = res.text
            # Get temp creds
            res = requests.get(_AWS_EC2_URI+_AWS_EC2_PATH+role,
                               headers=headers, timeout=_AWS_HTTP_TIMEOUT)
            res_json = res.json()
        except (ValueError, requests.exceptions.RequestException) as exc:
            raise PyMongoAuthAwsError(
                'temporary MONGODB-AWS credentials could not be obtained, '
                'error: %s' % (exc,))

    try:
        temp_user = res_json['AccessKeyId']
        temp_password = res_json['SecretAccessKey']
        token = res_json['Token']
    except KeyError:
        # If temporary credentials cannot be obtained then drivers MUST
        # fail authentication and raise an error.
        raise PyMongoAuthAwsError(
            'temporary MONGODB-AWS credentials could not be obtained')

    return AwsCredential(temp_user, temp_password, token)
예제 #5
0
def _aws_temp_credentials():
    """Construct temporary MONGODB-AWS credentials."""
    global _cached_credentials
    # Store the variable locally for safe threaded access.
    creds = _cached_credentials

    access_key = os.environ.get('AWS_ACCESS_KEY_ID')
    secret_key = os.environ.get('AWS_SECRET_ACCESS_KEY')
    if access_key and secret_key:
        return AwsCredential(access_key, secret_key,
                             os.environ.get('AWS_SESSION_TOKEN'))

    # Check to see if we have valid credentials.
    if creds and creds.expiration is not None:
        now_utc = datetime.now(utc)
        exp_utc = parse_to_aware_datetime(creds.expiration)
        if (exp_utc - now_utc).total_seconds() >= _credential_buffer_seconds:
            return creds

    # Check if environment variables exposed by IAM Roles for Service Accounts (IRSA) are present.
    # See https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html for details.
    irsa_web_id_file = os.getenv('AWS_WEB_IDENTITY_TOKEN_FILE')
    irsa_role_arn = os.getenv('AWS_ROLE_ARN')
    if irsa_web_id_file and irsa_role_arn:
        try:
            with open(irsa_web_id_file) as f:
                irsa_web_id_token = f.read()
            role_session_name = os.getenv('AWS_ROLE_SESSION_NAME',
                                          'pymongo-auth-aws')
            creds = _irsa_assume_role(irsa_role_arn, irsa_web_id_token,
                                      role_session_name)
            _cached_credentials = creds
            return creds
        except Exception as exc:
            raise PyMongoAuthAwsError(
                'temporary MONGODB-AWS credentials could not be obtained, '
                'error: %s' % (exc, ))

    # If the environment variable
    # AWS_CONTAINER_CREDENTIALS_RELATIVE_URI is set then drivers MUST
    # assume that it was set by an AWS ECS agent and use the URI
    # http://169.254.170.2/$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI to
    # obtain temporary credentials.
    relative_uri = os.environ.get('AWS_CONTAINER_CREDENTIALS_RELATIVE_URI')
    if relative_uri is not None:
        try:
            res = requests.get(_AWS_REL_URI + relative_uri,
                               timeout=_AWS_HTTP_TIMEOUT)
            res_json = res.json()
        except (ValueError, requests.exceptions.RequestException) as exc:
            raise PyMongoAuthAwsError(
                'temporary MONGODB-AWS credentials could not be obtained, '
                'error: %s' % (exc, ))
    else:
        # If the environment variable AWS_CONTAINER_CREDENTIALS_RELATIVE_URI is
        # not set drivers MUST assume we are on an EC2 instance and use the
        # endpoint
        # http://169.254.169.254/latest/meta-data/iam/security-credentials
        # /<role-name>
        # whereas role-name can be obtained from querying the URI
        # http://169.254.169.254/latest/meta-data/iam/security-credentials/.
        try:
            # Get token
            headers = {'X-aws-ec2-metadata-token-ttl-seconds': "30"}
            res = requests.put(_AWS_EC2_URI + 'latest/api/token',
                               headers=headers,
                               timeout=_AWS_HTTP_TIMEOUT)
            token = res.content
            # Get role name
            headers = {'X-aws-ec2-metadata-token': token}
            res = requests.get(_AWS_EC2_URI + _AWS_EC2_PATH,
                               headers=headers,
                               timeout=_AWS_HTTP_TIMEOUT)
            role = res.text
            # Get temp creds
            res = requests.get(_AWS_EC2_URI + _AWS_EC2_PATH + role,
                               headers=headers,
                               timeout=_AWS_HTTP_TIMEOUT)
            res_json = res.json()
        except (ValueError, requests.exceptions.RequestException) as exc:
            raise PyMongoAuthAwsError(
                'temporary MONGODB-AWS credentials could not be obtained, '
                'error: %s' % (exc, ))

    # See https://docs.aws.amazon.com/cli/latest/reference/sts/assume-role.html#examples for expected result format.
    try:
        temp_user = res_json['AccessKeyId']
        temp_password = res_json['SecretAccessKey']
        session_token = res_json['Token']
        expiration = res_json['Expiration']
    except KeyError:
        # If temporary credentials cannot be obtained then drivers MUST
        # fail authentication and raise an error.
        raise PyMongoAuthAwsError(
            'temporary MONGODB-AWS credentials could not be obtained')

    creds = AwsCredential(temp_user, temp_password, session_token, expiration)
    _cached_credentials = creds
    return creds