Files
Charon/docs/plans/current_spec.md
GitHub Actions 67f2f27cf8 feat: Add Import Success Modal and Certificate Status Card features
- Implemented ImportSuccessModal to replace alert with a modal displaying import results and guidance.
- Updated ImportCaddy to show the new modal with import summary and navigation options.
- Created CertificateStatusCard to display certificate provisioning status on the dashboard.
- Enhanced API types and hooks to support new features.
- Added unit tests for ImportSuccessModal and CertificateStatusCard components.
- Updated QA report to reflect the status of the new features and tests.
2025-12-12 00:42:27 +00:00

289 lines
9.5 KiB
Markdown

# Cerberus/Security Feature Issues - Diagnostic Plan
**Date:** December 12, 2025
**Status:** Analysis Complete
---
## Issue Summary
| # | Issue | Severity |
|---|-------|----------|
| 1 | Cerberus shows ON by default on first load (should be OFF) | High |
| 2 | Cerberus dashboard header shows "disabled" even when enabled | Medium |
| 3 | CrowdSec toggle auto-enables when Cerberus is enabled | Medium |
| 4 | CrowdSec toggle unresponsive + Config button grayed out | High |
---
## Root Cause Analysis
### Issue 1: Cerberus Shows ON by Default
**Root Cause:** The `feature_flags_handler.go` has a default value of `true` for all feature flags including `feature.cerberus.enabled`.
**File:** [backend/internal/api/handlers/feature_flags_handler.go#L39-L42](../../backend/internal/api/handlers/feature_flags_handler.go#L39-L42)
```go
// Line 39-42
for _, key := range defaultFlags {
defaultVal := true // <-- THIS IS THE BUG
if v, ok := defaultFlagValues[key]; ok {
defaultVal = v
}
```
**Problem:** The code sets `defaultVal := true` for all flags, then only overrides it if the key exists in `defaultFlagValues`. However, `feature.cerberus.enabled` is NOT in `defaultFlagValues`:
```go
// Line 29-31
var defaultFlagValues = map[string]bool{
"feature.crowdsec.console_enrollment": false,
}
```
**Result:** On first load with an empty database, `feature.cerberus.enabled` defaults to `true` instead of `false`.
**Additional Context:**
- The [backend/internal/config/config.go#L60](../../backend/internal/config/config.go#L60) correctly defaults `CerberusEnabled` to `false`:
```go
CerberusEnabled: getEnvAny("false", "CERBERUS_SECURITY_CERBERUS_ENABLED", ...) == "true"
```
- However, the feature flags handler ignores this config and uses its own default.
---
### Issue 2: Dashboard Header Shows "Disabled" Even When Enabled
**Root Cause:** The header banner logic in `Security.tsx` checks `status.cerberus?.enabled` which comes from the security status API, but there's a **data source mismatch**.
**Files:**
- [frontend/src/pages/Security.tsx#L141-L153](../../frontend/src/pages/Security.tsx#L141-L153) - Header banner logic
- [backend/internal/api/handlers/security_handler.go#L35-L49](../../backend/internal/api/handlers/security_handler.go#L35-L49) - Security status API
**Problem Flow:**
1. **Security.tsx** checks `status.cerberus?.enabled` from `/api/v1/security/status`
2. **security_handler.go** reads from config AND settings table:
```go
// Line 36-48
enabled := h.cfg.CerberusEnabled
var settingKey = "security.cerberus.enabled" // <-- WRONG KEY!
if h.db != nil {
var setting struct{ Value string }
if err := h.db.Raw("SELECT value FROM settings WHERE key = ? LIMIT 1", settingKey).Scan(&setting).Error; ...
```
3. **SystemSettings.tsx** toggles `feature.cerberus.enabled` (via feature flags API)
**The Mismatch:**
| Component | Key Used |
|-----------|----------|
| SystemSettings toggle | `feature.cerberus.enabled` |
| Security status API | `security.cerberus.enabled` |
The toggle writes to `feature.cerberus.enabled` but the security status reads from `security.cerberus.enabled` - **two different keys!**
---
### Issue 3: CrowdSec Auto-Enables When Cerberus is Enabled
**Root Cause:** The `docker-compose.override.yml` and `docker-compose.local.yml` both set `CHARON_SECURITY_CROWDSEC_MODE=local`:
**File:** [docker-compose.override.yml#L21](../../docker-compose.override.yml#L21)
```yaml
- CHARON_SECURITY_CROWDSEC_MODE=local
```
**Problem:** When the container starts:
1. Config loads with `CrowdSecMode: "local"` from env var
2. Security status API returns `crowdsec.enabled: true` because mode is "local"
3. Frontend shows CrowdSec as enabled
**File:** [backend/internal/api/handlers/security_handler.go#L59-L62](../../backend/internal/api/handlers/security_handler.go#L59-L62)
```go
// Allow runtime override for CrowdSec enabled flag via settings table
crowdsecEnabled := mode == "local" // <-- Auto-true if mode is "local"
```
---
### Issue 4: CrowdSec Toggle Unresponsive + Config Button Grayed Out
**Root Cause:** Multiple issues combine to break the toggle:
**A. Toggle Disabled Logic:**
**File:** [frontend/src/pages/Security.tsx#L127](../../frontend/src/pages/Security.tsx#L127)
```tsx
const crowdsecToggleDisabled = cerberusDisabled || crowdsecPowerMutation.isPending
```
**File:** [frontend/src/pages/Security.tsx#L126](../../frontend/src/pages/Security.tsx#L126)
```tsx
const cerberusDisabled = !status.cerberus?.enabled
```
Since `status.cerberus?.enabled` is `false` due to Issue 2 (wrong settings key), `cerberusDisabled` is `true`, making the toggle disabled.
**B. Config Button Disabled:**
**File:** [frontend/src/pages/Security.tsx#L128](../../frontend/src/pages/Security.tsx#L128)
```tsx
const crowdsecControlsDisabled = cerberusDisabled || crowdsecPowerMutation.isPending
```
Same logic - the controls are disabled because Cerberus appears disabled.
**C. Switch Component Event Handling:**
**File:** [frontend/src/components/ui/Switch.tsx#L17-L20](../../frontend/src/components/ui/Switch.tsx#L17-L20)
The Switch component passes `disabled` to the native checkbox input, which prevents click events. This is correct behavior - the issue is the `disabled` prop is incorrectly `true`.
---
## Recommended Fixes
### Fix 1: Update Feature Flag Defaults
**File:** `backend/internal/api/handlers/feature_flags_handler.go`
```go
// Change defaultFlagValues to include cerberus.enabled as false
var defaultFlagValues = map[string]bool{
"feature.cerberus.enabled": false, // ADD THIS
"feature.crowdsec.console_enrollment": false,
"feature.uptime.enabled": true, // Uptime can default ON
}
```
### Fix 2: Align Settings Keys
**Option A (Recommended):** Update security_handler.go to read from feature flags key
**File:** `backend/internal/api/handlers/security_handler.go`
```go
// Line 37: Change from
var settingKey = "security.cerberus.enabled"
// To
var settingKey = "feature.cerberus.enabled"
```
**Option B:** Create a sync mechanism between feature flags and security settings
### Fix 3: Remove CrowdSec Mode Override from Docker Compose
**Files:**
- `docker-compose.override.yml`
- `docker-compose.local.yml`
```yaml
# Remove or comment out:
# - CHARON_SECURITY_CROWDSEC_MODE=local
# Or change to:
- CHARON_SECURITY_CROWDSEC_MODE=disabled
```
### Fix 4: No Additional Fix Needed
Issue 4 is a symptom of Issues 1-2. Once those are fixed:
- `cerberusDisabled` will be `false` when Cerberus is enabled
- `crowdsecToggleDisabled` will be `false`
- `crowdsecControlsDisabled` will be `false`
- Toggle and Config button will be interactive
---
## Test Scenarios
### Test 1: Fresh Install Default State
```
Given: Clean database, no env vars set
When: User loads the Settings > System page
Then: Cerberus toggle should be OFF
And: /api/v1/feature-flags returns { "feature.cerberus.enabled": false }
```
### Test 2: Cerberus Toggle Sync
```
Given: User is on Settings > System page
When: User enables Cerberus toggle
Then: /api/v1/security/status returns { "cerberus": { "enabled": true } }
And: Security dashboard header banner is NOT displayed
```
### Test 3: CrowdSec Toggle Interaction
```
Given: Cerberus is enabled
And: User is on Security dashboard
When: User clicks CrowdSec toggle
Then: Toggle should respond to click
And: CrowdSec enabled state should change
And: Toast notification should appear
```
### Test 4: CrowdSec Config Button
```
Given: Cerberus is enabled
And: User is on Security dashboard
When: User clicks CrowdSec "Config" button
Then: User should navigate to /security/crowdsec
And: Button should NOT be grayed out
```
### Test 5: Environment Variable Override
```
Given: CERBERUS_SECURITY_CERBERUS_ENABLED=true set
When: User loads Settings > System (fresh DB)
Then: Cerberus toggle should be ON (env override)
```
---
## Implementation Priority
| Priority | Fix | Effort | Impact |
|----------|-----|--------|--------|
| P0 | Fix 2 (Key alignment) | Low | High - Fixes Issues 2, 4 |
| P1 | Fix 1 (Default values) | Low | High - Fixes Issue 1 |
| P2 | Fix 3 (Docker compose) | Low | Medium - Fixes Issue 3 |
---
## Files to Modify
1. **backend/internal/api/handlers/feature_flags_handler.go** - Add default value for cerberus
2. **backend/internal/api/handlers/security_handler.go** - Change settings key to `feature.cerberus.enabled`
3. **docker-compose.override.yml** - Remove or change CrowdSec mode
4. **docker-compose.local.yml** - Remove or change CrowdSec mode
---
## Additional Observations
1. **Dual Control Systems:** There are two overlapping control systems:
- Feature flags (`feature.cerberus.enabled`) - toggled in SystemSettings.tsx
- Security config (`SecurityConfig.Enabled` in DB) - used by Enable/Disable endpoints
Consider consolidating to one source of truth.
2. **Config vs Settings:** The `config.SecurityConfig` struct loaded from env vars is separate from DB-backed `SecurityConfig` model. This creates confusion about which takes precedence.
3. **No Migration:** When updating default values, existing users may need a migration or reset to see the new defaults.
---
## Code Reference Summary
| File | Line | Purpose |
|------|------|---------|
| `feature_flags_handler.go` | L29-31 | Missing cerberus default |
| `feature_flags_handler.go` | L39 | `defaultVal := true` bug |
| `security_handler.go` | L37 | Wrong settings key |
| `Security.tsx` | L126-128 | Disabled state logic |
| `SystemSettings.tsx` | L99-105 | Feature toggle UI |
| `docker-compose.override.yml` | L21 | CrowdSec mode env var |
| `config.go` | L60 | Correct cerberus default |