Skip to content

Latest commit

 

History

History
900 lines (669 loc) · 33.9 KB

File metadata and controls

900 lines (669 loc) · 33.9 KB

🍿 Opaflix

Okta Privileged Access Session Replay Tool — Replay OPA SSH and RDP session recordings from AWS S3 with Okta OIDC authentication.

License Node.js Docker Vercel AWS Python Okta


📑 Table of Contents


🎯 Overview

Opaflix is a web application for replaying Okta Privileged Access (OPA) session recordings stored in AWS S3. It provides a web interface for browsing and playing back SSH terminal sessions (.cast) and RDP desktop sessions (.mkv).

🍿 Like Netflix, but for your PAM Session recordings! Opaflix delivers a seamless experience for security teams, auditors, and administrators to review privileged access sessions with ease.

Tip

Quick Start - Get running in 5 minutes! Jump to Quick Start for the fastest setup path.

❓ Why Opaflix?

OPA session recording provides the audit foundation. Opaflix adds a lightweight companion interface for teams that want to browse, search, and replay exported recordings from a centralized S3 archive.

It's highly inspired by an old project of our former colleague Daniel Harris: opa-utils. Since the project is not maintained anymore, I decided to create a new one with a more modern tech stack, and - to be transparent - the heavy help of the AI (Vibe coding with Claude Code).

Opaflix is built with Node.js, Express, and Handlebars for the backend, and uses Asciinema Player for SSH session playback and HTML5 video for RDP sessions. It also integrates with the OPA API to provide real-time data for the infrastructure graph.

Caution

Not an Official Okta Product - Opaflix is an independent community project.

While I am an employee of Okta, Inc. this project was developed by me entirely in my private capacity.  Opaflix is not an official Okta product and is not endorsed, supported or maintained by Okta.

Use at your own risk and always test in a non-production environment first.

🚀 Key Features

  • Dashboard — Overview page with session statistics, recent activity, and quick access to session lists.

  • Session Lists — Browse SSH and RDP sessions with:

    • Server-side Pagination — Handles thousands of sessions efficiently
    • Sortable Columns — Click headers to sort ascending/descending
    • Resizable Columns — Drag column borders to resize
    • Different search behaviors:
      • Simple Search — Quick text search across all fields
      • Advanced Search — Filter by server, username, project, team, date range
  • Sessions Playback:

    • SSH Sessions — Asciinema-player with play/pause, speed control, seek
    • RDP Sessions — HTML5 video player with standard controls
    • Direct Download — Download button on playback pages for offline access
    • Direct S3 Streaming — Sessions are streamed directly from S3 using presigned URLs (no server bandwidth consumed for playback)
  • IAM Roles Anywhere — Certificate-based AWS auth for external deployments (static keys supported as well)

  • OPA API Integration — Dropdowns populated with real data from your OPA tenant

  • Infrastructure Graph — Visual topology showing:

    • Gateways → Projects → Servers relationships
    • Interactive navigation with expand/collapse
    • Real-time data from OPA API
  • Single or Multi-Tenant — Simple ENV-based config or database-backed multi-team deployment

    • Single-Tenant (default): No database required, config from environment variables
    • Multi-Tenant: PostgreSQL database required, supports multiple teams with isolated configs.
  • Configuration UI — Web interface for managing tenant settings (multi-tenant) or viewing current config (single-tenant):

    • Authentication Method Selector — Choose between Access Keys or IAM Roles Anywhere
    • Certificate Management — Upload PEM files directly, view certificate CN and expiration date
    • Auto-Cleanup — Switching auth methods automatically removes unused credentials
    • Expiration Alerts — Color-coded certificate status (valid, expiring soon, expired)

📸 Screenshots

Dashboard Session List Playback RDP Playback SSH
Dashboard Sessions Playback RDP Playback SSH
Configuration UI Search & Sorting Graph
Configuration UI Simple Search Advanced Search Sorting Infrastructure Graph Graph Details

🎥 Sample Video

opaflix-demo.mp4

