In this guide, we will demonstrate how to set up CloudQuery for customizable Attack Surface Management (ASM) and how to get started with utilizing graph visualization to execute security queries. With cloud infrastructure and security, graph visualization can be used effectively to understand relationships between assets, attack paths, and attack surface to aid with Attack Surface Management and improving security posture of your organization.
Once we're done with the post, you'll be able to create and build your own Attack Surface Management and Graph Visualization queries and relationships in Neo4j similar to the example graph of AWS IAM Users, their permissions, and IAM user access keys. We'll show how to build the queries and relationships for the below graph visualization and other examples.
Walkthrough #
Prerequisites #
In this guide, we will use Neo4j as a destination and AWS as a source. For more information on how to set those up, see our documentation on
Neo4j and
AWS.
For the example queries, we'll be using AWS Identity and Access Management (IAM) Users and Amazon Relational Database Service (RDS) along with some common infrastructure associated with those resources that include IAM User Access Keys, IAM Managed and Inline Policies, IAM Groups, Amazon Virtual Private Cloud (VPC), Amazon VPC Security Groups, and Internet Gateways. Our demo environment will have those sample resources already created and the graph visualizations will look different than results from your walk through.
Refer to
Neo4j's installation documentation for help setting up Neo4j. For this walk through, make sure a local instance of Neo4j is up and running. Also make sure to install
Awesome Procedures on Cypher (APOC) for Neo4j as we'll be using useful functionality in APOC to assist with our attack surface management use cases.
Step 1: Install or Deploy CloudQuery #
You can get started with CloudQuery without any installation or deployment using our
Cloud Syncs.
To use CloudQuery locally, check out our
quickstart guide and
AWS source plugin for how to configure CloudQuery.
Step 2: Sync Data from CloudQuery to Neo4j #
The following command will sync data from AWS as a source to Neo4J as a destination:
cloudquery sync aws-neo.yml neo4j.yml
The configuration we're running locally looks like this:
Example aws-neo.yml
source configuration file:
kind: source
spec:
name: "aws"
path: "cloudquery/aws"
registry: "cloudquery"
version: "v28.8.0"
destinations: ["neo4j"]
tables: ["*"]
spec:
regions:
- us-east-1
accounts:
- id: "123412341234" #Your AWS Account Number here
- local_profile: "test-neo4j-example"
Example neo4j.yml destination configuration file:
kind: destination
spec:
name: "neo4j"
registry: "cloudquery"
path: "cloudquery/neo4j"
version: "v5.4.0"
spec:
connection_string: "bolt://localhost:7687"
username: "${USERNAME}"
password: "${PASSWORD}"
You should see a sync completed successfully
message. CloudQuery has now synced your AWS data to Neo4j.
Step 3: Test Out Neo4j #
If running Neo4j locally, navigate to http://localhost:7474/browser/
for the Neo4j browser.
Let's start with a simple query to find all our IAM Roles.
MATCH (n:aws_iam_roles) RETURN n
Step 4: Run Custom ASM Queries and Create Relationships in Neo4j #
Example 1: IAM User Access Keys and their linked permissions
Let's start with IAM User Access Keys. With this query, we'll look for the 4 distinct ways with identity policies an IAM User can be granted permissions and link those to the IAM Users and to the IAM User Access Keys. In this example, we've already created the following resources in AWS: IAM Users, IAM Groups, Inline Policies for both Groups and Users, Managed Policies for both Groups and Users, and IAM User Access Keys.
An IAM User can be granted permissions from:
Direct Inline Policies of the IAM User
Directly Attached Managed Policies to the IAM User
Inline Policies via IAM Group Membership
Attached Managed Policies via IAM Group Membership
By running the following command, we create a relationship between IAM User nodes and IAM User Access Key nodes.
MATCH
(a:aws_iam_user_access_keys),
(b:aws_iam_users)
WHERE a.`_cq_parent_id` = b.`_cq_id`
CREATE (b)-[r:has_access_keys]->(a)
RETURN type(r)
Next, let's create a relationship between IAM Users and IAM Groups. In AWS, IAM Users can be members of IAM Groups and inherit their IAM policies.
MATCH
(iamusers:aws_iam_users),
(usergroups:aws_iam_user_groups),
(iamgroups:aws_iam_groups)
WHERE iamusers.arn = usergroups.user_arn and iamgroups.arn = usergroups.arn
CREATE (iamusers)-[r:is_a_member_of]->(iamgroups)
RETURN type(r)
Now, we'll create relationships between IAM Users and all IAM User inline policies.
MATCH
(iamusers:aws_iam_users),
(inlinep:aws_iam_user_policies)
WHERE iamusers.arn = inlinep.user_arn
CREATE (iamusers)-[r:has_inline_policy]->(inlinep)
RETURN type(r)
Now, we'll create relationships between IAM Users and directly attached managed policies.
MATCH
(iamusers:aws_iam_users),
(attachp:aws_iam_user_attached_policies)
WHERE iamusers.arn = attachp.user_arn
CREATE (iamusers)-[r:has_attached_policy]->(attachp)
RETURN type(r)
Next, we'll create relationships between IAM Groups and their inline policies.
MATCH
(iamgroups:aws_iam_groups),
(groupinline:aws_iam_group_policies)
WHERE iamgroups.arn = groupinline.group_arn
CREATE (iamgroups)-[r:has_inline_policy]->(groupinline)
RETURN type(r)
Lastly, we'll create relationships between IAM Groups and their attached managed policies.
MATCH
(n:aws_iam_groups),
(policies:aws_iam_policies)
UNWIND (keys(apoc.convert.fromJsonMap(n.policies))) as y
WITH y, policies, n
WHERE y = policies.arn
CREATE (n)-[r:has_attached_policy]->(policies)
RETURN type(r)
After all these relationships have been created, we can run a MATCH (n:aws_iam_users) return n
to return all IAM Users.
In the UI, feel free to play around with node labels, colors, and expansion of nodes and relationships.
In our sample environment, we have 3 IAM Users. The following image shows the following and their relationships:
We'll use the following query to show our IAM Users: MATCH (n:aws_iam_users) return n
:
Example 2: Data in RDS
In this example, we will focus on Relational Databases (AWS RDS) and their data. This will include network connectivity such as VPCs, Internet Gateways, and Security Groups. We will also look at possible data access and encryption via KMS Keys, their KMS Key Policies, and KMS Key Grants. In this example, we've already created the following resources in AWS: RDS Databases, KMS Keys, KMS Key Policies, KMS Key Grants, an AWS VPC, Security Groups, and an Internet Gateway.
Let's first create relationships between RDS instances and their encryption from KMS Keys.
MATCH
(rdsinstances:aws_rds_instances),
(kmskeys:aws_kms_keys)
WHERE rdsinstances.kms_key_id = kmskeys.arn
CREATE (rdsinstances)-[r:is_encrypted_by_key]->(kmskeys)
RETURN type(r)
Let's connect KMS Keys with all their Key Grants and the
access that the Key Grants may permit to those KMS Keys and data.
MATCH
(keygrants:aws_kms_key_grants),
(kmskeys:aws_kms_keys)
WHERE keygrants.key_arn = kmskeys.arn
CREATE (kmskeys)-[r:has_kms_key_grant]->(keygrants)
RETURN type(r)
Let's now link Security Groups to the RDS instances.
MATCH
(rds_is:aws_rds_instances),
(sgs:aws_ec2_security_groups)
UNWIND (apoc.convert.fromJsonList(rds_is.vpc_security_groups)) as secgroups
WITH secgroups, rds_is, sgs
WHERE
secgroups['VpcSecurityGroupId']
= sgs.group_id
CREATE (rds_is)-[r:uses_security_group]->(sgs)
RETURN type(r)
In the next cypher query, we will connect KMS Keys with their KMS Key Policies.
MATCH
(keypolicies:aws_kms_key_policies),
(keys:aws_kms_keys)
WHERE keypolicies.key_arn = keys.arn
CREATE (keys)-[r:has_key_policy]->(keypolicies)
RETURN type(r)
Now, we'll create relationships between RDS Instances and the VPC networks they belong to.
MATCH
(rds_is:aws_rds_instances),
(vpcs:aws_ec2_vpcs)
WHERE apoc.convert.fromJsonMap(rds_is.db_subnet_group)['VpcId'] = vpcs.vpc_id
CREATE (rds_is)-[r:is_in_vpc]->(vpcs)
return type(r)
Lastly, we'll create relationships between Internet Gateways and their VPCs. In this cypher query, we use [0]
to pull the first item in the list. Internet Gateways can only be attached to 1 VPC, so this will return the only attachment in the attachment properties of the Internet Gateway resource.
MATCH
(igws:aws_ec2_internet_gateways),
(vpcs:aws_ec2_vpcs)
WHERE apoc.convert.fromJsonList(igws.attachments)[0]['VpcId'] = vpcs.vpc_id
CREATE (vpcs)-[r:has_internet_gateway]->(igws)
return type(r)
We've now created relationships for encryption and networking for our RDS instances.
In our sample environment, we have 4 RDS Instances. The following image shows the following and their relationships:
RDS Instances in yellow.
VPCs in blue.
Security Groups in green.
Internet Gateways in light blue.
KMS Keys in orange.
KMS Key Policies in light pink.
KMS Key Grants in dark pink.
To display our graph, the nodes, and relationships, we will use MATCH (n:aws_rds_instances) return n;
to return a graph visualization of our data.
Summary #
We have demonstrated how to get started with Attack Surface Management (ASM) and graph visualization with Neo4j along with some starter queries regarding databases, data, and AWS Identity and Access Management. Now you should be able to customize and create more queries, relationships, and utilize CloudQuery to help improve the security posture of your organization!
Want help getting started? Join the
CloudQuery community to connect with other users and experts, or message our team directly
here if you have any questions.
Ready to dive deeper? Contact CloudQuery here or join the
CloudQuery Community to connect with other users and experts. You can also try out CloudQuery locally with our
quick start guide or explore
the CloudQuery Platform (currently in beta) for a more scalable solution.
Want help getting started? Join the
CloudQuery community to connect with other users and experts, or message our team directly
here if you have any questions.