/
site.zeek
337 lines (280 loc) · 11.2 KB
/
site.zeek
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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
##! Definitions describing a site - which networks and DNS zones are "local"
##! and "neighbors", and servers running particular services.
@load ./patterns
module Site;
export {
## A list of subnets that are considered private address space.
##
## By default, it has address blocks defined by IANA as not being routable over the Internet.
##
## See the `IPv4 Special-Purpose Address Registry <https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml>`_
## and the `IPv6 Special-Purpose Address Registry <https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml>`_
option private_address_space: set[subnet] = {
# "This network", see :rfc:`791`
0.0.0.0/8,
# 0.0.0.0/8 as a 6to4 address, see :rfc:`791` and :rfc:`3056`
[2002::]/24,
# Private-Use, see :rfc:`1918`
10.0.0.0/8,
# 10.0.0.0/8 as a 6to4 address, see :rfc:`1918` and :rfc:`3056`
[2002:a00::]/24,
# Shared Address Space (also known as Carrier-grade NAT), see :rfc:`6598`
100.64.0.0/10,
# 100.64.0.0/10 as a 6to4 address, see :rfc:`6598` and :rfc:`3056`
[2002:6440::]/26,
# Loopback, see :rfc:`1122`
127.0.0.0/8,
# 127.0.0.0/8 as a 6to4 address, see :rfc:`1122` and :rfc:`3056`
[2002:7f00::]/24,
# Link Local, see :rfc:`3927`
169.254.0.0/16,
# 169.254.0.0/16 as a 6to4 address, see :rfc:`3927` and :rfc:`3056`
[2002:a9fe::]/32,
# Private-Use, see :rfc:`1918`
172.16.0.0/12,
# 172.16.0.0/12 as a 6to4 address, see :rfc:`1918` and :rfc:`3056`
[2002:ac10::]/28,
# IETF Protocol Assignments, see :rfc:`6890`
192.0.0.0/24,
# 192.0.0.0/24 as a 6to4 address, see :rfc:`6890` and :rfc:`3056`
[2002:c000::]/40,
# Documentation (TEST-NET-1), see :rfc:`5737`
192.0.2.0/24,
# 192.0.2.0/24 as a 6to4 address, see :rfc:`5737` and :rfc:`3056`
[2002:c000:200::]/40,
# Private-Use, see :rfc:`1918`
192.168.0.0/16,
# 192.168.0.0/16 as a 6to4 address, see :rfc:`1918` and :rfc:`3056`
[2002:c0a8::]/32,
# Benchmarking, see :rfc:`2544`
198.18.0.0/15,
# 198.18.0.0/15 as a 6to4 address, see :rfc:`2544` and :rfc:`3056`
[2002:c612::]/31,
# Documentation (TEST-NET-2), see :rfc:`5737`
198.51.100.0/24,
# 198.51.100.0/24 as a 6to4 address, see :rfc:`5737` and :rfc:`3056`
[2002:c633:6400::]/40,
# Documentation (TEST-NET-3), see :rfc:`5737`
203.0.113.0/24,
# 203.0.113.0/24 as a 6to4 address, see :rfc:`5737` and :rfc:`3056`
[2002:cb00:7100::]/40,
# Reserved, see :rfc:`1112`
240.0.0.0/4,
# 240.0.0.0/4 as a 6to4 address, see :rfc:`1112` and :rfc:`3056`
[2002:f000::]/20,
# Limited Broadcast, see :rfc:`919` and :rfc:`8190`
255.255.255.255/32,
# 255.255.255.255/32 as a 6to4 address, see :rfc:`8190` and :rfc:`3056`
[2002:ffff:ffff::]/48,
# Unspecified Address, see :rfc:`4291`
[::]/128,
# Loopback Address, see :rfc:`4291`
[::1]/128,
# IPv4-IPv6 Translation, see :rfc:`8215`
[64:ff9b:1::]/48,
# Discard-Only Address Block, see :rfc:`6666`
[100::]/64,
# IETF Protocol Assignments, see :rfc:`2928`
[2001::]/23,
# Benchmarking, see :rfc:`5180`
[2001:2::]/48,
# Documentation, see :rfc:`3849`
[2001:db8::]/32,
# Unique-Local, see :rfc:`4193` and :rfc:`8190`
[fc00::]/7,
# Link-Local Unicast, see :rfc:`4291`
[fe80::]/10,
};
## Networks that are considered "local". Note that ZeekControl sets
## this automatically.
option local_nets: set[subnet] = {};
## Whether Zeek should automatically consider private address ranges
## "local". On by default, this setting ensures that the initial value
## of :zeek:id:`Site::private_address_space` as well as any later
## updates to it get copied over into :zeek:id:`Site::local_nets`.
const private_address_space_is_local = T &redef;
## This is used for retrieving the subnet when using multiple entries in
## :zeek:id:`Site::local_nets`. It's populated automatically from there.
## A membership query can be done with an
## :zeek:type:`addr` and the table will yield the subnet it was found
## within.
global local_nets_table: table[subnet] of subnet = {};
## Networks that are considered "neighbors".
option neighbor_nets: set[subnet] = {};
## If local network administrators are known and they have responsibility
## for defined address space, then a mapping can be defined here between
## networks for which they have responsibility and a set of email
## addresses.
option local_admins: table[subnet] of set[string] = {};
## DNS zones that are considered "local".
option local_zones: set[string] = {};
## DNS zones that are considered "neighbors".
option neighbor_zones: set[string] = {};
## Function that returns true if an address corresponds to one of
## the local networks, false if not.
## The function inspects :zeek:id:`Site::local_nets`.
global is_local_addr: function(a: addr): bool;
## Function that returns true if an address corresponds to one of
## the neighbor networks, false if not.
## The function inspects :zeek:id:`Site::neighbor_nets`.
global is_neighbor_addr: function(a: addr): bool;
## Function that returns true if an address corresponds to one of
## the private/unrouted networks, false if not.
## The function inspects :zeek:id:`Site::private_address_space`.
global is_private_addr: function(a: addr): bool;
## Function that returns true if a host name is within a local
## DNS zone.
## The function inspects :zeek:id:`Site::local_zones`.
global is_local_name: function(name: string): bool;
## Function that returns true if a host name is within a neighbor
## DNS zone.
## The function inspects :zeek:id:`Site::neighbor_zones`.
global is_neighbor_name: function(name: string): bool;
## Function that returns a comma-separated list of email addresses
## that are considered administrators for the IP address provided as
## an argument.
## The function inspects :zeek:id:`Site::local_admins`.
global get_emails: function(a: addr): string;
}
# Please ignore, this is an internally used variable.
global local_dns_suffix_regex: pattern = /MATCH_NOTHING/;
global local_dns_neighbor_suffix_regex: pattern = /MATCH_NOTHING/;
# A state bit to indicate to the Site::local_nets change handler whether it
# still needs to take into account Site::private_address_space.
global local_nets_needs_private_address_space = T;
function is_local_addr(a: addr): bool
{
return a in local_nets;
}
function is_neighbor_addr(a: addr): bool
{
return a in neighbor_nets;
}
function is_private_addr(a: addr): bool
{
return a in private_address_space;
}
function is_local_name(name: string): bool
{
return local_dns_suffix_regex in name;
}
function is_neighbor_name(name: string): bool
{
return local_dns_neighbor_suffix_regex in name;
}
# This is a hack for doing a for loop.
const one_to_32: vector of count = {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};
# TODO: make this work with IPv6
function find_all_emails(ip: addr): set[string]
{
if ( ip !in local_admins ) return set();
local output_values: set[string] = set();
local tmp_subnet: subnet;
local i: count;
local emails: string;
for ( i in one_to_32 )
{
tmp_subnet = mask_addr(ip, one_to_32[i]);
if ( tmp_subnet in local_admins )
for ( email in local_admins[tmp_subnet] )
{
if ( email != "" )
add output_values[email];
}
}
return output_values;
}
function fmt_email_string(emails: set[string]): string
{
local output="";
for( email in emails )
{
if ( output == "" )
output = email;
else
output = fmt("%s, %s", output, email);
}
return output;
}
function get_emails(a: addr): string
{
return fmt_email_string(find_all_emails(a));
}
function update_local_nets_table(id: string, new_value: set[subnet]): set[subnet]
{
local result = new_value;
# If private address ranges are to be local, ensure they remain
# in Site::local_nets during this update. If we just got here
# because Site::private_address_space got updated, use the pending
# state its change handler created.
if ( private_address_space_is_local )
{
if ( local_nets_needs_private_address_space )
result = new_value | Site::private_address_space;
local_nets_needs_private_address_space = T;
}
# Refresh the local_nets mapping table.
local_nets_table = {};
for ( cidr in result )
local_nets_table[cidr] = cidr;
return result;
}
function update_local_zones_regex(id: string, new_value: set[string]): set[string]
{
# Double backslashes are needed due to string parsing.
local_dns_suffix_regex = set_to_regex(new_value, "(^\\.?|\\.)(~~)$");
return new_value;
}
function update_neighbor_zones_regex(id: string, new_value: set[string]): set[string]
{
local_dns_neighbor_suffix_regex = set_to_regex(new_value, "(^\\.?|\\.)(~~)$");
return new_value;
}
function update_private_address_space(id: string, new_value: set[subnet]): set[subnet]
{
# This change handler mirrors the changes to private ranges into
# Site::local_nets. It does not use clusterization: the update to the
# private address space already propagates, so we just apply the change
# locally.
local new_privates = new_value - private_address_space;
local old_privates = private_address_space - new_value;
# Compute the update to local nets here. Note that local_nets may not
# yet have the private-space additions, if this is running at startup,
# so we merge it explicitly, and then apply the deltas:
local new_local_nets = (local_nets | private_address_space) - old_privates;
new_local_nets += new_privates; # Can't currently chain +/- set ops.
# Subtle: calling Option::set() on Site::local_nets will cause its
# change handler update_local_nets_table() to trigger directly. It
# normally adds Site::private_address_space to Site::local_nets, but the
# former will still have its old value since this change handler hasn't
# returned yet. Since we just computed the new local_nets value above,
# we can signal to the change handler that adding
# Site::private_address_space is not required:
local_nets_needs_private_address_space = F;
# The special location value "<skip-config-log"> signals to the config
# framework's own catch-all change handler that this update is internal
# and need not be logged.
Option::set("Site::local_nets", new_local_nets, "<skip-config-log>");
return new_value;
}
event zeek_init() &priority=10
{
# Have these run with a lower priority so we account for additions/removals
# from user created change handlers.
Option::set_change_handler("Site::local_nets", update_local_nets_table, -5);
Option::set_change_handler("Site::local_zones", update_local_zones_regex, -5);
Option::set_change_handler("Site::neighbor_zones", update_neighbor_zones_regex, -5);
# If private address ranges are to be local, add a change handler to sync
# these over in the future, and trigger it once to bring local_nets up
# to speed immediately.
if ( private_address_space_is_local )
{
Option::set_change_handler("Site::private_address_space", update_private_address_space, -5);
update_private_address_space("Site::private_address_space", Site::private_address_space);
}
# Use change handler to initialize local_nets mapping table and zones
# regexes.
update_local_nets_table("Site::local_nets", Site::local_nets);
update_local_zones_regex("Site::local_zones", Site::local_zones);
update_neighbor_zones_regex("Site::neighbor_zones", Site::neighbor_zones);
}