Nextcloud + Caddy + Crowdsec: Frequent false-positive bans of own devices

1. The problem I’m having:

I’m running Nextcloud in a Docker container, with Caddy acting as a reverse proxy and Crowdsec handling, well, security.

A few months ago, whenever I stepped out of the house, I started getting notifications from Crowdsec that the IP I was using my phone from (be it the one of the mobile network or the VPN, if the phone was connected to one) was banned due to crowdsecurity/http-generic-bf scenario. After some digging, I discovered that Crowdsec was being triggered by Caddy log entries such as the one below.

The question I have for you is: am I doing anything wrong with the way I’m proxying Nextcloud? Why is Caddy assuming that the authentication failed, when Nextcloud itself is not reporting any problems and, more importantly, I can sync my contacts without any problems (when I’m not banned, that is)?

Any and all insight into this problem would be greatly appreciated!

Thank you!

2. Error messages and/or full log output:

Crowdsec Alert entry

################################################################################################

 - ID           : 4803
 - Date         : 2025-06-03T07:20:46Z
 - Machine      : localhost
 - Simulation   : false
 - Remediation  : true
 - Reason       : crowdsecurity/http-generic-bf
 - Events Count : 6
 - Scope:Value  : Ip:212.39.89.45
 - Country      : BG
 - AS           : T-Mobile
 - Begin        : 2025-06-03 07:20:42.231681397 +0000 UTC
 - End          : 2025-06-03 07:20:45.875052315 +0000 UTC
 - UUID         : bd35afff-83b7-4aa1-a647-f499250769e5

╭─────────────────────────────────────────────────────────────────────────╮
│ Active Decisions                                                        │
├──────────┬─────────────────┬────────┬────────────┬──────────────────────┤
│    ID    │   scope:value   │ action │ expiration │      created_at      │
├──────────┼─────────────────┼────────┼────────────┼──────────────────────┤
│ 22275058 │ Ip:212.39.89.45 │ ban    │ 3h46m37s   │ 2025-06-03T07:20:46Z │
╰──────────┴─────────────────┴────────┴────────────┴──────────────────────╯

 - Context  :
╭────────────┬──────────────────────────────────────────────────────────────╮
│     Key    │                             Value                            │
├────────────┼──────────────────────────────────────────────────────────────┤
│ method     │ REPORT                                                       │
│ method     │ PROPFIND                                                     │
│ status     │ 401                                                          │
│ target_uri │ /remote.php/dav/addressbooks/users/zkvvoob/z-server-generat  │
│            │ ed--system/                                                  │
│ target_uri │ /remote.php/dav/addressbooks/users/zkvvoob/1/                │
│ target_uri │ /remote.php/dav/addressbooks/users/zkvvoob/z-app-generated-  │
│            │ -contactsinteraction--recent/                                │
│ target_uri │ /remote.php/dav/principals/users/zkvvoob/                    │
│ target_uri │ /remote.php/dav/addressbooks/users/zkvvoob/                  │
│ user_agent │ iOS/18.5 (22F76) dataaccessd/1.0                             │
╰────────────┴──────────────────────────────────────────────────────────────╯

 - Events  :

- Date: 2025-06-03 10:20:42 +0300 +0300
╭─────────────────┬──────────────────────────────────────────────────────────────╮
│       Key       │                             Value                            │
├─────────────────┼──────────────────────────────────────────────────────────────┤
│ ASNNumber       │ 8866                                                         │
│ ASNOrg          │ T-Mobile                                                     │
│ IsInEU          │ true                                                         │
│ IsoCode         │ BG                                                           │
│ SourceRange     │ 212.39.64.0/19                                               │
│ datasource_path │ /var/log/caddy/mydomain.com.log                              │
│ datasource_type │ file                                                         │
│ http_args_len   │ 0                                                            │
│ http_path       │ /remote.php/dav/addressbooks/users/zkvvoob/z-server-generat  │
│                 │ ed--system/                                                  │
│ http_status     │ 401                                                          │
│ http_user_agent │ iOS/18.5 (22F76) dataaccessd/1.0                             │
│ http_verb       │ REPORT                                                       │
│ log_type        │ http_access-log                                              │
│ service         │ http                                                         │
│ source_ip       │ 212.39.89.45                                                 │
│ sub_type        │ auth_fail                                                    │
│ target_fqdn     │ cloud.mydomain.com                                           │
│ timestamp       │ 2025-06-03T10:20:42+03:00                                    │
╰─────────────────┴──────────────────────────────────────────────────────────────╯

