Wazuh behind traefik on tcp port

Hi, I'm having trouble getting wazuh to work behind traefik.
I installed wazuh in a proxmox container and connected via traefik, installed in another container running docker and where traefik is installed.
Questa è la configurazione del dynamics file :
dynamics.yml:

http:
  routers:
    wazuh-router:
      rule: "Host(`wazuh.example.com`)"
      entryPoints:
        - websecure
      service: wazuh-service
      tls:
        certResolver: dns01
services:
    wazuh-service:
      loadBalancer:
        serversTransport: insecureTransport
        servers:
          - url: "https://10.20.40.2:443"

tcp:
  routers:
    wazuh-r1515:
      entryPoints:
        - 1515/tcp
      rule: "HostSNI(`wazuh.example.com`)"
      service: wazuh-s1515
      tls:
        certResolver: dns01
services:
    wazuh-s1515:
      loadBalancer:
        servers:
        - address: 10.20.40.2:1515

In the static configuration I created an entrypoint:

entryPoints:
 1515/tcp:
    address: :1515/tcp

the traefik dashboard shows no errors.
I have an internal DNS server with bind9 and I pointed the domain towards traefik, in fact the wazuh dashboard is reachable, but if I configure the wazuh agent with wazuh.example.com it doesn't work. On Traefik open all the TCP ports that Wazuh requires, to simplify I have given you the example of one since I configured the others in the exact same way.
if I try to nmap wazuh.example.com -p 1515 I get the port open:

Host is up (0.014s latency).

PORT     STATE SERVICE
1515/tcp open  ifor-protocol

But if I configure the agent the connection does not occur, unlike making it point directly to the IP, same result with zabbix.
I can't figure out if it's a traefik configuration error or I need to look elsewhere.
Any suggestions?

1 Like

Share your full Traefik static and dynamic config, and docker-compose.yml if used.

Enable and check Traefik debug log.

I recommend to use regular alphanumeric characters for names/labels. We see in Traefik examples that - works, too. Would not use other special characters like /.

Dynamics:

http:
  routers:
    bookstack-router:
      rule: "Host(`kb.lab.local`)"
      entryPoints:
        - websecure
      service: bookstack-service
      tls:
        certResolver: production

    proxmox-router:
      rule: "Host(`proxmox.lab.local`)"
      entryPoints:
        - websecure
      service: proxmox-service
      tls:
        certResolver: dns01

    portainer-router:
      rule: "Host(`portainer.lab.local`)"
      entryPoints:
        - websecure
      service: portainer-service
      tls:
        certResolver: dns01

    wazuh-router:
      rule: "Host(`wazuh.lab.local`)"
      entryPoints:
        - websecure
      service: wazuh-service
      tls:
        certResolver: dns01

    zabbix-router:
      rule: "Host(`zabbix.lab.local`)"
      entryPoints:
        - websecure
      service: zabbix-service
      tls:
        certResolver: dns01


##############################################################
  services:
    bookstack-service:
      loadBalancer:
        servers:
          - url: "http://10.20.40.9:443"
          
    proxmox-service:
      loadBalancer:
        serversTransport: insecureTransport
        servers:
          - url: "https://10.20.40.1:8006"

    portainer-service:
      loadBalancer:
        serversTransport: insecureTransport
        servers:
          - url: "https://10.20.40.6:9443"

    wazuh-service:
      loadBalancer:
        serversTransport: insecureTransport
        servers:
          - url: "https://10.20.40.2:443"

    zabbix-service:
      loadBalancer:
        serversTransport: insecureTransport
        servers:
          - url: "http://10.20.40.8/zabbix"
          
# Middlewares http
# ---
  middlewares:
    crowdsec-bouncer:
      forwardauth:
        address: http://bouncer-traefik:8080/api/v1/forwardAuth
        trustForwardHeader: true

  serversTransports:
    insecureTransport:
      insecureSkipVerify: true



#########TCP##############################
tcp:
  routers:
    wazuh-r55000:
      entryPoints:
        - 55000/tcp
      rule: "HostSNI(`wazuh.lab.local`)"
      service: wazuh-s55000
      tls:
        certResolver: dns01

    wazuh-r1514tcp:
      entryPoints:
        - 1514/tcp
      rule: "HostSNI(`wazuh.lab.local`)"
      service: wazuh-s1514tcp
      tls:
        certResolver: dns01

    wazuh-r1515:
      entryPoints:
        - 1515/tcp
      rule: "HostSNI(`wazuh.lab.local`)"
      service: wazuh-s1515
      tls:
        certResolver: dns01

    wazuh-r1516:
      entryPoints:
        - 1516/tcp
      rule: "HostSNI(`wazuh.lab.local`)"
      service: wazuh-s1516
      tls:
        certResolver: dns01

    zabbix-r10050:
      entryPoints:
        - 10050/tcp
      rule: "HostSNI(`zabbix.lab.local`)"
      service: zabbix-s10050
      tls:
        certResolver: dns01

    zabbix-r10051:
      entryPoints:
        - 10051/tcp
      rule: "HostSNI(`zabbix.lab.local`)"
      service: zabbix-s10051
      tls:
        certResolver: dns01

  services:
    wazuh-s55000:
      loadBalancer:
        servers:
        - address: 10.20.40.2:55000

    wazuh-s1514tcp:
      loadBalancer:
        servers:
        - address: 10.20.40.2:1514

    wazuh-s1515:
      loadBalancer:
        servers:
        - address: 10.20.40.2:1515

    wazuh-s1516:
      loadBalancer:
        servers:
        - address: 10.20.40.2:1516

    zabbix-s10050:
      loadBalancer:
        servers:
        - address: 10.20.40.8:10050

    zabbix-s10051:
      loadBalancer:
        servers:
        - address: 10.20.40.8:10051

