Security Features and Architecture in Geode

Security is foundational to Geode’s design, not an afterthought. Geode implements defense-in-depth security with multiple layers of protection, from mandatory network encryption to fine-grained data access controls. This zero-trust architecture makes Geode suitable for regulated industries including healthcare, finance, and government.

Introduction to Database Security

Modern database security requires protection across multiple dimensions:

Network Security: Encrypt data in transit, prevent eavesdropping Authentication: Verify user identity before granting access Authorization: Control what authenticated users can access Data Encryption: Protect data at rest from unauthorized access Audit Logging: Track access for compliance and forensics Data Integrity: Prevent unauthorized modifications

Traditional databases often make security optional or add it as an afterthought. Geode makes security mandatory and deeply integrated.

Geode’s Security Architecture

Mandatory TLS 1.3 Encryption

Geode requires TLS 1.3 for all network connections with no plaintext fallback:

Why TLS 1.3:

  • Strong cipher suites only: No legacy weak ciphers (AES-GCM, ChaCha20)
  • Forward secrecy: Past sessions remain secure if keys are compromised
  • Faster handshake: 0-RTT resumption for returning clients
  • Simplified configuration: Removes insecure options

Server configuration:

# Generate self-signed certificate (development)
openssl req -x509 -newkey rsa:4096 -nodes \
  -keyout server-key.pem -out server-cert.pem -days 365 \
  -subj "/CN=localhost"

# Start server with TLS
geode serve --listen 0.0.0.0:3141 \
  --tls-cert=server-cert.pem \
  --tls-key=server-key.pem

Client connection (Python):

from geode_client import Client

# Production: Verify server certificate
client = Client(
    host="geodedb.example.com",
    port=3141,
    ca_cert="/path/to/ca-bundle.crt"
)
async with client.connection() as conn:
    result, _ = await conn.query("MATCH (n:User) RETURN count(n) AS total")

# Development: Self-signed certificate
client = Client(
    host="localhost",
    port=3141,
    skip_verify=True  # Skip verification (dev only!)
)
async with client.connection() as conn:
    result, _ = await conn.query("MATCH (n:User) RETURN count(n) AS total")

Certificate management best practices:

  • Use Let’s Encrypt for public-facing servers (free, automated)
  • Use internal CA for private networks
  • Rotate certificates before expiration (automate with certbot)
  • Store private keys securely (never in version control)

Authentication

Geode supports multiple authentication mechanisms:

Built-in Username/Password:

-- Create user with password
CREATE USER alice PASSWORD 'secure_password_here' REQUIRE SECURE TRANSPORT;

-- User connects with credentials
-- (Credentials sent over TLS-encrypted connection)

Python client authentication:

from geode_client import AuthClient, Client

client = Client(host="localhost", port=3141)
async with client.connection() as conn:
    auth = AuthClient(conn)
    await auth.login("alice", "secure_password_here")
    result, _ = await conn.query("MATCH (n:Node) RETURN n")

Password hashing (server-side):

  • Uses Argon2id (winner of Password Hashing Competition)
  • Configurable time cost, memory cost, parallelism
  • Resistant to GPU/ASIC attacks
  • Automatically salted

Certificate-Based Authentication (mTLS):

# Server requires client certificates
geode serve --listen 0.0.0.0:3141 \
  --tls-cert=server-cert.pem \
  --tls-key=server-key.pem \
  --tls-client-auth=required \
  --tls-ca-file=client-ca.pem
# Client provides certificate
client = Client(
    host="localhost",
    port=3141,
    ca_cert="/path/to/ca.pem",
    client_cert="/path/to/client-cert.pem",
    client_key="/path/to/client-key.pem"
)
async with client.connection() as conn:
    result, _ = await conn.query("MATCH (n:Node) RETURN n")

LDAP/Active Directory Integration (Enterprise):