- Date: 2025-06-03 10:20:43 +0300 +0300
╭─────────────────┬────────────────────────────────────────────────╮
│       Key       │                      Value                     │
├─────────────────┼────────────────────────────────────────────────┤
│ ASNNumber       │ 8866                                           │
│ ASNOrg          │ T-Mobile                                       │
│ IsInEU          │ true                                           │
│ IsoCode         │ BG                                             │
│ SourceRange     │ 212.39.64.0/19                                 │
│ datasource_path │ /var/log/caddy/mydomain.com.log                │
│ datasource_type │ file                                           │
│ http_args_len   │ 0                                              │
│ http_path       │ /remote.php/dav/addressbooks/users/zkvvoob/1/  │
│ http_status     │ 401                                            │
│ http_user_agent │ iOS/18.5 (22F76) dataaccessd/1.0               │
│ http_verb       │ REPORT                                         │
│ log_type        │ http_access-log                                │
│ service         │ http                                           │
│ source_ip       │ 212.39.89.45                                   │
│ sub_type        │ auth_fail                                      │
│ target_fqdn     │ cloud.mydomain.com                             │
│ timestamp       │ 2025-06-03T10:20:43+03:00                      │
╰─────────────────┴────────────────────────────────────────────────╯

- Date: 2025-06-03 10:20:44 +0300 +0300
╭─────────────────┬──────────────────────────────────────────────────────────────╮
│       Key       │                             Value                            │
├─────────────────┼──────────────────────────────────────────────────────────────┤
│ ASNNumber       │ 8866                                                         │
│ ASNOrg          │ T-Mobile                                                     │
│ IsInEU          │ true                                                         │
│ IsoCode         │ BG                                                           │
│ SourceRange     │ 212.39.64.0/19                                               │
│ datasource_path │ /var/log/caddy/mydomain.com.log                              │
│ datasource_type │ file                                                         │
│ http_args_len   │ 0                                                            │
│ http_path       │ /remote.php/dav/addressbooks/users/zkvvoob/z-app-generated-  │
│                 │ -contactsinteraction--recent/                                │
│ http_status     │ 401                                                          │
│ http_user_agent │ iOS/18.5 (22F76) dataaccessd/1.0                             │
│ http_verb       │ PROPFIND                                                     │
│ log_type        │ http_access-log                                              │
│ service         │ http                                                         │
│ source_ip       │ 212.39.89.45                                                 │
│ sub_type        │ auth_fail                                                    │
│ target_fqdn     │ cloud.mydomain.com                                           │
│ timestamp       │ 2025-06-03T10:20:44+03:00                                    │
╰─────────────────┴──────────────────────────────────────────────────────────────╯

- Date: 2025-06-03 10:20:44 +0300 +0300
╭─────────────────┬────────────────────────────────────────────╮
│       Key       │                    Value                   │
├─────────────────┼────────────────────────────────────────────┤
│ ASNNumber       │ 8866                                       │
│ ASNOrg          │ T-Mobile                                   │
│ IsInEU          │ true                                       │
│ IsoCode         │ BG                                         │
│ SourceRange     │ 212.39.64.0/19                             │
│ datasource_path │ /var/log/caddy/mydomain.com.log            │
│ datasource_type │ file                                       │
│ http_args_len   │ 0                                          │
│ http_path       │ /remote.php/dav/principals/users/zkvvoob/  │
│ http_status     │ 401                                        │
│ http_user_agent │ iOS/18.5 (22F76) dataaccessd/1.0           │
│ http_verb       │ PROPFIND                                   │
│ log_type        │ http_access-log                            │
│ service         │ http                                       │
│ source_ip       │ 212.39.89.45                               │
│ sub_type        │ auth_fail                                  │
│ target_fqdn     │ cloud.mydomain.com                         │
│ timestamp       │ 2025-06-03T10:20:44+03:00                  │
╰─────────────────┴────────────────────────────────────────────╯

