File: wforce.conf.example

package info (click to toggle)
weakforced 3.0.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,040 kB
  • sloc: cpp: 20,397; python: 2,002; sh: 700; makefile: 432
file content (255 lines) | stat: -rw-r--r-- 9,694 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
addListener("0.0.0.0:8084", true, "/etc/tls/certificate.pem", "/etc/tls/private_key.pem", {minimum_protocol="TLSv1.3"})
setWebserverPassword("aVerySecurePassword")
setKey("Ay9KXgU3g4ygK+qWT0Ut4gH8PPz02gbtPeXWPdjD0HE=")
controlSocket("0.0.0.0:4004")

addSibling("192.168.1.79")
addSibling("192.168.1.30")
addSibling("192.168.1.54")
siblingListener("0.0.0.0:4001")

addACL("127.0.0.0/8")
addACL("192.168.0.0/16")

-- Number of lua states used for allow/report queries. 
-- Increasing this number will reduce lock contention if that is a problem
-- A lot of states will increase memory, but not by too much
setNumLuaStates(6)

-- Number of worker threads used for allow/report queries
-- Increasing this number increases concurrency but too many could cause thrashing
-- Should be >= number of lua states, and ideally == number of cores
setNumWorkerThreads(4)

-- Number of worker threads used to process siblings reports
-- Increasing this number increases concurrency but too many could cause thrashing
-- Should be around numWorkerThreads/2
setNumSiblingThreads(2)

-- Number of threads to use for sending webhook events
setNumWebHookThreads(2)

-- Register a webhook for "report" events
local config_keys={}
config_keys["url"] = "http://localhost:8080/webhook/regression"
config_keys["secret"] = "verysecretcode"
local events = { "report" }
addWebHook(events, config_keys)

-- Register a custom webhook for custom events
config_keys={}
config_keys["url"] = "http://localhost:8080/webhook/regression"
config_keys["secret"] = "verysecretcustomcode"
-- Change Content-Type of the HTTP post from the default application/json
-- config_keys["content-type"] = "text/plain"
addCustomWebHook("mycustomhook", config_keys)

local bulkRetrievers = newNetmaskGroup()
bulkRetrievers:addMask("130.161.0.0/16")
bulkRetrievers:addMask("145.132.0.0/16")

-- Field map contains as many different fields as you like
-- Supported types are:
--			 "int" - simple counter
--			 "hll" - cardinality (count how many different things you put in the bucket)
--			 "countmin" - like a multi-valued bloom filter. Counts how many of each type there are.
local field_map = {}
field_map["countLogins"] = "int"
field_map["diffPasswords"] = "hll"
field_map["diffIPs"] = "hll"
field_map["countryCount"] = "countmin"
field_map["osCount"] = "countmin"

-- create a db for storing stats. 
-- You can create multiple dbs with different window lengths for storing stats over different time windows
-- This one is 6 windows of 10 minutes each, so an hour in total. Every 10 minutes the oldest windows data will expire
newStringStatsDB("OneHourDB",600,6,field_map)

-- Persist the blacklist entries in the specified redis DB
-- blacklistPersistDB("127.0.0.1", 6379)

-- Only set this option if every wforce server is using a separate redis DB
-- Do not set when there is a central redis DB used by all wforce servers
-- blacklistPersistReplicated()

-- This one is 24 windows of 1 hour each, so 24 hours in total. Useful for tracking long-term stats.
-- You can reuse field_map or create a different one
newStringStatsDB("24HourDB",3600, 24, field_map)

-- Only initialize the GeoIPDB if you need it - this initializes the "country"
-- GeoIP DBs (ipv4 and ipv6)
initGeoIPDB()
-- You can also initialize the "city" and "ISP" databases (ensure you have
-- downloaded them, again you need both ipv4 and ipv6 DBs)
-- initGeoIPCityDB()
-- initGeoIPISPDB()

-- The report function is used to store custom stats
-- The allow and report functions cannot access any lua variables defined outside, hence the get... functions
function report(lt)
   local sdb = getStringStatsDB("OneHourDB")
   local sdb_day = getStringStatsDB("24HourDB")
   local cur_ct = lookupCountry(lt.remote)
   
   -- A note on keys: You can use strings or ComboAddresses.
   -- You can specify a separate ipv4 and v6 netmasks for the DB, which will be used for all ComboAddress keys, thus allowing aggregation of IP stats
   -- Example for IPv4 addresses: sdb:twSetv4Prefix(24)
   -- Example for IPv6 addresses: sdb:twSetv6Prefix(64)
   -- twAdd() is the generic way to add things to the stats bucket. For integers it does addition
   sdb:twAdd(lt.login, "countLogins", 1)
   sdb:twAdd(lt.remote, "countLogins", 1)
   sdb:twAdd(lt.login, "diffPasswords", lt.pwhash)
   sdb:twAdd(lt.login, "diffIPs", lt.remote)
   sdb_day:twAdd(lt.login, "countryCount", cur_ct)
   
   -- look for things in device_attrs
   if (not ((lt.device_attrs["os.family"] == "") or
             (lt.device_attrs["os.family"] == nil)))
   then
      -- store the os family
      sdb:twAdd(lt.login, "osCount", lt.device_attrs["os.family"])
   end
   
   -- twSub() can be used for the integer type. It does what you expect