# Configure LDAP authentication
geode serve --listen 0.0.0.0:3141 \
  --auth-method=ldap \
  --ldap-url="ldaps://ldap.example.com:636" \
  --ldap-bind-dn="cn=geode,ou=services,dc=example,dc=com" \
  --ldap-bind-password="service_password" \
  --ldap-user-dn="ou=users,dc=example,dc=com"

Authorization and Access Control

Geode implements fine-grained access control at multiple levels:

Database-Level Permissions:

-- Grant database access
GRANT ACCESS ON DATABASE sales TO alice;

-- Revoke access
REVOKE ACCESS ON DATABASE sales FROM alice;

Label-Level Permissions:

-- Grant read access to specific node labels
GRANT SELECT ON LABEL User TO alice;
GRANT SELECT ON LABEL Product TO alice;

-- Grant write access
GRANT INSERT, UPDATE, DELETE ON LABEL Order TO alice;

-- Read-only user
GRANT SELECT ON ALL LABELS TO readonly_user;

Property-Level Permissions:

-- Grant access to specific properties only
GRANT SELECT ON User(id, name, email) TO support_team;
-- support_team cannot see User.ssn or User.salary

-- Grant update on specific properties
GRANT UPDATE ON User(email, phone) TO profile_editor;
-- profile_editor can modify contact info, not other fields

Row-Level Security (RLS):

RLS policies filter query results based on user context:

-- Create RLS policy: Users can only see their own data
CREATE POLICY user_isolation ON User
FOR SELECT
USING (id = current_user_id());

-- Create RLS policy: Managers can see their team's data
CREATE POLICY team_visibility ON Employee
FOR SELECT
USING (
    EXISTS {
        MATCH (e:Employee)-[:REPORTS_TO]->(manager:Employee)
        WHERE manager.user_id = current_user_id()
    }
);

-- Enable RLS on table
ALTER LABEL User ENABLE ROW LEVEL SECURITY;
ALTER LABEL Employee ENABLE ROW LEVEL SECURITY;

Python usage with RLS:

from geode_client import AuthClient, Client

# User alice logs in
client = Client(host="localhost", port=3141)
async with client.connection() as conn:
    auth = AuthClient(conn)
    await auth.login("alice", "alice_password")
    # Query automatically filtered by RLS policy
    result, _ = await conn.query("""
        MATCH (e:Employee)
        RETURN e.name, e.salary
    """)
    # Only returns employees alice manages (RLS policy applied)

# Different user sees different results
client = Client(host="localhost", port=3141)
async with client.connection() as conn:
    auth = AuthClient(conn)
    await auth.login("bob", "bob_password")
    result, _ = await conn.query("""
        MATCH (e:Employee)
        RETURN e.name, e.salary
    """)
    # Only returns employees bob manages

RLS Policy Examples:

Multi-tenant isolation:

CREATE POLICY tenant_isolation ON ALL LABELS
FOR ALL
USING (tenant_id = current_tenant_id());

Time-based access:

CREATE POLICY business_hours ON SensitiveData
FOR SELECT
USING (extract_hour(current_timestamp()) BETWEEN 9 AND 17);

Role-based filtering:

CREATE POLICY role_based_access ON Document
FOR SELECT
USING (
    security_level <= (
        MATCH (u:User {id: current_user_id()})
        RETURN u.clearance_level
    )
);

Data Encryption at Rest

Transparent Data Encryption (TDE):

Geode encrypts database files automatically using AES-256-GCM:

# Generate encryption key
openssl rand -hex 32 > database-encryption-key.txt

# Start server with TDE
geode serve --listen 0.0.0.0:3141 \
  --data-dir=/var/lib/geode \
  --encryption-enabled=true \
  --encryption-key-file=database-encryption-key.txt

Key characteristics:

  • AES-256-GCM (authenticated encryption)
  • Per-page encryption (16KB blocks)
  • Low overhead (~5% performance impact)
  • Protects against physical theft, backup leaks

