Databases

Secure Neon with cnspec

Scan Neon Postgres organizations, projects, and branches against security and compliance best practices with cnspec.

Scan your Neon Postgres organizations and projects to find security risks before they become incidents. cnspec evaluates network exposure, multi-factor authentication requirements, password storage, branch protection, passwordless compute endpoints, project sharing, trusted identity providers, API key hygiene, and dozens of other Neon controls.

If you're new to cnspec, start with the Quickstart. For an overview of every database cnspec can scan, see the database scanning overview.

Prerequisites

To scan Neon with cnspec, you must have:

Authenticate

cnspec connects to Neon using an API key. To create one:

  1. Log in to the Neon Console.
  2. Go to Account settings > API keys.
  3. Create a key and copy the value. Neon shows the key only once.

A Neon API key is either personal or organization-scoped, and the two reach different things:

  • A personal key reaches the organizations your account belongs to, their projects, and your personal API keys. It can read an organization's member roster only where your account is an admin.
  • An organization key reaches that organization's projects, member roster, and organization-scoped keys. No user sits behind it, so neon.currentUser is null.

You can pass the key on the command line with --token, or export it once and reuse it across commands:

export NEON_API_KEY=YOUR_API_KEY

cnspec also accepts NEON_TOKEN. When either variable is set, you can omit the --token flag from the commands below.

By default cnspec discovers every organization the key can access, along with the projects those organizations own. To scope a scan to a single organization, pass its ID with --organization.

Note: The --organization value must be the organization ID (org-...), not its display name. An ID the key isn't a member of returns empty organization and project lists instead of failing the scan.

Connection options

OptionDescription
--tokenNeon API key for authentication
--organizationScope discovery to a single Neon organization (ID)
--discoverWhat to discover: auto (default), all, organizations, or projects

Verify with a quick Neon check

Confirm that cnspec can reach your Neon account by opening a cnspec shell:

cnspec shell neon --token YOUR_API_KEY
cnspec> neon.currentUser { email plan }
neon.currentUser: {
  email: "admin@example.com"
  plan: "scale"
}

If cnspec connects and shows your account, you're ready to scan. A null currentUser alongside a populated project list means the key is organization-scoped, which is expected.

Scan Neon

cnspec scan neon --token YOUR_API_KEY

To scan a single organization:

cnspec scan neon --token YOUR_API_KEY --organization org-example-12345678

When a scan completes, cnspec prints a summary of all the checks it ran, grouped by policy, along with a risk score from 0 (no risk) to 100 (highest risk). Failed checks include remediation guidance to help you fix issues. To learn more about reading scan results, read Understand cnspec Results.

Mondoo doesn't yet ship an out-of-the-box Neon policy, so use the checks below as a starting point and create your own policies to meet your specific requirements.

Explore and test checks interactively

Open a cnspec shell to discover resources and try out checks:

cnspec shell neon --token YOUR_API_KEY

List all organizations

cnspec> neon.organizations { name plan requireMfa }
neon.organizations: [
  0: {
    name: "Acme Inc"
    plan: "scale"
    requireMfa: true
  }
]

List all projects

cnspec> neon.projects { name regionId pgVersion }

Inspect a project's network exposure

An empty allowedIps list means every address is permitted, which is the default:

cnspec> neon.projects { name blockPublicConnections allowedIps }

List the branches of a project

cnspec> neon.projects[0].branches { name default protected initSource }

List organization members

Reading the roster takes organization admin rights. Where the key lacks them, members is null rather than empty:

cnspec> neon.organizations { name members { email role hasMfa } }

List API keys

cnspec> neon.apiKeys { name createdAt lastUsedAt lastUsedFromAddr }

Walk from a resource to its neighbors

Projects, branches, endpoints, roles, and databases reference each other, so a query can start from any of them and walk in either direction:

cnspec> neon.projects { endpoints { host branch { name protected } } }
cnspec> neon.projects { branches { databases { name owner { name } } } }

Example security checks

Ensure all organizations require multi-factor authentication

cnspec> neon.organizations.all(requireMfa == true)
[ok] value: true

Ensure every organization member has enrolled a second factor

cnspec> neon.organizations.all(members.all(hasMfa == true))
[ok] value: true

Ensure no project is reachable from any address on the internet

A project with public connections allowed and an empty allowedIps list accepts connections from anywhere:

cnspec> neon.projects.none(blockPublicConnections == false && allowedIps.length == 0)
[ok] value: true

Ensure allowed-address lists cover every branch

When allowedIpsProtectedBranchesOnly is true, the list constrains protected branches and every other branch stays reachable from any address:

cnspec> neon.projects.all(allowedIpsProtectedBranchesOnly == false)
[ok] value: true

Ensure Neon doesn't retain role passwords

Storing passwords makes them recoverable through the API by anyone the key authorizes:

cnspec> neon.projects.all(storePasswords == false)
[ok] value: true

Ensure projects retain a restore window

A historyRetentionSeconds value of zero means a branch can't be restored to an earlier point:

cnspec> neon.projects.all(historyRetentionSeconds > 0)
[ok] value: true

Ensure branches carrying production data are protected

A branch initialized from parent-data holds the parent's rows at full fidelity, so an unprotected one is a second copy of production with weaker controls:

cnspec> neon.projects.all(branches.where(initSource == "parent-data").all(protected == true))
[ok] value: true

Ensure no compute endpoint accepts passwordless connections

cnspec> neon.projects.all(endpoints.all(passwordlessAccess == false))
[ok] value: true

Ensure no project is shared with an outside account

A null revokedAt means the grant is still live:

cnspec> neon.projects.all(permissions.where(revokedAt == null).length == 0)
[ok] value: true

Ensure trusted identity providers are reviewed

Each JWKS endpoint is an outside identity provider whose tokens can act as the listed database roles:

cnspec> neon.projects { name jwksEndpoints { providerName jwksUrl roleNames } }

Ensure no API key has gone unused

Neon doesn't expire API keys, so a key with a null lastUsedAt keeps working until someone revokes it:

cnspec> neon.apiKeys.all(lastUsedAt != null)
[ok] value: true

Learn more

On this page