- Date: 2025-06-03 10:20:45 +0300 +0300
╭─────────────────┬──────────────────────────────────────────────╮
│       Key       │                     Value                    │
├─────────────────┼──────────────────────────────────────────────┤
│ ASNNumber       │ 8866                                         │
│ ASNOrg          │ T-Mobile                                     │
│ IsInEU          │ true                                         │
│ IsoCode         │ BG                                           │
│ SourceRange     │ 212.39.64.0/19                               │
│ datasource_path │ /var/log/caddy/mydomain.com.log              │
│ datasource_type │ file                                         │
│ http_args_len   │ 0                                            │
│ http_path       │ /remote.php/dav/addressbooks/users/zkvvoob/  │
│ http_status     │ 401                                          │
│ http_user_agent │ iOS/18.5 (22F76) dataaccessd/1.0             │
│ http_verb       │ PROPFIND                                     │
│ log_type        │ http_access-log                              │
│ service         │ http                                         │
│ source_ip       │ 212.39.89.45                                 │
│ sub_type        │ auth_fail                                    │
│ target_fqdn     │ cloud.mydomain.com                           │
│ timestamp       │ 2025-06-03T10:20:45+03:00                    │
╰─────────────────┴──────────────────────────────────────────────╯

- Date: 2025-06-03 10:20:45 +0300 +0300
╭─────────────────┬──────────────────────────────────────────────────────────────╮
│       Key       │                             Value                            │
├─────────────────┼──────────────────────────────────────────────────────────────┤
│ ASNNumber       │ 8866                                                         │
│ ASNOrg          │ T-Mobile                                                     │
│ IsInEU          │ true                                                         │
│ IsoCode         │ BG                                                           │
│ SourceRange     │ 212.39.64.0/19                                               │
│ datasource_path │ /var/log/caddy/mydomain.com.log                              │
│ datasource_type │ file                                                         │
│ http_args_len   │ 0                                                            │
│ http_path       │ /remote.php/dav/addressbooks/users/zkvvoob/z-server-generat  │
│                 │ ed--system/                                                  │
│ http_status     │ 401                                                          │
│ http_user_agent │ iOS/18.5 (22F76) dataaccessd/1.0                             │
│ http_verb       │ REPORT                                                       │
│ log_type        │ http_access-log                                              │
│ service         │ http                                                         │
│ source_ip       │ 212.39.89.45                                                 │
│ sub_type        │ auth_fail                                                    │
│ target_fqdn     │ cloud.mydomain.com                                           │
│ timestamp       │ 2025-06-03T10:20:45+03:00                                    │
╰─────────────────┴──────────────────────────────────────────────────────────────╯

Caddy log

{
    "level": "info",
    "ts": 1748935246.9288347,
    "logger": "http.log.access.log0",
    "msg": "handled request",
    "request": {
        "remote_ip": "212.39.89.45",
        "remote_port": "41850",
        "client_ip":"212.39.89.45",
        "proto":"HTTP/2.0",
        "method":"PROPFIND",
        "host":"cloud.mydomain.com",
        "uri":"/remote.php/dav/addressbooks/users/zkvvoob/z-app-generated--contactsinteraction--recent/",
        "headers":{
            "Accept":["*/*"],
            "Accept-Encoding":["gzip, deflate, br"],
            "Content-Length":["181"],
            "Content-Type":["text/xml"],
            "Depth":["0"],
            "Accept-Language":["bg-BG,bg;q=0.9"],
            "Prefer":["return=minimal"],
            "Brief":["t"],
            "User-Agent":["iOS/18.5 (22F76) dataaccessd/1.0"]
        },
        "tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"cloud.mydomain.com"}
    },
        "bytes_read":181,
        "user_id":"",
        "duration":0.024832559,
        "size":477,
        "status":401,
        "resp_headers":{
            "Server":["nginx"],
            "X-Content-Type-Options":["nosniff"],
            "Content-Type":["application/xml;charset=utf-8"],
            "Content-Security-Policy":["default-src 'none';"],
            "X-Permitted-Cross-Domain-Policies":["none"],
            "Via":["2.0 Caddy"],
            "Strict-Transport-Security":["max-age=31536000;"],
            "Referrer-Policy": ["no-referrer"],
            "Alt-Svc": ["h3=\":443\"; ma=2592000"],
            "X-Xss-Protection": [
                "1",
                "1; mode=block"
            ],
            "Date": ["Tue, 03 Jun 2025 07:20:46 GMT"],
            "Set-Cookie": ["REDACTED"],
            "X-Frame-Options":["SAMEORIGIN"],
            "Www-Authenticate":["Basic realm=\"Nextcloud\", charset=\"UTF-8\""],
            "X-Robots-Tag": ["noindex, nofollow"]
        }
    }

3. Caddy version:

2.10

4. How I installed and ran Caddy:

Docker compose

c. Service/unit/compose file:

FROM caddy:builder-alpine AS builder

RUN xcaddy build \
    --with github.com/caddy-dns/porkbun \
    --with github.com/hslatman/caddy-crowdsec-bouncer/http \
    --with github.com/hslatman/caddy-crowdsec-bouncer/crowdsec \
    --with github.com/hslatman/caddy-crowdsec-bouncer/layer4

FROM caddy:latest

COPY --from=builder /usr/bin/caddy /usr/bin/caddy
services:
  caddy:
    image: caddy-porkbun:v2.10.0
    container_name: caddy
    restart: unless-stopped
    security_opt:
      - label:disable
    ports:
      - "443:443"
      - "443:443/udp"
    volumes:
      - ./config:/etc/caddy
      - ./logs:/logs
      - /etc/localtime:/etc/localtime:ro
    cap_add:
      - NET_ADMIN
    networks:
      - proxy

networks:
  proxy:
    external: true

d. My complete Caddy config:

*.mydomain.com {
    @nextcloud host cloud.mydomain.com 
	handle @nextcloud {
		header {
			Strict-Transport-Security "max-age=31536000;"
			X-XSS-Protection "1"
		}
		route {
			crowdsec
			reverse_proxy https://nextcloud {
				transport http {
					tls
					tls_insecure_skip_verify
				}
			}
		}
	}
}

5. Links to relevant resources:

The 401 is not coming from Caddy.

1 Like

Well, if you meant to suggest that it was coming from Nextcloud, there is no record of any kind in its logs, despite the fact that the logging level is set to Info.

Well that would be a nextcloud issue. The response is not from Caddy. All the indicators show that Caddy sent the request to Nextcloud, and the response is coming from upstream Nginx/Nextcloud. If there’s basic auth configured on that nginx, you’ll have to resolve it.

1 Like

So, I installed Radicale today in order to replace Nextcloud’s Calendar and Contacts, hoping that the underlying issue was due to the fact that Nextcloud was using OIDC, which, somehow, didn’t work quite well with basic authentication for Cal/CardDav.

Unfortunately, minutes ago even Radicale returned the same error. Observe, if you please:

{
    "level": "info",
    "ts": 1749310433.1873071,
    "logger": "http.log.access.log0",
    "msg": "handled request",
    "request": {
        "remote_ip": "212.39.89.45",
        "remote_port": "50826",
        "client_ip": "212.39.89.45",
        "proto": "HTTP/2.0",
        "method": "PROPFIND",
        "host": "dav.mydomain.com",
        "uri": "/zkvvoob/",
        "headers": {
            "Accept": [
                "*/*"
            ],
            "Accept-Encoding": [
                "gzip, deflate, br"
            ],
            "Prefer": [
                "return=minimal"
            ],
            "Depth": [
                "0"
            ],
            "Brief": [
                "t"
            ],
            "Content-Length": [
                "267"
            ],
            "User-Agent": [
                "iOS/18.5 (22F76) dataaccessd/1.0"
            ],
            "Accept-Language": [
                "bg-BG,bg;q=0.9"
            ],
            "Content-Type": [
                "text/xml"
            ]
        },
        "tls": {
            "resumed": false,
            "version": 772,
            "cipher_suite": 4865,
            "proto": "h2",
            "server_name": "dav.mydomain.com"
        }
    },
    "bytes_read": 267,
    "user_id": "",
    "duration": 0.001797431,
    "size": 61,
    "status": 401,
    "resp_headers": {
        "Date": [
            "Sat, 07 Jun 2025 15:33:53 GMT"
        ],
        "Server": [
            "WSGIServer/0.2 CPython/3.12.11"
        ],
        "Content-Type": [
            "text/plain; charset=utf-8"
        ],
        "Www-Authenticate": [
            "Basic realm=\"Radicale - Password Required\""
        ],
        "Content-Encoding": [
            "gzip"
        ],
        "Via": [
            "1.0 Caddy"
        ],
        "Alt-Svc": [
            "h3=\":443\"; ma=2592000"
        ],
        "Content-Length": [
            "61"
        ]
    }
}