Key Management Service (KMS) Integration:

# Use AWS KMS for key management
geode serve --listen 0.0.0.0:3141 \
  --encryption-enabled=true \
  --encryption-kms=aws \
  --encryption-kms-key-id=arn:aws:kms:us-east-1:123456789012:key/abc-def \
  --encryption-kms-region=us-east-1

Field-Level Encryption (FLE):

Encrypt specific sensitive fields in application:

from cryptography.fernet import Fernet
from geode_client import Client

# Application-managed encryption key
encryption_key = Fernet.generate_key()
cipher = Fernet(encryption_key)

async def create_user_with_encrypted_ssn(name, ssn):
    # Encrypt SSN before storing
    encrypted_ssn = cipher.encrypt(ssn.encode()).decode()

    client = Client(host="localhost", port=3141)

    async with client.connection() as conn:
        await conn.execute("""
            CREATE (u:User {
                name: $name,
                ssn_encrypted: $ssn_encrypted
            })
        """, {"name": name, "ssn_encrypted": encrypted_ssn})

async def get_user_ssn(user_id):
    client = Client(host="localhost", port=3141)
    async with client.connection() as conn:
        result, _ = await conn.query("""
            MATCH (u:User {id: $id})
            RETURN u.ssn_encrypted
        """, {"id": user_id})

        encrypted_ssn = result.bindings[0]['ssn_encrypted']
        # Decrypt after retrieving
        ssn = cipher.decrypt(encrypted_ssn.encode()).decode()
        return ssn

FLE vs TDE:

  • TDE: Protects stored files, transparent to application, database can query
  • FLE: Protects specific fields, application-managed, database sees ciphertext

Audit Logging

Comprehensive logging for compliance and forensics:

Enable audit logging:

geode serve --listen 0.0.0.0:3141 \
  --audit-log-enabled=true \
  --audit-log-file=/var/log/geode/audit.jsonl \
  --audit-log-level=verbose

Audit log format (JSON Lines):

{
  "timestamp": "2026-01-24T14:32:45.123Z",
  "event_type": "authentication",
  "username": "alice",
  "source_ip": "192.168.1.100",
  "status": "success"
}
{
  "timestamp": "2026-01-24T14:32:46.456Z",
  "event_type": "query",
  "username": "alice",
  "database": "production",
  "query": "MATCH (u:User {id: $id}) RETURN u",
  "params": {"id": 123},
  "rows_returned": 1,
  "duration_ms": 2.4
}
{
  "timestamp": "2026-01-24T14:32:50.789Z",
  "event_type": "authorization_failure",
  "username": "bob",
  "database": "production",
  "attempted_action": "DELETE",
  "resource": "User",
  "reason": "insufficient_privileges"
}

Audit log analysis:

import json

def analyze_audit_log(log_file):
    failed_logins = {}

    with open(log_file) as f:
        for line in f:
            event = json.loads(line)

            if event['event_type'] == 'authentication' and event['status'] == 'failure':
                username = event['username']
                failed_logins[username] = failed_logins.get(username, 0) + 1

    # Alert on brute force attempts
    for username, count in failed_logins.items():
        if count > 10:
            print(f"ALERT: {username} has {count} failed login attempts")

analyze_audit_log("/var/log/geode/audit.jsonl")

Security Best Practices

Principle of Least Privilege

Grant minimum necessary permissions:

-- BAD: Overly permissive
GRANT ALL PRIVILEGES ON ALL LABELS TO app_user;

-- GOOD: Specific permissions
GRANT SELECT ON User(id, name, email) TO app_user;
GRANT INSERT, UPDATE ON Order TO app_user;
GRANT SELECT ON Product TO app_user;

Secure Credential Management

Never hardcode credentials:

# BAD: Hardcoded password
client = Client("localhost", 3141, username="app", password="hardcoded_pass")

