weakforced-policy

% WFORCE_POLICY(8) % Dovecot Oy % 2018

Trackalert Policy Documentation

Trackalert is a daemon that is designed to supplement the functionality of wforce by integrating with a database (Elasticsearch by default) which stores long-term information about logins. It uses the information in that database to:

Pre-Requisites

You must be using wforce in order for trackalert to function correctly. In addition, wforce must be configured to send a copy of all reports to an Elasticsearch DB, as well as a copy of those reports to trackalert. The Elasticsearch DB needs to be sized appropriately to handle the volume of login reports; these could be many thousands per second in busy periods. Sizing and performance of the Elasticsearch DB are out of scope of this document. Potentially every login report could generate a query from trackalert to the Elasticsearch DB, thus performance is critical. Having said that, the trackalert policy described in this document does attempt to lessen the load on Elasticsearch by caching the most important attributes about logins (devices used, protocols used, and locations logged in from) in a Redis DB.

Trackalert needs the reports to have been stored in Elasticsearch for a (configurable - default 2 weeks) period of time before it will make decisions about suspicious logins. This is to ensure that “normal” behaviour is baselined, however it may mean that it is more difficult to detect users whose accounts have already been compromised.

Suspicious Login Detection

Suspicious login detection is based on detecting something “anomalous” about a new login. Currently this is based on querying Elasticsearch to determine attributes about previous devices, protocols and locations used to login. For each attribute that doesn’t match, a “score” is added and if the score exceeds a threshold, then the login is considered suspicious. Each attribute can be given a (configurable) weight, and the overall threshold is also configurable.

The default weights are as follows:

The default threshold is 4, which means that either a new protocol or a new device would trigger a suspicious login, but a new location by itself would not.

Once a suspicious login has been detected, trackalert performs two functions:

Abusive IP Detection

Potentially abusive IP addresses are detected by running a periodic search of the Elasticsearch DB to look for IPs that have a higher than “normal” failed login rate. The time period for the search is 1.5x the periodicity of the function. Then information about each of those IPs is gathered; specifically:

All of that information is then sent using a webhook to a configured endpoint for all suspicious IPs found. The information can be used by abuse teams to further investigate those IPs and potentially block them etc.

Compromised Account Detection

Potentially compromised accounts are detected by running a periodic search of the Elasticsearch DB to look for logins that have a higher than “normal” failed login rate. The time period for the search is 1.5x the periodicity of the function. Then information about each of those logins is gathered; specifically:

All of that information is then sent using a webhook to a configured endpoint for all suspicious logins found. The information can be used by abuse teams to further investigate those logins and potentially block them etc.

Configuration

The configuration file is a Lua script, and a simple config file ships with policy. However the configuration needs to be modified to work in a production environment. By default the configuration script is /etc/wforce/trackalert.conf

The policy is loaded using the following:

ta = require("policy.trackalert")

Initialisation and override of the configuration is performed with the following (this shows a simple example where no override is done):

ta.init({})

The init() function takes a parameter which is a table containing override parameters for the configuration. The following lists all the possible configuration parameters that can be overriden along with their default values:

local override_config = {
   thresholds = {
      suspicious_threshold = 4, -- Change this based on the weights you select
      new_device_weight = 10,
      new_location_weight = 3, -- Location means country
      new_protocol_weight = 5 -- Protocol means e.g. smtp, pop, http etc.
   },
   suspicious_logins = {
      enabled = true,
      max_num = 250, -- maximum number of results to return
      interval = "08:00:00", -- run every 8 hours
      webhook = {
	 name = "suspicious_logins",
	 url = "", -- enter the URL of your webhook endpoint
	 secret = nil, -- Generate an HMAC digest in X-Wforce-Signature
	 ["basic-auth"] = nil -- username:password if required
      }
   },
   suspicious_ips = {
      enabled = true,
      max_num = 500, -- maximum number of results to return
      interval = "08:00:00", -- run every 8 hours
      webhook = {
	 name = "suspicious_ips",
	 url = "", -- enter the URL of your webhook endpoint
	 secret = nil, -- Generate an HMAC digest in X-Wforce-Signature
	 ["basic-auth"] = nil -- username:password if required
      }
   },
   elasticsearch = {
      -- Set the minimum length of time that we need to have records for
      -- This uses elasticsearch datemath, for example "1M", "2w", "1d", or "1m"
      -- See https://www.elastic.co/guide/en/elasticsearch/reference/5.3/common-options.html#date-math
      report_age_threshold = "2w",
      hosts = {
         -- add all your ES nodes here - you can add multiple nodes
         {
            protocol = "http",
            host = "localhost",
            port = 9200
         }
      },
      params = {
         connectionPool = "StaticConnectionPool",
         maxRetryCount = 0,
         logLevel = warning
      }
   },
   redis = {
       redis_enabled = true,
       expire_secs = 5184000, -- 60 days expiry for redis cache entries
       redis_server = "127.0.0.1",
       redis_port = 6379,
       device_attrs = {'os.family', 'browser.family', 'imapc.family', 'device.family', 'app.name'}, -- don't alter these unless you really know what you're doing
       debug_log = false
   }
   debug_log = false,
   countryDBFile = "/usr/share/GeoIP/GeoLite2-Country.mmdb", -- change this if your GeoIP2 DB is in a different location
   cityDBFile = "/usr/share/GeoIP/GeoLite2-City.mmdb" -- change this if your GeoIP2 DB is in a different location
}

