#################################################################
# logstash parsing logic and tagging for elk-hole               #
# created by n9nes                                              #
# feel free to star the rep - https://github.com/nin9s/elk-hole #
#################################################################

input {
       beats {
       port => 5141
       type => "logs"
       tags => ["pihole","5141"]
       }
}

filter {

  if "pihole" in [tags] {
    grok {
      patterns_dir => ["/etc/logstash/patterns/"]
      match => {
                "message" => [

 # request - query type
 "^%{DNSMASQPREFIX} query\[%{WORD:query_type}\] %{FQDN:domain_request} from %{IP:request_from}$",
 # reponse domain to ip
 "^%{DNSMASQPREFIX} reply %{FQDN:domain_request} is %{IP:ip_response}$",
 # response domain is NXDOMAIN
 "^%{DNSMASQPREFIX} reply %{FQDN:domain_request} is NXDOMAIN$",
 # response config domain is NXDOMAIN
 "^%{DNSMASQPREFIX} config %{FQDN:domain_request} is NXDOMAIN$",
 # response config domain is no-DATA
 "^%{DNSMASQPREFIX} config %{FQDN:domain_request} is NODATA-IPv[4,6]$",
 # reponse domain to ip cname
 "^%{DNSMASQPREFIX} reply %{FQDN:domain_request} is \<CNAME\>$",
 # respone ip to domain
 "^%{DNSMASQPREFIX} reply %{IP:ip_request} is %{FQDN:domain_response}$",
 # piholed
 "^%{DNSMASQPREFIX} \/etc\/pihole\/gravity\.list %{FQDN:blocked_domain} is %{IP:pihole}$",
 # piholed local
 "^%{DNSMASQPREFIX} \/etc\/pihole\/local\.list %{FQDN:blocked_domain} is %{IP:pihole}$",
 # blacklist
 "^%{DNSMASQPREFIX} \/etc\/pihole\/black\.list %{FQDN:blocked_domain} is %{IP:pihole}$",
 # regex
 "^%{DNSMASQPREFIX} \/etc\/pihole\/regex\.list %{FQDN:blocked_domain} is %{IP:pihole}$",
 # reverse response etc hosts ip to domain
 "^%{DNSMASQPREFIX} \/etc\/hosts %{IP:ip_request} is %{FQDN:domain_response}$",
 # reverse response etc hosts domain to ip
 "^%{DNSMASQPREFIX} \/etc\/hosts %{FQDN:domain_request} is %{IP:ip_response}$",
 # forward dns to
 "^%{DNSMASQPREFIX} forwarded %{FQDN:domain_request} to %{IP:dns_forward_to}$",
 # cached domain to ip
 "^%{DNSMASQPREFIX} cached %{FQDN:domain_request} is %{IP:ip_response}$",
 # cached ip to domain
 "^%{DNSMASQPREFIX} cached %{IP:ip_request} is %{FQDN:domain_response}$",
 # cached domain to ip cname
 "^%{DNSMASQPREFIX} cached %{FQDN:domain_request} is \<CNAME\>$",
 # cached domain is NXDOMAIN
 "^%{DNSMASQPREFIX} cached %{FQDN:domain_request} is NXDOMAIN$",
 # cached domain is no-DATA
 "^%{DNSMASQPREFIX} cached %{FQDN:domain_request} is NODATA-IPv[4,6]$",
 # domain is no-DATA
 "^%{DNSMASQPREFIX} reply %{FQDN:domain_request} is NODATA-IPv[4,6]$",
 # SRV
 "^%{DNSMASQPREFIX} query\[%{WORD:query_type}\] %{HOSTNAMEPTR:request} from %{IP:request_from}$",
 # SRV forwarded
 "^%{DNSMASQPREFIX} forwarded %{HOSTNAMEPTR:request} to %{IP:dns_forward_to}$" ,
 # SERVFAIL
 "^%{DNSMASQPREFIX} reply error is SERVFAIL"

                  ]
      }
}

# to do cached and cached reverse

      if [message] =~ "cached" and [message] =~ "NXDOMAIN" {
        mutate {
          add_tag => [ "cached NXDOMAIN" ]
        }
      }

      else if [NODATA-IPv4] {
        mutate {
          add_tag => [ "NODATA" ]
        }
      }

      else if [NODATA-IPv6] {
        mutate {
          add_tag => [ "NODATA" ]
        }
      }

      else if [request_from] and [message] =~ "query" {
        mutate {
          add_tag => [ "request and query type" ]
        }
      }

      else if [ip_response] and [message] =~ "reply" {
        geoip {
          source => "ip_response"
        }
        mutate {
          add_tag => [ "response domain to ip" ]
        }
      }

      else if [message] =~ "CNAME" and [message] =~ "reply" {
        mutate {
          add_tag => [ "response domain to ip CNAME" ]
        }
      }

      else if [domain_response] and [message] =~ "reply" {
        mutate {
          add_tag => [ "response ip to domain" ]
        }
        geoip {
          source => "ip_request"
        }
      }

      else if [blocked_domain] {
        mutate {
          add_tag => [ "piholed" ]
        }
      }

      else if [message] =~ "\/etc\/hosts" {
        mutate {
          add_tag => [ "reverse hostsfile" ]
        }
      }

      else if [dns_forward_to] {
        mutate {
          add_tag => [ "dns forward" ]
        }
      }

      else if [ip_request] and [message] =~ "cached" {
        mutate {
          add_tag => [ "cached ip to domain" ]
        }
        geoip {
          source => "ip_request"
        }
      }

      else if [domain_request] and [message] =~ "cached" and [message] =~ "CNAME" {
        mutate {
          add_tag => [ "cached domain to ip cname" ]
        }
      }

      else if [domain_request] and [message] =~ "cached" {
        mutate {
          add_tag => [ "cached domain to ip" ]
        }
        geoip {
          source => "ip_response"
        }
      }


  mutate {
      add_field => {
        "[source_fqdn]" => "%{source_host}"
      }
    }

    dns {
      reverse => ["source_fqdn"]
      action => "replace"
      hit_cache_size => 4096
      hit_cache_ttl => 900
      failed_cache_size => 512
      failed_cache_ttl => 900
    }


  date {
    match => [ "date", "MMM  d HH:mm:ss","MMM dd HH:mm:ss" ]
  }

  }
}


output {
#       stdout { codec => rubydebug }
        if "pihole" in [tags]{
                elasticsearch {
                        hosts => ["ELASTICSEARCHHOST:PORT"]
                        manage_template => false
                        index => "logstash-syslog-dns-%{+YYYY.MM}"
                }
        }
}