⚠️ Limitations

  • Opaflix v1 access model - Opaflix currently applies one flat recording access scope. Use it for trusted PAM administrators and auditors, and rely on deployment isolation, Okta app assignment, and network controls until granular in-app authorization is added.
  • S3 only - Opaflix currently focuses on S3-backed exported recordings. Other storage backends may be evaluated in the future based on feedback.
  • Powered by Vibe Coding - Even if the code was revised and tested, I'm not a developer and Opaflix was written with the heavy usage of Vibe Coding (powered by Claude Code), so I can't exclude bugs and security issues. This is a community-built tool and has not undergone formal product security review. Use with caution and always test in a non-production environment first.

🔄 How It Works (High-Level Data Flow)

The following is a high-level overview of the Opaflix data flow:

---
config:
  layout: elk
---
flowchart LR
 subgraph User["1️⃣   Privileged Access"]
        Admin["👤   Administrator"]
  end
   subgraph Gateway["2️⃣   OPA Gateway"]
        Broker["SFT Broker"]
        Session[("Session Recording<br>/var/log/sft/sessions/*.asa")]
  end
    subgraph Target["3️⃣ &nbsp; Target"]
      Server["🖥️ &nbsp; Server"]
  end
 subgraph SyncService["4️⃣ &nbsp; opaflix-sync"]
        Convert["Session Conversion<br>.asa → .cast / .mkv"]
  end

 subgraph Storage["5️⃣ &nbsp; AWS S3"]
        S3[("Converted Sessions<br>.cast / .mkv")]
  end
 subgraph Playback["6️⃣ &nbsp; Opaflix"]
        Web["Web Application"]
        Browser["🌐 &nbsp; Browser"]
  end


    Broker --> Session & Server
    Session --> Convert
    Convert -- Upload --> S3
    S3 --> Web
    Web --> Browser
    Admin -.-> Server
    Admin --> Broker

    style Admin fill:#ffffff
    style Convert fill:#ffffff
    style Broker fill:#ffffff
    style Session fill:#ffffff
    style S3 fill:#ffffff
    style Web fill:#ffffff
    style Browser fill:#ffffff
    style Server fill:#ffffff
    style User fill:#BBDEFB
    style Gateway fill:#C8E6C9
    style Storage fill:#FFF9C4
    style Playback fill:#FFCDD2
    style SyncService fill:#E1BEE7
Loading
Step Actor Description
1 Administrator Connects to target server via SSH or RDP using OPA Gateway as passthrough / bastion host
2 OPA Gateway Proxies the connection to the target server and records the session in native .asa format
3 Target Server The actual server being accessed (recorded by the gateway)
4 opaflix-sync Converts .asa.cast (SSH) or .mkv (RDP), uploads to S3
5 AWS S3 Stores converted session recordings
6 Opaflix Web interface for browsing and playing back sessions

Note

The opaflix-sync script may be installed on the same server as the OPA Gateway or on a separate server with access to the .asa files. See Session Conversion for details.

🏗️ Opaflix Application Architecture

The following diagram illustrates the architecture of the Opaflix application, including its interactions with Okta for authentication, AWS S3 for storage, and the OPA API for real-time data:

---
config:
  layout: dagre
---
flowchart LR
 subgraph Client["Client"]
        Browser["🌐 Browser"]
  end
 subgraph Opaflix["Opaflix"]
        App["Express.js App"]
        Auth["Okta OIDC"]
  end
 subgraph AWS["AWS"]
        S3[("AWS S3")]
  end
  subgraph OktaCloud["Okta Cloud"]
        OPA["OPA API"]
        Okta["Okta IdP"]
  end
  subgraph Database["Database"]
        PG[("PostgreSQL")]
  end
    Browser --> App
    App --> Auth & S3 & OPA
    App -.-> PG
    Auth --> Okta
Loading

🚀 Quick Start

Get Opaflix running in 5 minutes using single-tenant mode (no database required):

