| Follow @lancinimarco

Reading time ~22 minutes

Mapping Moving Clouds:
How to stay on top of your ephemeral environments with Cartography

Staying on top of ephemeral environments is a challenge many organizations face. This blog post describes the process we undertook at Thought Machine, a cloud-native company with environments spanning across multiple cloud providers, to identify a solution able to detect, identify, categorize, and visualize all the cloud assets being deployed in an organization.

Starting from what the challenges posed by ephemeral environments are, and continuing with what Cartography is, why it is useful, the high-level design of its deployment, and concluding on how to consume the data it collects. We are also going to open source a set of custom queries and tooling we created to simplify data consumption.

Let’s start by defining what problem we are going to tackle and how Cartography can help with it.


The Challenges Posed by Ephemeral Environments

If you look for common root causes of recent security breaches, you will see that a considerable amount of them can be traced back to assets that slipped out of the organization’s inventory management, or that have been misconfigured.

Infact, there are multiple articles online describing the challenges of a cloud-native environment. Although these are out of scope for this post, they are best summarized by the “Cloud Adoption: DevOps and the realities of multi-cloud” whitepaper released by HashiCorp. Here are some take away points that can help to describe this problem:

  • The scale of cloud infrastructure is essentially infinite”: the server fleet really has no practical limit since the cloud providers operate at enormous scale.
  • There are challenges in managing scale and elasticity”: since cloud providers expose their services using APIs, operations teams are adopting the notion of infrastructure as code. In this manner, an infrastructure topology can be codified as a simple script and then executed each time that infrastructure is required. By changing simple elements of the code, for example n = 1 to n = 1,000, practically infinite amounts of identical infrastructure can be provisioned instantaneously.
  • Heterogeneity”: because each infrastructure provider has a unique provisioning model, a challenge for operations teams is to determine a strategy that gives them a consistent provisioning workflow regardless of infrastructure type while leveraging the unique capabilities of each cloud provider.
  • Connectivity”: rather than discovering and connecting hosts, we need to think about how to discover and connect services, particularly challenging given the ephemeral nature of infrastructure in the cloud model. Operations teams in the cloud model then must adopt a dynamic registry that provides a shared and real-time understanding of services in use at any time. This provides the core inventory of components that can then be connected.

From these points we can then gather that, nowadays, one of the core problems many organizations are facing is to keep track of all their assets. This gets aggravated especially in a multi-cloud environment, where services are heterogeneous, do not provide a single pane of glass for their management, and, thanks to Infrastructure as Code, can easily be deployed from (potentially) every engineer within the company.

Thought Machine is, by default, a multi-cloud native organization, hence we needed a way to detect, identify, categorize, and visualize all the assets being deployed in our estate, regardless of the cloud provider in use.

If we want to synthesize all of this, we could draft a list of requirements for selecting a solution able to address these challenges:

  1. Assets identification and categorization: the solution should be able to identify all assets deployed in cloud environments, whether they are resources provided by CSPs (e.g., EC2, S3, etc.) or Kubernetes clusters.
    • Multi-cloud support: at least AWS and GCP should be covered.
    • Identity provider support: since we don’t want to limit ourselves to tracking of assets, the solution should be able to parse data from identity providers (GSuite and/or Okta) to be able to map which employee has access to which resource.
    • Data enrichment: the solution should provide APIs to allow integration with an application inventory.
  2. Assets visualization: the solution should provide a common unified view for analyzing the estate, abstracting from the specific provider and/or system.
  3. Assets analysis: the solution should make it easy to:
    • Understand dependencies between assets: enabling the understanding of where each asset lives, and its inter-dependencies with other assets.
    • Identify mismanaged resources: enabling to query for specific (and well known) class of misconfigurations.
  4. Deployment on Kubernetes: the chosen solution should be containerized and deployed on Kubernetes.
  5. Third party integrations: the system should be interfaceable with other data analysis platforms like ElasticSearch, Google Data Studio, or Jupyter Notebooks.

Enter Cartography

