Can't get Crowdsec to send info to Gotify

Hi all, I’m having some troubles getting Crowdsec to send information to Gotify.

I was following this YouTube video about setting up Gotify, and was able to get other services (such as Uptime Kuma) to send stuff to Gotify, so I believe the Gotify side of things is configured correctly.

Unfortunately, I cannot get Crowdsec to work for the life of me.
Both Gotify and Crowdsec are running as Docker containers.

I have included the contents of these files at the end of my post:
/crowdsec/config/notifications/http.yaml
/crowdsec/config/profiles.yaml
/crowdsec/docker-compose-crowdsec.yml
/gotify/docker-compose-gotify.yml

(which I modified following this guide: Gotify | CrowdSec)

I have restarted both Crowdsec and Gotify, and the Docker host entirely.

No matter what I do, Crowdsec refuses to work.
There is nothing obvious in either the Crowdsec or Gotify logs.

There were two lines in the Crowdsec logs, but they seem to relate to Traefik(?):

time="2024-07-24T05:28:53Z" level=error msg="UnmarshalJSON : invalid character '-' after top-level value" line="2024-07-24T15:28:53+10:00 ERR error="service \"api\" error: unable to find the IP address for the container \"/artifacts-management-1\": the server is ignored" container=management-artifacts-5693df10b3bfd907e8369eabcd18b6c024cc6957d9345b979d0c2285cb41550a providerName=docker"
time="2024-07-24T05:28:53Z" level=warning msg="failed to run filter : invalid character '-' after top-level value (1:1)\n | UnmarshalJSON(evt.Parsed.message, evt.Unmarshaled, "traefik") in ["", nil]\n | ^" id=cold-smoke name=child-crowdsecurity/traefik-logs stage=s01-parse

Any ideas what I might’ve done wrong please?

Thanks in advance.
Dave

Contents of /crowdsec/config/notifications/http.yaml:

type: http          # Don't change
name: http_default # Must match the registered plugin in the profile

# One of "trace", "debug", "info", "warn", "error", "off"
log_level: info

# group_wait:         # Time to wait collecting alerts before relaying a message to this plugin, eg "30s"
# group_threshold:    # Amount of alerts that triggers a message before <group_wait> has expired, eg "10"
# max_retry:          # Number of attempts to relay messages to plugins in case of error
# timeout:            # Time to wait for response from the plugin before considering the attempt a failure, eg "10s"

#-------------------------
# plugin-specific options

# The following template receives a list of models.Alert objects
# The output goes in the http request body
format: |
  {{ range . -}}
  {{ $alert := . -}}
  {
    "extras": {
      "client::display": {
      "contentType": "text/markdown"
    }
  },
  "priority": 3,
  {{range .Decisions -}}
  "title": "{{.Type }} {{ .Value }} for {{.Duration}}",
  "message": "{{.Scenario}}  \n\n[crowdsec cti](https://app.crowdsec.net/cti/{{.Value -}})  \n\n[shodan](https://shodan.io/host/{{.Value -}})"
  {{end -}}
  }
  {{ end -}}

# The plugin will make requests to this url, eg:  https://www.example.com/
url: https://gotify.mydomain.com/message

# Any of the http verbs: "POST", "GET", "PUT"...
method: POST

headers:
  X-Gotify-Key: keyHere #This is the key from Gotify > Apps > Crowdsec > Token
  Content-Type: application/json
# skip_tls_verification:  # true or false. Default is false

Contents of /crowdsec/config/profiles.yaml:

name: default_ip_remediation
#debug: true
filters:
 - Alert.Remediation == true && Alert.GetScope() == "Ip"
decisions:
 - type: ban
   duration: 4h
#duration_expr: Sprintf('%dh', (GetDecisionsCount(Alert.GetValue()) + 1) * 4)
notifications:
#   - slack_default  # Set the webhook in /etc/crowdsec/notifications/slack.yaml before enabling this.
#   - splunk_default # Set the splunk url and token in /etc/crowdsec/notifications/splunk.yaml before enabling this.
   - http_default   # Set the required http parameters in /etc/crowdsec/notifications/http.yaml before enabling this.
#   - email_default  # Set the required email parameters in /etc/crowdsec/notifications/email.yaml before enabling this.
on_success: break

Contents of /crowdsec/docker-compose-crowdsec.yml:

services:
  crowdsec:
    image: crowdsecurity/crowdsec:latest
    container_name: crowdsec
    environment:
      GID: "${GID-1000}"
      COLLECTIONS: "crowdsecurity/linux crowdsecurity/traefik"
    volumes:
      - /home/david/dockerfilesNotOnNAS/crowdsec/acquis.yaml:/etc/crowdsec/acquis.yaml
      - /home/david/dockerfilesNotOnNAS/crowdsec/db:/var/lib/crowdsec/data/
      - /home/david/dockerfilesNotOnNAS/crowdsec/config:/etc/crowdsec/
      - /home/david/dockerfilesNotOnNAS/traefik/logs:/var/log/traefik/:ro
    networks:
      - proxy
    security_opt:
      - no-new-privileges:true
    restart: unless-stopped

  bouncer-traefik:
    image: docker.io/fbonalair/traefik-crowdsec-bouncer:latest
    container_name: bouncer-traefik
    env_file: .env # use .env
    environment:
      CROWDSEC_BOUNCER_API_KEY: ${CROWDSEC_BOUNCER_API_KEY}
      CROWDSEC_AGENT_HOST: crowdsec:8080
    networks:
      - proxy
    depends_on:
      - crowdsec
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
networks:
  proxy:
    external: true

Contents of /gotify/docker-compose-gotify.yml:

services:
  gotify:
    image: gotify/server
    container_name: gotify
    dns: 172.22.0.53
    volumes:
      - /home/david/dockerfilesNotOnNAS/gotify/data:/app/data
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      proxy:
    environment:
      - TZ=Australia/Melbourne
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.gotify.entrypoints=http"
      - "traefik.http.routers.gotify.rule=Host(`gotify.mydomain.com`)"
      - "traefik.http.middlewares.gotify-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.gotify.middlewares=gotify-https-redirect"
      - "traefik.http.routers.gotify-secure.entrypoints=https"
      - "traefik.http.routers.gotify-secure.rule=Host(`gotify.mydomain.com`)"
      - "traefik.http.routers.gotify-secure.tls=true"
      - "traefik.http.routers.gotify-secure.service=gotify"
      - "traefik.http.services.gotify.loadbalancer.server.port=80"
      - "traefik.docker.network=proxy"
      - homepage.group=Monitoring
      - homepage.name=Gotify
      - homepage.icon=gotify
      - homepage.href=https://gotify.mydomain.com
      - homepage.description=Notifications
      - homepage.statusStyle=dot
      - homepage.widget.type=gotify
      - homepage.widget.url=https://gotify.mydomain.com
      - homepage.widget.key=Key #Get a Gotify client token from an existing client or create a new one on your Gotify admin page.

networks:
  proxy:
    external: true

It seems treafik is logging an error from Gotify, I would check the traefik / gotify logs to see what the actual error is.

Thanks for your reply.

The Traefik logs aren’t much help: it is completely empty.

If I search the Gotify logs for “err”, I see these four lines:

WebSocket: ReadError read tcp 172.22.0.16:80->172.22.0.4:44432: use of closed network connection
WebSocket: ReadError read tcp 172.22.0.4:80->172.22.0.9:41540: i/o timeout
WebSocket: ReadError websocket: close 1006 (abnormal closure): unexpected EOF
WebSocket: ReadError websocket: close 1005 (no status)

Not really much to go on, there, sadly…

And if you exec into the crowdsec container and try cscli notifications test http_default ?

Thank you. Running that command revealed that Crowdsec couldn’t resolve the FQDN I had set for my Gotify instance. I added a DNS line to the Crowdsec compose file, restarted, and re-ran that command, and bingo - a test notification appeared in Gotify.
Excellent.

Thank you for your help.

1 Like