end

-- initialise some DNS lookup objects
newDNSResolver("Resolv")
local resolv = getDNSResolver("Resolv")
newDNSResolver("RBLResolv")
local rblresolv = getDNSResolver("RBLResolv")

-- Configure some resolvers for it to use
resolv:addResolver("8.8.8.8", 53)
rblresolv:addResolver("127.0.0.1", 5353)

-- The allow and report functions cannot access any lua variables defined outside, hence the get... functions
function allow(lt)
   local sdb = getStringStatsDB("OneHourDB")
   local sdb_day = getStringStatsDB("24HourDB")
   local resolv = getDNSResolver("Resolv")
   local rblresolv = getDNSResolver("RBLResolv")
   
   -- look for things in device_attrs
   if (lt.device_attrs["os.family"] == "iOS")
   then
      -- do something special for iOS
   end
   
   if (bulkRetrievers:match(lt.remote))
   then
      -- Must return 4 args - <return value>, <return msg>, <log msg>, <log key values>
      return 0, "bulkRetrievers", "bulkRetrievers", { bulkRetrievers=1 }
   end
   
   -- check optional attrs for things that indicate badness
   for k, v in pairs(lt.attrs) do
      if ((k == "accountStatus") and (v == "blocked"))
      then
         return -1, "account blocked", "account blocked", { accountStatus="blocked" }
      end
   end
   -- attrs can be multi-valued, in which case they will appear in attrs_mv automagically
   for k, v in pairs(lt.attrs_mv) do
      for i, vi in ipairs(v) do
         if ((k == "countryList") and (vi == "Blockmestan"))
         then
            return -1, "blocked country list", "blocked country list", { countryList="Blockmestan" }
         end
      end
   end
   
   -- Example GeoIP lookup
   if (lookupCountry(lt.remote) == "XX")
   then
      return -1, "country blocked", "country blocked", { remoteCountry="XX" }
   end
   -- You can also lookup ISP names:
   -- lookupISP(lt.remote)
   -- And more detailed information like city/lat/long:
   -- gip_record = lookupCity(lt.remote)
   -- local my_city = gip_record.city

   -- Example DNS lookups
   local dnames = resolv:lookupNameByAddr(lt.remote)
   for i, dname in ipairs(dnames) do
      if (string.match(dname, "XXX"))
      then
         return -1, "Reverse IP", "Reverse IP", { ptrContains="XXX" }
      end
   end
   
   dnames = rblresolv:lookupRBL(lt.remote, "sbl.spamhaus.org")
   for i, dname in ipairs(dnames) do
      if (string.match(dname, "127.0.0.2"))
      then
         -- tarpit the connection
         return 5, "RBL", "RBL", { spamhaus=1 }
      end
   end
   
   -- twGet() returns the "sum" of all the values over all the time windows. 
   -- For integer and countmin types, this is just a sum. For HLL, it's a union.
   if (sdb:twGet(lt.login, "diffPasswords") > 90)
   then
      -- blacklist the login for 300 seconds
      blacklistLogin(lt.login, 300, "too many different incorrect password attempts")
      return -1, "too many different password attempts for this account", "diffPasswords", { diffPasswords=90 }
   end
   
   -- If user is logging in from multiple IPs and we haven't seen this country in the last 24 hours then reject
   local cur_ct = lookupCountry(lt.remote)
   if ((sdb:twGet(lt.login, "diffIPs") > 5) and (sdb_day.twGet(lt.login, "countryCount", cur_ct) < 2))
   then
      return -1, "Too many IPs and countries", "diffIPs", { diffIPs=5, countryCount24Hrs=1, country=cur_ct }
   end
   
   -- We also have:
   --		 twGetCurrent() which returns the value for the current window 
   --		 twGetWindows() which returns a table with all window values - the first item in the list is the "current" window. The last is the "oldest" window.
   
   -- return must have these 4 arguments
   return 0, "allowed", "allowed", {}
end

-- Use this function to reset stats if needed for particular IPs, logins or both
function reset(type, login, ip)
   local sdb = getStringStatsDB("OneHourDB")
   local sdb_day = getStringStatsDB("24HourDB")
   if (string.find(type, "ip"))
   then
      sdb:twReset(ip)
      -- if you set a non-default prefix for IP addresses, then reset will not necessarily do what you expect
      -- for example if v4Prefix==24 and you reset an IP address it will reset the stats for all IPs in that range
   end
   if (string.find(type, "login"))
   then
      -- we do not actually set any login-only keys
      sdb:twReset(login)
      sdb_day:twReset(login)
   end
   if (string.find(type, "ip") and string.find(type, "login"))
   then
      -- we do not set any compound keys in this policy
   end
   return true
end

setReport(report)
setAllow(allow)
setReset(reset)

function custom(args)
   for k,v in pairs(args.attrs) do
      infoLog("custom func argument attrs", { key=k, value=v });
   end
   
   runCustomWebHook("mycustomhook", "{ \"foo\":\"bar\" }")
   
   -- return consists of a boolean, followed by { key-value pairs }
   return true, { key=value }
end

-- Register a custom endpoint
-- Parameters: name, send arguments to report sink?, function)
setCustomEndpoint("custom", false, custom)