Having defined our list of requirements, we started scouting for potential solutions, including both paid SaaS-based offerings and open source alternatives. In the end we decided to settle with Cartography from Lyft.

Cartography is described as:

A Python tool that consolidates infrastructure assets and the relationships between them in an intuitive graph view powered by a Neo4j database.

Cartography aims to enable a broad set of exploration and automation scenarios. It focuses on flexibility and exploration, and is particularly good at exposing otherwise hidden dependency relationships between assets so that you may validate assumptions about security risks.

As stated on its GitHub page, Cartography is not the only security graph tool out there, but it differentiates itself by being fully-featured yet generic and extensible enough to help make anyone better understand their risk exposure, regardless of what platforms they use.

In the end, Cartography allows you to do obtain data like the one shown in the following image (more on this in the sections below):

Visualization of Cross-Account Role Access.
Visualization of Cross-Account Role Access.

Cartography’s Value Proposition

First of all, let’s see how it compares with our list of requirements:

Requirement Cartography
Assets identification and categorization
  • Multi-cloud support: AWS and GCP are covered.
  • Identity provider support: GSuite is supported.
  • Data enrichment: Highly extensible, as shown by the offensive community with BloodHound (e.g., BloodHound: Intro to Cypher, and Extending BloodHound).
Assets visualization
  • Cartography is backed by Neo4j, a graph database explicitly designed to track relationships between elements.
Assets analysis
  • Understand dependencies between assets: Neo4j makes it easy to visualize relationships.
  • Identify mismanaged resources: feasible with custom queries (as explained later in this post).
Deployment on Kubernetes
  • The GitHub repository of Cartography doesn't contain Dockerfiles or any reference to a Kubernetes deployment, but it is possible to containerize it fairly quickly (more on this below).
Third party integrations
  • Neo4j can be integrated, among others, with both ElasticSearch and Jupyter (as shown in the sections below).

From a technical point of view, Cartography did satisfy the set of hard requirements for a solution able to stay on top of all the assets spawned within the organization. In addition, though, we wanted to be sure it could bring value not only to the Security organization, but also the broader business:

  • Business Value: from a business point of view, a tool like Cartography can, first of all, help to identify the various assets being utilized within the organization. Having an up to date inventory, which reflects the live status of the infrastructure and provides relationships between assets, can aid not only the Security team but also other parts of the business:
    • Visualizing and understanding dependencies across assets can be also used to identify over-provisioning or instances of “forgotten” assets (i.e., drift).
    • Being a multi-cloud solution, it would provide a centralized database, regardless of the specific CSP used.
    • Neo4j also allows data to be extended and enriched with metadata. This could be leveraged to integrate infrastructure-related data with initiatives of application-focused inventory. Enriching the infrastructure data with the application inventory could provide the base for improved accountability for engineering teams (by being able to quickly identify which team owns which service/resource).
  • Security Value: in addition to the benefits listed above, a solution like Cartography can help the Security team:
    • With the detection of vulnerabilities (e.g., S3 bucket exposed to the internet) and of mismanaged resources (e.g., unrestricted security groups).
    • With providing a dashboard able to highlight shortcomings/missing controls required by different compliance bodies. It has to be noted that, in order to leverage such dashboard, the requirements of the different policies will have to be manually mapped and custom Neo4j queries will have to be created.
    • Integrations with Elasticsearch can be leveraged to increase the amount of data made available to the monitoring team, and could complement the set of alerts at their disposal.
    • Integrations with identity providers could be leveraged by the Internal Security team to provide assurance on the level of access granted to different groups of users.

Real World Setup

So far we discussed all the nice features offered by Cartography, but what actually is Cartography under the hood? Well, the code that ingests cloud assets (i.e., Cartography itself) is a python tool which stores the data it collects into a Neo4j graph database.

Here, we are first going to see how to prepare both AWS and GCP environments for data collection by properly configuring IAM access, and then we will focus on a deployment of both Cartography and Neo4j on Kubernetes.

Multi-Cloud Auditing

