moat docs ยท language 1
GitHub

Language Lexical elements

Lexical elements the words and values a moat file is made of.

A moat file is plain text, read top to bottom. This one uses most of the language; every word in it is explained below.

# A game server: drop known bad sources, keep queries in check,
# and ban whoever floods the game port.
version 1
default pass

use std.bogons

const game_ports = {27015, 27020-27030}

set blocklist from "blocklist.txt"
set banned addresses ttl 10m size 100k

var under_attack = false
var query_rate = 20

rule known_bad { when ip.src in blocklist or ip.src in banned then drop }

rule bad_flags { when tcp.flags has {syn, fin} then drop }

rule queries {
    when udp.dport in game_ports and payload[0:4] == 0xffffffff
    then limit query_rate/s per ip.src burst 40
}

rule floods {
    when udp.dport in game_ports and not ip.src in {10.0.0.0/8}
    then limit 500/s per ip.src/24 burst 1000 on exceed add ip.src to banned ttl 1h, drop
}

rule probes {
    when under_attack and tcp and payload contains "GET /admin"
    then drop
}

Comments and names

  • A comment starts with # and runs to the end of the line. It can sit on a line of its own or after code.
  • A name starts with a letter or _ and may contain letters, digits, _ and .: game_ports, banned, under_attack. Names you declare (constants, sets, variables, rules) share one namespace, so a set and a rule cannot both be called banned.
  • Field names such as ip.src and tcp.dport are names too, built in. The Conditions page lists them all.
  • Upper and lower case are different: Banned is not banned.

Reserved words

These 32 words have a fixed meaning and cannot be used as names. They are grouped below by where they appear.

The file

WordWhat it doesExample
versionSays which version of the language the file is written in. Must be the first declaration.version 1
defaultThe verdict for packets no rule decides, pass or drop. Required once.default pass
useInserts the rules of a standard module at that place.use std.bogons

More in Declarations and Modules.

Constants, sets and variables

WordWhat it doesExample
constGives a name to a value, a range or a list, to use in conditions.const game_ports = {27015, 27020-27030}
setDeclares a set of addresses or networks that rules test with in.set banned addresses
fromFills a set from a file at load time, one address or prefix per line.set blocklist from "blocklist.txt"
prefixesA set of networks, filled from outside with moat set.set allowed prefixes
addressesA set of exact addresses, filled from outside or by rules with add.set seen addresses
ttlHow long entries stay in an addresses set. Also used after add.set banned addresses ttl 10m
sizeHow many entries a set can hold. The default is 65536.set banned addresses size 100k
varA value you can change while the rules run, with moat var set.var query_rate = 20
true, falseThe two values of an on/off variable, which is a condition on its own.var under_attack = false

More in Sets and variables.

Rules

WordWhat it doesExample
ruleDeclares a rule, with a name and a body in braces.rule known_bad { ... }
whenStarts the condition: which packets the rule is about.when udp.dport == 53
thenStarts the actions: what happens to those packets.then drop

More in Rules.

Conditions

WordWhat it doesExample
andBoth sides must hold.udp and udp.dport == 53
orEither side is enough.icmp or icmp6
notTurns a test around.not ip.src in {10.0.0.0/8}
inTests membership of a set, a list, a range or a prefix.ip.src in banned
hasTCP flags: true when the packet has these flags set, maybe with others.tcp.flags has {syn, fin}
payloadThe bytes after the transport header, whole or as a slice.payload[0:4] == 0xffffffff
containsLooks for text or bytes anywhere in the payload.payload contains "GET /admin"

and binds tighter than or; use parentheses when in doubt. tcp.flags == {syn} asks for exactly those flags, has for at least them. More in Conditions.

Actions

WordWhat it doesExample
passLets the packet through. No later rule sees it.then pass
dropDrops the packet. No later rule sees it.then drop
addPuts the source (or destination) into an addresses set. The next rule still runs.add ip.src to banned
toNames the set add writes to.add ip.src to banned ttl 1h
limitA rate limit. Under the rate the packet goes on to the next rule; over it, the on exceed actions run, or the packet is dropped.limit 100/s burst 200
perGives each source, or each source network, its own budget.limit 500/s per ip.src/24 burst 1000
burstHow many packets can arrive at once before the rate applies. Required.limit 10/s burst 20
on, exceedAlways together: what to do with packets over the rate.on exceed add ip.src to banned, drop

Several actions are separated by commas, and pass or drop must come last. More in Actions.

Values

KindExamplesNotes
integer53, 0xffffffffDecimal or hexadecimal. A hex literal also has a width in bytes, used by payload comparisons: 0xffffffff is 4 bytes.
string"blocklist.txt", "GET /admin"Double quoted. Escapes: \" and \\.
address192.0.2.1, 2001:db8::1IPv4 or IPv6.
prefix10.0.0.0/8, 2001:db8::/32A network. Host bits must be zero: 10.0.0.1/8 is an error, and moat check suggests 10.0.0.0/8.
range8000-8100Inclusive, both ends count.
list{53, 123, 1000-2000}Integers, ranges, addresses, prefixes or TCP flag names. A trailing comma is allowed.
duration500ms, 30s, 10m, 1hFor ttl.
size64k, 1M, 2GThousand, million, billion, for size. A plain integer works too.
rate1000/s, 10k/s, 10mbit/sPackets per second, or bits per second with bit, kbit, mbit, gbit. A variable works too: query_rate/s.

The TCP flag names used in lists are fin, syn, rst, psh, ack, urg, ece and cwr.

The formal grammar is in grammar.ebnf.