Local Node.js

  1. Clone the Repository

    git clone https://github.com/fabiograsso/okta-opaflix.git
    cd opaflix
  2. Create Environment File

    cp .env.example .env
  3. Configure Settings

    Edit .env with your credentials:

    # Single-tenant mode (default)
    MULTITENANT=NO
    
    # Okta Authentication
    OKTA_ISSUER=https://your-tenant.okta.com
    OKTA_CLIENT_ID=your-client-id
    OKTA_CLIENT_SECRET=your-client-secret
    
    # AWS S3
    AWS_ACCESS_KEY_ID=your-access-key
    AWS_SECRET_ACCESS_KEY=your-secret-key
    AWS_REGION=us-east-1
    AWS_S3_BUCKET=your-bucket
    
    # Application
    BASE_URI=http://localhost:3000
    SESSION_SECRET=your-secure-secret-minimum-32-characters-long
  4. Start the Server

    • 💻 Option 1 - Local Node.js
    npm install
    npm start
    • 🐳 Option 2 - Docker
    docker run -d -p 3000:3000 --env-file .env --name opaflix opaflix:latest
    
    # Or (Makefile with Docker Compose)
    
    make start
    
    # Or (direct Docker Compose)
    
    docker compose --profile prod up -d
  5. Access Opaflix

    Open http://localhost:3000

Tip

Single-tenant mode is the simplest deployment. For serving multiple teams, see Multi-Tenant Mode.

▲ Deploy to Vercel

You can deploy Opaflix to Vercel in single-tenant mode with one click. This setup uses S3 Access Key, but you can also use IAM Roles Anywhere for AWS authentication (see AWS.md). You can also switch in multi-tenant mode, but it requires a PostgreSQL database (such as Neon).

Deploy with Vercel

Warning

Security Warning

  • Use Vercel Environment Variables to securely store your AWS and Okta credentials. Do not hardcode secrets in your code or commit them to version control.
  • For production deployments, consider using IAM Roles Anywhere for AWS authentication to avoid static access keys. See AWS.md for setup instructions.
  • Do not allow public access to your Opaflix deployment. Use Vercel's password protection or IP allowlisting features to restrict access.

Vercel Prerequisites

Before deploying to Vercel, you need:

  1. AWS Infrastructure — S3 bucket with Access Key credentials (or you need to change the env variables for IAM Roles Anywhere)
  2. Okta OIDC App — Web application for authentication
  3. OPA API Credentials (optional) — For infrastructure graph visualization

Why Vercel?

  • Free Tier — Perfect for small teams and testing
  • Serverless — Pay only for what you use, scales automatically
  • No Server Management — Focus on functionality, not infrastructure
  • Global CDN — Fast worldwide access

Vercel Configuration

The included vercel.json configures:

  • Static file serving from public/ directory
  • Node.js serverless function for the Express app
  • Route mapping for CSS, JS, and other assets

📦 Prerequisites

Common Requirements

Component Required Description
Node.js 24+ Yes JavaScript runtime
Okta OIDC App Yes Authentication via Okta SSO
AWS S3 Bucket Yes Storage for converted session recordings
OPA API Credentials No Optional — for infrastructure graph visualization

Additional for Multi-Tenant Mode

Component Required Description
PostgreSQL Yes Tenant configuration storage (Neon recommended)

Okta OIDC Setup

  1. Create a new Web Application in Okta Admin Console
  2. Set Sign-in redirect URI: http://localhost:3000/authorization-code/callback (or your custom domain)
  3. Set Sign-out redirect URI: http://localhost:3000/login (or your custom domain)
  4. Copy Client ID and Secret

🏠 Single-Tenant Mode

Single-tenant mode is the default and simplest deployment option. All configuration comes from environment variables — no database required.

When to Use

  • Single team or organization
  • Simple deployment without database infrastructure
  • Development and testing

Configuration

# .env file
MULTITENANT=NO #Default is NO, can be omitted

# Required: Application
BASE_URI=http://localhost:3000
SESSION_SECRET=your-secure-secret-minimum-32-characters-long

# Required: Okta Authentication
OKTA_ISSUER=https://your-tenant.okta.com
OKTA_CLIENT_ID=your-client-id
OKTA_CLIENT_SECRET=your-client-secret

# Required: AWS S3
AWS_REGION=us-east-1
AWS_S3_BUCKET=your-bucket

# Method 1: Static Access Keys (simple)
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key

