From 256e7eb47d43d00b2f505c84214895d07867ebaa Mon Sep 17 00:00:00 2001 From: Snider Date: Thu, 29 Jan 2026 21:20:00 +0000 Subject: [PATCH] docs: add architecture and security documentation Adds comprehensive documentation for the core-developer package: - architecture.md: directory structure, boot lifecycle, component patterns - security.md: threat model, authorization layers, data protection Co-Authored-By: Claude Opus 4.5 --- docs/architecture.md | 390 +++++++++++++++++++++++++++++++++++++++++++ docs/security.md | 269 +++++++++++++++++++++++++++++ 2 files changed, 659 insertions(+) create mode 100644 docs/architecture.md create mode 100644 docs/security.md diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..b7539b8 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,390 @@ +--- +title: Architecture +description: Technical architecture of the core-developer package +updated: 2026-01-29 +--- + +# Architecture + +The `core-developer` package provides administrative developer tools for the Host UK platform. It is designed exclusively for "Hades" tier users (god-mode access) and includes debugging, monitoring, and server management capabilities. + +## Package Overview + +| Aspect | Detail | +|--------|--------| +| Namespace | `Core\Developer\` | +| Type | L1 Module (Laravel Package) | +| Dependencies | `host-uk/core`, `host-uk/core-admin` | +| PHP Version | 8.2+ | +| Laravel Version | 11.x / 12.x | +| Livewire Version | 3.x / 4.x | + +## Directory Structure + +``` +src/ +├── Boot.php # Service provider & event handlers +├── Controllers/ +│ └── DevController.php # REST API endpoints +├── Concerns/ +│ └── RemoteServerManager.php # SSH connection trait +├── Console/Commands/ +│ └── CopyDeviceFrames.php # Asset management command +├── Data/ +│ └── RouteTestResult.php # DTO for route test results +├── Exceptions/ +│ └── SshConnectionException.php +├── Lang/ +│ └── en_GB/developer.php # Translations +├── Listeners/ +│ └── SetHadesCookie.php # Login event listener +├── Middleware/ +│ ├── ApplyIconSettings.php # Icon preferences from cookies +│ └── RequireHades.php # Authorization middleware +├── Migrations/ +│ └── 0001_01_01_000001_create_developer_tables.php +├── Models/ +│ └── Server.php # SSH server model +├── Providers/ +│ ├── HorizonServiceProvider.php +│ └── TelescopeServiceProvider.php +├── Routes/ +│ └── admin.php # Route definitions +├── Services/ +│ ├── LogReaderService.php # Log file parsing +│ └── RouteTestService.php # Route testing logic +├── Tests/ +│ └── UseCase/ +│ └── DevToolsBasic.php # Feature tests +└── View/ + ├── Blade/ + │ └── admin/ # Blade templates + │ ├── activity-log.blade.php + │ ├── cache.blade.php + │ ├── database.blade.php + │ ├── logs.blade.php + │ ├── route-inspector.blade.php + │ ├── routes.blade.php + │ └── servers.blade.php + └── Modal/ + └── Admin/ # Livewire components + ├── ActivityLog.php + ├── Cache.php + ├── Database.php + ├── Logs.php + ├── RouteInspector.php + ├── Routes.php + └── Servers.php +``` + +## Event-Driven Module Loading + +The module uses the Core Framework's event-driven lazy loading pattern. The `Boot` class declares which events it listens to: + +```php +public static array $listens = [ + AdminPanelBooting::class => 'onAdminPanel', + ConsoleBooting::class => 'onConsole', +]; +``` + +This ensures routes, views, and commands are only registered when the admin panel or console is actually used. + +### Lifecycle Events + +| Event | Handler | What Happens | +|-------|---------|--------------| +| `AdminPanelBooting` | `onAdminPanel()` | Registers views, routes, Pulse override | +| `ConsoleBooting` | `onConsole()` | Registers Artisan commands | + +## Core Components + +### 1. Livewire Admin Pages + +All admin pages are full-page Livewire components using attribute-based configuration: + +```php +#[Title('Application Logs')] +#[Layout('hub::admin.layouts.app')] +class Logs extends Component +``` + +Each component: +- Checks Hades access in `mount()` +- Uses `developer::admin.{name}` view namespace +- Has corresponding Blade template in `View/Blade/admin/` + +### 2. API Controller + +`DevController` provides REST endpoints for: +- `/hub/api/dev/logs` - Recent log entries +- `/hub/api/dev/routes` - Route listing +- `/hub/api/dev/session` - Session/request info +- `/hub/api/dev/clear/{type}` - Cache clearing + +All endpoints are protected by `RequireHades` middleware and rate limiting. + +### 3. Services + +**LogReaderService** +- Memory-efficient log reading (reads from end of file) +- Parses Laravel log format +- Automatic sensitive data redaction +- Multi-log file support (daily/single channels) + +**RouteTestService** +- Route discovery and formatting +- Request building with parameters +- In-process request execution +- Response formatting and metrics + +### 4. RemoteServerManager Trait + +Provides SSH connection management for classes that need remote server access: + +```php +class DeployApplication implements ShouldQueue +{ + use RemoteServerManager; + + public function handle(): void + { + $this->withConnection($this->server, function () { + $this->run('cd /var/www && git pull'); + }); + } +} +``` + +Key methods: +- `connect()` / `disconnect()` - Connection lifecycle +- `withConnection()` - Guaranteed cleanup pattern +- `run()` / `runMany()` - Command execution +- `fileExists()` / `readFile()` / `writeFile()` - File operations +- `getDiskUsage()` / `getMemoryUsage()` - Server stats + +## Data Flow + +### Admin Page Request + +``` +Browser Request + ↓ +Laravel Router → /hub/dev/logs + ↓ +Livewire Component (Logs.php) + ↓ +mount() → checkHadesAccess() + ↓ +loadLogs() → LogReaderService + ↓ +render() → developer::admin.logs + ↓ +Response (HTML) +``` + +### API Request + +``` +Browser/JS Request + ↓ +Laravel Router → /hub/api/dev/logs + ↓ +RequireHades Middleware + ↓ +Rate Limiter (throttle:dev-logs) + ↓ +DevController::logs() + ↓ +LogReaderService + ↓ +Response (JSON) +``` + +### SSH Connection + +``` +Servers Component + ↓ +testConnection($serverId) + ↓ +Server::findOrFail() + ↓ +Write temp key file + ↓ +Process::run(['ssh', ...]) + ↓ +Parse result + ↓ +Update server status + ↓ +Clean up temp file +``` + +## Database Schema + +### servers table + +| Column | Type | Description | +|--------|------|-------------| +| id | bigint | Primary key | +| workspace_id | bigint | FK to workspaces | +| name | varchar(128) | Display name | +| ip | varchar(45) | IPv4/IPv6 address | +| port | smallint | SSH port (default 22) | +| user | varchar(64) | SSH username | +| private_key | text | Encrypted SSH key | +| status | varchar(32) | pending/connected/failed | +| last_connected_at | timestamp | Last successful connection | +| timestamps | | created_at, updated_at | +| soft_deletes | | deleted_at | + +Indexes: +- `workspace_id` +- `(workspace_id, status)` composite + +## Admin Menu Structure + +The module registers a "Dev Tools" menu group with these items: + +``` +Dev Tools (admin group, priority 80) +├── Logs → /hub/dev/logs +├── Activity → /hub/dev/activity +├── Servers → /hub/dev/servers +├── Database → /hub/dev/database +├── Routes → /hub/dev/routes +├── Route Inspector → /hub/dev/route-inspector +└── Cache → /hub/dev/cache +``` + +The menu is only visible to users with `admin` flag (Hades tier). + +## Rate Limiting + +API endpoints have rate limits configured in `Boot::configureRateLimiting()`: + +| Limiter | Limit | Purpose | +|---------|-------|---------| +| `dev-cache-clear` | 10/min | Prevent rapid cache clears | +| `dev-logs` | 30/min | Log reading | +| `dev-routes` | 30/min | Route listing | +| `dev-session` | 60/min | Session info | + +Rate limits are per-user (or per-IP for unauthenticated requests). + +## Third-Party Integrations + +### Laravel Telescope + +Custom `TelescopeServiceProvider` configures: +- Gate for Hades-only access in production +- Entry filtering (errors, failed jobs in production) +- Sensitive header/parameter hiding + +### Laravel Horizon + +Custom `HorizonServiceProvider` configures: +- Gate for Hades-only access +- Notification routing from config (email, SMS, Slack) + +### Laravel Pulse + +Custom Pulse dashboard view override at `View/Blade/vendor/pulse/dashboard.blade.php`. + +## Configuration + +The module expects these config keys (should be in `config/developer.php`): + +```php +return [ + // Hades cookie token + 'hades_token' => env('HADES_TOKEN'), + + // SSH settings + 'ssh' => [ + 'connection_timeout' => 30, + 'command_timeout' => 60, + ], + + // Horizon notifications + 'horizon' => [ + 'mail_to' => env('HORIZON_MAIL_TO'), + 'sms_to' => env('HORIZON_SMS_TO'), + 'slack_webhook' => env('HORIZON_SLACK_WEBHOOK'), + 'slack_channel' => env('HORIZON_SLACK_CHANNEL', '#alerts'), + ], +]; +``` + +## Extension Points + +### Adding New Admin Pages + +1. Create Livewire component in `View/Modal/Admin/` +2. Create Blade view in `View/Blade/admin/` +3. Add route in `Routes/admin.php` +4. Add menu item in `Boot::adminMenuItems()` +5. Add translations in `Lang/en_GB/developer.php` + +### Adding New API Endpoints + +1. Add method to `DevController` +2. Add route in `Routes/admin.php` API group +3. Create rate limiter in `Boot::configureRateLimiting()` +4. Apply `throttle:limiter-name` middleware + +### Using RemoteServerManager + +```php +use Core\Developer\Concerns\RemoteServerManager; + +class MyJob +{ + use RemoteServerManager; + + public function handle(Server $server): void + { + $this->withConnection($server, function () { + // Commands executed on remote server + $result = $this->run('whoami'); + // ... + }); + } +} +``` + +## Performance Considerations + +1. **Log Reading** - Uses backwards reading to avoid loading entire log into memory. Configurable `maxBytes` limit. + +2. **Route Caching** - Routes are computed once per request. The `RouteInspector` uses `#[Computed(cache: true)]` for route list. + +3. **Query Log** - Enabled only in local environment (`Boot::boot()`). + +4. **SSH Connections** - Always disconnect via `withConnection()` pattern to prevent resource leaks. + +## Dependencies + +### Composer Requirements + +- `host-uk/core` - Core framework +- `host-uk/core-admin` - Admin panel infrastructure +- `phpseclib3` - SSH connections (via RemoteServerManager) +- `spatie/laravel-activitylog` - Activity logging + +### Frontend Dependencies + +- Flux UI components +- Tailwind CSS +- Livewire 3.x + +## Testing Strategy + +Tests use Pest syntax and focus on: +- Page rendering and content +- Authorization enforcement +- API endpoint behaviour +- Service logic + +Test database: SQLite in-memory with Telescope/Pulse disabled. diff --git a/docs/security.md b/docs/security.md new file mode 100644 index 0000000..ae737f6 --- /dev/null +++ b/docs/security.md @@ -0,0 +1,269 @@ +--- +title: Security +description: Security considerations and audit notes for core-developer +updated: 2026-01-29 +--- + +# Security Considerations + +The `core-developer` package provides powerful administrative capabilities that require careful security controls. This document outlines the security model, known risks, and mitigation strategies. + +## Threat Model + +### Assets Protected + +1. **Application logs** - May contain tokens, passwords, PII in error messages +2. **Database access** - Read-only query execution against production data +3. **SSH keys** - Encrypted private keys for server connections +4. **Cache data** - Application cache, session data, config cache +5. **Route information** - Full application route structure + +### Threat Actors + +1. **Unauthorized users** - Non-Hades users attempting to access dev tools +2. **Compromised Hades account** - Attacker with valid Hades credentials +3. **SSRF/Injection** - Attacker manipulating dev tools to access internal resources +4. **Data exfiltration** - Extracting sensitive data via dev tools + +## Authorization Model + +### Hades Tier Requirement + +All developer tools require "Hades" access, verified via the `isHades()` method on the User model. This is enforced at multiple layers: + +| Layer | Implementation | File | +|-------|----------------|------| +| Middleware | `RequireHades::handle()` | `src/Middleware/RequireHades.php` | +| Component | `checkHadesAccess()` in `mount()` | All Livewire components | +| API | Controller `authorize()` calls | `src/Controllers/DevController.php` | +| Menu | `admin` flag filtering | `src/Boot.php` | + +### Defence in Depth + +The authorization is intentionally redundant: +- API routes use `RequireHades` middleware +- Livewire components check in `mount()` +- Some controller methods call `$this->authorize()` + +This ensures access is blocked even if one layer fails. + +### Known Issue: Test Environment + +Tests currently pass without setting Hades tier on the test user. This suggests authorization may not be properly enforced in the test environment. See TODO.md for remediation. + +## Data Protection + +### Log Redaction + +The `LogReaderService` automatically redacts sensitive patterns before displaying logs: + +| Pattern | Replacement | +|---------|-------------| +| Stripe API keys | `[STRIPE_KEY_REDACTED]` | +| GitHub tokens | `[GITHUB_TOKEN_REDACTED]` | +| Bearer tokens | `Bearer [TOKEN_REDACTED]` | +| API keys/secrets | `[KEY_REDACTED]` / `[REDACTED]` | +| AWS credentials | `[AWS_KEY_REDACTED]` / `[AWS_SECRET_REDACTED]` | +| Database URLs | Connection strings with `[USER]:[PASS]` | +| Email addresses | Partial: `jo***@example.com` | +| IP addresses | Partial: `192.168.xxx.xxx` | +| Credit card numbers | `[CARD_REDACTED]` | +| JWT tokens | `[JWT_REDACTED]` | +| Private keys | `[PRIVATE_KEY_REDACTED]` | + +**Limitation**: Patterns are regex-based and may not catch all sensitive data. Custom application secrets with non-standard formats will not be redacted. + +### SSH Key Storage + +Server private keys are: +- Encrypted at rest using Laravel's `encrypted` cast +- Hidden from serialization (`$hidden` array) +- Never exposed in API responses or views +- Stored in `text` column (supports long keys) + +### Database Query Tool + +The database query component restricts access to read-only operations: + +```php +protected const ALLOWED_STATEMENTS = ['SELECT', 'SHOW', 'DESCRIBE', 'EXPLAIN']; +``` + +**Known Risk**: The current implementation only checks the first word, which does not prevent: +- Stacked queries: `SELECT 1; DROP TABLE users` +- Subqueries with side effects (MySQL stored procedures) + +**Mitigation**: Use a proper SQL parser or prevent semicolons entirely. + +### Session Data Exposure + +The `/hub/api/dev/session` endpoint exposes: +- Session ID +- User IP address +- User agent (truncated to 100 chars) +- Request method and URL + +This is intentional for debugging but could be abused for session hijacking if credentials are compromised. + +## Rate Limiting + +All API endpoints have rate limits to prevent abuse: + +| Endpoint | Limit | Rationale | +|----------|-------|-----------| +| Cache clear | 10/min | Prevent DoS via rapid cache invalidation | +| Log reading | 30/min | Limit log scraping | +| Route listing | 30/min | Prevent enumeration attacks | +| Session info | 60/min | Higher limit for debugging workflows | + +Rate limits are per-user (authenticated) or per-IP (unauthenticated). + +## SSH Connection Security + +### Key Handling + +The `testConnection()` method in `Servers.php` creates a temporary key file: + +```php +$tempKeyPath = sys_get_temp_dir().'/ssh_test_'.uniqid(); +file_put_contents($tempKeyPath, $server->getDecryptedPrivateKey()); +chmod($tempKeyPath, 0600); +``` + +**Risk**: Predictable filename pattern and race condition window between write and use. + +**Recommendation**: Use `tempnam()` for unique filename, write with restrictive umask. + +### Connection Validation + +- `StrictHostKeyChecking=no` is used for convenience but prevents MITM detection +- `BatchMode=yes` prevents interactive prompts +- `ConnectTimeout=10` limits hanging connections + +### Workspace Isolation + +The `RemoteServerManager::connect()` method validates workspace ownership before connecting: + +```php +if (! $server->belongsToCurrentWorkspace()) { + throw new SshConnectionException('Unauthorised access to server.', $server->name); +} +``` + +This prevents cross-tenant server access. + +## Route Testing Security + +### Environment Restriction + +Route testing is only available in `local` and `testing` environments: + +```php +public function isTestingAllowed(): bool +{ + return App::environment(['local', 'testing']); +} +``` + +This prevents accidental data modification in production. + +### Destructive Operation Warnings + +Routes using `DELETE`, `PUT`, `PATCH`, `POST` methods are marked as destructive and show warnings in the UI. + +### CSRF Consideration + +Test requests bypass CSRF as they are internal requests. The `X-Requested-With: XMLHttpRequest` header is set by default. + +## Cookie Security + +### Hades Cookie + +The `SetHadesCookie` listener sets a cookie on login: + +| Attribute | Value | Purpose | +|-----------|-------|---------| +| Value | Encrypted token | Validates Hades status | +| Duration | 1 year | Long-lived for convenience | +| HttpOnly | true | Prevents XSS access | +| Secure | true (production) | HTTPS only in production | +| SameSite | lax | CSRF protection | + +### Icon Settings Cookie + +`ApplyIconSettings` middleware reads `icon-style` and `icon-size` cookies set by JavaScript. These are stored in session for Blade component access. + +**Risk**: Cookie values are user-controlled. Ensure they are properly escaped in views. + +## Audit Logging + +### Logged Actions + +| Action | What's Logged | +|--------|---------------| +| Log clear | user_id, email, previous_size_bytes, IP | +| Database query | user_id, email, query, row_count, execution_time, IP | +| Blocked query | user_id, email, query (attempted), IP | +| Route test | user_id, route, method, IP | +| Server failure | Server ID, failure reason (via activity log) | + +### Activity Log + +Server model uses Spatie ActivityLog for tracking changes: +- Logged fields: name, ip, port, user, status +- Only dirty attributes logged +- Empty logs suppressed + +## Third-Party Security + +### Telescope + +- Sensitive headers hidden: `cookie`, `x-csrf-token`, `x-xsrf-token` +- Sensitive parameters hidden: `_token` +- Gate restricts to Hades users (production) or all users (local) + +### Horizon + +- Gate restricts to Hades users +- Notifications configured via config (not hardcoded emails) + +## Security Checklist for New Features + +When adding new developer tools: + +- [ ] Enforce Hades authorization in middleware AND component +- [ ] Add rate limiting for API endpoints +- [ ] Redact sensitive data in output +- [ ] Audit destructive operations +- [ ] Restrict environment (local/testing) for dangerous features +- [ ] Validate and sanitize all user input +- [ ] Use prepared statements for database queries +- [ ] Clean up temporary files/resources +- [ ] Document security considerations + +## Incident Response + +### If Hades credentials are compromised: + +1. Revoke the user's Hades access +2. Rotate `HADES_TOKEN` environment variable +3. Review audit logs for suspicious activity +4. Check server access logs for SSH activity +5. Consider rotating SSH keys for connected servers + +### If SSH key is exposed: + +1. Delete the server record immediately +2. Regenerate SSH key on the actual server +3. Review server logs for unauthorized access +4. Update the server record with new key + +## Recommendations for Production + +1. **Separate Hades token per environment** - Don't use same token across staging/production +2. **Regular audit log review** - Monitor for unusual access patterns +3. **Limit Hades users** - Only grant to essential personnel +4. **Use hardware keys** - For servers, prefer hardware security modules +5. **Network segmentation** - Restrict admin panel to internal networks +6. **Two-factor authentication** - Require 2FA for Hades-tier accounts +7. **Session timeout** - Consider shorter session duration for Hades users