Skip to content

Security & Network Guide

This page is for the security reviewer signing off a Nexa deployment. Nexa is customer-managed: DataReadyAI ships container images and infrastructure-as-code, and your platform team runs it entirely inside your own cloud account against your own Snowflake or Databricks. DataReadyAI has no standing access to your environment. Everything below describes the reference architecture and calls out, honestly, what is hardened by default and what you must configure before production.

Store Encryption Key
Postgres (RDS) Enabled (storage_encrypted = true) AWS-managed key
S3 raw data lake AES256 server-side encryption AWS-managed key
S3 Terraform state AES256, versioned, access-logged AWS-managed key
ECR images AES256 AWS-managed key
Node EBS volumes Provisioned via the EBS CSI driver AWS-managed key
Secrets (AWS Secrets Manager, SSM SecureString) Encrypted at rest AWS-managed key
  • External traffic terminates TLS at the gateway with a Let’s Encrypt certificate (see Network). Plain HTTP on :80 is redirected to HTTPS (301).
  • Database: an rds.force_ssl parameter can require TLS on every Postgres connection. It is off in the reference dev config — enable it in production so no client connects in cleartext.
  • In-cluster service-to-service traffic uses cluster DNS over the pod network. There is no service mesh / mTLS by default; if you require encrypted east-west traffic, add a mesh (or Envoy mTLS) at the cluster layer.

Data residency & what leaves the environment

Section titled “Data residency & what leaves the environment”
  • All Nexa infrastructure — cluster, database, object storage, secrets — is created in a single region in your cloud account. Nothing is multi-region unless you configure it.
  • Your business data stays in your Snowflake/Databricks and your S3 raw data lake. Nexa’s cross-account S3 role is scoped to a single prefix (raw/) and gated by an ExternalId.
  • What leaves the environment: requests Nexa makes to your data platform (Snowflake/Databricks) and to any AI model endpoints you configure, plus outbound calls to the code repositories Nexa writes pipelines to (via GITHUB_TOKEN) and to Let’s Encrypt for certificate issuance. DataReadyAI receives only what you deliberately send with a support case (see troubleshooting).
  • End users authenticate to the web UI; the backend issues signed tokens (NEXA_AUTH_JWT_SECRET, NEXA_AUTH_TOKEN_TTL_SECONDS) and can federate to your data platform’s SSO (e.g. the Databricks OAuth callback). Auth enforcement is controlled by NEXA_AUTH_ENABLED — keep it true.
  • Service-to-service calls carry a shared NEXA_SERVICE_AUTH_TOKEN. A NEXA_SERVICE_AUTH_TOKEN_PREVIOUS slot allows zero-downtime rotation (both accepted during a rollout).
  • Data-platform access uses OAuth service-principal credentials (Databricks client id/secret, or the Snowflake equivalent) stored as secrets — no interactive credentials in the platform.
  • Cloud/AWS: pods get AWS permissions through EKS Pod Identity (not static keys, not IRSA). Each service has its own ServiceAccount mapped to an IAM role scoped to only the secrets it needs. Cluster add-ons (EBS CSI, Secrets Store CSI, cert-manager, ArgoCD Image Updater) each have their own least-privilege role.
  • Kubernetes: one namespace per service per environment (<service>-<env>) isolates workloads. The reference deployment relies on default namespace RBAC plus per-service ServiceAccounts; it does not ship custom app-level Role/RoleBinding objects, so restrict human kubectl access with your own cluster RBAC.
  • Data platform: governance (business glossary, PII tagging, change review) is enforced inside Nexa on top of your warehouse’s native grants — Nexa does not replace Snowflake/Unity Catalog permissions.
  • Secrets are held in AWS Secrets Manager and mounted into pods by the Secrets Store CSI driver, optionally synced to a Kubernetes Secret for envFrom. Terraform creates secret containers empty; values are populated out-of-band so they never land in Terraform state.
  • Deletion is protected by a recovery window (default 7 days).
  • Rotation is operator-driven: update the value in Secrets Manager, reconcile the CSI-synced secret, and roll the consuming Deployments. Full steps are in the operations guide.
  • The Secrets Store CSI driver’s role is broad by design (read across account secrets). If you need tighter isolation, scope it to a secret-name prefix.

Nexa runs in a dedicated VPC per environment with public and private subnets. Ingress is handled by Envoy Gateway (Kubernetes Gateway API) fronted by a cloud load balancer; TLS certificates are issued and renewed by cert-manager.

  • The nexa-gateway (namespace envoy-gateway-system) provisions a network load balancer and listens on :443 (TLS terminate) and :80 (redirect to HTTPS).
  • Certificates are Let’s Encrypt wildcards issued by cert-manager over the DNS-01 challenge (no inbound HTTP-01 needed), authenticated to the DNS provider by Pod Identity — no DNS API keys are stored. Certs auto-renew (rotationPolicy: Always, renewBefore 30 days).
  • Envoy SecurityPolicy applies a per-route IP allowlist (default-deny with explicit CIDRs) and CORS; a BackendTrafficPolicy applies rate limiting keyed on the client IP. The web route also drops known scanner paths (/.env, /.git, /wp-, etc.) with a direct 403.

Outbound traffic from private subnets flows through a NAT gateway. Nexa’s egress targets are your data platform, configured AI model endpoints, your Git host, container registry pulls, and Let’s Encrypt/DNS for certificates. Restrict egress to those destinations if your policy requires an allowlist.

  • VPC: dedicated per environment (default 10.0.0.0/16), two public and two private /24 subnets across two AZs.
  • Load balancer: an AWS NLB provisioned by Envoy Gateway via the AWS Load Balancer Controller.
  • Database SG: Postgres (5432) is reachable only from the VPC CIDR plus any explicit rds_allowed_cidr_blocks.
  • NAT: a single NAT gateway by default (not per-AZ) — add per-AZ NATs for HA.

Harden before production (the reference IaC does not do these):

  • The EKS API endpoint has both public and private access with no CIDR allowlist — restrict public_access_cidrs or go private-only.
  • There are no VPC endpoints — add interface/gateway endpoints (S3, ECR, Secrets Manager, STS, SSM) to keep AWS API traffic off the internet.
  • There is no WAF on the load balancer — attach AWS WAF if you need L7 filtering beyond the Envoy allowlist.
  • RDS Multi-AZ is off and dev allows public accessibility — enable Multi-AZ and set rds_publicly_accessible = false for production.

Container-level defaults already in place: containers run with allowPrivilegeEscalation: false, all Linux capabilities dropped, and most services run as a non-root user. Audit logging is on (EKS control-plane logs and Postgres logs to CloudWatch).

Recommended before a production sign-off:

Area Default Recommended action
Keys AWS-managed Use customer-managed KMS keys with rotation for RDS, S3, Secrets
RDS in-transit force_ssl off (dev) Require TLS on every DB connection
RDS availability Single-AZ, public in dev Multi-AZ, private-only, longer backup retention
EKS API endpoint Public, no allowlist Restrict CIDRs or make the endpoint private
AWS API traffic Over NAT/internet Add VPC endpoints for S3/ECR/Secrets/STS/SSM
Edge filtering Envoy IP allowlist + rate limit Add a managed WAF
Cluster RBAC Default namespace RBAC Add least-privilege human access roles
East-west traffic Cluster network, no mTLS Add a service mesh if mTLS is required

For the exact variable names and defaults behind these, see the configuration reference; for the topology, see the reference architecture.