Files

11 KiB
Raw Permalink Blame History

Flowfish - Security Design

🔒 Overview

Flowfish platform security is designed according to Defense in Depth (layered defense) principles.


🎯 Security Principles

  1. Least Privilege: Minimum necessary privileges
  2. Defense in Depth: Multi-layered security
  3. Zero Trust: Verify every request
  4. Encryption: Data encryption (at-rest & in-transit)
  5. Audit: Log all operations
  6. Isolation: Multi-tenant isolation

🔐 Authentication

1. JWT Token Authentication

Token Structure:

{
  "header": {
    "alg": "HS256",
    "typ": "JWT"
  },
  "payload": {
    "user_id": 123,
    "username": "admin",
    "roles": ["Super Admin"],
    "exp": 1704067200,
    "iat": 1704063600
  }
}

Token Lifecycle:

  • Access Token: 1 hour (short-lived)
  • Refresh Token: 7 days (long-lived)
  • Storage: HttpOnly cookie (XSS protection)
  • Transmission: Authorization header

Token Generation:

import jwt
from datetime import datetime, timedelta

def create_access_token(user_id, username, roles):
    payload = {
        'user_id': user_id,
        'username': username,
        'roles': roles,
        'exp': datetime.utcnow() + timedelta(hours=1),
        'iat': datetime.utcnow()
    }
    return jwt.encode(payload, SECRET_KEY, algorithm='HS256')

Token Validation:

  • Signature verification
  • Expiration check
  • Blacklist check (Redis)
  • Role/permission check

2. OAuth 2.0 / SSO

Supported Providers:

  • Google Workspace
  • Microsoft Azure AD / Entra ID
  • Okta
  • Keycloak

OAuth Flow (Authorization Code):

User → Login Button → Flowfish Frontend
  → Redirect to OAuth Provider
  → User authenticates
  → Provider redirects with auth code
  → Flowfish exchanges code for token
  → Create/update user in database
  → Issue JWT token
  → Redirect to dashboard

Security Controls:

  • State parameter (CSRF protection)
  • PKCE (Proof Key for Code Exchange)
  • Token validation
  • User account linking

3. Kubernetes Service Account

In-Cluster Authentication:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: flowfish-backend
  namespace: flowfish
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: flowfish-reader
rules:
- apiGroups: [""]
  resources: ["pods", "services", "namespaces"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: flowfish-reader-binding
subjects:
- kind: ServiceAccount
  name: flowfish-backend
  namespace: flowfish
roleRef:
  kind: ClusterRole
  name: flowfish-reader
  apiGroup: rbac.authorization.k8s.io

👥 Authorization

RBAC (Role-Based Access Control)

Role Hierarchy:

Super Admin (ALL permissions)
  └─ Platform Admin (Management + Analysis)
      └─ Security Analyst (Security + Read)
          └─ Developer (Read-only)

Permission Model:

Permission = Resource + Action
Example: clusters.view, analyses.execute, users.delete

Permission Matrix:

Resource Super Admin Platform Admin Security Analyst Developer
clusters.*
analyses.*
dependencies.view
anomalies.*
users.*
audit.view

Middleware Implementation:

async def require_permission(permission: str):
    def decorator(func):
        @wraps(func)
        async def wrapper(request: Request, *args, **kwargs):
            user = request.state.user
            if not has_permission(user, permission):
                raise HTTPException(403, "Insufficient permissions")
            return await func(request, *args, **kwargs)
        return wrapper
    return decorator

@app.get("/api/v1/clusters")
@require_permission("clusters.view")
async def get_clusters():
    ...

Multi-Tenant Isolation

Tenant Separation:

  • Cluster-level: Users can only access assigned clusters
  • Namespace-level: Namespace-based access control
  • Data-level: SQL WHERE clauses filter by user

Row-Level Security (PostgreSQL):

-- Enable RLS
ALTER TABLE workloads ENABLE ROW LEVEL SECURITY;

-- Policy: Users can only see workloads from their clusters
CREATE POLICY workload_isolation ON workloads
    FOR SELECT
    USING (cluster_id IN (
        SELECT cluster_id FROM user_cluster_access
        WHERE user_id = current_user_id()
    ));

🔒 Data Encryption

1. At-Rest Encryption

Database Encryption:

PostgreSQL:

# Transparent Data Encryption (TDE)
# Using encrypted volumes or PostgreSQL 15+ TDE
pgcrypto extension for column-level encryption

Encryption Example:

-- Encrypt sensitive fields
CREATE TABLE oauth_providers (
    id SERIAL PRIMARY KEY,
    client_secret_encrypted TEXT NOT NULL,
    ...
);

-- Encrypt on insert
INSERT INTO oauth_providers (client_secret_encrypted)
VALUES (pgp_sym_encrypt('secret_value', 'encryption_key'));

-- Decrypt on select
SELECT pgp_sym_decrypt(client_secret_encrypted::bytea, 'encryption_key')
FROM oauth_providers;

ClickHouse:

  • Disk encryption via OS (LUKS)
  • Column-level encryption (optional)

Neo4j:

  • Volume encryption via Kubernetes PVC

Redis:

  • Volume encryption
  • No built-in encryption

Key Management:

  • Development: Environment variables
  • Production: Kubernetes Secrets + External Secrets Operator
  • Enterprise: HashiCorp Vault, AWS KMS, Azure Key Vault

2. In-Transit Encryption

TLS/SSL Everywhere:

Frontend ↔ User:

HTTPS (TLS 1.3)
- Certificate: Let's Encrypt or corporate CA
- Strong ciphers only
- HSTS header enabled

Frontend ↔ Backend:

HTTPS (TLS 1.2+)
- Internal service mesh (optional: mTLS via Istio)
- Certificate rotation

Backend ↔ Databases:

PostgreSQL:

# Connection string with SSL
DATABASE_URL = "postgresql://user:pass@host:5432/db?sslmode=require"

ClickHouse:

<clickhouse>
    <https_port>8443</https_port>
    <openSSL>
        <server>
            <certificateFile>/etc/clickhouse-server/server.crt</certificateFile>
            <privateKeyFile>/etc/clickhouse-server/server.key</privateKeyFile>
        </server>
    </openSSL>
</clickhouse>

Neo4j:

# SSL enabled connection
ssl:
  enable: true
  cert_path: /path/to/cert.pem
  key_path: /path/to/key.pem

🛡️ Application Security

1. Input Validation

Backend Validation (Pydantic):

from pydantic import BaseModel, validator, constr

class ClusterCreate(BaseModel):
    name: constr(min_length=3, max_length=255)
    api_url: HttpUrl
    
    @validator('name')
    def validate_name(cls, v):
        if not re.match(r'^[a-z0-9-]+$', v):
            raise ValueError('Name must be lowercase alphanumeric with hyphens')
        return v

SQL Injection Prevention:

# ✅ Good: Parameterized query
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))