ta.init(override_config)

UserDB Plugins

UserDB plugins are used when a suspicious login is detected. In order to send an alert to a user, a DB lookup may need to be performed in order to provide the information required to generate an email for example (or SMS etc.) Userdb plugins also allow for the userdb to determine which alert protocol will be used (and thus which alert plugin). Multiple userdb plugins can be registered, in which case they will be checked in turn.

The alert to a user will only be sent if a user db plugin returns true for that user.

Several UserDB plugins are provided:

All Users UserDB Plugin Configuration

The following will specify the allusers userdb plugin:

local udb_allusers = require("userdb_plugins.allusers")
udb_allusers.init("local", {})

No other configuration is necessary.

LDAP UserDB Plugin Configuration

The following shows how to specify and configure the LDAP userdb plugin:

local udb_ldap = require("userdb_plugins.ldap")
udb_ldap.init("ldap", {
   host = "localhost", -- the hostname of the LDAP server
   port = 389,
   usetls = false,
   bind_dn = "",
   bind_password = "",
   base_dn = "", -- base DN for searches
   -- LDAP attributes found in search results will be mapped as below to Lua
   -- These attributes are used by the alert plugins
   rcpt_attr_map = {
      email = "mail", -- Map LDAP "mail" attr to Lua email attr
      mobileNum = "mobile",
      firstname = "cn",
      surname = "sn"
   },
   -- This specifies which LDAP attribute contains the alert protocol selector
   -- which can be blank if LDAP doesn't contain such information
   protocol_attr_map = {
      protocol = "alertProtocol"
   },
   search_attr = "uid", -- Which LDAP attribute contains the "login" name
   search_scope = "subtree", -- Almost certainly do not change this
   sizelimit = 1, -- Max number of results to return
   timeout = 1, -- One second timeout for LDAP
   debug_log = false
   })

Alert Plugins

Alert plugins are used to send suspicious login alerts to users. If a userdb plugin indicates that the alert should proceed, then the alert is sent either to the alert plugin specified in the userdb, or to the default alert plugin. Multiple alert plugins can be registered.

There are three plugins provided:

Webhook Alert Plugin Configuration

The webhook alert plugin is used to send all data about a suspicious login to a webhook endpoint over HTTP(S). All data is json encoded, the Content-Type of the data is application/json, and it is delivered via an HTTP POST method. If the HTTP server supports multiplexing or pipelining then those wil be used to improve performance.

Use the setNumWebHookThreads(<num threads>) function to control how many threads are used to send webhooks.

The webhook alert plugin is configured as follows:

local alert_webhook = require("alert_plugins.webhook")
alert_webhook.init("webhook_alert",
                   { url="http://127.0.0.1:8080/webhook",
                     basic-auth = "username:password",
                     secret = "secret"})

SMTP Alert Plugin Configuration

The following shows the configuration of the SMTP alert plugin:

local alert_smtp = require("alert_plugins.smtp")
alert_smtp.init("smtp", { from = "Big Telco <support@bigtelco.com>",
                          subject = "Recent Login to BigTelco Mail",
                          smtp_host = "localhost",
                          smtp_port = 25,
                          smtp_auth = false,
                          smtp_user = nil,
                          smtp_password = nil,
                          text_template = "alert_plugins/templates/email_template.txt",
                          html_template = "alert_plugins/templates/email_template.html"
                          brand = "Big Telco"})

Note that the SMTP Email generated is based on templates, which are configurable. The generated emails contain both a text and HTML version of the alert.

The SMTP alert plugin expects the following mandatory attributes to be passed to it, which will need to be filled in by the userdb plugin:

The email address is obviously required, but the firstname and lastname attributes are also required to ensure trust in the email sent to the user. Email alerts about suspicious logins are often abused and sent as phishing messages, thus it’s important that real suspicious login alerts are not training users to click on phishing messages, hence the requirement for first and last name. Addtionally the templates themselves do not contain direct links for users to click on; instead they direct users to login via their normal methods.

Default Alert Plugin

If no alert plugin is specified by the userdb lookup, then the default plugin will be used. To set the default alert plugin, use:

-- ta is the name of the variable used in the "require" statement for
policy.trackalert
-- alert_webhook is the name of the variable used in the "require" statement for alert_plugins.webhook
ta.setDefaultAlert(alert_webhook)

Policy Files

The Lua “require” command is used to load all the policy files as Lua modules. To ensure this works correctly, trackalert will change the working directory to the directory containing the trackalert.conf file.

Layout

The policy consists of the files listed below. These will typically be installed in a manner which does not override the default configuration (e.g. in /etc/wforce/example-policy). In order to use the files as part of a standard install, they should be copied (not moved) to /etc/wforce (or wherever trackalert is configured to read its configuration from by default). The directory layout must be preserved, otherwise the policy will fail (i.e. the trackalert.lua file must be in a subdirectory relative to trackalert.conf named “policy”).

File layout:

Files marked * are not designed to be edited by the administrator, so consider these read-only unless you really know what you are doing.