# Method 2: IAM Roles Anywhere
# AWS_ROLES_ANYWHERE_TRUST_ANCHOR_ARN=arn:aws:rolesanywhere:region:account:trust-anchor/id
# AWS_ROLES_ANYWHERE_PROFILE_ARN=arn:aws:rolesanywhere:region:account:profile/id
# AWS_ROLES_ANYWHERE_ROLE_ARN=arn:aws:iam::account:role/RoleName
# AWS_ROLES_ANYWHERE_CERTIFICATE="-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
# AWS_ROLES_ANYWHERE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"

# Optional: OPA API (for infrastructure graph)
OPA_TENANT_URL=your-org.pam.okta.com      # Full OPA tenant URL
OPA_TEAM_NAME=your-team                   # Team name within the OPA tenant
OPA_API_KEY_ID=your-key-id
OPA_API_KEY_SECRET=your-key-secret

Behavior:

  • Session indices stored in-memory only (rebuilt on restart)
  • To change settings, update .env and restart the application

Important

In single-tenant mode, the /config page is read-only. All settings must be changed via environment variables.


🏢 Multi-Tenant Mode

Multi-tenant mode enables a single Opaflix deployment to serve multiple OPA teams, each with isolated configuration.

Enabling Multi-Tenant Mode

# .env file
MULTITENANT=YES

# Required: PostgreSQL Database
PGHOST=your-host.neon.tech
PGPORT=5432
PGDATABASE=neondb
PGUSER=your-user
PGPASSWORD=your-password
PGSSLMODE=require

# Required: Application
BASE_URI=http://localhost:3000
SESSION_SECRET=your-secure-secret-minimum-32-characters-long

PostgreSQL Setup

We recommend Neon for serverless PostgreSQL:

  1. Create a free database at neon.tech
  2. Copy connection details to your .env file
  3. Tables are auto-created on startup

Creating Tenants

The architecture uses a tenants table with a composite key on (tenant_url, team_name):

  • tenant_url: The OPA instance URL (e.g., demo-blue-sky-1234.pam.okta.com)
  • team_name: The team name within that OPA instance (e.g., blue-sky)

Each tenant requires at minimum the OIDC configuration (issuer, client ID, client secret). AWS and OPA settings can be configured later via the UI.

Create tenants via SQL:

-- PostgreSQL script to create tenant and configuration
DO $$
DECLARE
    v_tenant_url text := 'demo-blue-sky-1234.pam.okta.com';  -- Your OPA instance URL
    v_team_name text := 'blue-sky';  -- Your team name
    v_description text := 'Blue Sky Team';
    v_client_id text := 'your-okta-client-id';
    v_client_secret text := 'your-okta-client-secret';
    v_issuer text := 'https://your-tenant.okta.com/oauth2/default';
    v_tenant_id uuid;
BEGIN
    -- Create tenant (unique combination of tenant_url + team_name)
    INSERT INTO tenants (tenant_url, team_name, description, is_active)
    VALUES (v_tenant_url, v_team_name, v_description, true)
    RETURNING id INTO v_tenant_id;

    -- Insert tenant configuration
    INSERT INTO tenant_configs (tenant_id, config_key, config_value) VALUES
        (v_tenant_id, 'okta.clientId', v_client_id),
        (v_tenant_id, 'okta.clientSecret', v_client_secret),
        (v_tenant_id, 'okta.issuer', v_issuer);

    RAISE NOTICE 'Created tenant % / % (ID %)', v_tenant_url, v_team_name, v_tenant_id;
END $$;

Then configure at http://localhost:3000/config?tenant=demo-blue-sky-1234.pam.okta.com&team=blue-sky.

Tip

Preview vs Production: The environment is determined by the tenant URL:

  • Production: *.pam.okta.com
  • Preview: *.pam.oktapreview.com

Note

I'm evaluating improvements on the initial tenant creation process. In the future, I may add a UI for creating tenants directly from the application.

How It Works

flowchart LR
    Request["Request with\n?tenant=...&team=..."] --> Resolver["Tenant Resolver"]
    Resolver --> DB[("PostgreSQL")]
    DB --> Config["Team Config"]
    Config --> Services["OIDC / S3 / OPA API"]
Loading

URL Parameters