Our setup sees the bundle of Cartography and Neo4j running in the Kubernetes cluster of our dedicated tooling project. From there, we instructed Cartography to pull assets from every GCP Project and every AWS Account in our estate. To do so, we had to grant Cartography the minimum set of permissions necessary to pull data, without introducing an unnecessary risk to the infrastructure.

The picture below shows the multi-cloud setup at a glance.

High Level Multi-Cloud Auditing Setup.
High Level Multi-Cloud Auditing Setup.

I’ve already covered how to safely setup AWS and GCP for cross account auditing in a previous post titled “Cross Account Auditing in AWS and GCP”. If you are interested in replicating this setup I’d recommend you to give it a read. Anyway, I’ll report a high-level summary in the sections below.

Access Configuration: AWS IAM

For AWS we used the “Hub and Spoke” model, with the following being required in order for Cartography to be able to pull data from all accounts:

  1. One role (let’s name it role-security-audit), with the built-in SecurityAudit policy attached to it, to be created in every AWS account.
  2. One role (role-security-assume), in the Hub account, able to assume the role-security-audit on all the other Spoke accounts.
  3. One IAM user (user-security-audit), in the Hub account, able to assume the role-security-assume role.

Once setup, we persisted the credentials for user-security-audit in our Vault deployment:

$ vault write secret/default/tooling-project/user-security-audit AWS_ACCESS_KEY_ID=${key_id} AWS_SECRET_ACCESS_KEY=${secret}

Access Configuration: GCP IAM

In order for Cartography to be able to pull data from all GCP projects, it needs one Service Account (security-audit), with the securityReviewer/organizationViewer/folderViewer roles attached to it, defined at the Organization level. This because permissions set on an organizational level are passed down to the projects under that organization, and projects pass permissions down to resources, so our Service Account will get inherited in all the child projects of the Organization.

Like for AWS, we then persisted the Service Account in Vault:

$ vault write secret/default/tooling-project/security-audit security-audit-sa.json=@"$GAC_FILE"

Deployment on Kubernetes

Now that we have created the IAM roles and users needed, we can focus on deploying the two main components into a Kubernetes cluster: a StatefulSet for Neo4j, and a CronJob for Cartography. Since Cartography requires a running Neo4j instance in order to work properly (i.e., it is a dependency for its functioning), we are going to tackle Neo4j first.

Neo4j Deployment

Luckily for us, Neo4j already has a Helm chart that can be used as a reference for getting started. Personally, I decided not to deploy the chart itself, but I’ve used it as a reference for creating all the required resources (e.g., StatefulSet, PersistentVolumes, Services, etc.). In the figure below you can see the final deployment in a GKE cluster running in the dedicated “Tooling Project”:

Neo4j Deployment
Neo4j Deployment
  • The StatefulSet is made of two containers, one specific for neo4j, and one dedicated to oauth2-proxy (if you’ve never used it, I recommend you to take a look at its GitHub page, as it is a handy reverse proxy that provides authentication with Google, GitHub, and other Identity Providers).
  • Two services are exposing relevant ports:
    • http-service: as the name implies, exposes ports for HTTP-based services, like 7474 and 7473 used by the Neo4j web interface, and 4180 used by oauth2-proxy.
    • bolt-service: specifically dedicated to port 7687, allocated to the bolt protocol used to interact with Neo4j programmatically.
  • An Ingress is used as an entry point for connecting to the database:
    • Host https://neo4j.cluster (which points to http-service:4180) is used to reach the Neo4j web interface previous authentication via oauth2-proxy (which provides SSO). It first goes to port 4180 for authentication, and then gets redirected to the web interface running at 7474.
    • Host wss://neo4j-bolt.cluster:7687 (which points to bolt-service:7687) is used by the Cartography deployment to store information in the database. Notice how this endpoint is using websockets (wss://).
  • The Ingress is also interfaced to Let’s Encrypt, which provides TLS certificates.
  • The neo4j container is also associated to one or more Persistent Volumes for durable storage of data.
  • Finally, HashiCorp Vault is integrated with the GKE cluster so to provide secrets to the running containers (for example, the Neo4j password is stored within Vault).

