Back to blog
AWSBest PracticesCloud SecurityDatabasesStorage

Neptune Cluster Storage Not Encrypted: Detection, Fix, and Prevention

Learn why unencrypted Amazon Neptune storage is a risk, how to migrate to a KMS-encrypted cluster, and how to enforce encryption with SCPs and CI/CD gates.

TL;DR

This check flags Amazon Neptune clusters whose storage is not encrypted at rest, leaving graph data, snapshots, and backups readable if the underlying storage is ever exposed. Encryption can only be enabled at cluster creation, so the fix is to create a new KMS-encrypted cluster and migrate your data into it.

Amazon Neptune is a managed graph database that a lot of teams use for fraud detection, identity graphs, recommendation engines, and knowledge graphs. The data sitting in those clusters tends to be sensitive by nature: who is connected to whom, transaction relationships, account linkages. This check looks for Neptune clusters that store all of that without encryption at rest.

If your cluster reports StorageEncrypted: false, the data on disk, the automated backups, and any snapshots you take are stored in plaintext at the storage layer.


What this check detects

The neptune_unencrypted check inspects every Neptune DB cluster in your account and reports any cluster where encryption at rest is disabled. Under the hood it reads the StorageEncrypted attribute returned by the Neptune API. When that value is false, the check fails.

Encryption at rest for Neptune covers the cluster's underlying storage volumes, its automated backups, snapshots, and read replicas. It is backed by AWS Key Management Service (KMS), so when it is enabled, every block written to disk is encrypted with a KMS key before it lands on storage.

Note: Neptune encryption at rest uses the same AES-256 envelope encryption model as Amazon RDS and Aurora. You either use the AWS managed key for Neptune (aws/rds) or a customer managed KMS key (CMK) that you control.


Why it matters

Encryption at rest protects you against a specific class of threat: someone gaining access to the physical or logical storage behind your database without going through your application's authentication. That sounds abstract, but it shows up in real ways.

  • Snapshot leakage. The most common Neptune data exposure is not someone breaking into the live cluster, it is a snapshot that gets shared too broadly or copied to another account. An unencrypted snapshot is fully readable by whoever can restore it.
  • Backup exposure. Automated backups inherit the cluster's encryption state. Without encryption, your point-in-time recovery data is plaintext too.
  • Compliance failures. PCI DSS, HIPAA, SOC 2, and most internal data handling policies expect encryption at rest for anything holding regulated or personal data. Graph databases often hold exactly that kind of relationship data, so an unencrypted cluster is a straightforward audit finding.
  • Defense in depth. Encryption at rest is cheap insurance. It does not replace network controls or IAM, but it closes off the storage layer as an attack path entirely.

Warning: You cannot enable encryption on an existing unencrypted Neptune cluster by toggling a setting. Encryption is set at creation time only. Fixing this finding means creating a new encrypted cluster and moving your data, which requires planning around downtime.


How to fix it

Because encryption is immutable after creation, remediation follows a snapshot-and-restore pattern. The high-level flow is: take a snapshot of the unencrypted cluster, copy that snapshot while applying a KMS key, restore a new encrypted cluster from the encrypted copy, then cut traffic over and retire the old cluster.

Step 1: Take a snapshot of the existing cluster

aws neptune create-db-cluster-snapshot \
  --db-cluster-identifier my-neptune-cluster \
  --db-cluster-snapshot-identifier my-neptune-snapshot

Wait for the snapshot to become available:

aws neptune describe-db-cluster-snapshots \
  --db-cluster-snapshot-identifier my-neptune-snapshot \
  --query 'DBClusterSnapshots[0].Status'

Step 2: Copy the snapshot and apply a KMS key

The copy operation is where encryption gets introduced. Point it at your KMS key ARN.

aws neptune copy-db-cluster-snapshot \
  --source-db-cluster-snapshot-identifier my-neptune-snapshot \
  --target-db-cluster-snapshot-identifier my-neptune-snapshot-encrypted \
  --kms-key-id arn:aws:kms:us-east-1:111122223333:key/your-key-id

Step 3: Restore a new cluster from the encrypted snapshot

aws neptune restore-db-cluster-from-snapshot \
  --db-cluster-identifier my-neptune-cluster-encrypted \
  --snapshot-identifier my-neptune-snapshot-encrypted \
  --engine neptune

The restored cluster inherits encryption from the snapshot, so the new cluster comes up with StorageEncrypted: true. Then add your instances:

aws neptune create-db-instance \
  --db-instance-identifier my-neptune-encrypted-instance-1 \
  --db-cluster-identifier my-neptune-cluster-encrypted \
  --db-instance-class db.r5.large \
  --engine neptune

Step 4: Cut over and decommission the old cluster

Update your application's endpoint to the new cluster, validate connectivity and data integrity, then remove the old unencrypted cluster.

Danger: The command below permanently deletes a cluster and skips the final snapshot. Only run it after you have fully verified the new encrypted cluster is serving traffic and you have a backup you trust. There is no undo.

# Delete instances first
aws neptune delete-db-instance \
  --db-instance-identifier my-neptune-instance-1 \
  --skip-final-snapshot

# Then delete the cluster
aws neptune delete-db-cluster \
  --db-cluster-identifier my-neptune-cluster \
  --skip-final-snapshot

Tip: If you can tolerate a maintenance window, do the cutover during low traffic and keep the old cluster around (stopped or intact) for a few days before deleting it. Storage costs on an idle cluster are far cheaper than recovering from a botched migration.


Doing it with Terraform

If you manage Neptune with infrastructure as code, the fix is a single attribute. New clusters should always set storage_encrypted = true and reference a KMS key.

resource "aws_neptune_cluster" "main" {
  cluster_identifier                  = "my-neptune-cluster-encrypted"
  engine                              = "neptune"
  storage_encrypted                   = true
  kms_key_arn                         = aws_kms_key.neptune.arn
  iam_database_authentication_enabled = true
  backup_retention_period             = 7
  preferred_backup_window             = "07:00-09:00"
  skip_final_snapshot                 = false
}

resource "aws_kms_key" "neptune" {
  description             = "KMS key for Neptune cluster encryption"
  deletion_window_in_days = 30
  enable_key_rotation     = true
}

Because Terraform cannot flip storage_encrypted in place either, applying this change to an existing unencrypted cluster forces a replacement. Plan that carefully or run the snapshot migration manually and import the result.


How to prevent it from happening again

Remediating one cluster is the easy part. Stopping unencrypted clusters from being created in the first place is what keeps the finding from coming back.

Use a Service Control Policy to deny unencrypted creation

An SCP at the organization level can block any attempt to create a Neptune cluster without encryption, regardless of who issues the call.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyUnencryptedNeptune",
      "Effect": "Deny",
      "Action": "rds:CreateDBCluster",
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "rds:DatabaseEngine": "neptune"
        },
        "Bool": {
          "rds:StorageEncrypted": "false"
        }
      }
    }
  ]
}

Note: Neptune shares the RDS API surface, so the relevant action and condition keys live under the rds: namespace even though the service is Neptune.

Gate it in CI/CD with policy as code

Catch the problem before it ever reaches AWS by scanning Terraform plans in your pipeline. Tools like Checkov or tfsec flag unencrypted database resources. A Checkov run in your pipeline looks like this:

checkov -d ./infra --framework terraform \
  --check CKV_AWS_44

Fail the build if the scan returns a finding. That way the unencrypted cluster never gets merged, let alone deployed.

Tip: Pair the CI gate with continuous detection in Lensix. The pipeline check stops new mistakes, and the runtime scan catches clusters created out of band through the console or one-off scripts that bypassed your IaC workflow.


Best practices

  • Encrypt every new cluster from day one. Make encryption the default in your modules and templates so engineers never have to remember it.
  • Use customer managed KMS keys for sensitive workloads. A CMK gives you control over key rotation, key policy, and the ability to revoke access by disabling the key, which the AWS managed key does not.
  • Enable automatic key rotation. Set enable_key_rotation = true so the key material rotates annually without operational effort.
  • Encrypt snapshots before sharing. If you copy a snapshot to another account or region, the copy must use a key the target account can access. Never share an unencrypted snapshot.
  • Pair encryption at rest with encryption in transit. Neptune supports TLS for connections. Enforce HTTPS connections so data is protected both on disk and on the wire.
  • Audit regularly. Encryption state should be part of your standing compliance posture, not a one-time cleanup. Schedule recurring scans rather than checking only during audits.

Encryption at rest for Neptune costs nothing extra in service fees, has negligible performance impact, and removes an entire category of risk. The only real cost is the migration effort for clusters that were created without it, which is exactly why getting it right at creation time matters so much.

Fix Unencrypted Neptune Cluster Storage on AWS | Lensix