# 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**:
```json
{
"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**:
```python
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**:
```yaml
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**:
```python
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):
```sql
-- 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**:
```bash
# Transparent Data Encryption (TDE)
# Using encrypted volumes or PostgreSQL 15+ TDE
pgcrypto extension for column-level encryption
```
**Encryption Example**:
```sql
-- 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**:
```python
# Connection string with SSL
DATABASE_URL = "postgresql://user:pass@host:5432/db?sslmode=require"
```
**ClickHouse**:
```xml
8443
/etc/clickhouse-server/server.crt
/etc/clickhouse-server/server.key
```
**Neo4j**:
```yaml
# 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):
```python
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**:
```python
# β
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**:
```http
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**:
```python
response.set_cookie(
key="refresh_token",
value=token,
httponly=True,
secure=True,
samesite="strict"
)
```
**CSRF Token**:
```python
# 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**:
```python
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**:
```python
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**:
```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: flowfish
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
```
**Allow Backend β Database**:
```yaml
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**:
```yaml
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**:
```yaml
apiVersion: v1
kind: Secret
metadata:
name: database-credentials
namespace: flowfish
type: Opaque
stringData:
username: flowfish_user
password:
```
**External Secrets Operator**:
```yaml
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):
```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