diff --git a/SECURITY_IMPLEMENTATION_PLAN.md b/SECURITY_IMPLEMENTATION_PLAN.md index 6450d685..93d08ea0 100644 --- a/SECURITY_IMPLEMENTATION_PLAN.md +++ b/SECURITY_IMPLEMENTATION_PLAN.md @@ -22,8 +22,8 @@ We will introduce a new set of environment variables to control these services. | `CPM_SECURITY_CROWDSEC_API_URL` | URL (e.g., `http://crowdsec:8080`) | Required if mode is `external`. | | `CPM_SECURITY_CROWDSEC_API_KEY` | String | Required if mode is `external`. | | `CPM_SECURITY_WAF_MODE` | `disabled` (default), `enabled` | Enables Coraza WAF with OWASP Core Rule Set (CRS). | -| `CPM_SECURITY_RATELIMIT_ENABLED` | `true`, `false` (default) | Enables global rate limiting controls. | -| `CPM_SECURITY_ACL_ENABLED` | `true`, `false` (default) | Enables IP-based Access Control Lists. | +| `CPM_SECURITY_RATELIMIT_MODE` | `disabled` (default), `enabled` | Enables global rate limiting controls. | +| `CPM_SECURITY_ACL_MODE` | `disabled` (default), `enabled` | Enables IP-based Access Control Lists. | --- diff --git a/backend/internal/api/handlers/security_handler.go b/backend/internal/api/handlers/security_handler.go index c64de894..c17b98fc 100644 --- a/backend/internal/api/handlers/security_handler.go +++ b/backend/internal/api/handlers/security_handler.go @@ -33,10 +33,12 @@ func (h *SecurityHandler) GetStatus(c *gin.Context) { "enabled": h.cfg.WAFMode == "enabled", }, "rate_limit": gin.H{ - "enabled": h.cfg.RateLimitEnabled, + "mode": h.cfg.RateLimitMode, + "enabled": h.cfg.RateLimitMode == "enabled", }, "acl": gin.H{ - "enabled": h.cfg.ACLEnabled, + "mode": h.cfg.ACLMode, + "enabled": h.cfg.ACLMode == "enabled", }, }) } diff --git a/backend/internal/api/handlers/security_handler_test.go b/backend/internal/api/handlers/security_handler_test.go index a3d47153..4e4315f2 100644 --- a/backend/internal/api/handlers/security_handler_test.go +++ b/backend/internal/api/handlers/security_handler_test.go @@ -24,10 +24,10 @@ func TestSecurityHandler_GetStatus(t *testing.T) { { name: "All Disabled", cfg: config.SecurityConfig{ - CrowdSecMode: "disabled", - WAFMode: "disabled", - RateLimitEnabled: false, - ACLEnabled: false, + CrowdSecMode: "disabled", + WAFMode: "disabled", + RateLimitMode: "disabled", + ACLMode: "disabled", }, expectedStatus: http.StatusOK, expectedBody: map[string]interface{}{ @@ -41,9 +41,11 @@ func TestSecurityHandler_GetStatus(t *testing.T) { "enabled": false, }, "rate_limit": map[string]interface{}{ + "mode": "disabled", "enabled": false, }, "acl": map[string]interface{}{ + "mode": "disabled", "enabled": false, }, }, @@ -51,10 +53,10 @@ func TestSecurityHandler_GetStatus(t *testing.T) { { name: "All Enabled", cfg: config.SecurityConfig{ - CrowdSecMode: "local", - WAFMode: "enabled", - RateLimitEnabled: true, - ACLEnabled: true, + CrowdSecMode: "local", + WAFMode: "enabled", + RateLimitMode: "enabled", + ACLMode: "enabled", }, expectedStatus: http.StatusOK, expectedBody: map[string]interface{}{ @@ -68,9 +70,11 @@ func TestSecurityHandler_GetStatus(t *testing.T) { "enabled": true, }, "rate_limit": map[string]interface{}{ + "mode": "enabled", "enabled": true, }, "acl": map[string]interface{}{ + "mode": "enabled", "enabled": true, }, }, diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go index 54e083c2..44a033d9 100644 --- a/backend/internal/config/config.go +++ b/backend/internal/config/config.go @@ -24,12 +24,12 @@ type Config struct { // SecurityConfig holds configuration for optional security services. type SecurityConfig struct { - CrowdSecMode string - CrowdSecAPIURL string - CrowdSecAPIKey string - WAFMode string - RateLimitEnabled bool - ACLEnabled bool + CrowdSecMode string + CrowdSecAPIURL string + CrowdSecAPIKey string + WAFMode string + RateLimitMode string + ACLMode string } // Load reads env vars and falls back to defaults so the server can boot with zero configuration. @@ -47,12 +47,12 @@ func Load() (Config, error) { JWTSecret: getEnv("CPM_JWT_SECRET", "change-me-in-production"), ACMEStaging: getEnv("CPM_ACME_STAGING", "") == "true", Security: SecurityConfig{ - CrowdSecMode: getEnv("CPM_SECURITY_CROWDSEC_MODE", "disabled"), - CrowdSecAPIURL: getEnv("CPM_SECURITY_CROWDSEC_API_URL", ""), - CrowdSecAPIKey: getEnv("CPM_SECURITY_CROWDSEC_API_KEY", ""), - WAFMode: getEnv("CPM_SECURITY_WAF_MODE", "disabled"), - RateLimitEnabled: getEnv("CPM_SECURITY_RATELIMIT_ENABLED", "false") == "true", - ACLEnabled: getEnv("CPM_SECURITY_ACL_ENABLED", "false") == "true", + CrowdSecMode: getEnv("CPM_SECURITY_CROWDSEC_MODE", "disabled"), + CrowdSecAPIURL: getEnv("CPM_SECURITY_CROWDSEC_API_URL", ""), + CrowdSecAPIKey: getEnv("CPM_SECURITY_CROWDSEC_API_KEY", ""), + WAFMode: getEnv("CPM_SECURITY_WAF_MODE", "disabled"), + RateLimitMode: getEnv("CPM_SECURITY_RATELIMIT_MODE", "disabled"), + ACLMode: getEnv("CPM_SECURITY_ACL_MODE", "disabled"), }, } diff --git a/docker-compose.local.yml b/docker-compose.local.yml index 8e0a2e9c..b9ca7cb1 100644 --- a/docker-compose.local.yml +++ b/docker-compose.local.yml @@ -29,8 +29,8 @@ services: - CPM_SECURITY_CROWDSEC_API_URL= - CPM_SECURITY_CROWDSEC_API_KEY= - CPM_SECURITY_WAF_MODE=disabled - - CPM_SECURITY_RATELIMIT_ENABLED=false - - CPM_SECURITY_ACL_ENABLED=false + - CPM_SECURITY_RATELIMIT_MODE=disabled + - CPM_SECURITY_ACL_MODE=disabled extra_hosts: - "host.docker.internal:host-gateway" cap_add: diff --git a/docs/security.md b/docs/security.md new file mode 100644 index 00000000..426e4b98 --- /dev/null +++ b/docs/security.md @@ -0,0 +1,84 @@ +# Security Services + +CaddyProxyManager+ (CPM+) includes optional, high-value security integrations to protect your services. These features are disabled by default to keep the application lightweight but can be easily enabled via environment variables. + +## Available Services + +### 1. CrowdSec (Intrusion Prevention) +[CrowdSec](https://www.crowdsec.net/) is a collaborative security automation tool that analyzes logs to detect and block malicious behavior. + +**Modes:** +* **Local**: Installs the CrowdSec agent *inside* the CPM+ container. Useful for single-container setups. + * *Note*: Increases container startup time and resource usage. +* **External**: Connects to an existing CrowdSec agent running elsewhere (e.g., on the host or another container). + * *Recommended* for production or multi-server setups. + +### 2. WAF (Web Application Firewall) +Uses [Coraza](https://coraza.io/), a Go-native WAF, with the **OWASP Core Rule Set (CRS)** to protect against common web attacks (SQL Injection, XSS, etc.). + +### 3. Access Control Lists (ACL) +Allows you to define IP allow/block lists to restrict access to your services. + +### 4. Rate Limiting +Protects your services from abuse by limiting the number of requests a client can make within a specific time frame. + +--- + +## Configuration + +All security services are controlled via environment variables in your `docker-compose.yml`. + +### CrowdSec Configuration + +| Variable | Value | Description | +| :--- | :--- | :--- | +| `CPM_SECURITY_CROWDSEC_MODE` | `disabled` | (Default) CrowdSec is turned off. | +| | `local` | Installs and runs CrowdSec agent inside the container. | +| | `external` | Connects to an external CrowdSec agent. | +| `CPM_SECURITY_CROWDSEC_API_URL` | URL | (Required for `external`) e.g., `http://crowdsec:8080` | +| `CPM_SECURITY_CROWDSEC_API_KEY` | String | (Required for `external`) Your CrowdSec bouncer API key. | + +**Example (Local Mode):** +```yaml +environment: + - CPM_SECURITY_CROWDSEC_MODE=local +``` + +**Example (External Mode):** +```yaml +environment: + - CPM_SECURITY_CROWDSEC_MODE=external + - CPM_SECURITY_CROWDSEC_API_URL=http://192.168.1.50:8080 + - CPM_SECURITY_CROWDSEC_API_KEY=your-bouncer-key-here +``` + +### WAF Configuration + +| Variable | Value | Description | +| :--- | :--- | :--- | +| `CPM_SECURITY_WAF_MODE` | `disabled` | (Default) WAF is turned off. | +| | `enabled` | Enables Coraza WAF with OWASP CRS. | + +**Example:** +```yaml +environment: + - CPM_SECURITY_WAF_MODE=enabled +``` + +### Rate Limiting & ACLs + +| Variable | Value | Description | +| :--- | :--- | :--- | +| `CPM_SECURITY_RATELIMIT_MODE` | `enabled` / `disabled` | Enable global rate limiting. | +| `CPM_SECURITY_ACL_MODE` | `enabled` / `disabled` | Enable IP-based Access Control Lists. | + +--- + +## Dashboard + +You can view the status of these services in the CPM+ web interface under the **Security** tab. + +* **CrowdSec**: Shows connection status and mode. +* **WAF**: Indicates if the Core Rule Set is loaded. +* **ACLs**: Manage your Block/Allow lists. +* **Rate Limits**: Configure global request limits. diff --git a/frontend/src/pages/Security.tsx b/frontend/src/pages/Security.tsx index cce32db4..f25ee86e 100644 --- a/frontend/src/pages/Security.tsx +++ b/frontend/src/pages/Security.tsx @@ -35,7 +35,7 @@ export default function Security() {