# GOOD: Environment variables
import os
client = Client(
    "localhost",
    3141,
    username=os.getenv("GEODE_USERNAME"),
    password=os.getenv("GEODE_PASSWORD")
)

# BETTER: Secrets management service
from boto3.session import Session
secrets_client = Session().client('secretsmanager', region_name='us-east-1')
secret = secrets_client.get_secret_value(SecretId='geode/prod/credentials')
credentials = json.loads(secret['SecretString'])

client = Client(
    "localhost",
    3141,
    username=credentials['username'],
    password=credentials['password']
)

Regular Security Audits

# Automated security checks
async def security_audit():
    client = Client("localhost", 3141, username="admin")
    async with client.connection() as client:
        # Check for users without passwords
        result, _ = await client.query("""
            SHOW USERS WHERE password_set = false
        """)
        if result.bindings:
            print(f"WARNING: {len(result.bindings)} users without passwords")

        # Check for overly permissive grants
        result, _ = await client.query("""
            SHOW GRANTS WHERE privilege = 'ALL' AND grantee NOT IN ('admin', 'root')
        """)
        if result.bindings:
            print(f"WARNING: {len(result.bindings)} users with ALL privileges")

        # Check TLS enforcement
        result, _ = await client.query("SHOW VARIABLES LIKE 'require_secure_transport'")
        if result.bindings[0]['value'] != 'ON':
            print("WARNING: TLS not enforced")

await security_audit()

Defense in Depth

Layer multiple security controls:

  1. Network: Firewall rules, VPN, TLS
  2. Authentication: Strong passwords, MFA, certificate-based
  3. Authorization: RBAC, RLS policies
  4. Encryption: TDE for data at rest, TLS for data in transit
  5. Auditing: Comprehensive logging, monitoring, alerts
  6. Application: Input validation, parameterized queries, rate limiting

Compliance and Standards

Geode’s security features support compliance with:

GDPR (General Data Protection Regulation):

  • RLS for data access control
  • Audit logging for data access tracking
  • Encryption at rest and in transit
  • Right to be forgotten (data deletion)

HIPAA (Health Insurance Portability and Accountability Act):

  • Encryption of PHI (Protected Health Information)
  • Access controls and authentication
  • Audit trails for PHI access
  • Data integrity verification

PCI DSS (Payment Card Industry Data Security Standard):

  • Strong access control measures
  • Network security (TLS 1.3)
  • Encryption of cardholder data
  • Logging and monitoring

SOC 2 (Service Organization Control 2):

  • Access controls and authentication
  • Encryption and secure data handling
  • Audit logging and monitoring
  • Incident response procedures

Troubleshooting Security Issues

Issue: TLS Handshake Failures

Symptoms: Connection refused or TLS errors

Diagnosis:

# Test TLS connection
openssl s_client -connect localhost:3141 -tls1_3

# Check certificate validity
openssl x509 -in server-cert.pem -text -noout

Solutions:

  • Verify certificate hasn’t expired
  • Ensure client trusts server certificate
  • Check TLS cipher suite compatibility

Issue: Authentication Failures

Symptoms: “Access denied” errors

Diagnosis: Check audit logs:

grep "authentication.*failure" /var/log/geode/audit.jsonl

Solutions:

  • Verify username/password are correct
  • Check account isn’t locked
  • Ensure user has database access

Issue: Permission Denied Errors

Symptoms: “Insufficient privileges” errors

Diagnosis:

SHOW GRANTS FOR alice;

Solutions:

  • Grant necessary permissions
  • Check RLS policies aren’t overly restrictive
  • Verify user is accessing correct database

Further Reading

  • Security Overview: /docs/security/overview/
  • Authentication Guide: /docs/security/authentication/
  • Authorization and RBAC: /docs/security/authorization/
  • Encryption Best Practices: /docs/security/encryption/
  • Compliance Guide: /docs/security/compliance/
  • Security Hardening Checklist: /docs/security/hardening/

Related Articles