Neo4j has worked closely with AWS engineering for years, optimizing our integration with AWS PrivateLink and steadily adding to our AWS partner credentials and competencies. In 2023, we attained the AWS PrivateLink Service Ready designation, and we recently published a detailed blog post on setting up Neo4j Aura with AWS PrivateLink. We offer our product on AWS both as a self-managed version and SaaS.
AWS recently announced a new feature that allows users to access resources across VPCs using AWS PrivateLink and VPC Lattice. We’ve been collaborating with AWS on this feature for several months, and in this post we’ll explore how it simplifies PrivateLink setup and discuss how we’re planning to use it in the future.
An Innovative New Capability for PrivateLink and Lattice
AWS’s new capability for AWS PrivateLink and VPC Lattice provides resource owners and consumers with private, pointed, and unidirectional connectivity to individual resources across VPCs, accounts, and even on-premises environments. This connectivity is entirely private, ensuring traffic stays off the public internet and leverages the security and privacy benefits inherent to AWS PrivateLink and VPC Lattice.
For Neo4j, a leading graph database ISV on AWS, this feature is particularly transformative. Resource owners can now share specific Neo4j resources — whether deployed on EC2 or EKS — without requiring a load balancer (ALB/NLB) on their side or exposing other resources within the same VPC. Consumers, in turn, can privately access the shared Neo4j database via VPC endpoints, making it ideal for graph analytics, improving AI/ML accuracy, and building an intelligent agent. This simplified, secure setup is a game-changer for Neo4j customers looking to collaborate across AWS environments while maintaining strict security and privacy standards.
The Advantage of Using PrivateLink and Lattice for Secure Database Sharing
Let’s assume you have Neo4j Enterprise Edition installed within a VPC in your AWS account, either deployed on an EC2 instance or within an EKS cluster. For organizations needing to provide secure, seamless access to this Neo4j database from another AWS account, AWS’s PrivateLink and VPC Lattice service offers a robust solution. Using AWS PrivateLink and VPC Lattice provides a secure, straightforward method to share Neo4j database resources in a few simple steps. By leveraging VPC endpoints and AWS’s built-in network controls like security groups, resource owners can control data access securely without needing extensive infrastructure changes or private connectivity configurations.
The environment we’re setting up will look like this:
Setting Up AWS PrivateLink and VPC Lattice for Neo4j Database Sharing
We’ll cover the setup process for both Neo4j providers (resource owners) and Neo4j consumers (those accessing the shared resources).
Part 1: Steps for the Neo4j Provider (Resource Owner)
These steps guide the Neo4j provider in setting up the foundational infrastructure to securely share a Neo4j resource. The process involves creating a secure gateway, defining the resource to be shared, and granting access to the consumer through AWS Resource Access Manager.
Step 1: Create a Resource-Gateway
Set up a Resource-Gateway in your VPC as the entry point for traffic to the Neo4j database server. This gateway acts as a secure ingress point for external VPC access.
Step 2: Define the Resource-Configuration
The Resource-Configuration is a logical representation of the Neo4j database or resource that will be shared. This configuration specifies the resource details, including the IP address or domain and the range of ports required for connectivity.
Step 3: Share the Resource-Configuration via Resource Access Manager (RAM)
Use AWS Resource Access Manager to share the Resource-Configuration with the consumer’s AWS account. This step securely grants access to the Neo4j resource, ensuring that only authorized consumers can interact with the database.
Here are the scripts to create all the necessary resources and share them across two AWS accounts:
#!/bin/bash echo "Here are the environment variables to share a resource:" resource_owner_aws_account_id="xxxx1974" resource_consumer_aws_account_id="xxxx2298" aws_region="us-west-2" vpc_id="vpc-01234" subnet_id="subnet-01234" security_group_ids="sg-01234" resource_gateway_name="neo4j-owner-rgw" neo4j_resource_ip_addr="10.0.0.221" neo4j_port_ranges="7474-7687" resource_config_name="neo4j-rcgf" resource_share_name="neo4j-db-share" head -n 15 $0 echo read -p "Press any key to continue... " -n1 -s echo "Step 1: Create the Resource Gateway" echo "Executing: aws vpc-lattice create-resource-gateway \ --vpc-identifier \"$vpc_id\" \ --subnet-ids \"$subnet_id\" \ --security-group-ids \"$security_group_ids\" \ --name \"$resource_gateway_name\" \ --region \"$aws_region\"" echo read -p "Press any key to continue... " -n1 -s create_response=$(aws vpc-lattice create-resource-gateway \ --vpc-identifier "$vpc_id" \ --subnet-ids "$subnet_id" \ --security-group-ids "$security_group_ids" \ --name "$resource_gateway_name" \ --region "$aws_region") resource_gateway_id=$(echo "$create_response" | jq -r '.id') echo "Step 2: Wait for Resource Gateway to become ACTIVE" status="" echo "Executing: aws vpc-lattice get-resource-gateway \ --resource-gateway-identifier "$resource_gateway_id" \ --region "$aws_region"" echo read -p "Press any key to continue... " -n1 -s while [ "$status" != "ACTIVE" ]; do get_response=$(aws vpc-lattice get-resource-gateway \ --resource-gateway-identifier "$resource_gateway_id" \ --region "$aws_region") status=$(echo "$get_response" | jq -r '.status') sleep 20 done echo "Step 3: Create the Resource Configuration" echo "Executing: aws vpc-lattice create-resource-configuration \ --type SINGLE \ --resource-configuration-definition "{ \"ipResource\": { \"ipAddress\": \"$neo4j_resource_ip_addr\" } }" \ --port-ranges "$neo4j_port_ranges" \ --protocol TCP \ --resource-gateway-identifier "$resource_gateway_id" \ --name "$resource_config_name" \ --region "$aws_region"" echo read -p "Press any key to continue... " -n1 -s echo create_config_response=$(aws vpc-lattice create-resource-configuration \ --type SINGLE \ --resource-configuration-definition "{ \"ipResource\": { \"ipAddress\": \"$neo4j_resource_ip_addr\" } }" \ --port-ranges "$neo4j_port_ranges" \ --protocol TCP \ --resource-gateway-identifier "$resource_gateway_id" \ --name "$resource_config_name" \ --region "$aws_region") resource_config_id=$(echo "$create_config_response" | jq -r '.id') echo "Step 4: Create the Resource Share" echo echo "Executing: aws ram create-resource-share \ --principals "$resource_consumer_aws_account_id" \ --resource-arns "arn:aws:vpc-lattice:$aws_region:$resource_owner_aws_account_id:resourceconfiguration/$resource_config_id" \ --name "$resource_share_name" \ --region "$aws_region"" echo read -p "Press any key to continue... " -n1 -s echo aws ram create-resource-share \ --principals "$resource_consumer_aws_account_id" \ --resource-arns "arn:aws:vpc-lattice:$aws_region:$resource_owner_aws_account_id:resourceconfiguration/$resource_config_id" \ --name "$resource_share_name" \ --region "$aws_region" # Final message echo "The Neo4j resource from the $neo4j_resource_ip_addr IP address is sent as a shared resource invitation to the AWS Account: $resource_consumer_aws_account_id"
Part 2: Steps for the Neo4j Consumer (Resource Recipient)
This section outlines how consumers can configure their environment to access the shared Neo4j resource. It includes creating a service network, creating secure endpoints, and associating their VPC with the shared resource to enable private access.
Step 1: Create a Service-Network
On the consumer side, create a Service-Network to logically group all resources associated with the Neo4j database.
Step 2: Add the Neo4j Resource to the Service-Network
Set up a Service-Network-Resource-Association to link the Neo4j resource with the Service-Network, representing the Neo4j database resource in the consumer’s network.
Step 3: Establish a Service-Network VPC Endpoint
Create a Service-Network Endpoint (a specialized VPC endpoint) to access the resources within the Service-Network from the consumer’s VPC. This endpoint provides secure, direct access to the Neo4j resource without needing additional connectivity setups like VPN.
Step 4: Associate the Consumer’s VPC With the Service-Network
Finalize access by creating a Service-Network-VPC-Association, enabling the consumer’s VPC to communicate with resources within the Service-Network.
Here is the script to accept the resource invite and create a service network and VPC endpoint:
#!/bin/bash echo "Here are the environment variables to consume a shared resource:" aws_profile="neo4j-consumer-profile" vpc_id="vpc-1234" subnet_ids="subnet-1234 subnet-1234" security_group_id="sg-1234" resource_gateway_id="rgw-1234" resource_config_name="neo4j-rcgf" aws_region="us-west-2" service_network_name="neo4j-share-sn" head -n 13 $0 echo read -p "Press any key to continue... " -n1 -s echo "Step 1: Get Resource Share Invitations" echo echo "Executing: AWS_PROFILE=$aws_profile aws ram get-resource-share-invitations --region $aws_region" echo read -p "Press any key to continue... " -n1 -s invites=$(AWS_PROFILE=$aws_profile aws ram get-resource-share-invitations --region $aws_region) pending_invite_arn=$(echo "$invites" | jq -r '.resourceShareInvitations[] | select(.status=="PENDING") | .resourceShareInvitationArn') echo "Step 2: Accept the Resource Share Invitation" echo if [[ -n "$pending_invite_arn" ]]; then echo echo "Executing: AWS_PROFILE=$aws_profile aws ram accept-resource-share-invitation --resource-share-invitation-arn \"$pending_invite_arn\" --region $aws_region" echo read -p "Press any key to continue... " -n1 -s AWS_PROFILE=$aws_profile aws ram accept-resource-share-invitation --resource-share-invitation-arn "$pending_invite_arn" --region $aws_region echo "Resource share invitation accepted successfully." else echo "No pending resource share invitation found. Proceeding with the assumption it was accepted via the AWS console." fi echo "Step 3: Create Service Network" echo echo "Executing: AWS_PROFILE=$aws_profile aws vpc-lattice create-service-network --name $service_network_name --region $aws_region" echo read -p "Press any key to continue... " -n1 -s service_network_response=$(AWS_PROFILE=$aws_profile aws vpc-lattice create-service-network --name $service_network_name --region $aws_region) neo4j_serv_nw_id=$(echo "$service_network_response" | jq -r '.id') neo4j_serv_nw_arn=$(echo "$service_network_response" | jq -r '.arn') echo "Step 4: List Resource Configurations and Filter for the Target Config" echo echo "Executing: AWS_PROFILE=$aws_profile aws vpc-lattice list-resource-configurations --region $aws_region" echo read -p "Press any key to continue... " -n1 -s resource_configs=$(AWS_PROFILE=$aws_profile aws vpc-lattice list-resource-configurations --region $aws_region) res_conf_id=$(echo "$resource_configs" | jq -r ".items[] | select(.resourceGatewayId==\"$resource_gateway_id\" and .name==\"$resource_config_name\") | .id") echo "Step 5: Add Resource Configuration to the Service Network" if [[ -n "$res_conf_id" ]]; then echo echo "Executing: AWS_PROFILE=$aws_profile aws vpc-lattice create-service-network-resource-association --resource-configuration-identifier $res_conf_id --service-network-identifier $neo4j_serv_nw_id --region $aws_region" echo read -p "Press any key to continue... " -n1 -s snra_response=$(AWS_PROFILE=$aws_profile aws vpc-lattice create-service-network-resource-association --resource-configuration-identifier "$res_conf_id" --service-network-identifier "$neo4j_serv_nw_id" --region $aws_region) snra_domain=$(echo "$snra_response" | jq -r '.dnsEntry.domainName') echo "SNRA Domain: " $snra_domain else echo "Resource configuration ID not found. Please check the input parameters." exit 1 fi echo "Step 6: Create VPC Service Network Endpoint" echo echo "Executing: AWS_PROFILE=$aws_profile aws ec2 create-vpc-endpoint --vpc-endpoint-type ServiceNetwork --vpc-id $vpc_id --subnet-ids $subnet_ids --service-network-arn $neo4j_serv_nw_arn --security-group-id $security_group_id --region $aws_region" echo read -p "Press any key to continue... " -n1 -s vpc_endpoint_response=$(AWS_PROFILE=$aws_profile aws ec2 create-vpc-endpoint --vpc-endpoint-type ServiceNetwork --vpc-id "$vpc_id" --subnet-ids $subnet_ids --service-network-arn "$neo4j_serv_nw_arn" --security-group-id "$security_group_id" --region "$aws_region") vpc_endpoint_id=$(echo "$vpc_endpoint_response" | jq -r '.VpcEndpoints[0].VpcEndpointId') # Combine VPC Endpoint ID and Service Network Resource Association Domain endpoint_domain="${vpc_endpoint_id}-${snra_domain}" echo echo "Endpoint Domain Name: $endpoint_domain"
Here is the GitHub repository of the above scripts.
Cross-VPC resource access using AWS PrivateLink and VPC Lattice offers a flexible, secure, and streamlined approach for sharing Neo4j resources across accounts. With this capability, Neo4j providers and consumers can set up and access shared resources quickly, enhancing productivity and collaboration.
Video Demo
Here’s a video that walks you through the entire setup:
Exploring Future Integrations
In this blog post, we demonstrated how to set up a Neo4j EE cluster. With our database populated, we showed how to set up a simple application that reads from Neo4j over the AWS PrivateLink connection.
This Neo4j EE setup on EKS is similar to how Neo4j Aura runs. That product runs in a Neo4j owned AWS account. Customer clusters on AWS use EKS, managed by a Kubernetes Operator that Neo4j built.
In future work, we plan to explore VPC Lattice more and develop a better understanding of how it works with AWS PrivateLink VPC endpoints.
This blog post and demo are a step forward in exploring how Neo4j can leverage service-network VPC endpoints. We’re currently evaluating how this feature might be included in our Neo4j Aura offering. In the meantime, you can get started with Neo4j Aura and PrivateLink by following this guide.
If you have any questions, please reach out to ecosystem@neo4j.com. If you’d like to learn more about how we work with AWS, check out this page: https://neo4j.com/amazon.