TRACKALERT.CONF(5)

πŸ“¦
version 3.0.0 Β· 3 versions
, in ubuntu stonking

πŸ’– TRACKALERT.CONF(5) πŸ’–

okay so this is the manual page for trackalert.conf, the config file for the trackalert daemon. bestie, you're gonna wanna read this. πŸ’…


✨ NAME ✨

trackalert.conf β€” configuration file for the trackalert daemon πŸ’–


🌸 DESCRIPTION 🌸

Okay so trackalert reads this file, and it's literally a Lua script. It has Lua commands that do two big jobs: (a) configure the trackalert daemon, and (b) define functions that control how the daemon behaves when it gets HTTP commands β€” specifically the "report" command. ✨

You can also point it at a different config file with:

trackalert -C private_trackalert.conf ...

πŸŽ€ CONFIGURATION-ONLY FUNCTIONS πŸŽ€

These functions are for configuring trackalert only. You can't call them inside the report or background functions. Periodt.

  • setACL(<list of netmasks>) β€” Set the access control list for the HTTP Server. For example, to allow access from any IP address, specify:
setACL({"0.0.0.0/0"})
  • addACL(<netmask>) β€” Add a netmask to the access control list for the HTTP server. For example, to allow access from 127.0.0.0/8, specify:
addACL("127.0.0.0/8")
  • addCustomWebHook(<custom webhook name>, <config key map>) β€” Add a custom webhook, like one you can call from Lua (using "runCustomWebHook()" β€” see below) with arbitrary data, using the specified configuration keys. See trackalert_webhook(5) for the supported config keys, and details of the HTTP(S) POST sent to webhook URLs. For example:
config_keys={}
config_keys["url"] = "http://webhooks.example.com:8080/webhook/"
config_keys["secret"] = "verysecretcode"
addCustomWebHook("mycustomhook", config_keys)
  • addCustomStat(<stat name>) β€” Add a custom counter which can be used to track statistics. The stats for custom counters are logged every 5 minutes. The counter is incremented with the "incCustomCounter" command. For example:
addCustomStat("custom_stat1")
  • webserver(<IP:port>, <password>) β€” (deprecated β€” see addListener() instead) Listen for HTTP commands on the specified IP address and port. The password is used to authenticate client connections using basic authentication. For example:
webserver("0.0.0.0:8084", "super")
  • addListener(<IP:port>, <useSSL>, <cert file>, <key file>, <options>) β€” (replacement for webserver()) Listen for HTTP commands on the specified IP address and port. If useSSL is true, then HTTPS must be used, and cert_file and key file are used, otherwise they are empty. Options contains a list of key value pairs to configure the TLS connection; these follow the command line option names in https://www.openssl.org/docs/manmaster/man3/SSL_CONF_cmd.html. For example, "min_protocol" to set the minimum TLS protocol version. You can add as many listeners as you choose. For example:
addListener("0.0.0.0:8084", false, "", "", {})
addListener("1.2.3.4:1234", true, "/etc/wforce/cert.pem", "/etc/wforce/key.pem", {minimum_protocol="TLSv1.2"})
addListener("[::1]:9000", true, "/etc/wforce/cert.pem", "/etc/wforce/key.pem", {minimum_protocol="TLSv1.3"})
  • setWebserverPassword(<Password>) β€” (replacement for webserver password parameter) Sets the basic authentication password for access to the webserver. This has been decoupled from the addListener() command because multiple listeners can now be created, which was not previously possible. For example:
setWebserverPassword("foobar")
  • controlSocket(<IP[:port]>) β€” Listen for control connections on the specified IP address and port. If port is not specified it defaults to 5199. For example:
controlSocket("0.0.0.0:4004")
  • setKey(<key>) β€” Use the specified key for authenticating connections from siblings. The key must be generated with makeKey() from the console. See trackalert(1) for instructions on running a console client. Returns false if the key could not be set (e.g. invalid base64). For example:
