Rsyslog udp logs reverse proxied by nginx with proxy_bind not importing into Graylog?

Expected Behavior

Nginx reverse proxy sends log data to Graylog and Graylog imports it.

Actual Behavior

Log data makes it to the Graylog container but Graylog doesn’t import it.
If proxy_bind $remote_addr transparent; is not included in nginx config, log data does import correctly, although with the source IP of the reverse proxy.

Additional Information

I verified this with tcpdump in the Graylog container. Logs are being shipped and reaching the container but for some reason Graylog doesn’t import them. It doesn’t appear to have any logs indicating issues either.

Configurations

Nginx docker startup script:

docker service create \
--mode global \
--name nginx-service \
--network lb-net \
--publish published=2514,target=2514,protocol=udp,mode=host \
--endpoint-mode dnsrr \
--mount type=bind,source=/mnt/shared/etc/nginx/rsyslog.conf,destination=/etc/nginx/nginx.conf \
--constraint node.labels.LB-NODE==yes \
nginx:alpine

/mnt/shared/etc/nginx/rsyslog.conf aka /etc/nginx/nginx.conf:

user root;
worker_processes 1;

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events { worker_connections 1024; }

stream {

    resolver 127.0.0.11 valid=5s;

    upstream graylog_udp {
        hash $remote_addr;
        server graylog:2514;
    }
    server {

	    listen 2514 udp;

        proxy_timeout 10s;
        #proxy_bind $remote_addr transparent; #if this line isn't commented then logs don't make it into graylog.
        proxy_pass graylog_udp;
        proxy_responses 0;
        #return $remote_addr;

    }

}

docker-compose.yml for stack (simplified):

version: '3.2'

services:
  mongo:
    image: mongo:3
    volumes:
      - gl_mongodb:/data/db
    networks: 
      lb-net: {}
    deploy:
      placement:
        constraints: [node.role == worker]
      mode: replicated
      replicas: 1
      endpoint_mode: dnsrr
      restart_policy:
        condition: on-failure

  elasticsearch:
    image: 'bitnami/elasticsearch:6.8.0-debian-9-r2'
    volumes:
      - 'gl_elastic:/bitnami/elasticsearch/data'
    environment:
      - ELASTICSEARCH_CLUSTER_NAME=graylog
    networks: 
      lb-net: {}
    deploy:
      placement:
        constraints: [node.role == worker]
      mode: replicated
      replicas: 1
      endpoint_mode: dnsrr
      restart_policy:
        condition: on-failure

  # Graylog: https://hub.docker.com/r/graylog/graylog/
  graylog:
    image: registry.redacted.com/graylog:latest
    hostname: gl_master
    volumes:
      - "/mnt/shared/services/graylog/config/node-id-master:/usr/share/graylog/data/config/node-id"
      - gl_journal:/graylog
      - "/mnt/shared/services/graylog/shadowCA/cert/CA/shadowCA.der:/usr/share/graylog/cert/CA/shadowCA.der"
      - "/mnt/shared/services/graylog/shadowCA/cert/graylog.redacted.com/graylog.redacted.com.crt:/usr/share/graylog/cert/graylog.crt"
      - "/mnt/shared/services/graylog/shadowCA/cert/graylog.redacted.com/graylog.redacted.com.key:/usr/share/graylog/cert/graylog.key"
      - "/mnt/shared/services/graylog/certs-init.sh:/usr/local/bin/graylog-custom.sh"
      - "/mnt/shared/services/graylog/shadowCA/cert/trusted_clients:/usr/share/graylog/cert/trusted_clients"
    environment:
      - GRAYLOG_IS_MASTER=true
      - GRAYLOG_PASSWORD_SECRET=redacted
      - GRAYLOG_ROOT_PASSWORD_SHA2=redacted
      - GRAYLOG_HTTP_EXTERNAL_URI=https://grayl.redacted.com/
      - GRAYLOG_TRUSTED_PROXIES=192.168.5.4/32,10.0.2.0/24,184.69.119.142/32,184.71.21.162/32,184.69.23.106/32,172.18.0.0/24
      - GRAYLOG_ELASTICSEARCH_HOSTS=http://elasticsearch:9200
      - GRAYLOG_JAVA_OPTS="-Djavax.net.ssl.trustStore=/usr/share/graylog/cacerts.jks"
      - GRAYLOG_ROOT_TIMEZONE=Canada/Pacific
    networks: 
      lb-net: {}
    deploy:
      placement:
        constraints: [node.role == worker]
      mode: replicated
      replicas: 1
      endpoint_mode: dnsrr
      restart_policy:
        condition: on-failure
    depends_on:
      - mongodb
      - elasticsearch
    command:
      - graylog-custom.sh

volumes:
  gl_elastic:
    external: true
  gl_mongodb:
    external: true
  gl_journal:
    external: true

networks:
  lb-net:
    external: true

Sorry if this isn’t enough information - if more is needed I’ll provide it.

what input did you create in Graylog to receive what kind of messages?

Hi Jan, Graylog input is UDP Syslog input. It works without the proxy_bind directive in the nginx config:

allow_override_date:true
bind_address:0.0.0.0
expand_structured_data:false
force_rdns:false
number_worker_threads:2
override_source:<empty>
port:2514
recv_buffer_size:262139
store_full_message:false


Throughput / Metrics
1 minute average rate: 1 msg/s
Network IO: 456.0B 0B (total: 1.9MiB 0B )
Empty messages discarded: 0

But as soon as I add that proxy_bind to nginx the syslog input shows 0 messages received. Checked tcpdump and can confirm the messages are being received by the graylog container both with and without the proxy_bind. With proxy_bind the messages show as being from the originating source IP’s rather than the nginx container which is ideal.

Edit for more clarification:
With the graylog stack deployed and nginx started without proxy_bind, messages received by container:

root@gl_master:/usr/share/graylog# tcpdump udp port 2514 -vv -X
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
17:11:46.341665 IP (tos 0x0, ttl 64, id 13891, offset 0, flags [DF], proto UDP (17), length 162)
    nginx-service.eb8x03wknug13i6kb1peq2yr3.tl7118p4lwp4339w0znywmuv0.lb-net.50854 > gl_master.2514: [udp sum ok] UDP, length 134
  0x0000:  4500 00a2 3643 4000 4011 eb62 0a00 0255  E...6C@.@..b...U
  0x0010:  0a00 0251 c6a6 09d2 008e 4e64 3c31 3334  ...Q......Nd<134
...

And actually, if the nginx-service is removed and restarted with the proxy_bind directive, the messages stop flowing into the graylog rsyslog input immediately. They are still received by the graylog container but they don’t show as received by the input:

19:53:15.293202 IP (tos 0x0, ttl 64, id 58443, offset 0, flags [DF], proto UDP (17), length 166)
  172.18.0.1.39335 > gl_master.2514: [udp sum ok] UDP, length 138
0x0000:  4500 00a6 e44b 4000 4011 9d97 ac12 0001  E....K@.@.......
0x0010:  0a00 0251 99a7 09d2 0092 d3c0 3c31 3334  ...Q........<134
...


Throughput / Metrics
1 minute average rate: 0 msg/s
Network IO: 0B 0B (total: 0B 0B )
Empty messages discarded: 0

does the Graylog log give you any indication about an error?

Hi Jan,

No error messages. It appears the logs also don’t get inserted into the Elasticsearch cluster either (which I read was an issue for others). Something I’m wondering about - is there a better way of accomplishing what I’m trying to do?

Rsyslog outputs from various external pfSense routers into our central logging system. Graylog is setup using docker swarm and located on private network, with a forward facing virtual machine running haproxy and nginx containers also with swarm ingress routing. I’m tempted to move a slave graylog node directly onto the forward facing/exposed virtual machine and use the host networking to expose the rsyslog input directly but was hoping to avoid that. If you have any other ideas I’d love to hear them.

Thanks

For the sake of trying I created a slave on each of the front facing vm’s (uses keepalived to try to achieve higher availability) and they appear to work fine but interestingly enough running tcpdump on the VM with an active IP results in getting rsyslog packets where as running it from within the VM on that container using host networking and proper forwarding results in no rsyslog packets received.

Maybe it’s an issue with how I’ve defined the port? Hopefully can get this figured out soon.

slave1:
  image: registry.example.com/graylog:latest
  volumes:
    - "/mnt/shared/services/graylog/config/node-id-slave1:/usr/share/graylog/data/config/node-id"
    - gl_journal:/graylog
    - "/mnt/shared/services/graylog/shadowCA/cert/CA/shadowCA.der:/usr/share/graylog/cert/CA/shadowCA.der"
    - "/mnt/shared/services/graylog/shadowCA/cert/graylog.example.com/graylog.example.com.crt:/usr/share/graylog/cert/graylog.crt"
    - "/mnt/shared/services/graylog/shadowCA/cert/graylog.example.com/graylog.example.com.key:/usr/share/graylog/cert/graylog.key"
    - "/mnt/shared/services/graylog/certs-init.sh:/usr/local/bin/graylog-custom.sh"
    - "/mnt/shared/services/graylog/shadowCA/cert/trusted_clients:/usr/share/graylog/cert/trusted_clients"
  environment:
    - GRAYLOG_IS_MASTER=false
    - GRAYLOG_PASSWORD_SECRET=asdf
    - GRAYLOG_ROOT_PASSWORD_SHA2=asdf
    - GRAYLOG_ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    - GRAYLOG_JAVA_OPTS="-Djavax.net.ssl.trustStore=/usr/share/graylog/cacerts.jks"
    - GRAYLOG_HTTP_EXTERNAL_URI=https://grayl.example.com/
    - GRAYLOG_HTTP_PUBLISH_URI=https://grayl.example.com/
    - GRAYLOG_HTTP_BIND_ADDRESS=0.0.0.0
    - GRAYLOG_ROOT_TIMEZONE=America/Vancouver
  networks: 
    lb-net: {}
  ports:
    - target: 2514
      published: 2514
      protocol: udp
      mode: host
  deploy:
    placement:
      constraints: [node.labels.GL==slave1]
    mode: replicated
    replicas: 1
    endpoint_mode: dnsrr
    restart_policy:
      condition: on-failure
  depends_on:
    - mongodb
    - elasticsearch
  command:
    - graylog-custom.sh

Only error in the logs:

gl_slave2.1.jmpp2snfpo1w@ohmast2    | 2019-12-12 15:48:55,599 WARN : org.graylog2.inputs.transports.UdpTransport - receiveBufferSize (SO_RCVBUF) for input SyslogUDPInput{title=Syslog UDP, type=org.graylog2.inputs.syslog.udp.SyslogUDPInput, nodeId=gl_slave2} (channel [id: 0x20cf5ab1, L:/0.0.0.0:2514]) should be 262144 but is 425984.

sorry to say it this harsh - but if you can’t debug your stack, the stack is to complex …

you have already identified that the messages should have been routed to Graylog - I guess that if you use the forwarder the messages are tampered in some way. Did you already tried a RAW Input? Because this way you do not need to have a specific format and it accepts everything.

Your graylog server.log and the elaticsearch log file might tell you if other problems are given.

No I think that’s a fair thing to say and appreciate your honesty.

I did end up getting it working last night, but still am not entirely sure why it wasn’t working in the first place. I created an additional docker network, connected the graylog slaves to it and was able to receive log input to their docker containers. Since I didn’t define an internal docker network or connect the new external docker network to the other docker network, they weren’t able to connect to the mongodb instances or the master.

I then switched back to the original network (I tried to use both, actually, but docker didn’t like that), and upon switching back it just “worked”. I suspect there may have been a leftover route configured for my initial nginx containers that wasn’t allowing the port to pass through but don’t really know because now that it’s working I don’t really care.

If I come across this again I’ll be sure to post back but this is a temporary setup until I can obtain some additional hardware and switch to a k8s solution so the chances of that are unlikely.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.