TRACKALERT.CONF(5)
π 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 from127.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. Seetrackalert_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 β seeaddListener()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 forwebserver()) Listen for HTTP commands on the specified IP address and port. IfuseSSLis true, then HTTPS must be used, andcert_fileandkey fileare 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 inhttps://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 theaddListener()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 to5199. For example:
controlSocket("0.0.0.0:4004")
setKey(<key>)β Use the specified key for authenticating connections from siblings. The key must be generated withmakeKey()from the console. Seetrackalert(1)for instructions on running a console client. Returnsfalseif 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 to10if not specified. See alsosetNumReportThreads()andsetNumSchedulerThreads(). Should be at least equal toNumReportThreads + NumSchedulerThreads. For example:
setNumLuaStates(10)
setNumWorkerThreads(<num threads>)β Set the number of threads in the pool used to receive webserver commands. Note that thereportfunction itself is run in a separate report thread pool controlled bysetNumReportThreads(). Defaults to4if 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 totrackalert. It defaults to10,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, (seesetNumLuaStates()). Defaults to6if 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 withsetNumLuaStates(). Defaults to4if not specified. For example:
setNumSchedulerThreads(2)
setNumWebHookThreads(<num threads>)β Set the number of threads in the pool used to send webhook events. Defaults to4if 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 thegetGeoIP2DB()function. For example:
newGeoIP2DB("CityDB", "/usr/share/GeoIP/GeoLite2-City.mmdb")
initGeoIPDB()β (Deprecated β usenewGeoIP2DB()). Initializes the country-level IPv4 and IPv6 GeoIP databases. If either of these databases is not installed, this command will fail andtrackalertwill not start. For example:
initGeoIPDB()
initGeoIPCityDB()β (Deprecated β usenewGeoIP2DB()). Initializes the city-level IPv4 and IPv6 GeoIP databases. If either of these databases is not installed, this command will fail andtrackalertwill 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 β usenewGeoIP2DB()). Initializes the ISP-level IPv4 and IPv6 GeoIP databases. If either of these databases is not installed, this command will fail andtrackalertwill not start. For example:
initGeoIPISPDB()
setNumWebHookThreads(<num threads>)β Set the number of threads in the pool used to send webhook events. Defaults to5if not specified. For example:
setNumWebHookThreads(2)
setNumWebHookConnsPerThread(<num conns>)β Set the maximum number of connections used by each WebHook thread. Defaults to10if 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 is50000, 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>)β Telltrackalertto use the specified Lua function for handling all"report"commands. For example:
setReport(report)
setBackground(<name>, <lua function>β ThesetBackgroundfunction registers a background function with a given name. The name is used when scheduling the function using thexxxScheduleBackgroundFunc()functions.
setBackground("mybg", backgroundFunc)
cronScheduleBackgroundFunc(<cron string>, <background function name>)β Tellstrackalertto run the specified function according to the given cron schedule (note the name given insetBackground()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>)β Tellstrackalertto run the specified function at the interval given in duration string. The duration string is of the formath[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 thetrackalertREST 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 β seeCustomFuncArgsbelow). 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/metricsendpoint. 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 withnewCA(). 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 fromnewCA(). 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 asinfoLog, logs at LOG_INFO with all those"key=value"strings β but only if trackalert was started with the (undocumented)-vflag, 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)-vflag (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 peepdevice_attrs.LoginTuple.device_attrsβ extra array of attributes about the device, parsed out of thedevice_idstring. The protocol string decides howdevice_idgets parsed, so that MUST be present too. Ifdevice_attrsis 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,mobileapietc.LoginTuple.protocolMUST be set fordevice_idto get parsed intodevice_attrsβ but heads up, currently onlyhttp,imapandmobileapiare recognized protocols when parsingdevice_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 thatlookupCity()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.386001586914GeoIPRecord.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
- ubuntu stonking: weakforced-trackalert 3.0.0-5build2 Β· GPL-3.0-only
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms