docs: create Netgrimoire/Network/Security/Caddy
This commit is contained in:
parent
fc05032a7c
commit
7a2129a851
1 changed files with 522 additions and 0 deletions
522
Netgrimoire/Network/Security/Caddy.md
Normal file
522
Netgrimoire/Network/Security/Caddy.md
Normal file
|
|
@ -0,0 +1,522 @@
|
||||||
|
---
|
||||||
|
title: Caddy Reverse Proxy
|
||||||
|
description: Curreent and future config
|
||||||
|
published: true
|
||||||
|
date: 2026-02-23T22:09:16.106Z
|
||||||
|
tags:
|
||||||
|
editor: markdown
|
||||||
|
dateCreated: 2026-02-23T22:09:16.106Z
|
||||||
|
---
|
||||||
|
|
||||||
|
# Caddy Reverse Proxy
|
||||||
|
|
||||||
|
**Host:** znas (Docker Swarm node)
|
||||||
|
**Internal IP:** 192.168.5.10
|
||||||
|
**Data Path:** `/export/Docker/caddy/`
|
||||||
|
**Networks:** `netgrimoire` (service network), `vpn`
|
||||||
|
**Ports:** 80 (mapped to host 8900), 443
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Caddy serves as the primary reverse proxy for all public and internal web services. It uses the `caddy-docker-proxy` pattern, which allows services to register themselves with Caddy by adding Docker labels to their compose files — no manual Caddyfile edits required per service.
|
||||||
|
|
||||||
|
Configuration is **hybrid**: some services are defined entirely via Docker labels, others are defined statically in the Caddyfile, and most use both (labels for routing, Caddyfile for shared snippets). The `caddy-docker-proxy` container merges both sources at runtime.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Current State
|
||||||
|
|
||||||
|
### Image
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
image: lucaslorentz/caddy-docker-proxy:ci-alpine
|
||||||
|
```
|
||||||
|
|
||||||
|
This image provides the Docker Proxy module only. It has no CrowdSec, GeoIP, or rate limiting built in.
|
||||||
|
|
||||||
|
### Docker Compose (`/export/Docker/caddy/docker-compose.yml`)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
configs:
|
||||||
|
caddy-basic-content:
|
||||||
|
file: ./Caddyfile
|
||||||
|
labels:
|
||||||
|
caddy:
|
||||||
|
|
||||||
|
services:
|
||||||
|
caddy:
|
||||||
|
image: lucaslorentz/caddy-docker-proxy:ci-alpine
|
||||||
|
ports:
|
||||||
|
- 8900:80
|
||||||
|
- 443:443
|
||||||
|
environment:
|
||||||
|
- CADDY_INGRESS_NETWORKS=netgrimoire
|
||||||
|
networks:
|
||||||
|
- netgrimoire
|
||||||
|
- vpn
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- /export/Docker/caddy/Caddyfile:/etc/caddy/Caddyfile
|
||||||
|
- /export/Docker/caddy:/data
|
||||||
|
#- /export/Docker/caddy/logs:/var/log/caddy # Placeholder for CrowdSec log mount
|
||||||
|
deploy:
|
||||||
|
placement:
|
||||||
|
constraints:
|
||||||
|
- node.hostname == znas
|
||||||
|
|
||||||
|
networks:
|
||||||
|
netgrimoire:
|
||||||
|
external: true
|
||||||
|
vpn:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Caddyfile (`/export/Docker/caddy/Caddyfile`)
|
||||||
|
|
||||||
|
The Caddyfile defines shared authentication snippets and static site blocks. These snippets are available to all services — including label-defined ones — via `import`.
|
||||||
|
|
||||||
|
```caddyfile
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# AUTH SNIPPETS
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
(authentik) {
|
||||||
|
route /outpost.goauthentik.io/* {
|
||||||
|
reverse_proxy http://authentik:9000
|
||||||
|
}
|
||||||
|
|
||||||
|
forward_auth http://authentik:9000 {
|
||||||
|
uri /outpost.goauthentik.io/auth/caddy
|
||||||
|
header_up X-Forwarded-URI {http.request.uri}
|
||||||
|
copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Email \
|
||||||
|
X-Authentik-Name X-Authentik-Uid X-Authentik-Jwt \
|
||||||
|
X-Authentik-Meta-Jwks X-Authentik-Meta-Outpost X-Authentik-Meta-Provider \
|
||||||
|
X-Authentik-Meta-App X-Authentik-Meta-Version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(authelia) {
|
||||||
|
forward_auth http://authelia:9091 {
|
||||||
|
uri /api/verify?rd=https://login.wasted-bandwidth.net/
|
||||||
|
copy_headers Remote-User Remote-Groups Remote-Email Remote-Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# MAIL SNIPPETS
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
(email-proxy) {
|
||||||
|
redir https://mail.netgrimoire.com/sogo 301
|
||||||
|
}
|
||||||
|
|
||||||
|
(mailcow-proxy) {
|
||||||
|
reverse_proxy nginx-mailcow:80
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# STATIC SITE BLOCKS — NETGRIMOIRE.COM
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
cloud.netgrimoire.com {
|
||||||
|
reverse_proxy http://nextcloud-aio-apache:11000
|
||||||
|
}
|
||||||
|
|
||||||
|
log.netgrimoire.com {
|
||||||
|
reverse_proxy http://graylog:9000
|
||||||
|
}
|
||||||
|
|
||||||
|
win.netgrimoire.com {
|
||||||
|
reverse_proxy http://192.168.5.10:8006
|
||||||
|
}
|
||||||
|
|
||||||
|
docker.netgrimoire.com {
|
||||||
|
reverse_proxy http://portainer:9000
|
||||||
|
}
|
||||||
|
|
||||||
|
immich.netgrimoire.com {
|
||||||
|
reverse_proxy http://192.168.5.10:2283
|
||||||
|
}
|
||||||
|
|
||||||
|
npm.netgrimoire.com {
|
||||||
|
reverse_proxy http://librenms:8000
|
||||||
|
}
|
||||||
|
|
||||||
|
#jellyfin.netgrimoire.com {
|
||||||
|
# reverse_proxy http://jellyfin:8096
|
||||||
|
#}
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# AUTHENTICATED — NETGRIMOIRE.COM
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
dozzle.netgrimoire.com {
|
||||||
|
import authentik
|
||||||
|
reverse_proxy http://192.168.4.72:8043
|
||||||
|
}
|
||||||
|
|
||||||
|
dns.netgrimoire.com {
|
||||||
|
import authentik
|
||||||
|
reverse_proxy http://192.168.5.7:5380
|
||||||
|
}
|
||||||
|
|
||||||
|
webtop.netgrimoire.com {
|
||||||
|
import authentik
|
||||||
|
reverse_proxy http://webtop:3000
|
||||||
|
}
|
||||||
|
|
||||||
|
jackett.netgrimoire.com {
|
||||||
|
import authentik
|
||||||
|
reverse_proxy http://gluetun:9117
|
||||||
|
}
|
||||||
|
|
||||||
|
transmission.netgrimoire.com {
|
||||||
|
import authentik
|
||||||
|
reverse_proxy http://gluetun:9091
|
||||||
|
}
|
||||||
|
|
||||||
|
scrutiny.netgrimoire.com {
|
||||||
|
import authentik
|
||||||
|
reverse_proxy http://192.168.5.10:8081
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# AUTHENTICATED — WASTED-BANDWIDTH.NET (Authelia)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
stash.wasted-bandwidth.net {
|
||||||
|
import authelia
|
||||||
|
reverse_proxy http://192.168.5.10:9999
|
||||||
|
}
|
||||||
|
|
||||||
|
namer.wasted-bandwidth.net {
|
||||||
|
import authelia
|
||||||
|
reverse_proxy http://192.168.5.10:6980
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# PUBLIC — PNCHARRIS.COM / WASTED-BANDWIDTH.NET
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
fish.pncharris.com {
|
||||||
|
reverse_proxy http://web
|
||||||
|
}
|
||||||
|
|
||||||
|
www.wasted-bandwidth.net {
|
||||||
|
reverse_proxy http://web
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# MAILCOW — MULTI-DOMAIN
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
mail.netgrimoire.com, autodiscover.netgrimoire.com, autoconfig.netgrimoire.com, \
|
||||||
|
mail.wasted-bandwidth.net, autodiscover.wasted-bandwidth.net, autoconfig.wasted-bandwidth.net, \
|
||||||
|
mail.gnarlypandaproductions.com, autodiscover.gnarlypandaproductions.com, autoconfig.gnarlypandaproductions.com, \
|
||||||
|
mail.pncfishandmore.com, autodiscover.pncfishandmore.com, autoconfig.pncfishandmore.com, \
|
||||||
|
mail.pncharrisenterprises.com, autodiscover.pncharrisenterprises.com, autoconfig.pncharrisenterprises.com, \
|
||||||
|
mail.pncharris.com, autodiscover.pncharris.com, autoconfig.pncharris.com, \
|
||||||
|
mail.florosafd.org, autodiscover.florosafd.org, autoconfig.florosafd.org {
|
||||||
|
import mailcow-proxy
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker Label Pattern (label-defined services)
|
||||||
|
|
||||||
|
Services not in the Caddyfile are registered via labels on their own containers. The snippet defined in the Caddyfile is available to them via `caddy.import`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
labels:
|
||||||
|
- caddy=homepage.netgrimoire.com
|
||||||
|
- caddy.import=authentik
|
||||||
|
- caddy.reverse_proxy={{upstreams 3000}}
|
||||||
|
```
|
||||||
|
|
||||||
|
For services that need no auth:
|
||||||
|
```yaml
|
||||||
|
labels:
|
||||||
|
- caddy=myservice.netgrimoire.com
|
||||||
|
- caddy.reverse_proxy={{upstreams 8080}}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Authentication Layers
|
||||||
|
|
||||||
|
Two identity proxies are in use, each serving different domains/use cases:
|
||||||
|
|
||||||
|
| Provider | Domain Pattern | Snippet |
|
||||||
|
|----------|----------------|---------|
|
||||||
|
| Authentik | `*.netgrimoire.com` internal tools | `import authentik` |
|
||||||
|
| Authelia | `*.wasted-bandwidth.net` | `import authelia` |
|
||||||
|
|
||||||
|
Services without an auth import are either public (e.g. `fish.pncharris.com`) or carry their own authentication (e.g. Nextcloud, Graylog, Portainer).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Current Security Posture
|
||||||
|
|
||||||
|
CrowdSec protection exists only at the **OPNsense firewall level** — IP reputation blocking before traffic reaches Caddy. CrowdSec does not currently inspect HTTP traffic at the application layer. This means:
|
||||||
|
|
||||||
|
- Known-bad IPs are blocked at the perimeter
|
||||||
|
- Application-layer attacks (SQLi in URLs, malicious paths, bad user agents, brute force on specific endpoints) are not blocked at the Caddy level
|
||||||
|
- Services behind Authentik/Authelia have an additional protection layer; unauthenticated public services do not
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Future State: CrowdSec + GeoIP + Rate Limiting
|
||||||
|
|
||||||
|
### Target Image
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
image: ghcr.io/serfriz/caddy-crowdsec-geoip-ratelimit-security-dockerproxy:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
This is a drop-in replacement for `lucaslorentz/caddy-docker-proxy`. All existing Docker labels and Caddyfile site blocks continue to work unchanged. The image is automatically rebuilt monthly when Caddy releases updates — no custom image maintenance required.
|
||||||
|
|
||||||
|
**Included modules:**
|
||||||
|
- `caddy-docker-proxy` — same label-based config as current
|
||||||
|
- `caddy-crowdsec-bouncer` — inline HTTP blocking based on CrowdSec decisions
|
||||||
|
- `caddy-geoip` — GeoIP filtering at the application layer
|
||||||
|
- `caddy-ratelimit` — per-endpoint rate limiting
|
||||||
|
- `caddy-security` — additional auth/security middleware
|
||||||
|
|
||||||
|
### Updated Compose
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
configs:
|
||||||
|
caddy-basic-content:
|
||||||
|
file: ./Caddyfile
|
||||||
|
labels:
|
||||||
|
caddy:
|
||||||
|
|
||||||
|
services:
|
||||||
|
caddy:
|
||||||
|
image: ghcr.io/serfriz/caddy-crowdsec-geoip-ratelimit-security-dockerproxy:latest
|
||||||
|
ports:
|
||||||
|
- 8900:80
|
||||||
|
- 443:443
|
||||||
|
environment:
|
||||||
|
- CADDY_INGRESS_NETWORKS=netgrimoire
|
||||||
|
- CADDY_DOCKER_EVENT_THROTTLE_INTERVAL=2000 # Prevents non-deterministic reload with CrowdSec module
|
||||||
|
- CROWDSEC_API_KEY=${CROWDSEC_API_KEY}
|
||||||
|
networks:
|
||||||
|
- netgrimoire
|
||||||
|
- vpn
|
||||||
|
- crowdsec_net
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- /export/Docker/caddy/Caddyfile:/etc/caddy/Caddyfile
|
||||||
|
- /export/Docker/caddy:/data
|
||||||
|
- caddy-logs:/var/log/caddy
|
||||||
|
deploy:
|
||||||
|
placement:
|
||||||
|
constraints:
|
||||||
|
- node.hostname == znas
|
||||||
|
|
||||||
|
crowdsec:
|
||||||
|
image: crowdsecurity/crowdsec
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
COLLECTIONS: "crowdsecurity/caddy crowdsecurity/http-cve crowdsecurity/whitelist-good-actors"
|
||||||
|
BOUNCER_KEY_CADDY: ${CROWDSEC_API_KEY} # Pre-registers the Caddy bouncer automatically
|
||||||
|
volumes:
|
||||||
|
- crowdsec-db:/var/lib/crowdsec/data
|
||||||
|
- ./crowdsec/acquis.yaml:/etc/crowdsec/acquis.yaml
|
||||||
|
- caddy-logs:/var/log/caddy:ro
|
||||||
|
networks:
|
||||||
|
- crowdsec_net
|
||||||
|
deploy:
|
||||||
|
placement:
|
||||||
|
constraints:
|
||||||
|
- node.hostname == znas
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
caddy-logs:
|
||||||
|
crowdsec-db:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
netgrimoire:
|
||||||
|
external: true
|
||||||
|
vpn:
|
||||||
|
external: true
|
||||||
|
crowdsec_net:
|
||||||
|
driver: overlay # Swarm overlay network
|
||||||
|
```
|
||||||
|
|
||||||
|
### CrowdSec Log Acquisition (`./crowdsec/acquis.yaml`)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
filenames:
|
||||||
|
- /var/log/caddy/access.log
|
||||||
|
labels:
|
||||||
|
type: caddy
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment File (`.env`)
|
||||||
|
|
||||||
|
```env
|
||||||
|
CROWDSEC_API_KEY=<generate-with-cscli-or-set-before-first-boot>
|
||||||
|
```
|
||||||
|
|
||||||
|
The `BOUNCER_KEY_CADDY` env var in the CrowdSec container pre-registers the bouncer key at startup. Set the same value in `.env` as `CROWDSEC_API_KEY` and both sides will be in sync on first boot — no need to run `cscli bouncers add` manually.
|
||||||
|
|
||||||
|
### Updated Caddyfile Additions
|
||||||
|
|
||||||
|
Add a global block at the top of the Caddyfile and a new `crowdsec` snippet. All other existing content remains unchanged.
|
||||||
|
|
||||||
|
```caddyfile
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# GLOBAL BLOCK — add this at the very top before any snippets
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
{
|
||||||
|
crowdsec {
|
||||||
|
api_url http://crowdsec:8080
|
||||||
|
api_key {$CROWDSEC_API_KEY}
|
||||||
|
}
|
||||||
|
log {
|
||||||
|
output file /var/log/caddy/access.log {
|
||||||
|
roll_size 50mb
|
||||||
|
roll_keep 5
|
||||||
|
}
|
||||||
|
format json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# CROWDSEC SNIPPET — add alongside existing auth snippets
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
(crowdsec) {
|
||||||
|
route {
|
||||||
|
crowdsec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Applying CrowdSec to Existing Services
|
||||||
|
|
||||||
|
Once the snippet exists, add `import crowdsec` to site blocks and container labels. This is a **gradual rollout** — services without it remain fully functional, just without Caddy-level CrowdSec inspection (they still have OPNsense perimeter protection).
|
||||||
|
|
||||||
|
**In the Caddyfile:**
|
||||||
|
```caddyfile
|
||||||
|
# Before
|
||||||
|
cloud.netgrimoire.com {
|
||||||
|
reverse_proxy http://nextcloud-aio-apache:11000
|
||||||
|
}
|
||||||
|
|
||||||
|
# After
|
||||||
|
cloud.netgrimoire.com {
|
||||||
|
import crowdsec
|
||||||
|
reverse_proxy http://nextcloud-aio-apache:11000
|
||||||
|
}
|
||||||
|
|
||||||
|
# With auth
|
||||||
|
dozzle.netgrimoire.com {
|
||||||
|
import crowdsec
|
||||||
|
import authentik
|
||||||
|
reverse_proxy http://192.168.4.72:8043
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**In Docker labels:**
|
||||||
|
```yaml
|
||||||
|
labels:
|
||||||
|
- caddy=homepage.netgrimoire.com
|
||||||
|
- caddy.import=crowdsec
|
||||||
|
- caddy.import=authentik
|
||||||
|
- caddy.reverse_proxy={{upstreams 3000}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### CrowdSec Rollout Priority
|
||||||
|
|
||||||
|
Roll out `import crowdsec` in this order based on risk exposure:
|
||||||
|
|
||||||
|
**High priority — do first (public, no auth):**
|
||||||
|
- `cloud.netgrimoire.com` (Nextcloud)
|
||||||
|
- `immich.netgrimoire.com`
|
||||||
|
- `docker.netgrimoire.com` (Portainer)
|
||||||
|
- `fish.pncharris.com`
|
||||||
|
- `www.wasted-bandwidth.net`
|
||||||
|
|
||||||
|
**Medium priority — high value behind auth:**
|
||||||
|
- `log.netgrimoire.com` (Graylog)
|
||||||
|
- `win.netgrimoire.com` (Proxmox)
|
||||||
|
- All `dozzle`, `dns`, `webtop`, `jackett`, `transmission`, `scrutiny`
|
||||||
|
|
||||||
|
**Lower priority — already protected by Authelia/Authentik:**
|
||||||
|
- `stash.wasted-bandwidth.net`
|
||||||
|
- `namer.wasted-bandwidth.net`
|
||||||
|
- All label-defined services behind auth
|
||||||
|
|
||||||
|
**Skip:**
|
||||||
|
- Mailcow block — handled by nginx-mailcow, different threat model
|
||||||
|
|
||||||
|
### Behavior if CrowdSec Container Goes Down
|
||||||
|
|
||||||
|
The bouncer is designed to **fail open** by default. If `crowdsec` is unreachable, Caddy continues serving traffic normally — enforcement is temporarily suspended but the site stays up. This is the safe default for a homelab. To change this behavior, set `enable_hard_fails true` in the global crowdsec block (will cause 500 errors if CrowdSec is down — not recommended for homelab).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bootstrap Steps
|
||||||
|
|
||||||
|
When ready to migrate to the new image:
|
||||||
|
|
||||||
|
**Step 1 — Add the CrowdSec global block and snippet to the Caddyfile** before changing the image. This ensures the Caddyfile is valid for the new image on startup.
|
||||||
|
|
||||||
|
**Step 2 — Create `./crowdsec/acquis.yaml`** with the content above.
|
||||||
|
|
||||||
|
**Step 3 — Create `.env`** with a strong random value for `CROWDSEC_API_KEY`:
|
||||||
|
```bash
|
||||||
|
openssl rand -hex 32
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 4 — Update the image and add the CrowdSec service to the compose file**, then redeploy:
|
||||||
|
```bash
|
||||||
|
docker stack deploy -c docker-compose.yml caddy
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 5 — Verify CrowdSec is reading Caddy logs:**
|
||||||
|
```bash
|
||||||
|
docker exec <crowdsec_container> cscli metrics
|
||||||
|
```
|
||||||
|
Look for the `Acquisition Metrics` table showing hits from `/var/log/caddy/access.log`.
|
||||||
|
|
||||||
|
**Step 6 — Test a ban manually:**
|
||||||
|
```bash
|
||||||
|
docker exec <crowdsec_container> cscli decisions add --ip 1.2.3.4 --duration 5m
|
||||||
|
# Verify the IP gets a 403 from Caddy
|
||||||
|
curl -I https://yoursite.com --resolve yoursite.com:443:1.2.3.4
|
||||||
|
docker exec <crowdsec_container> cscli decisions delete --ip 1.2.3.4
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 7 — Gradually add `import crowdsec`** to site blocks and labels per the priority order above.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
/export/Docker/caddy/
|
||||||
|
├── Caddyfile # Shared snippets and static site blocks
|
||||||
|
├── docker-compose.yml # Caddy + CrowdSec services
|
||||||
|
├── .env # CROWDSEC_API_KEY (future)
|
||||||
|
├── data/ # Caddy data volume (TLS certs, etc.)
|
||||||
|
├── logs/ # caddy-logs volume mount point (future)
|
||||||
|
└── crowdsec/
|
||||||
|
└── acquis.yaml # Tells CrowdSec where to read Caddy logs (future)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Known Issues / Notes
|
||||||
|
|
||||||
|
- Port 80 is mapped to host port 8900 — this is intentional for Swarm. OPNsense NAT handles the external 80→8900 translation.
|
||||||
|
- The `CADDY_DOCKER_EVENT_THROTTLE_INTERVAL=2000` setting is **required** with the CrowdSec module to prevent non-deterministic domain matching behavior during container label reloads (see [issue #61](https://github.com/hslatman/caddy-crowdsec-bouncer/issues/61)).
|
||||||
|
- Jellyfin is commented out in the Caddyfile — likely served via a different path or disabled temporarily.
|
||||||
|
- The `web` upstream referenced by `fish.pncharris.com` and `www.wasted-bandwidth.net` resolves to a container named `web` on the `netgrimoire` network.
|
||||||
|
- Authelia redirect URL is `https://login.wasted-bandwidth.net/` — update if this changes.
|
||||||
|
- The serfriz image is rebuilt on the **1st of each month** for module updates, and on every new Caddy release. Force a module update by recreating the container: `docker service update --force caddy_caddy`.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue