# Open Notebook CORS Configuration and Security Hardening: Production Deployment Guide

> Secure your Open Notebook production deployment. Learn how to configure CORS origins, enable password authentication, and harden your container for maximum security with this essential guide.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: how-to-guide
- Published: 2026-06-22

---

**Secure your Open Notebook deployment by restricting CORS origins to specific domains via the `CORS_ORIGINS` environment variable, enabling password authentication with `OPEN_NOTEBOOK_PASSWORD`, and applying container hardening measures including localhost binding and privilege restrictions.**

Open Notebook ships with a development-friendly CORS policy that accepts requests from any origin (`"*"`), making it critical to harden settings before deploying to production. The FastAPI backend in `lfnovo/open-notebook` provides environment-based configuration for origin restrictions, password authentication, and encryption key management. This guide covers the exact steps to secure your API using the configuration system implemented in the source code.

## CORS Configuration for Production

### Default Development Behavior

If the environment variable `CORS_ORIGINS` is undefined, the API logs a security warning and accepts cross-origin requests from any origin. This behavior is implemented in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) during application startup, where the middleware initializes with a wildcard origin list when no explicit configuration is provided.

### Restricting Allowed Origins

For production, define `CORS_ORIGINS` as a comma-separated list of exact URLs including the scheme and port. The value is parsed once at module load by the `_parse_cors_origins` function and fed to FastAPI’s `CORSMiddleware`. Only the listed origins receive the `Access-Control-Allow-Origin` header, while browsers block unlisted origins and omit the header from error responses to prevent credential leakage.

```bash

# .env production example

CORS_ORIGINS=https://notebook.example.com,https://admin.example.com:8080

```

```python

# api/main.py - CORS middleware initialization

app.add_middleware(
    CORSMiddleware,
    allow_origins=CORS_ALLOWED_ORIGINS,  # Derived from CORS_ORIGINS env var

    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

```

## Authentication and Encryption

### Password Protection

The `PasswordAuthMiddleware` in [`api/auth.py`](https://github.com/lfnovo/open-notebook/blob/main/api/auth.py) enforces bearer-token authentication on every request except explicitly excluded public endpoints. The middleware retrieves the password from the `OPEN_NOTEBOOK_PASSWORD` environment variable or a corresponding Docker secret file.

```python

# api/auth.py

class PasswordAuthMiddleware(BaseHTTPMiddleware):
    def __init__(self, app, excluded_paths: Optional[list] = None):
        super().__init__(app)
        self.password = get_secret_from_env("OPEN_NOTEBOOK_PASSWORD")

```

### API Key Encryption

Stored credentials are encrypted using Fernet symmetric encryption derived from `OPEN_NOTEBOOK_ENCRYPTION_KEY`. The key must be supplied in production; otherwise, the application fails to store credentials securely and logs a warning during startup in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py). The key retrieval logic handles both environment variables and Docker secrets in [`open_notebook/utils/encryption.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/encryption.py).

```bash

# Generate secure keys

OPEN_NOTEBOOK_PASSWORD=$(openssl rand -base64 24)
OPEN_NOTEBOOK_ENCRYPTION_KEY=$(openssl rand -base64 32)

```

## Container and Network Hardening

### Docker Security Options

Run the container as a non-privileged user with explicit resource constraints. Bind the API port only to localhost to prevent direct external access, forcing traffic through a reverse proxy.

```yaml

# docker-compose.yml production configuration

services:
  open_notebook:
    image: lfnovo/open_notebook:v1-latest
    ports:
      - "127.0.0.1:8502:8502"  # Localhost only

    environment:
      - CORS_ORIGINS=https://notebook.example.com
      - OPEN_NOTEBOOK_PASSWORD=${OPEN_NOTEBOOK_PASSWORD}
      - OPEN_NOTEBOOK_ENCRYPTION_KEY=${OPEN_NOTEBOOK_ENCRYPTION_KEY}
    security_opt:
      - no-new-privileges:true
    deploy:
      resources:
        limits:
          memory: 2G
          cpus: "1.0"
    restart: always

```

### Firewall and Reverse Proxy

Block direct access to internal ports using `ufw` or `iptables`. The API typically listens on port `8502` (SurrealDB) and `5055` (API), which should not be exposed to the public internet. Terminate TLS at a reverse proxy (nginx, Caddy, or Traefik) and forward requests to the internal bound address.

```bash

# UFW firewall configuration

sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw deny 8502/tcp   # SurrealDB

sudo ufw deny 5055/tcp   # API

sudo ufw enable

```

## Additional Production Hardening

*   **Secret Management** – Never commit credentials to source control; use Docker secrets or environment files with restricted permissions.
*   **HTTPS Enforcement** – Since passwords are transmitted as bearer tokens, TLS encryption is mandatory to prevent eavesdropping.
*   **External Authentication** – Consider integrating OAuth2 or SSO for multi-user enterprise deployments instead of relying solely on shared passwords.
*   **Monitoring** – Enable log aggregation and alerting for authentication failures; implement rate-limiting at the reverse proxy level.

## Summary

*   Set `CORS_ORIGINS` to a comma-separated list of specific domains to replace the default wildcard policy in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py).
*   Configure `OPEN_NOTEBOOK_PASSWORD` and `OPEN_NOTEBOOK_ENCRYPTION_KEY` to enable the `PasswordAuthMiddleware` and secure credential storage.
*   Bind Docker ports to `127.0.0.1` only, apply `no-new-privileges:true`, and set resource limits to harden the container runtime.
*   Block direct access to ports `8502` and `5055` using firewall rules and use a TLS-terminating reverse proxy for all traffic.
*   Reference the security documentation in [`docs/5-CONFIGURATION/security.md`](https://github.com/lfnovo/open-notebook/blob/main/docs/5-CONFIGURATION/security.md) for comprehensive hardening guidelines.

## Frequently Asked Questions

### What happens if I don't set CORS_ORIGINS in production?

The application will log a security warning and allow requests from any origin (`"*"`), exposing your API to cross-origin attacks from malicious websites. The `CORSMiddleware` in [`api/main.py`](https://github.com/lfnovo/open-notebook/blob/main/api/main.py) defaults to permissive settings only when `CORS_ORIGINS` is undefined, making explicit configuration critical for production security.

### How does Open Notebook handle password authentication?

The `PasswordAuthMiddleware` class in [`api/auth.py`](https://github.com/lfnovo/open-notebook/blob/main/api/auth.py) intercepts every incoming request and validates a bearer token against the `OPEN_NOTEBOOK_PASSWORD` environment variable. Requests to protected endpoints must include an `Authorization: Bearer <password>` header, while a few public paths (like health checks) are excluded from this requirement.

### Can I use Docker secrets instead of environment variables for sensitive keys?

Yes. The application checks for suffixed `*_FILE` environment variables (e.g., `OPEN_NOTEBOOK_PASSWORD_FILE`) and reads the secret content from the specified file path. This pattern is implemented in the utility functions referenced in [`open_notebook/utils/encryption.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/encryption.py) and supported for both the encryption key and authentication password.

### Why should I bind the container port to 127.0.0.1?

Binding to localhost (`127.0.0.1:8502:8502`) ensures the API is not directly accessible from the network interface, eliminating attack vectors against the FastAPI application and SurrealDB. All external traffic must route through your reverse proxy, which handles TLS termination and request filtering before forwarding to the internal localhost address.