if not setKey("Ay9KXgU3g4ygK+qWT0Ut4gH8PPz02gbtPeXWPdjD0HE=")
    then
      ...
  • setNumLuaStates(<num states>) β€” Set the number of Lua Contexts that will be created to run report commands. Defaults to 10 if not specified. See also setNumReportThreads() and setNumSchedulerThreads(). Should be at least equal to NumReportThreads + NumSchedulerThreads. For example:
setNumLuaStates(10)
  • setNumWorkerThreads(<num threads>) β€” Set the number of threads in the pool used to receive webserver commands. Note that the report function itself is run in a separate report thread pool controlled by setNumReportThreads(). Defaults to 4 if not specified. For example:
setNumWorkerThreads(4)
  • setMaxWebserverConns(<max conns>) β€” Set the maximum number of active connections to the webserver. This can be used to limit the effect of too many queries to trackalert. It defaults to 10,000. For example:
setMaxWebserverConns(5000)
  • setNumReportThreads(<num threads>) β€” Set the number of threads in the pool used to run Lua report functions. Each thread uses a separate Lua Context, (see setNumLuaStates()). Defaults to 6 if not specified. For example:
setNumReportThreads(6)
  • setNumSchedulerThreads(<num threads>) β€” Set the number of threads in the pool used to run scheduled background Lua functions. Each thread uses a separate Lua Context, the number of which is set with setNumLuaStates(). Defaults to 4 if not specified. For example:
setNumSchedulerThreads(2)
  • setNumWebHookThreads(<num threads>) β€” Set the number of threads in the pool used to send webhook events. Defaults to 4 if not specified. For example:
setNumWebHookThreads(2)
  • newGeoIP2DB(<db name>, <filename>) β€” Opens and initializes a GeoIP2 database. A name must be chosen, and the filename of the database to open must also be supplied. To obtain an object allowing lookups to be performed against the database, use the getGeoIP2DB() function. For example:
newGeoIP2DB("CityDB", "/usr/share/GeoIP/GeoLite2-City.mmdb")
  • initGeoIPDB() β€” (Deprecated β€” use newGeoIP2DB()). Initializes the country-level IPv4 and IPv6 GeoIP databases. If either of these databases is not installed, this command will fail and trackalert will not start. For example:
initGeoIPDB()
  • initGeoIPCityDB() β€” (Deprecated β€” use newGeoIP2DB()). Initializes the city-level IPv4 and IPv6 GeoIP databases. If either of these databases is not installed, this command will fail and trackalert will not start. Ensure these databases have the right names if you’re using the free/lite DBs β€” you may need to create symbolic links e.g. GeoIPCityv6.dat β†’ GeoLiteCityv6.dat. For example:
initGeoIPCityDB()
  • initGeoIPISPDB() β€” (Deprecated β€” use newGeoIP2DB()). Initializes the ISP-level IPv4 and IPv6 GeoIP databases. If either of these databases is not installed, this command will fail and trackalert will not start. For example:
initGeoIPISPDB()
  • setNumWebHookThreads(<num threads>) β€” Set the number of threads in the pool used to send webhook events. Defaults to 5 if not specified. For example:
setNumWebHookThreads(2)
  • setNumWebHookConnsPerThread(<num conns>) β€” Set the maximum number of connections used by each WebHook thread. Defaults to 10 if not specified. This setting replaces the deprecated "num_conns" per-hook configuration setting. For example:
setNumWebHookConnsPerThread(50)
  • setWebHookQueueSize(<queue size>) β€” Set the size of the queue for webhook events. If the queue gets too big, then webhooks will be discarded, and an error will be logged. The default queue size is 50000, which should be appropriate for most use-cases.
setWebHookQueueSize(100000)
  • setWebHookTimeoutSecs(<timeout secs>) β€” Set the maximum time a request can take for webhooks. For example:
setWebHookTimeoutSecs(2)
  • setReport(<report func>) β€” Tell trackalert to use the specified Lua function for handling all "report" commands. For example:
setReport(report)
  • setBackground(<name>, <lua function> β€” The setBackground function registers a background function with a given name. The name is used when scheduling the function using the xxxScheduleBackgroundFunc() functions.
setBackground("mybg", backgroundFunc)
  • cronScheduleBackgroundFunc(<cron string>, <background function name>) β€” Tells trackalert to run the specified function according to the given cron schedule (note the name given in setBackground() is used, not the actual function name). Note that cron ranges are not currently supported β€” if you want to schedule the same function to run for example on two different days of the week, then you would use two different calls to this function to achieve that. For example:
cronScheduleBackgroundFunc("0 0 1 * *", "mybg")
cronScheduleBackgroundFunc("0 0 6 * *", "mybg")
  • intervalScheduleBackgroundFunc(<duration string>, <background function name>) β€” Tells trackalert to run the specified function at the interval given in duration string. The duration string is of the format h[h][:mm][:ss], so for example "10" would indicate an interval of 10 hours, "00:00:01" would indicate an interval of 1 second, and "5:01" would indicate an interval of 5 hours and 1 minute. For example:
intervalScheduleBackgroundFunc("00:30:00", "mybg")
intervalScheduleBackgroundFunc("24", "mybg")
  • setCustomEndpoint(<name of endpoint>, <custom lua function>) β€” Create a new custom REST endpoint with the given name, which when invoked will call the supplied custom lua function. This allows admins to arbitrarily extend the trackalert REST API with new REST endpoints. Admins can create as many custom endpoints as they require. Custom endpoints can only be accessed via a POST method, and all arguments are passed as key-value pairs of a top-level "attrs" json object (these will be split into two tables β€” one for single-valued attrs, and the other for multi-valued attrs β€” see CustomFuncArgs below). Return information is passed with a boolean "success" and "r_attrs" json object containing return key-value pairs. For example:
function custom(args)
  for k,v in pairs(args.attrs) do
    infoLog("custom func argument attrs", { key=k, value=v });
  end
  -- return consists of a boolean, followed by { key-value pairs }
  return true, { key=value }
end
setCustomEndpoint("custom", custom)

so that's the whole wiring, bestie β€” you define your custom function, you return true, { key=value } when you're happy, and then you hand the whole thing over to setCustomEndpoint("custom", custom) and let it live its truth. πŸ¦‹


  • disableCurlPeerVerification() β€” turns OFF the checking of peer certificates on every single outbound HTTPS connection you make. That's a big deal, okay? So like, this is not recommended except for debugging or development purposes. Do not rawdog this in prod, I'm begging. 😭
disableCurlPeerVerification()
  • disableCurlHostVerification() β€” turns OFF the checking of the hostname inside the peer certificate, again for all outbound HTTPS connections. Also not recommended except for debugging or development purposes. Same energy as above: cute for science, cursed for production. πŸ’€
disableCurlHostVerification()
  • setCurlCABundleFile(<Path to CA File>) β€” tells trackalert where to find a file full of the certificate authorities to use when it's checking HTTPS server certificates. Reach for this when the standard installed root certs just don't have the certs you need (rude of them, honestly). The file should contain 1:N certs in PEM format. πŸ”
setCurlCABundleFile("/etc/ca/local_cas.pem")
  • setCurlClientCertAndKey(<Path to Cert File>, <Path to Key File>) β€” points at the certificate file and the key file to use for mutual TLS authentication (both in PEM format). Two paths, one very secure little handshake. 🫢
setCurlClientCertAndKey("/etc/certs/clientcert.pem", "/etc/certs/clientkey.pem")
  • setMetricsNoPassword() β€” disables password protection for the /metrics endpoint. The door is unlocked, bestie. πŸšͺ
heads up β€” the two disableCurl*Verification() functions are explicitly not recommended unless you're debugging or developing. They're vibes-only tools, not a lifestyle. ✨
  • GeoIP2DB:lookupCity(<ComboAddress>) β€” hands you back a map stuffed with info about that IP, like the city name and the latitude/longitude. Peep GeoIPRecord below for the whole field list. Like:
local gip_record = citydb:lookupCity(lt.remote)
local my_city = gip_record.city
local my_latitude = gip_record.latitude
  • lookupCountry(<ComboAddress>) β€” (deprecated, bestie β€” use the new GeoIP2 one instead). Returns the two-character country code of wherever that IP lives. You can build a ComboAddress with newCA(). Example:
my_country = lookupCountry(my_ca)
  • lookupISP(<ComboAddress>) β€” (also deprecated, pls use the new GeoIP2 version). Gives you the name of the ISP hosting the IP. ComboAddress comes from newCA(). Example:
local my_isp = lookupISP(newCA("128.243.16.21"))
  • lookupCity(<ComboAddress>) β€” (deprecated too, GeoIP2 is right there). Returns a map with info about the IP, like city name, latitude and longitude. Check GeoIPRecord below for every field. Example:
local gip_record = lookupCity(lt.remote)
local my_city = gip_record.city
local my_latitude = gip_record.latitude
  • newCA(<IP[:port]>) β€” makes you a shiny object representing an IP address (v4 or v6) plus an optional port. That object is a ComboAddress, and it's iconic. Like:
my_ca = newCA("192.128.12.82")
  • ComboAddress:tostring() β€” gives you back a string version of the IP address. Like:
mystr = my_ca:tostring()
  • newNetmaskGroup() β€” returns a NetmaskGroup object, which is literally the efficient way to match IPs/subnets against a whole range. Like:
mynm = newNetmaskGroup()
  • NetmaskGroup:addMask(<cidr>) β€” drops a mask into the NetmaskGroup, in cidr format. Like:
mynm:addMask("182.22.0.0/16")
  • NetmaskGroup:match(<ip address>) β€” checks an IP address against a NetmaskGroup. The IP has to be a ComboAddress object, no exceptions. Like:
if (mynm.match(lt.remote))
then
    print("ip address matched")
end
  • runCustomWebHook(<custom webhook name>, <webhook data>) β€” runs a custom webhook you already configured, with the data you hand it. Default Content-Type is "application/json", but you can flip that with the "content-type" config key when you set the custom webhook up. Custom webhooks run on the same thread pool as the normal ones, no cap. Like:
runCustomWebHook(mycustomhook", "{ \"foo\":\"bar\" }")
  • infoLog(<log string>, <key-value map>) β€” logs your string at LOG_INFO level, and staples "key=value" strings onto the log for every kv in the map. Like:
infoLog("Logging is very important", { logging=1, foo=bar })
  • vinfoLog(<log string>, <key-value map>) β€” same energy as infoLog, logs at LOG_INFO with all those "key=value" strings β€” but only if trackalert was started with the (undocumented) -v flag, aka verbose. Like:
vinfoLog("Logging is very important", { logging=1, foo=bar })
  • warnLog(<log string>, <key-value map>) β€” logs at LOG_WARN level with all those "key=value" strings added for every kv in the map. Like:
warnLog("Logging is very important", { logging=1, foo=bar })
  • errorLog(<log string>, <key-value map>) β€” logs at LOG_ERR level, plus "key=value" strings for every kv in the map. Like:
errorLog("Logging is very important", { logging=1, foo=bar })
  • debugLog(<log string>, <key-value map>) β€” logs at LOG_DEBUG level with the "key=value" strings for every kv in the map, but only if trackalert was started with the (undocumented) -v flag (verbose, obviously). Like:
debugLog("This will only log if trackalert is started with -v", { logging=1, foo=bar })
  • LoginTuple β€” the only parameter the report function ever gets, and it's a LoginTuple table. It's carrying these fields:
    • LoginTuple.remote β€” an IP address of type ComboAddress, aka the system doing the login.
    • LoginTuple.login β€” the username of whoever's logging in.
    • LoginTuple.pwhash β€” a hashed representation of that user's password.
    • LoginTuple.success β€” whether the login went through or not.
    • LoginTuple.policy_reject β€” set when the login failed only because of a policy-based reject from trackalert (meaning the username and password were actually correct).
    • LoginTuple.attrs β€” extra array of (single valued) attributes about the login, like info about the user from LDAP. Example:
for k, v in pairs(lt.attrs) do
 if (k == "xxx")
 then
         -- do something
 end
end
  • LoginTuple.attrs_mv β€” extra array of (multi-valued) attributes about the login. Example:
for k, v in pairs(lt.attrs_mv) do
 for i, vi in ipairs(v) do
         if ((k == "xxx") and (vi == "yyy"))
         then
                 -- do something
         end
 end
end
  • LoginTuple.device_id β€” an optional string for the device the user logged in from. Also peep device_attrs.
  • LoginTuple.device_attrs β€” extra array of attributes about the device, parsed out of the device_id string. The protocol string decides how device_id gets parsed, so that MUST be present too. If device_attrs is already populated, it gets used as-is, periodt. For all protocols, these keys are set wherever possible: os.family, os.major, os.minor. For http, these extra keys are set wherever possible: device.family, device.model, device.brand, browser.family, browser.major, browser.minor. For imap, these extra keys are set wherever possible: imapc.family, imapc.major, imapc.minor. For mobileapi, these extra keys get set: app.name, app.brand, app.major, app.minor, device.family. Example:
if (lt.device_attrs["os.family"] == "Mac OS X")
then
    -- do something special for macOS
end
  • LoginTuple.protocol β€” a string for the protocol used to access mail, i.e. http, imap, pop3, mobileapi etc. LoginTuple.protocol MUST be set for device_id to get parsed into device_attrs β€” but heads up, currently only http, imap and mobileapi are recognized protocols when parsing device_id. Example:
if (lt.protocol == "http")
then
        -- do something
end
  • LoginTuple.tls β€” a boolean for whether the login session used TLS or not. If the client is using TLS offload proxies then it may be set to false, so don't come for me.
  • GeoIPRecord β€” the type that lookupCity() hands back. Fields below:
    • GeoIPRecord.country_code β€” the two-letter country code, e.g. "US".
    • GeoIPRecord.country_name β€” the country name, e.g. "United States"
    • GeoIPRecord.region β€” the region, e.g. "CA"
    • GeoIPRecord.city β€” the city name, e.g. "Mountain View"
    • GeoIPRecord.postal_code β€” the postal code, e.g. "93102" or "BA216AS"
    • GeoIPRecord.latitude β€” the latitude, e.g. 37.386001586914
    • GeoIPRecord.longitude β€” the longitude, e.g. -122.08380126953
  • CustomFuncArgs β€” the only parameter custom functions get, and it's a CustomFuncArgs table. Fields go a little something like this:
    • CustomFuncArgs.attrs β€” array of (single valued) attributes handed over by the caller. Example:
for k, v in pairs(args.attrs) do
 if (k == "xxx")
 then
         -- do something
 end
end
  • CustomFuncArgs.attrs_mv β€” array of (multi-valued) attributes handed over by the caller. Example:
for k, v in pairs(args.attrs_mv) do
 for i, vi in ipairs(v) do
         if ((k == "xxx") and (vi == "yyy"))
         then
                 -- do something
         end
 end
end
  • incCustomStat(<stat name>) β€” bumps a custom statistics counter. The value of that counter gets logged every 5 minutes, and then it resets. Like:
incCustomStat("custom_stat1")

🌸 FILES 🌸

/etc/wforce/trackalert.conf

That's the one. That's the whole vibe. πŸ’…


πŸ‘€ SEE ALSO πŸ‘€

trackalert(1) β€” go read it, bestie, iykyk 🫢


✨ AUTHOR ✨

Open-Xchange 2018 πŸŽ€


okay and that's a wrap on the whole page, fr fr πŸ’– this lil manual is from trackalert.conf(5), dated 2026-07-28, and it lives in the trackalert source/version fam. go forth and log responsibly, main character πŸ¦‹βœ¨

πŸ“¦ where this page comes from