Parameter Values Description
?tenant= Tenant URL Required — the OPA instance URL (e.g., demo-blue-sky-1234.pam.okta.com)
?team= Team name Required — identifies the team within the tenant

Examples:

http://localhost:3000/?tenant=demo-blue-sky-1234.pam.okta.com&team=blue-sky
http://localhost:3000/sessions/list?tenant=demo-blue-sky-1234.pam.okta.com&team=blue-sky
http://localhost:3000/?tenant=demo-blue-sky-1234.pam.oktapreview.com&team=blue-sky

Note

The tenant and team are stored in your session and in cookies after first access. Switch by visiting a URL with different parameters.

Database Schema

erDiagram
    tenants ||--o{ tenant_configs : has
    tenants ||--o{ session_indices : has

    tenants {
        uuid id PK
        varchar tenant_url
        varchar team_name
        text description
        boolean is_active
    }

    tenant_configs {
        uuid id PK
        uuid tenant_id FK
        varchar config_key
        text config_value
    }

    session_indices {
        uuid id PK
        uuid tenant_id FK
        varchar session_type
        jsonb index_data
        integer session_count
    }
Loading
Table Purpose Key Constraint
tenants Tenant (OPA instance + team combination) UNIQUE(tenant_url, team_name)
tenant_configs Per-tenant settings (Okta, AWS, OPA) UNIQUE(tenant_id, config_key)
session_indices Cached session list for fast search UNIQUE(tenant_id, session_type)

Config Keys (stored in tenant_configs):

Category Keys
Okta okta.issuer, okta.clientId, okta.clientSecret
AWS (Keys) aws.region, aws.bucket, aws.accessKeyId, aws.secretAccessKey
AWS (Roles Anywhere) aws.roleArn, aws.rolesAnywhereTrustAnchorArn, aws.rolesAnywhereProfileArn, aws.rolesAnywhereCertificate, aws.rolesAnywherePrivateKey
OPA API opaApi.keyId, opaApi.keySecret

Team name for OPA API comes from the tenants.team_name column.

Example: Multiple teams in one OPA instance:

-- Multiple teams in the same OPA instance (each is a separate tenant row)
INSERT INTO tenants (tenant_url, team_name, description) VALUES
    ('demo-blue-sky-1234.pam.okta.com', 'blue-sky', 'Blue Sky Team'),
    ('demo-blue-sky-1234.pam.okta.com', 'red-sky', 'Red Sky Team');

Note

Tables are auto-created on startup.


⚙️ Configuration

Environment Variables

Variable Single-Tenant Multi-Tenant Default Description
MULTITENANT NO Mode: YES/TRUE/1 or NO/FALSE/0
OKTA_ISSUER Required Okta issuer URL
OKTA_CLIENT_ID Required Okta client ID
OKTA_CLIENT_SECRET Required Okta client secret
AWS_REGION Required AWS region
AWS_S3_BUCKET Required S3 bucket name
Method 1: Access Keys
AWS_ACCESS_KEY_ID Conditional AWS access key (if not using Roles Anywhere)
AWS_SECRET_ACCESS_KEY Conditional AWS secret key (if not using Roles Anywhere)
Method 2: IAM Roles Anywhere
AWS_ROLES_ANYWHERE_TRUST_ANCHOR_ARN Conditional Trust Anchor ARN
AWS_ROLES_ANYWHERE_PROFILE_ARN Conditional Profile ARN
AWS_ROLES_ANYWHERE_ROLE_ARN Conditional IAM Role ARN
AWS_ROLES_ANYWHERE_CERTIFICATE Conditional X.509 certificate (PEM)
AWS_ROLES_ANYWHERE_PRIVATE_KEY Conditional Private key (PEM)
OPA Configuration
OPA_TENANT_URL Optional OPA instance URL (e.g., demo-blue-sky-1234.pam.okta.com)
OPA_TEAM_NAME Optional Team name within the OPA instance
OPA_API_KEY_ID Optional OPA API key ID (for graph)
OPA_API_KEY_SECRET Optional OPA API key secret
Database (Multi-Tenant Only)
PGHOST Required PostgreSQL hostname
PGPORT Optional 5432 PostgreSQL port
PGDATABASE Required Database name
PGUSER Required Database user
PGPASSWORD Required Database password
PGSSLMODE Optional require SSL mode
Application
BASE_URI Required Required Application URL
SESSION_SECRET Required Required Session key (min 32 chars)
NODE_ENV Optional Optional production Environment
PORT Optional Optional 3000 HTTP port
LOG_LEVEL Optional Optional info Logging level

Tip

For AWS authentication, use either Access Keys (Method 1) or IAM Roles Anywhere (Method 2). IAM Roles Anywhere is recommended for external deployments. See AWS.md for detailed setup.

Tenant Configuration (Multi-Tenant Only)

In multi-tenant mode, per-tenant settings are managed via the /config page and stored in the database:

Section Settings Editable
Okta OIDC Issuer, Client ID, Client Secret Read-only
AWS S3 Auth method, Region, Bucket, Access Keys or Roles Anywhere Yes
OPA API API Key ID, API Key Secret Yes

AWS Authentication Options:

  • Access Keys — Traditional access key ID + secret access key
  • IAM Roles Anywhere — X.509 certificate-based auth (recommended for external deployments)

For detailed AWS setup, see docs/AWS.md.

The config page displays certificate details (CN, expiration date, status) when using IAM Roles Anywhere. Switching authentication methods automatically cleans up unused credentials from the database.

Important

Okta settings are read-only in the UI for security. You can modify them only directly in the database.


🔧 OPA Gateway Setup

Opaflix requires session recordings in a specific filename format.

Required Filename Format

{protocol}~{timestamp}~{teamName}~{projectName}~{serverName}~{username}~{hash}.asa

Gateway Configuration

Edit /etc/sft/sft-gatewayd.yaml:

LogFileNameFormats:
  SSHRecording: "{{.Protocol}}~{{.StartTime}}~{{.TeamName}}~{{.ProjectName}}~{{.ServerName}}~{{.Username}}~"
  RDPRecording: "{{.Protocol}}~{{.StartTime}}~{{.TeamName}}~{{.ProjectName}}~{{.ServerName}}~{{.Username}}~"

Restart the gateway:

sudo systemctl restart sft-gatewayd

Okta Documentation


🔄 Session Conversion

OPA recordings (.asa) must be converted to playable formats before uploading to S3.

Prerequisites

Tool Purpose Installation
sft CLI Convert session recordings Okta Privileged Access Docs
RDP Transcoder RDP to MKV conversion RDP Transcoder Docs

Conversion Commands

# SSH: .asa → .cast
sft session-logs export --insecure --format asciinema /path/source.asa --output /path/output.cast

# RDP: .asa → .mkv
sft session-logs export --insecure --format mkv --output /path/output.mkv /path/source.asa

Note

The --insecure flag skips signature verification for offline/batch processing. Use only in trusted environments.

Conversion Script

A sample python script is provided, it will automatically look for any new files, convert them and upload them to S3.

Features:

  • Instant File Detection: Uses watchdog filesystem events for immediate processing of new files
  • S3 Integration: Uploads converted files directly to S3 via boto3
  • Duplicate Detection: Skips files already present in S3
  • Sequential Processing: Processes one file at a time for stability
  • Configurable Cleanup: Optionally deletes old source files
  • Graceful Shutdown: Handles SIGTERM/SIGINT for clean service stops
  • Comprehensive Logging: Detailed logs with configurable verbosity
  • Systemd Integration: Ready-to-use service file included

See scripts/convert-sessions/ for details.

S3 Upload

After conversion, sync to S3:

aws s3 sync /var/log/sft/converted/ssh/ s3://your-bucket/ --include "*.cast"
aws s3 sync /var/log/sft/converted/rdp/ s3://your-bucket/ --include "*.mkv"

Tip

Remember to add your Opaflix domain to the CORS configuration of your S3 bucket to allow direct streaming. You can find more information in the AWS Documentation


📡 API Reference

Routes

Method Path Auth Description
GET / Yes Dashboard
GET /sessions/list Yes Session list (?type=ssh or ?type=rdp)
GET /sessions/playback/ssh Yes SSH player
GET /sessions/playback/rdp Yes RDP player
GET /sessions/stream/rdp Yes Stream RDP video
GET /config Yes Configuration page
POST /config Yes Update configuration (multi-tenant only)
GET /graph Yes Infrastructure graph
GET /api/filter-options Yes Filter dropdown data (from sessions)
GET /api/refresh/status Yes Session refresh status
GET /login No Okta login
GET /logout Yes Clear session
GET /health No Health check

🔒 Security

Warning

Security Considerations: While basic security measures are implemented, Opaflix has not undergone a formal security audit. Use with caution, especially in production environments. I warmly suggest deploying behind a WAF and/or VPN, and not exposing it directly to the internet without proper protections in place.

Authentication

  • All routes except /health and /login require Okta authentication via OpenID Connect
  • Session cookies are httpOnly, secure, and sameSite

Security Headers

Helmet.js provides:

  • Content Security Policy (CSP)
  • X-Content-Type-Options
  • X-Frame-Options
  • Strict-Transport-Security (HSTS)
  • X-XSS-Protection

Input Validation

  • Path traversal prevention on S3 keys
  • Joi schema validation on all inputs
  • XSS prevention via Handlebars auto-escaping

Rate Limiting

  • Prevents abuse and ensures fair usage
  • Configurable in src/config/constants.js
Endpoint Type Limit Window
Session lists / API 10 req 1 minute
Playback / Downloads 10 req 1 minute

Rate limits apply per authenticated user. Exceeding limits returns HTTP 429.

Health Check

curl http://localhost:3000/health

Response:

{
  "status": "healthy",
  "timestamp": "2024-01-15T10:30:00.000Z",
  "checks": { "server": "ok", "s3": "ok", "memory": "ok" }
}

📊 Scalability Considerations

Opaflix stores session indices in JSONB format for fast search and pagination. The table below shows estimated memory usage and S3 listing times for different session counts:

Sessions JSONB Size In-Memory S3 Listing Time Recommendation
1,000 ~350 KB ~900 KB ~0.2s ✅ Optimal
10,000 ~3.5 MB ~9 MB ~2s ✅ Good
25,000 ~9 MB ~23 MB ~5s ✅ Good
50,000 ~18 MB ~45 MB ~10s ⚠️ Acceptable
75,000 ~26 MB ~68 MB ~15s ⚠️ Acceptable
100,000 ~35 MB ~90 MB ~20s ⚠️ Near limit
200,000 ~70 MB ~180 MB ~40s ❌ Consider alternatives
300,000 ~105 MB ~270 MB ~60s ❌ Not recommended
500,000 ~175 MB ~450 MB ~1.5 min ❌ Not recommended
1,000,000 ~350 MB ~900 MB ~3 min ❌ Architecture change needed

Notes:

  • JSONB Size: Storage in PostgreSQL session_indices table (~350 bytes per session)
  • In-Memory: RAM usage when index is loaded with enriched fields (~2.5x storage)
  • S3 Listing Time: Initial index build time (1,000 objects per API call, ~200ms each)

Important

For deployments exceeding 100,000 sessions, consider:

  • Implementing time-based partitioning (archive old sessions)
  • Migrating to a dedicated sessions table with proper SQL indexing
  • Using event-driven architecture (S3 triggers → Lambda → DB)

📚 Resources

Project Documentation

Document Description
README.md Main documentation and quick start guide (this file)
docs/AWS.md AWS S3 setup and configuration
scripts/convert-sessions/README.md Sessions conversion scripts
scripts/aws/README.md AWS CLI utilities and CloudFormation template
CHANGELOG.md Version history
CLAUDE.md AI assistant context

External Resources


👤 Author

Fabio Grasso

🙏 Special Thanks

  • Pascale Kik - For her invaluable feedback and testing efforts during the early stages of development. Her insights helped shape the user experience and identify critical issues.
  • Okta Community - For their support and engagement, which inspired the creation of Opaflix as a tool to benefit PAM administrators and auditors.

Last Updated: 2026-07-15

Made with ❤️ for the Okta community

  • While I am an employee of Okta, Inc. this project was developed by me entirely in my private capacity.  Opaflix is not an official Okta product and is not endorsed, supported or maintained by Okta.*