# ❌ Bad: String concatenation
cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")

2. XSS Prevention

React:

  • Default XSS protection (JSX escaping)
  • DangerouslySetInnerHTML avoided

Content Security Policy:

Content-Security-Policy: 
  default-src 'self';
  script-src 'self' 'unsafe-inline' 'unsafe-eval';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  connect-src 'self' wss:;
  frame-ancestors 'none';

3. CSRF Protection

SameSite Cookies:

response.set_cookie(
    key="refresh_token",
    value=token,
    httponly=True,
    secure=True,
    samesite="strict"
)

CSRF Token:

# Generate token
csrf_token = secrets.token_urlsafe(32)
session['csrf_token'] = csrf_token

# Validate token
if request.form['csrf_token'] != session['csrf_token']:
    raise HTTPException(403, "CSRF token mismatch")

4. Rate Limiting

API Rate Limiting:

from slowapi import Limiter
from slowapi.util import get_remote_address

limiter = Limiter(key_func=get_remote_address)

@app.get("/api/v1/clusters")
@limiter.limit("100/hour")
async def get_clusters():
    ...

Redis-based Rate Limiting:

def check_rate_limit(user_id, limit=100, window=3600):
    key = f"rate_limit:{user_id}"
    current = redis.incr(key)
    if current == 1:
        redis.expire(key, window)
    if current > limit:
        raise HTTPException(429, "Rate limit exceeded")

🔐 Kubernetes Security

1. Network Policies

Default Deny:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: flowfish
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

Allow Backend → Database:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-to-postgres
  namespace: flowfish
spec:
  podSelector:
    matchLabels:
      app: postgresql
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: backend
    ports:
    - protocol: TCP
      port: 5432

2. Pod Security

Pod Security Standards:

apiVersion: v1
kind: Pod
metadata:
  name: backend
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    fsGroup: 2000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: backend
    image: flowfish/backend:latest
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
        - ALL
    resources:
      limits:
        memory: "4Gi"
        cpu: "2000m"
      requests:
        memory: "1Gi"
        cpu: "500m"

3. Secrets Management

Kubernetes Secrets:

apiVersion: v1
kind: Secret
metadata:
  name: database-credentials
  namespace: flowfish
type: Opaque
stringData:
  username: flowfish_user
  password: <strong-password>

External Secrets Operator:

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: vault-backend
  namespace: flowfish
spec:
  provider:
    vault:
      server: "https://vault.example.com"
      path: "secret"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "flowfish"
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: database-secret
  namespace: flowfish
spec:
  refreshInterval: 15m
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: database-credentials
  data:
  - secretKey: password
    remoteRef:
      key: database/credentials
      property: password

📝 Audit Logging

Comprehensive Logging

Log Everything:

  • User login/logout
  • API requests (method, path, user, IP)
  • Database changes (CREATE, UPDATE, DELETE)
  • Permission changes
  • Configuration changes
  • Anomaly detections
  • Import/export operations

Log Format (JSON):

{
  "timestamp": "2024-01-15T10:30:45.123Z",
  "level": "INFO",
  "user_id": 123,
  "username": "admin",
  "action": "create_cluster",
  "resource_type": "cluster",
  "resource_id": "cluster-prod-01",
  "ip_address": "10.0.1.50",
  "user_agent": "Mozilla/5.0...",
  "request_id": "abc-def-123",
  "success": true,
  "details": {...}
}
Fixing a typo introduced in `06-security.md` JSON.

<tool▁calls▁begin><tool▁call▁begin> StrReplace