AWS Security ChangesHomeSearch

AWS neptune documentation change

Service: neptune · 2026-04-04 · Documentation low

File: neptune/latest/userguide/iam-auth-connecting-python.md

Summary

Complete rewrite of Python IAM authentication documentation to use boto3 neptunedata client instead of manual SigV4 signing, with updated examples for Gremlin, openCypher, and SPARQL queries, plus sections on temporary credentials and AWS Lambda integration

Security assessment

The change promotes better security practices by recommending the official boto3 client which handles SigV4 signing automatically, reducing the risk of implementation errors. It emphasizes using IAM policies, temporary credentials, and avoiding embedded credentials. However, there's no evidence this addresses a specific security vulnerability - it's a documentation improvement for better security practices.

Diff

diff --git a/neptune/latest/userguide/iam-auth-connecting-python.md b/neptune/latest/userguide/iam-auth-connecting-python.md
index 70e48d30a..615347fc2 100644
--- a//neptune/latest/userguide/iam-auth-connecting-python.md
+++ b//neptune/latest/userguide/iam-auth-connecting-python.md
@@ -4,0 +5,2 @@
+PrerequisitesConnecting with default credentialsUsing temporary credentialsUsing with AWS Lambda
+
@@ -7 +9 @@
-This section shows an example program written in Python that illustrates how to work with Signature Version 4 for Amazon Neptune. This example is based on the examples in the [Signature Version 4 Signing Process](https://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html) section in the _Amazon Web Services General Reference_.
+The `boto3` `neptunedata` client provides the simplest way to connect to an IAM-enabled Neptune database from Python. The client handles Signature Version 4 signing automatically, so you don't need to sign requests yourself.
@@ -9 +11 @@ This section shows an example program written in Python that illustrates how to
-To work with this example program, you need the following:
+## Prerequisites
@@ -11 +13 @@ To work with this example program, you need the following:
-  * Python 3.x installed on your computer, which you can get from the [Python site](https://www.python.org/downloads/). These programs were tested using Python 3.6.
+  * Python 3.x
@@ -13 +15 @@ To work with this example program, you need the following:
-  * The [Python requests library](https://pypi.python.org/pypi/requests), which is used in the example script to make web requests. A convenient way to install Python packages is to use `pip`, which gets packages from the Python package index site. You can then install `requests` by running `pip install requests` at the command line.
+  * The `boto3` library: `pip install boto3`
@@ -15 +17 @@ To work with this example program, you need the following:
-  * An access key (access key ID and secret access key) in environment variables named `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. As a best practice, we recommend that you do _not_ embed credentials in code. For more information, see [Best Practices for AWS accounts](https://docs.aws.amazon.com//accounts/latest/reference/best-practices.html) in the _AWS Account Management Reference Guide_.
+  * AWS credentials configured through any standard method (environment variables, AWS config file, instance profile, or Lambda execution role)
@@ -17 +19 @@ To work with this example program, you need the following:
-The Region of your Neptune DB cluster in an environment variable named `SERVICE_REGION`.
+  * An IAM policy that allows `neptune-db:*` actions on your Neptune cluster
@@ -19 +20,0 @@ The Region of your Neptune DB cluster in an environment variable named `SERVICE_
-If you are using temporary credentials, you must specify `AWS_SESSION_TOKEN` in addition to `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `SERVICE_REGION`.
@@ -21 +21,0 @@ If you are using temporary credentials, you must specify `AWS_SESSION_TOKEN` in
-###### Note
@@ -23 +22,0 @@ If you are using temporary credentials, you must specify `AWS_SESSION_TOKEN` in
-If you are using temporary credentials, they expire after a specified interval, _including the session token_.
@@ -25 +24 @@ If you are using temporary credentials, they expire after a specified interval,
-You must update your session token when you request new credentials. For more information, see [Using Temporary Security Credentials to Request Access to AWS Resources](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html).
+## Connecting with default credentials
@@ -26,0 +26 @@ You must update your session token when you request new credentials. For more in
+This approach works when `boto3` can resolve credentials automatically, such as on Amazon Elastic Compute Cloud instances with instance profiles, in AWS Lambda functions, or with configured AWS profiles.
@@ -27,0 +28 @@ You must update your session token when you request new credentials. For more in
+Replace the endpoint URL with your Neptune cluster endpoint.
@@ -28,0 +30 @@ You must update your session token when you request new credentials. For more in
+**Gremlin and openCypher**
@@ -30 +31,0 @@ You must update your session token when you request new credentials. For more in
-The following example shows how to make signed requests to Neptune using Python. The request makes a either a GET or POST request. Authentication information is passed using the `Authorization` request header.
@@ -32 +33,2 @@ The following example shows how to make signed requests to Neptune using Python.
-This example also works as an AWS Lambda function. For more information, see [Setting Up AWS Lambda for Neptune IAM Authentication](./iam-auth-temporary-credentials.html#iam-auth-temporary-credentials-lambda).
+    import boto3
+    from botocore.config import Config
@@ -34 +36 @@ This example also works as an AWS Lambda function. For more information, see [Se
-###### To make signed requests to the Gremlin and SPARQL Neptune endpoints
+    cfg = Config(retries={"total_max_attempts": 1, "mode": "standard"}, read_timeout=None)
@@ -36 +38,3 @@ This example also works as an AWS Lambda function. For more information, see [Se
-  1. Create a new file named `neptunesigv4.py`, and open it in a text editor.
+    neptune = boto3.client('neptunedata', config=cfg,
+        region_name='us-east-1',
+        endpoint_url='https://your-neptune-endpoint:8182')
@@ -38 +42,3 @@ This example also works as an AWS Lambda function. For more information, see [Se
-  2. Copy the following code and paste it into the `neptunesigv4.py` file.
+    # Gremlin
+    resp = neptune.execute_gremlin_query(gremlinQuery='g.V().limit(1)')
+    print(resp['result'])
@@ -40 +46,5 @@ This example also works as an AWS Lambda function. For more information, see [Se
-        # Amazon Neptune version 4 signing example (version v3)
+    # openCypher
+    resp = neptune.execute_open_cypher_query(openCypherQuery='MATCH (n) RETURN n LIMIT 1')
+    print(resp['results'])
+
+**SPARQL**
@@ -42,6 +52 @@ This example also works as an AWS Lambda function. For more information, see [Se
-    # The following script requires python 3.6+
-    #    (sudo yum install python36 python36-virtualenv python36-pip)
-    # => the reason is that we're using urllib.parse() to manually encode URL
-    #    parameters: the problem here is that SIGV4 encoding requires whitespaces
-    #    to be encoded as %20 rather than not or using '+', as done by previous/
-    #    default versions of the library.
+The `boto3` `neptunedata` client does not currently support SPARQL. To send SPARQL queries to an IAM-enabled Neptune database, sign requests manually by using Signature Version 4 with the `botocore` request-signing utilities and the `requests` library (`pip install boto3 requests`).
@@ -50,6 +55 @@ This example also works as an AWS Lambda function. For more information, see [Se
-    # See: https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html
-    import sys, datetime, hashlib, hmac
-    import requests  # pip3 install requests
-    import urllib
-    import os
-    import json
+    import requests
@@ -58,218 +58 @@ This example also works as an AWS Lambda function. For more information, see [Se
-    from botocore.credentials import ReadOnlyCredentials
-    from types import SimpleNamespace
-    from argparse import RawTextHelpFormatter
-    from argparse import ArgumentParser
-    
-    # Configuration. https is required.
-    protocol = 'https'
-    
-    # The following lines enable debugging at httplib level (requests->urllib3->http.client)
-    # You will see the REQUEST, including HEADERS and DATA, and RESPONSE with HEADERS but without DATA.
-    #
-    # The only thing missing will be the response.body which is not logged.
-    #
-    # import logging
-    # from http.client import HTTPConnection
-    # HTTPConnection.debuglevel = 1
-    # logging.basicConfig()
-    # logging.getLogger().setLevel(logging.DEBUG)
-    # requests_log = logging.getLogger("requests.packages.urllib3")
-    # requests_log.setLevel(logging.DEBUG)
-    # requests_log.propagate = True
-    
-    
-    # Read AWS access key from env. variables. Best practice is NOT
-    # to embed credentials in code.
-    access_key = os.getenv('AWS_ACCESS_KEY_ID', '')
-    secret_key = os.getenv('AWS_SECRET_ACCESS_KEY', '')
-    region = os.getenv('SERVICE_REGION', '')
-    
-    # AWS_SESSION_TOKEN is optional environment variable. Specify a session token only if you are using temporary
-    # security credentials.
-    session_token = os.getenv('AWS_SESSION_TOKEN', '')
-    
-    ### Note same script can be used for AWS Lambda (runtime = python3.6).
-    ## Steps to use this python script for AWS Lambda
-    # 1. AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN and AWS_REGION variables are already part of Lambda's Execution environment
-    #    No need to set them up explicitly.
-    # 3. Create Lambda deployment package https://docs.aws.amazon.com/lambda/latest/dg/lambda-python-how-to-create-deployment-package.html
-    # 4. Create a Lambda function in the same VPC and assign an IAM role with neptune access
-    
-    def lambda_handler(event, context):
-        # sample_test_input = {
-        #     "host": "END_POINT:8182",
-        #     "method": "GET",
-        #     "query_type": "gremlin",
-        #     "query": "g.V().count()"
-        # }
-    
-        # Lambda uses AWS_REGION instead of SERVICE_REGION
-        global region
-        region = os.getenv('AWS_REGION', '')
-    
-        host = event['host']
-        method = event['method']
-        query_type = event['query_type']
-        query =  event['query']
-    
-        return make_signed_request(host, method, query_type, query)
-    
-    def validate_input(method, query_type):
-        # Supporting GET and POST for now:
-        if (method != 'GET' and method != 'POST'):
-            print('First parameter must be "GET" or "POST", but is "' + method + '".')
-            sys.exit()
-    
-        # SPARQL UPDATE requires POST
-        if (method == 'GET' and query_type == 'sparqlupdate'):
-            print('SPARQL UPDATE is not supported in GET mode. Please choose POST.')
-            sys.exit()
-    
-    def get_canonical_uri_and_payload(query_type, query, method):
-        # Set the stack and payload depending on query_type.
-        if (query_type == 'sparql'):
-            canonical_uri = '/sparql/'
-            payload = {'query': query}
-    
-        elif (query_type == 'sparqlupdate'):
-            canonical_uri = '/sparql/'
-            payload = {'update': query}
-    
-        elif (query_type == 'gremlin'):
-            canonical_uri = '/gremlin/'
-            payload = {'gremlin': query}
-            if (method == 'POST'):
-                payload = json.dumps(payload)
-    
-        elif (query_type == 'openCypher'):
-            canonical_uri = '/openCypher/'
-            payload = {'query': query}
-    
-        elif (query_type == "loader"):
-            canonical_uri = "/loader/"
-            payload = query
-    
-        elif (query_type == "status"):
-            canonical_uri = "/status/"
-            payload = {}
-    
-        elif (query_type == "gremlin/status"):
-            canonical_uri = "/gremlin/status/"
-            payload = {}
-    
-        elif (query_type == "openCypher/status"):
-            canonical_uri = "/openCypher/status/"
-            payload = {}
-    
-        elif (query_type == "sparql/status"):
-            canonical_uri = "/sparql/status/"
-            payload = {}
-    
-        else:
-            print(
-                'Third parameter should be from ["gremlin", "sparql", "sparqlupdate", "loader", "status] but is "' + query_type + '".')
-            sys.exit()
-        ## return output as tuple
-        return canonical_uri, payload
-