Sunday, April 26, 2026

Logscale, dashboards, and DNS

Futurama gif with Fry screaming 'Fix It! Fix it! Fix it!' to Leela

Losgcale, or as it rather be called Crowdstrike Falcon Logscale (say that 3 times fast and Bettlejuice may show up), is a Security Information and Event Management(SIEM) made by Crowdstrike. In other words, it is a glorified log collector that allows you to ask questions, create alerts based on those questions, and present the answers in pretty dashboards and graphs. It is not the only game in town -- there are quite a few open source and commercial ones -- but it is the one we will be talking about today.

Dashboards, dashboards everywhere

A dashboard is way to show what the logs know in a pretty graphical way: most people just want to see what they need to care about the logs; they should not be forced to learn how to do that manually.

For instance, what if you want to show all the times the logs reported a specific event happened? Which event? I don't know. Maybe a failed sshsession. Since we will be using fake data, how about if we want to know if the logs detected the presence of The Dude? Maybe that would look like this in the logs:

<47>Apr 24 08:06:46 192.168.12.9 banana/error[285]: dude-abides=false
Note: This is being fed by my Python-based fake log generator script. So, it says whatever I want.

Now, let's create a quick dashboard since this article is not about how to create one. All we want it to do is to find The Dude and let us know when and where the presence of His Dudeness was detected. Where here would be the host name; it would be nice if we have the full name (balcony.example.org in this example), or FQDN. Technically logscale has a filed called host that should provide us that. Problem is that it gives us whatever the log had: if that was a hostname, we are good. But, what the host does not have the full name, instead just the balcony or IP (192.168.12.9) address? Losgcale provide the query command reverseDNS(), which can return the FQDN:

| reverseDNS(host, as="host_name")

With that, we could create a dashboard whose query looks like this:

tail(100)
| reverseDNS(host, as="host_name")
| has_dude := if(text:contains(string=msg, substring="dude-abides"), then="Yes", else="No")
| table([@timestamp, host_name, type, has_dude, msg])

Just before anyone asks, this is a very simple query because writing dashboards is not the topic of this article. All I want it to do is to get logs like this (one of them has a message body I saved from a real alert down to the IP address)

<63>Apr 24 08:06:41 192.16.94.101 tiktok/error[1197]: Did you see that?
<47>Apr 24 08:06:46 192.168.12.9 banana/error[285]: dude-abides=false
<66>Apr 24 08:06:47 192.168.55.85 tiktok/error[867]: right_droid=false
<1>Apr 24 08:06:48 192.16.55.6 unamed[1155]: Did you see that?
<8>Apr 24 08:06:50 192.168.12.137 rm/error[1733]: Cannot destroy all humans
<20>Apr 24 08:06:51 192.16.55.6 tiktok/error[121]: %RCMD-4-RSHPORTATTEMPT: Attempted to connect to RSHELL from 217.69.139.202
<45>Apr 24 08:06:52 192.168.55.85 tiktok[426]: right_droid=false

which have IP addresses for hostnames (note the IP range; it will be important later). The "job" of the query is to identify those logs containing dude-abides. To make it look pretty, we are using the function reverseDNS() to convert the IPs to FQDNs.

The output should look like a table with 4 columns (you can stretch the window to look pretty):

@timestamp host_name has_dude msg
2026-04-24 04:06:41.000 balcony.example.org No Did you see that?
2026-04-24 08:06:46.000 balcony.example.org Yes dude-abides=false
2026-04-24 04:06:47.000 saladbar.example.org No right-droid=false
2026-04-24 04:06:48.000 kitchen.example.org No Did you see that?
2026-04-24 04:06:50.000 bathroom.example.org No Cannot destroy all humans
2026-04-24 04:06:51.000 kitchen.example.org No %RCMD-4-RSHPORTATTEMPT: Attempted to connect to RSHELL from 217.69.139.202
2026-04-24 04:06:52.000 saladbar.example.org No right-droid=false
Note: The reason we have 000 in the end of the time (ex: 08:06:46.000) is that logscale can handle/expects/would like that to be in microseconds. When it does not get them, it makes microseconds = 000.

It is broken!

When we run it the dashboard, something goes wrong: where is the host_name?

Screen capture of the logscale dashboard we created (looks like the table above). The host_name column is missing

So, how to make it work? By default logscale uses the dns servers known by the host it is running on. In Linux they could be defined in resolv.conf (I am going to use dns1, dns2, and so on to define the dns servers instead of using IPs so it is easy to see them; this is getting long):

[banana@logscale-box ~]$ cat /etc/resolv.conf
# Generated by NetworkManager
search example.org
nameserver dns1
nameserver dns2
[banana@logscale-box ~]$

dns1 and dns2, being our internal dns servers, do know all of our hosts in the 192.168.xx.xx (technically I should represent that as 192.168.0.0/16) range, which is a private IP range

Note: Private IP Addresses are those defined by RFC 1918 to belong to one of the following ranges: 192.168.x.x, 10.x.x.x, and 172.16.x.x

It turns out by default (again that word) logscale will not convert IPs in the private IP address ranges, you have to force it to do so. And that is done using 3 environmental variables (I call them constants. but what do I know?) defined in /etc/logscale/humio.conf: RDNS_DEFAULT_SERVER, IP_FILTER_RDNS_SERVER, and IP_FILTER_RDNS:

RDNS_DEFAULT_SERVER
  • Defines the server used when no server is explicitly specified in the function call
  • Ex: RDNS_DEFAULT_SERVER=192.168.5.3
IP_FILTER_RDNS
  • For reverse DNS lookups in reserved IP addresses
  • Which IP addresses can be queried with rdns() and reverseDns()
  • Ex: IP_FILTER_RDNS=deny 10.0.5.0/24; allow all
    • Cannot query hosts in the 10.0.5.0/24 range
    • Can query reserved IP addresses that are in other ranges.
IP_FILTER_RDNS_SERVER
  • For RDNS server filtering
  • Only restricts which servers can be explicitly specified in rdns() or reverseDns() function calls
  • If RDNS_DEFAULT_SERVER is defined, include it
  • Ex: IP_FILTER_RDNS_SERVER=allow 192.168.7.1; deny all
    • Allows 192.168.7.1 (and 192.168.5.3) as rdns servers for reserved IP addresses

Since dns1 and dns2 can resolve the problem, all we would need to do is to add the following lines to /etc/logscale/humio.conf:

IP_FILTER_RDNS_SERVER=allow all
IP_FILTER_RDNS=allow all

The following screen capture does show hostnames under host_name; I just deleted them because I used real names. I will manually alter the file(!) to put mine later.

Screen capture of the logscale dashboard we created (looks like the table above). The host_name column is now visible

Variations on the theme

  1. What if I just want to only allow the use of dns2 for reverse dns?

    Edit IP_FILTER_RDNS_SERVER

    IP_FILTER_RDNS_SERVER=allow dns2; deny all
    then, optionally, edit the dashboard query
    | reverseDNS(host, as="host_name", server="dns2")
  2. What if I want to only use a dns3 for reverse dns, but not define it in resolv.conf?

    Edit humio.conf

    RDNS_DEFAULT_SERVER=dns3
    IP_FILTER_RDNS=allow all
    IP_FILTER_RDNS_SERVER=allow dns3; deny all
    then, optionally, edit the dashboard query
    | reverseDNS(host, as="host_name", server="dns3")