I’m starting to think that there is something missing from my Caddy configuration:

*.mydomain.com {

	import dnsPorkbun
	import bots

	log {
		output file /logs/mydomain.com.log {
			roll_size 100MiB
			roll_keep 5
			roll_keep_for 100d
		}
		format json
		level INFO
        }
	
	# Authelia
	@authelia host auth.mydomain.com
	handle @authelia {
		crowdsec
		reverse_proxy http://authelia:9091
	}

	# Radicale
	@radicale host dav.mydomain.com
	handle @radicale {
		reverse_proxy http://radicale:5232 
	}

	handle * {
		abort
	}
}

Radicale uses LDAP for authentication and, again, when I’m at home, it has no issues syncing my calendar and contacts on both my computer and phone.

What’s difference when I’m away from home, please?

Still not Caddy. See the Server header:

The value of the Www-Authenticate header is present here as part of Nginx config:

Share your Radicale setup (its config and the Docker compose config)

1 Like

Radicale Docker Compose

services:
  radicale:
    image: tomsquest/docker-radicale
    container_name: radicale
    ports:
      - 5232:5232
    init: true
    read_only: true
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - SETUID
      - SETGID
      - CHOWN
      - KILL
    deploy:
      resources:
        limits:
          memory: 256M
          pids: 50
    healthcheck:
      test: curl -f http://127.0.0.1:5232 || exit 1
      interval: 30s
      retries: 3
    restart: unless-stopped
    volumes:
      - /mnt/apps/radicale/data:/data
      - /mnt/apps/radicale/config:/config:ro

networks:
  default:
    name: proxy
    external: true

Radicale config

[server]
hosts = 0.0.0.0:5232
ssl = False



[encoding]
# Encoding for responding requests
request = utf-8

# Encoding for storing local collections
stock = utf-8


[auth]
# Authentication method
# Value: none | htpasswd | remote_user | http_x_remote_user | dovecot | ldap | oauth2 | pam | denyall
type = ldap

# Cache logins for until expiration time
cache_logins = false

# Expiration time for caching successful logins in seconds
cache_successful_logins_expiry = 15

## Expiration time of caching failed logins in seconds
cache_failed_logins_expiry = 90

# URI to the LDAP server
ldap_uri = ldap://lldap:3890

# The base DN where the user accounts have to be searched
ldap_base = dc=mydomain,dc=com

# The reader DN of the LDAP server
ldap_reader_dn = uid=radicale,ou=people,dc=mydomain,dc=com

# Password of the reader DN
ldap_secret = password

# Path of the file containing password of the reader DN
# dap_secret_file = /run/secrets/ldap_password

# the attribute to read the group memberships from in the user's LDAP entry (default: not set)
ldap_groups_attribute = memberOf

# The filter to find the DN of the user. This filter must contain a python-style placeholder for the login
ldap_filter = (&(|(uid={0})(mail={0}))(objectClass=person))

# the attribute holding the value to be used as username after authentication
ldap_user_attribute = uid


[rights]
# Rights backend
# Value: authenticated | owner_only | owner_write | from_file
type = owner_only



[storage]

# Folder for storing local collections, created if not present
filesystem_folder = /data/collections

# Create predefined user collections
predefined_collections = {
   "addressbook": {
      "D:displayname": "Contacts",
      "tag": "VADDRESSBOOK"
   },
   "calendar": {
      "C:supported-calendar-component-set": "VEVENT,VJOURNAL,VTODO",
      "D:displayname": "Calendar",
      "tag": "VCALENDAR"
   }
 }


[web]
type = none


[logging]

# Threshold for the logger
level = warning

# Don't include passwords in logs
mask_passwords = True

# Log bad PUT request content
bad_put_request_content = True

# Log backtrace on level=debug
backtrace_on_debug = True

# Log request header on level=debug
request_header_on_debug = True

# Log request content on level=debug
request_content_on_debug = True

# Log response content on level=debug
response_content_on_debug = True

# Log rights rule which doesn't match on level=debug
rights_rule_doesnt_match_on_debug = True

# Log storage cache actions on level=debug
storage_cache_actions_on_debug = True

Some information or another is missing. Whatever causing you issues isn’t Caddy. What else is going on in your network? any interception? Do you happen to have 2 things running in parallel and the requests are alternating between them? have you checked, double checked, and triple checked your DNS?