Cartography Deployment

Cartography doesn’t currently come with a Docker container, but since it gets released as a pip package, it is fairly straightforward to containerize it. In the figure below you can see the final deployment in a GKE cluster running in the dedicated “Tooling Cluster”:

Cartography Deployment
Cartography Deployment

A CronJob (named cartography-run) is set to run daily. Here is an excerpt of its job spec:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
...
image: cartography_docker
env:
  - name: NEO4J_URI
    value: "bolt://bolt-service:7687"
  - name: GOOGLE_APPLICATION_CREDENTIALS
    value: /etc/vault/secrets/security-audit/security-audit-sa.json
command:
  - "/bin/sh"
  - "-c"
  - |
    # Retrieve credentials for the user-security-audit,
    # and setup config for the Spoke (child) accounts
    /docker-entrypoint.sh
    # Run Cartography
    cartography --neo4j-uri ${NEO4J_URI} --neo4j-user ${NEO4J_USER} --neo4j-password-env-var NEO4J_SECRETS_PASSWORD --aws-sync-all-profiles
volumeMounts:
  - name: neo4j-password-secrets-volume
    mountPath: /etc/vault/secret/neo4j-password-secrets
    readOnly: true
  - name: security-audit-sa-volume
    mountPath: /etc/vault/secret/security-audit
    readOnly: true
  - name: cartography-aws-secrets-volume
    mountPath: /etc/vault/aws/user-security-audit
    readOnly: true
  - name: aws-accounts-configmap-volume
    mountPath: /opt/aws-config
    readOnly: true
...
  • Among others, a couple of environment variables (line 3) proved to be essential:
    • NEO4J_URI points to port 7687 of the bolt-service defined in the Neo4j deployment, so that the Python code of Cartography will be able to connect and store data in Neo4j.
    • GOOGLE_APPLICATION_CREDENTIALS points to a path on the local filesystem of the container from where Cartography will be able to retrieve, at runtime, the Service Account for security-audit directly from Vault.
  • A set of volumeMounts (line 17) are used to load Vault secrets in the runtime of the container (like neo4j-password-secrets).
  • The image cartography_docker referenced by the Job (line 2) is based on a custom Dockerfile, which simply installs the cartography package on a python:3.7 base image, and then sets an entrypoint:
FROM python:3.7

RUN pip3 install --upgrade cartography python-dateutil==2.8.0

COPY docker-entrypoint.sh /
RUN chmod +x /docker-entrypoint.sh

USER app
ENTRYPOINT ["/docker-entrypoint.sh"]
  • The entrypoint referenced from the Dockerfile is used to instruct Cartography to fetch the user-security-audit credentials at runtime using the AWS backend enabled in Vault, and to store them into ~/.aws/credentials
#!/bin/sh
mkdir -p ~/.aws/

# Copy the config for the Spoke accounts from the configmap
cp /opt/aws-config/aws-config ~/.aws/config

# SETUP CREDENTIALS
#   [default]
#   aws_access_key_id = X
#   aws_secret_access_key = X
#   aws_session_token = x
cat <<EOF >> ~/.aws/credentials
[${AWS_DEFAULT_PROFILE}]
aws_access_key_id=$(cat /etc/vault/aws/user-security-audit/AWS_ACCESS_KEY_ID)
aws_secret_access_key=$(cat /etc/vault/aws/user-security-audit/AWS_SECRET_ACCESS_KEY)
aws_session_token=$(cat /etc/vault/aws/user-security-audit/AWS_SESSION_TOKEN)
EOF
  • Finally, the last piece of the setup is a Configmap used to hold the configuration details for each one of the Spoke accounts:
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-accounts-configmap
data:
  aws-config: |-
    # SETUP CONFIG
    #   [default]
    #   region=eu-west-1
    #   output=json
    [default]
    region=eu-west-1
    output=json

    # SETUP SPOKE ACCOUNTS
    #   [profile <AccountName>]
    #   role_arn = arn:aws:iam::<AccountId>:role/role-security-audit
    #   region=eu-west-1
    #   output=json
    #   source_profile=default

If you followed this far, congratulations! Now you should have a working instance of Cartography.


Data Consumption

Ok, we have our setup with Cartography running daily and pulling assets from all our environments. Let’s see how we can put the data collected to work: in this section I’m going to introduce a few different methods that can be used to extract actionable information from all the data gathered.

The Basics: Neo4j Browser

The most direct way to interact with Cartography data is to connect directly to the Browser built into Neo4j: you connect to the Ingress (https://neo4j.cluster) and manually run queries, as shown in the screenshot below.

Data Consumption via the Neo4j Browser.
Data Consumption via the Neo4j Browser.

If you are just starting with Neo4j, you might have noticed the query in the screenshot above (i.e., MATCH (a:AWSAccount)-[RESOURCE]->(p:AWSPrincipal) RETURN *) is not defined in SQL, but rather in a custom language that Neo4j called Cypher: even if it could seem daunting at first, it is actually a very idiomatic language once you learn its syntax. On this topic, the Neo4j website has plenty of tutorials and documentation on how to get up to speed with this query language, and the security community can help as well, since BloodHound uses Neo4j as well as its backend.

To simplify things, Cartography even comes with some basic queries: the “Sample queries” section on its GitHub page contains some good starters, like:

  • Which RDS instances have encryption turned off?: MATCH (a:AWSAccount)-[:RESOURCE]->(rds:RDSInstance{storage_encrypted:false}) return a.name, rds.id
  • Which EC2 instances are exposed (directly or indirectly) to the internet?: MATCH (instance:EC2Instance{exposed_internet: true}) RETURN instance.instanceid, instance.publicdnsname

If you start playing with data this way you’ll realise that, although the Neo4j Browser gives you freedom to perform unstructured exploration of the data, this method is also completely manual, as you’ll have to keep manually copy-pasting queries in the web interface.

The Automation: Programmatic Analysis

As we wanted to be able to run queries programmatically (automatically) against our data set, we first had to define a structured format for storing these queries, which allowed us to enrich them with metadata useful to any automated script to filter between them upon request.

The material related to this section is open source on GitHub:
https://github.com/marco-lancini/cartography-queries.
The repository contains our set of custom queries specifically created for analyzing data collected by Cartography, plus some Jupyter Notebooks to get you started, and, as a bonus, a little command line utility for inspecting/filtering queries.

Custom Query Format

For portability, all queries are stored in a JSON file (i.e., queries.json). This file is structured as a list of dictionaries, where each dictionary represents an annotated query (enriched with metadata):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[
    {
        "name": "ec2_list",
        "tags": [
            "inventory",
            "cloud",
            "aws",
            "list"
        ],
        "description": "List of EC2 instances by AWSAccount",
        "query": "MATCH (a:AWSAccount)-[:RESOURCE]->(instance:EC2Instance) RETURN ",
        "return": "a.name, a.id, instance.instanceid, instance.state, instance.exposed_internet, instance.launchtime, instance.region ORDER BY a.name, instance.state, instance.exposed_internet",
        "result_headers": [
            "account_name",
            "account_id",
            "instance_id",
            "state",
            "internet_exposed",
            "launchtime",
            "region"
        ]
    },
    ...
]

As shown in the snippet above, each query has the following fields:

  • name (line 3): the short name for the query.
  • tags (line 4): for ease of filtering. Usually it contains:
    • the source of data (e.g., aws, gcp, k8s)
    • any kind of query (e.g., security, inventory, networking)
    • any subtype (misconfig, anomaly, drift, list) that can assist in further filtering the queries
  • description (line 10): a human readable description of the query.
  • query (line 11): the actual Cypher that is going to be run against Neo4j (without any RETURN fields).
  • return (line 12): the fields to select from the query.
  • result_headers (line 13): a human readable list of the fields that are getting selected from the return field (for visualization purposes).

Creation of New Queries

Once defined a custom query format, we had to extend the sample queries provided by Cartography on its GitHub page. To do so, we relied on the reference schema for the resources it currently supports:

CSP Reference Support
AWS AWS Schema
  • Contains Orgs/Accounts, IAM, networking (subnets, VPCs, security groups, IPs), DNS, DynamoDB, EC2, S3.
  • No specific elements for EKS (yet).
GCP GCP Schema
  • Support for GCP is less established than the AWS counterpart.
  • Contains Orgs, Instances, networking (VPC, Subnet, IPs).
GSuite GSuite Schema
  • In addition, it does support GSuite natively, with Users and Groups.

As an example, the figure below shows the schema of all the AWS services currently supported by Cartography:

AWS services currently supported by Cartography
AWS services currently supported by Cartography (courtesy of Cartography's GitHub page).

Query Manager

As mentioned previously, we leveraged the reference schemas to add queries for security, inventory, and networking purposes (as you can find in the queries.json file of the cartography-queries repo). As I added queries, though, I wanted to have a quick command line option for inspecting/filtering (without actually running) them: that’s why I created a small python script (query_manager.py) for this. You’ll find the script in the companion repo, but for simplicity you can also use directly the custom Docker image created for it:

Command Action
docker run --rm marcolancini/cartography_queries:latest --count Count all available queries
docker run --rm marcolancini/cartography_queries:latest --get-all-tags List all available tags
docker run --rm marcolancini/cartography_queries:latest --tags=aws,security List queries filtered by tags (aws and security in this example)
❯ docker run --rm marcolancini/cartography_queries:latest --count
[+] Number of available queries: 56

❯ docker run --rm marcolancini/cartography_queries:latest --get-all-tags
[+] The following tags have been defined:
	anomaly
	aws
	cloud
	drift
	gcp
	inventory
	list
	misconfig
	networking
	security
	usage

❯ docker run --rm marcolancini/cartography_queries:latest --tags=aws,security
[+] Selected tags: ['aws', 'security']
[+] Queries matching selected tags:
	[iam_user_named] Which named AWSUsers do exist?
	[iam_user_password] Which AWSUser used a password to authenticate?
	[iam_accesskey_principal] What AccountAccessKey have been created (to authenticate to AWSPrincipals)?
	[iam_user_many_groups] Which AWSUsers are members of too many Groups?
	[iam_policy_many_accounts] Which AWSPolicy is used in too many AWSAccounts?
	[iam_role_assume_principal] Which AWSRole can be assumed by which AWSPrincipal?
	[iam_role_assume_role] Which AWSRole can be assumed by which AWSRole?
	[ec2_public] Which EC2 instances are directly exposed to the internet?
	[ec2_keypair] What EC2KeyPairs can login in which EC2 Instance?
	[ec2_keypair_unused] What EC2KeyPairs exist, but are unused (cannot login in any EC2 Instance)?
	[ec2_old] Which EC2 instances are older than 90 days?
	[rds_unencrypted] Which RDS instances have encryption turned off?
	[rds_public] Which RDS instances are directly exposed to the internet?
	[rds_no_auth] Which RDS instances have database authentication turned off?
	[s3_anonymous] Which S3 buckets have a policy granting any level of anonymous access to the bucket?
	[s3_acl] Which S3 ACLs are applied to S3Buckets?
	[vpc_peering] Which VPC peerings have been created?
	[vpc_peering_rules] List all VPC peerings that have been created, with their respective security group rules
	[loadbalancer_public] Which LoadBalancers are directly exposed to the internet?
	[loadbalancer_v2_public] Which LoadBalancersV2 are directly exposed to the internet?

Repeatability: Jupyter Notebooks

So far we defined a custom query format, and added a whole set of shiny new queries. The last piece of the puzzle was to find a method for empowering people (and teams) to perform analysis of the collected data, on-demand.

What better than Jupyter notebooks? They are already being heavily used by the security community for investigation purposes, so it felt natural to create runbooks for self-service consumption of data.

Some Jupyter notebooks, together with the python code that powers them, are available as well on GitHub:
https://github.com/marco-lancini/cartography-queries.

Code Structure

Code on the Jupyter server reflects the structure of the notebooks folder provided in the companion repo:

Notebooks on the Jupyter server.
Notebooks on the Jupyter server.

The core of the logic is contained in the query_builder.py file, which is a wrapper around the Neo4j python driver (source 1, source 2). The file introduces 3 abstraction objects:

  • NeoDB: a wrapper around neo4j.GraphDatabase, which is in charge of instaurating a connection with the backend Neo4j database. This should never be instantiated directly from within notebooks.
  • QueryBuilder: this is the main object, which gives high freedom in creating notebooks. The only exported method is the one named query, which expects a list of filters and will run all the associated queries. Usage is explained in the code snippet below:
# Instantiate the object
qb = QueryBuilder()

# The `query` method has 2 parameters:
#   1) TAGS
#       - list of tags like which usually includes the source (aws, gcp, k8s) and a kind of query (security)
#       - example: ['aws', 'security', 'inventory']
#   2) ACCOUNT
#       - optional account/project name
#       - example: account='test-account-1'
qb.query(['aws', 'security'])
qb.query(['gcp', 'security'], account='test-account-1')
  • ReportBuilder: abstraction over the QueryBuilder object, created with the aim of having pre-defined and easy to use reports. The reports provided in the GitHub repo are defined here. Usage is explained in the code snippet below:
# Instantiate the object
r = ReportBuilder()
# Run one of the pre-made reports
r.report_aws_inventory()

Run Notebooks

To get started with the notebooks provided you will just have to:

  1. Update the NEO4J_URI variable (currently set to bolt://bolt-service:7687) within the query_builder.py file.
  2. Set the NEO4J_USER and NEO4J_PASSWORD environment variables in the Jupyter environment.
%%capture
%env NEO4J_USER=neo4j
%env NEO4J_PASSWORD=<redacted>
Jupyter Notebook for the AWS Inventory Report.
Jupyter Notebook for the AWS Inventory Report.

Upgrade to Dashboards

We saw how the ReportBuilder object defined in the query_builder.py file can be used to create pre-defined reports for easy consumption. The idea is that any team could create their own reports by selecting the queries more relevant to them, and then visualize them as standalone dashboards directly within Jupyter thanks to the Voilà plugin.

To start, we created dashboards specific to 3 main domains (security, inventory, and networking), for both AWS and GCP:

Dashboard Description
inventory Contains security relevant queries for AWS accounts / GCP projects.
networking Contains networking relevant queries (VPCs, DNS, LBs, etc.) for AWS accounts / GCP projects.
security Provides an inventory of the assets deployed in AWS accounts / GCP projects.
Sample AWS Inventory Report. Sample AWS Inventory Report.
Sample AWS Inventory Report.

Conclusion and Next Steps

In this blog post we saw what benefits Cartography could have on the security posture of your organization, and I walked through the process we undertook to deploy it in a multi-cloud environment, from inception to self-service dashboards for data consumption.

But there is still much to do, and Cartography still has much more to offer. Some of the areas worth exploring are:

  • Integration with application inventory so to tie micro-services to cloud assets.
  • Integrations with GSuite and ERP systems like Workday to include employee data in the graph database, and tie them to the relevant micro-service and/or cloud resource.
  • Integration with Elasticsearch, so to generate alerts directly from data parsed by Cartography.
  • And, of course, integration with ticketing systems like Jira (because who doesn’t track tickets?).

But there are other avenues that can be explored. If you are curious, I’d suggest you to take a look at the talk that some members of the Lyft team behind Cartography delivered at BSidesSF 2019: “Lyft Cartography: Automating Security Visibility and Democratization”.

I hope you found this post useful and interesting, and I’m keen to get feedback on it! If you find the information shared was useful, if something is missing, or if you have ideas on how to improve it, please let me know on Twitter.

Marco Lancini

Marco Lancini
Hi, I'm Marco Lancini. I'm a Security Engineer, mainly interested in cloud native technologies, devops, and network security...  

Cross Account Auditing in AWS and GCP

Published on December 15, 2019