open-notebook/docs/deployment/security.md

13 KiB

Security Configuration

Open Notebook includes optional password protection and security features for users who need to deploy their instances publicly or in shared environments.

🔒 Password Protection

When to Use Password Protection

Password protection is recommended for:

  • Public cloud deployments - PikaPods, Railway, DigitalOcean, AWS, etc.
  • Shared network environments - Corporate networks, shared servers
  • Team deployments - Multiple users accessing the same instance
  • Production environments - Any deployment accessible beyond localhost

When NOT to Use Password Protection

Skip password protection for:

  • Local development - Running on your personal machine
  • Private networks - Secure, isolated network environments
  • Single-user setups - Only you have access to the machine
  • Testing environments - Temporary or development instances

🚀 Quick Setup

Docker Deployment

For Docker deployments, add the password to your environment:

# docker-compose.yml
services:
  open_notebook:
    image: lfnovo/open_notebook:v1-latest-single
    ports:
      - "8502:8502"
    environment:
      - OPENAI_API_KEY=your_openai_key
      - OPEN_NOTEBOOK_PASSWORD=your_secure_password
    volumes:
      - ./notebook_data:/app/data
    restart: always

Or using a .env file:

# .env file
OPENAI_API_KEY=your_openai_key
OPEN_NOTEBOOK_PASSWORD=your_secure_password

Development Setup

For development installations, add to your .env file:

# .env file in project root
OPEN_NOTEBOOK_PASSWORD=your_secure_password

🔐 Password Requirements

Choosing a Secure Password

  • Length: Minimum 12 characters, preferably 20+
  • Complexity: Mix of uppercase, lowercase, numbers, and symbols
  • Uniqueness: Don't reuse passwords from other services
  • Avoid: Common words, personal information, predictable patterns

Password Examples

# Good passwords
OPEN_NOTEBOOK_PASSWORD=MySecure2024!Research#Tool
OPEN_NOTEBOOK_PASSWORD=Notebook$Dev$2024$Strong!

# Bad passwords (don't use these)
OPEN_NOTEBOOK_PASSWORD=password123
OPEN_NOTEBOOK_PASSWORD=opennotebook
OPEN_NOTEBOOK_PASSWORD=admin

Password Management

  • Use a password manager to generate and store the password
  • Document the password in your team's secure password vault
  • Rotate passwords regularly for production deployments
  • Share securely - Never send passwords via email or chat

🛡️ How Security Works

React frontend Protection

When password protection is enabled:

  1. Login form appears on first visit
  2. Session storage - Password stored in browser session
  3. Persistent login - Users stay logged in until browser closure
  4. No logout button - Clear browser data to log out

API Protection

All API endpoints require authentication:

# API calls require Authorization header
curl -H "Authorization: Bearer your_password" \
  http://localhost:5055/api/notebooks

Excluded Endpoints

These endpoints work without authentication:

  • Health check: /health - System status
  • API documentation: /docs - OpenAPI documentation
  • OpenAPI spec: /openapi.json - API schema

🔧 Configuration Examples

Single Container with Security

# docker-compose.single.yml
services:
  open_notebook_single:
    image: lfnovo/open_notebook:v1-latest-single
    ports:
      - "8502:8502"
      - "5055:5055"
    environment:
      - OPENAI_API_KEY=sk-your-openai-key
      - OPEN_NOTEBOOK_PASSWORD=your_secure_password
      - ANTHROPIC_API_KEY=sk-ant-your-anthropic-key
    volumes:
      - ./notebook_data:/app/data
      - ./surreal_single_data:/mydata
    restart: always

Multi-Container with Security

# docker-compose.yml
services:
  surrealdb:
    image: surrealdb/surrealdb:v1-latest
    ports:
      - "127.0.0.1:8000:8000"  # Bind to localhost only
    command: start --log warn --user root --pass root file:///mydata/database.db
    volumes:
      - ./surreal_data:/mydata
    restart: always

  open_notebook:
    image: lfnovo/open_notebook:v1-latest
    ports:
      - "8502:8502"
      - "5055:5055"
    environment:
      - OPENAI_API_KEY=sk-your-openai-key
      - OPEN_NOTEBOOK_PASSWORD=your_secure_password
      - SURREAL_URL=ws://surrealdb:8000/rpc
      - SURREAL_USER=root
      - SURREAL_PASSWORD=root
    volumes:
      - ./notebook_data:/app/data
    depends_on:
      - surrealdb
    restart: always

Development with Security

# .env file for development
OPEN_NOTEBOOK_PASSWORD=dev_password_2024

# Database
SURREAL_URL=ws://localhost:8000/rpc
SURREAL_USER=root
SURREAL_PASSWORD=root
SURREAL_NAMESPACE=open_notebook
SURREAL_DATABASE=development

# AI Providers
OPENAI_API_KEY=sk-your-openai-key
ANTHROPIC_API_KEY=sk-ant-your-anthropic-key

🌐 Production Security

HTTPS/TLS Configuration

Always use HTTPS in production. Here's an nginx reverse proxy example:

# /etc/nginx/sites-available/open-notebook
server {
    listen 80;
    server_name your-domain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name your-domain.com;
    
    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
    
    # Security headers
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
    
    # React frontend
    location / {
        proxy_pass http://127.0.0.1:8502;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # WebSocket support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
    
    # API endpoints
    location /api/ {
        proxy_pass http://127.0.0.1:5055;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Firewall Configuration

Configure your firewall to restrict access:

# UFW (Ubuntu)
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw deny 8502/tcp  # Block direct access to Next.js
sudo ufw deny 5055/tcp  # Block direct access to API
sudo ufw enable

# iptables
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -p tcp --dport 8502 -j DROP
iptables -A INPUT -p tcp --dport 5055 -j DROP

Docker Security

# Production docker-compose.yml with security
services:
  open_notebook:
    image: lfnovo/open_notebook:v1-latest
    ports:
      - "127.0.0.1:8502:8502"  # Bind to localhost only
      - "127.0.0.1:5055:5055"
    environment:
      - OPEN_NOTEBOOK_PASSWORD=your_secure_password
    volumes:
      - ./notebook_data:/app/data
    restart: always
    security_opt:
      - no-new-privileges:true
    read_only: true
    tmpfs:
      - /tmp
      - /var/tmp
    deploy:
      resources:
        limits:
          memory: 2G
          cpus: "1.0"

🔍 API Authentication

Making Authenticated API Calls

# Get all notebooks
curl -H "Authorization: Bearer your_password" \
  http://localhost:5055/api/notebooks

# Create a new notebook
curl -X POST \
  -H "Authorization: Bearer your_password" \
  -H "Content-Type: application/json" \
  -d '{"name": "My Notebook", "description": "Research notes"}' \
  http://localhost:5055/api/notebooks

# Upload a file
curl -X POST \
  -H "Authorization: Bearer your_password" \
  -F "file=@document.pdf" \
  http://localhost:5055/api/sources/upload

Python API Client

import requests

class OpenNotebookClient:
    def __init__(self, base_url: str, password: str):
        self.base_url = base_url
        self.headers = {"Authorization": f"Bearer {password}"}
    
    def get_notebooks(self):
        response = requests.get(
            f"{self.base_url}/api/notebooks",
            headers=self.headers
        )
        return response.json()
    
    def create_notebook(self, name: str, description: str = None):
        data = {"name": name, "description": description}
        response = requests.post(
            f"{self.base_url}/api/notebooks",
            headers=self.headers,
            json=data
        )
        return response.json()

# Usage
client = OpenNotebookClient("http://localhost:5055", "your_password")
notebooks = client.get_notebooks()

🚨 Security Considerations

Important Limitations

Open Notebook's password protection provides basic access control, not enterprise-grade security:

  • Plain text transmission - Passwords sent over HTTP (use HTTPS)
  • No password hashing - Passwords stored in memory as plain text
  • No user management - Single password for all users
  • No session timeout - Sessions persist until browser closure
  • No rate limiting - No protection against brute force attacks
  • No audit logging - No security event logging

Risk Mitigation

  1. Use HTTPS - Always encrypt traffic with TLS
  2. Strong passwords - Use complex, unique passwords
  3. Network security - Implement proper firewall rules
  4. Regular updates - Keep containers and dependencies updated
  5. Monitoring - Monitor access logs and system resources
  6. Backup strategy - Regular backups of data and configurations

Enterprise Considerations

For enterprise deployments requiring advanced security:

  • SSO integration - Consider implementing OAuth2/SAML
  • Role-based access - Implement user roles and permissions
  • Audit logging - Track all user actions and API calls
  • Rate limiting - Implement API rate limiting
  • Database encryption - Encrypt data at rest
  • Network segmentation - Isolate services in secure networks

🔧 Troubleshooting

Common Security Issues

Password Not Working

# Check environment variable is set
docker compose exec open_notebook env | grep OPEN_NOTEBOOK_PASSWORD

# Check container logs
docker compose logs open_notebook | grep -i auth

# Test API directly
curl -H "Authorization: Bearer your_password" \
  http://localhost:5055/health

UI Shows Login Form but API Doesn't

# Environment variable might not be set for API
docker compose exec open_notebook env | grep OPEN_NOTEBOOK_PASSWORD

# Restart services
docker compose restart

# Check both services are using the same password
docker compose logs | grep -i password

401 Unauthorized Errors

# Check authorization header format
curl -v -H "Authorization: Bearer your_password" \
  http://localhost:5055/api/notebooks

# Verify password matches environment variable
echo $OPEN_NOTEBOOK_PASSWORD

# Check for special characters in password
echo "Password contains: $(echo $OPEN_NOTEBOOK_PASSWORD | wc -c) characters"

Cannot Access After Setting Password

# Clear browser cache and cookies
# Try incognito/private mode
# Check browser console for errors
# Verify password is correct

Security Testing

# Test without password (should fail)
curl http://localhost:5055/api/notebooks

# Test with correct password (should succeed)
curl -H "Authorization: Bearer your_password" \
  http://localhost:5055/api/notebooks

# Test health endpoint (should work without password)
curl http://localhost:5055/health

# Test documentation (should work without password)
curl http://localhost:5055/docs

📞 Getting Help

Security Issues

If you discover security vulnerabilities:

  1. Do not open public issues for security problems
  2. Contact the maintainer directly via email
  3. Provide detailed information about the vulnerability
  4. Allow time for fixes before public disclosure

Community Support

For security configuration help:

Best Practices

  1. Test security thoroughly before production deployment
  2. Monitor logs regularly for suspicious activity
  3. Keep updated with security patches and updates
  4. Follow principle of least privilege in network configuration
  5. Regular security reviews of your deployment

Ready to secure your deployment? Start with the Quick Setup section above and always use HTTPS in production!