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
# SightingDB configuration.
#
# Comments here are preserved: the management interface never rewrites this
# file. It writes only the separate acl_file below.
[]
# Set to false to run without the HTTP API (DNS or ZMQ ingest only).
= true
= "0.0.0.0"
= 9999
# Require an API key on the sighting API. The management interface always
# requires one regardless of this setting.
= false
# Detaching is opt-in; under systemd or Docker leave this false and let the
# supervisor do it. See etc/sightingdb.service.
= false
= true
= "ssl/cert.pem"
= "ssl/key.pem"
# Largest accepted bulk POST body, in bytes.
= 2_500_000_000
# Only used when daemonize = true.
= "/var/log/sightingdb/sightingdb.log"
= "/var/log/sightingdb/sightingdb.error.log"
# Where the database is snapshotted. Comment this out to run purely in memory,
# losing everything when the process stops.
= "/var/lib/sighting"
# Seconds between snapshots. The database is always saved on a clean shutdown;
# this bounds how much a crash can lose. 0 saves only on shutdown.
= 300
# Seconds between eviction sweeps, which reclaim expired values and the
# namespaces left empty by them. 0 disables the sweeper.
= 60
# Hourly statistics buckets kept per value. 720 is 30 days. Without a limit
# every value gains a bucket per hour for as long as it is written to.
# 0 keeps all of them.
= 720
# How long a shadow sighting (the record that something was searched for) is
# kept, in seconds. 0 keeps them forever, so `_shadow/*` grows without bound.
# 2592000 is 30 days.
= 2_592_000
# API keys live in their own file because the management interface rewrites it
# whenever a key is created or revoked. Without this, keys can be declared in an
# [acl] table below, but the interface will not be able to edit them.
= "acl.toml"
# API keys, used only when acl_file is not set.
#
# "<apikey>" = "<grant>[, <grant>...]"
#
# A grant is r, w or rw, optionally scoped with :<namespace prefix>, plus
# "admin" for access to the management interface at /_management/. Prefixes
# match whole path segments, so rw:feeds/misp covers feeds/misp/ips but not
# feeds/misp-internal.
#
# [acl]
# changeme = "rw, admin"
# analyst = "r"
# feed-misp = "rw:feeds/misp"
# ---------------------------------------------------------------------------
# Answer sightings over DNS, using the DNSBL conventions security tooling
# already speaks.
#
# READ THIS BEFORE ENABLING: DNS has no authentication. The ACL does not apply,
# so anything reachable here is readable by anyone who can send a UDP packet.
# Only the namespaces listed below answer, the listener binds to loopback unless
# you change it, and rate_limit exists because an open UDP responder is a
# reflection amplifier.
# ---------------------------------------------------------------------------
# [dns]
# enabled = true
# listen_ip = "127.0.0.1"
# listen_port = 5353
# zone = "sdb.example.com"
# TTL on answers, and how long resolvers cache a "never seen" reply.
# ttl = 60
# Queries per second per source address; 0 disables the limit.
# rate_limit = 100
# threads = 2
# Whether a DNS lookup counts as a search. Off by default: it is an
# unauthenticated write path into _shadow/*.
# shadow = false
#
# How a value is spelled in the query name:
# ip 4.3.2.1.malware.sdb.example.com -> 1.2.3.4 (DNSBL convention)
# domain evil.com.domains.sdb.example.com -> evil.com
# base32 <base32 of the value>.hashes.sdb.example.com
# [dns.namespaces]
# malware = { namespace = "malware/ips", encoding = "ip" }
# domains = { namespace = "malware/domains", encoding = "domain" }
# hashes = { namespace = "malware/hashes", encoding = "base32" }
# ---------------------------------------------------------------------------
# Ingest sightings from a ZeroMQ publisher such as MISP's. This is a SUB socket
# that connects out to a publisher you trust; nothing is exposed.
# ---------------------------------------------------------------------------
# [zmq]
# enabled = true
# endpoint = "tcp://misp.example.com:50000"
# MISP publishes "<topic> <json>"; subscribing to a prefix matches it.
# topics = ["misp_json_attribute", "misp_json"]
# "misp" reads MISP attributes and events; "native" reads
# {"items":[{"namespace":..,"value":..,"timestamp":..}]}
# format = "misp"
# Only ingest attributes MISP flagged as actionable.
# require_to_ids = true
# Where unmapped attribute types go; omit to discard them.
# default_namespace = ""
# TTL on ingested sightings, and seconds between reconnection attempts.
# ttl = 0
# reconnect = 5
#
# [zmq.types]
# ip-src = "misp/ips"
# ip-dst = "misp/ips"
# domain = "misp/domains"
# hostname = "misp/domains"
# url = "misp/urls"
# md5 = "misp/hashes"
# sha1 = "misp/hashes"
# sha256 = "misp/hashes"
# ---------------------------------------------------------------------------
# Importing STIX 2.1 bundles with --import-stix <file or directory>. Counts and
# time windows come from the bundle: an observed-data seen 12 times between two
# instants becomes 12 sightings spanning that window.
# ---------------------------------------------------------------------------
# [stix]
# default_namespace = ""
# ttl = 0
#
# Keys containing a dot must be QUOTED, or TOML reads them as a nested table.
# [stix.types]
# ipv4-addr = "stix/ips"
# ipv6-addr = "stix/ips"
# domain-name = "stix/domains"
# url = "stix/urls"
# "file.MD5" = "stix/hashes"
# "file.SHA-1" = "stix/hashes"
# "file.SHA-256" = "stix/hashes"