# Security Model & Best Practices ## Credential Management ### CRITICAL: Never Store Plaintext Passwords All SSH credentials must be handled securely: #### Option 1: Runtime Credentials (Recommended) User provides SSH credentials at push time: ```typescript // Frontend const response = await fetch('/api/v1/configurations/123/push', { method: 'POST', body: JSON.stringify({ configuration_id: 123, device_id: 456, ssh_username: 'admin', ssh_password: 'SecurePass123', // ONLY sent at push time confirmed: true, dry_run: false }) }) ``` Backend receives credentials in request, uses immediately, discards after execution. #### Option 2: Encrypted Storage (Advanced) If storing credentials (not recommended without vault): ```python # Backend from app.core.security import encrypt_credential, decrypt_credential # Encrypt before storing encrypted = encrypt_credential("plaintext_password") device.ssh_password_encrypted = encrypted # Decrypt only when pushing plaintext = decrypt_credential(device.ssh_password_encrypted) executor = SSHExecutor(..., password=plaintext) # Use immediately plaintext = None # Clear from memory ``` ⚠️ **Never** use hardcoded encryption keys. Use: - AWS KMS - HashiCorp Vault - Azure Key Vault - Environment variables (CI/CD only) ## Access Control ### User Isolation - Each user owns their projects - Projects contain devices & configs - API enforces `project.owner_id == current_user.id` ```python # Backend example def get_project(project_id: int, current_user: User): project = db.query(Project).filter(Project.id == project_id).first() if project.owner_id != current_user.id: raise HTTPException(status_code=403, detail="Forbidden") return project ``` ### Token-Based Authentication - JWT tokens expire after 30 minutes - Refresh token pattern (future: V1) - Token contains only user email (no sensitive data) ```python # core/security.py def create_access_token(data: dict): to_encode = data.copy() expire = datetime.utcnow() + timedelta(minutes=30) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode( to_encode, settings.secret_key, algorithm=settings.algorithm ) return encoded_jwt ``` ## SSH Security ### Pre-Push Validation 1. ✅ Dry-run mode (generate only, no execution) 2. ✅ CLI preview (user sees exact commands) 3. ✅ Confirmation modal (explicit yes/no) 4. ✅ Backup running-config before push ### Connection Security - SSH only (no Telnet) - Device authentication via username/password or SSH keys (future) - 30-second timeout (prevent hanging) - Error handling without exposing internals ```python # ssh/__init__.py def connect(self): try: device = { "device_type": "cisco_ios", "host": self.ip_address, "username": self.username, "password": self.password, "port": self.ssh_port, "timeout": self.timeout, # 30s timeout } self.connection = ConnectHandler(**device) except NetmikoAuthenticationException as e: raise SSHConnectionError(f"Auth failed") # No password leak except NetmikoTimeoutException as e: raise SSHConnectionError(f"Timeout") # Generic error ``` ### Audit Logging All SSH operations logged to `push_logs` table: ```sql -- What was pushed - device_id - configuration_id - commands_sent (plain text) - device_output (verbatim) -- When & by whom - created_at - pushed_by (username) -- Safety - pre_push_backup (running-config before) - was_rolled_back (if reverted) - rollback_reason ``` Example query to review push history: ```sql SELECT id, device_id, created_at, pushed_by, status FROM push_logs WHERE device_id = 456 ORDER BY created_at DESC LIMIT 10; ``` ## Network Security ### HTTPS in Production ```python # main.py production config if not settings.debug: # Redirect HTTP to HTTPS app.add_middleware(HTTPSRedirectMiddleware) # Add HSTS header app.add_middleware(HSTSMiddleware, max_age=31536000) ``` ### CORS Configuration ```python # main.py app.add_middleware( CORSMiddleware, allow_origins=["https://your-domain.com"], # NOT * allow_credentials=True, allow_methods=["GET", "POST", "PUT", "DELETE"], allow_headers=["Authorization", "Content-Type"], ) ``` ### API Rate Limiting (TODO: V1) ```python # Prevent brute force / DoS # 100 requests per minute per IP # 1000 requests per hour per user ``` ## Database Security ### SQL Injection Prevention Use SQLAlchemy ORM (parameterized queries): ```python # ✅ Safe - parameterized user = db.query(User).filter(User.email == user_email).first() # ❌ NEVER - raw SQL with string formatting user = db.execute(f"SELECT * FROM users WHERE email = '{user_email}'") ``` ### Sensitive Data - Passwords: BCrypt hashed (never plaintext) - SSH credentials: Encrypted or runtime-only - API keys: Environment variables (never in code) ## Disclaimer & User Awareness ### In UI ```html

⚠️ WARNING

This tool executes commands on live network devices.

Review all configurations before pushing.

Test in lab environment first.

Backup devices before any changes.

All operations are logged for audit.

``` ### In API ```python # main.py @app.get("/api/v1/docs") def docs(): """ Include security warning in OpenAPI docs """ return { "warning": "This API modifies network device configurations via SSH. Use with caution.", "docs": "...", } ``` ## Deployment Checklist - [ ] **Secrets**: Use environment variables (AWS Secrets Manager, etc.) - [ ] **SSL/TLS**: Enforce HTTPS only - [ ] **CORS**: Lock down to your domain - [ ] **Database**: PostgreSQL with strong credentials, encrypted connection - [ ] **SSH Keys**: Support SSH key auth (not just passwords) - [ ] **Logging**: Centralized logging (ELK, Datadog, etc.) - [ ] **Monitoring**: Alert on failed pushes, timeout errors - [ ] **Backup**: Database backups daily - [ ] **Compliance**: Log retention policy (min 90 days) - [ ] **Testing**: Penetration test before launch ## Future: V1 Features - [ ] SSH key authentication (instead of passwords) - [ ] OAuth 2.0 / SSO integration - [ ] Role-based access control (RBAC) - [ ] Encryption at rest (database) - [ ] Multi-factor authentication (MFA) - [ ] Webhook integration for audit events - [ ] Compliance reporting (PCI-DSS, SOC2)