static:

global:
  checkNewVersion: true
  sendAnonymousUsage: false  # true by default

# (Optional) Log information
# ---
log:
  level: DEBUG  # DEBUG, INFO, WARNING, ERROR, CRITICAL
  format: common  # common, json, logfmt
  filePath: /var/log/traefik/traefik.log

# (Optional) Accesslog
# ---
accesslog:
   format: common  # common, json, logfmt
   filePath: /var/log/traefik/access.log

# (Optional) Enable API and Dashboard
# ---
api:
  dashboard: true  # true by default
  insecure: true  # Don't do this in production!

# Entry Points configuration
# ---
entryPoints:
  web:
    address: :80
    http:
      middlewares:
        - crowdsec-bouncer@file
    # (Optional) Redirect to HTTPS
    # ---
    # http:
    #   redirections:
    #     entryPoint:
    #       to: websecure
    #       scheme: https

  websecure:
    address: :443
    http:
      middlewares:
        - crowdsec-bouncer@file
  1514/tcp:
    address: :1514/tcp
  1515/tcp:
    address: :1515/tcp
  1516/tcp:
    address: :1516/tcp
  55000/tcp:
    address: :55000/tcp
  10050/tcp:
    address: :10050/tcp
  10051/tcp:
    address: :10051/tcp


certificatesResolvers:
#   staging:
#     acme:
#       email: your-email@example.com
#       storage: /etc/traefik/certs/acme.json
#       caServer: "https://acme-staging-v02.api.letsencrypt.org/directory"
#       httpChallenge:
#         entryPoint: web
#
    production:
     acme:
       email: <MYEMAIL>
       storage: acme.json
       caServer: "https://acme-v02.api.letsencrypt.org/directory"
       httpChallenge:
         entryPoint: web

    dns01:
     acme:
       email: <MYEMAIL>
       storage: acme.json
       dnsChallenge:
         provider: cloudflare
         disablePropagationCheck: true
         delayBeforeCheck: 60
         resolvers:
          - "1.1.1.1:53"
          - "1.0.0.1:53"


# (Optional) Overwrite Default Certificates
# tls:
#   stores:
#     default:
#       defaultCertificate:
#         certFile: /etc/traefik/certs/cert.pem
#         keyFile: /etc/traefik/certs/cert-key.pem
# (Optional) Disable TLS version 1.0 and 1.1
#   options:
#     default:
#       minVersion: VersionTLS12

providers:
  docker:  
    exposedByDefault: false  # Default is true
    watch: true
  file:
    # watch for dynamic configuration changes
    directory: /dynamic_config
    watch: true

Docker Compose:

version: '3'

services:
  traefik:
    image: "traefik:latest"
    container_name: "traefik"
    restart: always
    ports:
      - "80:80" #web
      - "443:443" #websecure
      #Dashboard
      - "8080:8080"
      - "1514:1514"
      - "1514:1514/udp"
      - "1515:1515"
      - "1516:1516"
      - "55000:55000/tcp"
      - "514:514/udp"
    networks:
     proxynet:
      ipv4_address: 172.18.0.2
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro      
      #Dati
      - /mnt/docker/volumi/traefik/:/etc/traefik #configurazione traefik
      - /mnt/docker/volumi/traefik/log:/var/log/traefik/
      - /mnt/docker/volumi/traefik/cert/acme.json:/acme.json #certificati ssl
      - /mnt/docker/volumi/traefik/dynamic_config/:/dynamic_config:ro

    environment:
      - "CF_API_EMAIL=<MYEMAIL>"
      - "CF_API_KEY=<KEY>"



networks:
  proxynet:
    external: true

Debug log shows no errors for wazuh configuration

Which wazuh image do you run? Did you try to connect your client directly to the target container port?

The way you configured it, Traefik will terminate TLS and forward unencrypted data. Does wazuh expect an encrypted connection?

Wazuh is installed inside a lxc proxmox container, I followed the instructions in their documentation for installing on debian.
If I use the server's IP address, communication is ok.
I tried to edit like this but it does not work:

   wazuh-r1515:
      entryPoints:
        - p1515tcp
      rule: "HostSNI(`wazuh.lab.local`)"
      service: wazuh-s1515
      tls:
        passthrough: true

if I eliminate the passthrough instead and set HostSNI(*) it works:

   wazuh-r1515:
      entryPoints:
        - p1515tcp
      rule: "HostSNI(`wazuh.lab.local`)"
      service: wazuh-s1515

On wazuh there are self-signed certificates, from traefik I set let's encrypt, could this be the problem?

Yeah, TLS in front and back is always a bit more complicated.

tls:
        passthrough: true

only works when Traefik and target service share the same cert.

When Traefik uses LetsEncrypt, this is a challenge because you would need to export the LE cert like every 3 months and share it with the target service.

If you want to use different certs in front an back, you need to tell Traefik service to use the cert or a insecureskipverify transport.

If you want to use the custom cert from your target service, then you need to share it with Traefik to use regular HostSNI() with domain name.

If Traefik does not have a cert, then you can not activate any TLS on the entrypoint (port) and only use a TCP router with HostSNI(`*`).