183 lines
7.5 KiB
SQL
183 lines
7.5 KiB
SQL
-- Initial schema.
|
|
--
|
|
-- Design notes:
|
|
-- * Every searchable entity gets a real relational table. Only genuinely
|
|
-- list-shaped configuration (upstream resolvers, ACL networks) lives as a
|
|
-- JSON value inside the settings key/value table.
|
|
-- * Timestamps are stored as unix epoch seconds (INTEGER) so that they sort
|
|
-- and range-scan cheaply and carry no timezone ambiguity.
|
|
-- * Booleans are INTEGER 0/1.
|
|
|
|
CREATE TABLE settings (
|
|
key TEXT PRIMARY KEY,
|
|
value TEXT NOT NULL,
|
|
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
);
|
|
|
|
CREATE TABLE admin_user (
|
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
username TEXT NOT NULL,
|
|
password_hash TEXT NOT NULL,
|
|
must_change_password INTEGER NOT NULL DEFAULT 0,
|
|
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
last_login_at INTEGER
|
|
);
|
|
|
|
CREATE TABLE api_tokens (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL UNIQUE,
|
|
description TEXT NOT NULL DEFAULT '',
|
|
token_prefix TEXT NOT NULL,
|
|
token_hash TEXT NOT NULL,
|
|
enabled INTEGER NOT NULL DEFAULT 1,
|
|
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
last_used_at INTEGER
|
|
);
|
|
CREATE INDEX idx_api_tokens_prefix ON api_tokens (token_prefix);
|
|
|
|
CREATE TABLE zones (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL UNIQUE, -- normalised FQDN with trailing dot
|
|
kind TEXT NOT NULL DEFAULT 'forward'
|
|
CHECK (kind IN ('forward', 'reverse4', 'reverse6')),
|
|
description TEXT NOT NULL DEFAULT '',
|
|
enabled INTEGER NOT NULL DEFAULT 1,
|
|
default_ttl INTEGER NOT NULL DEFAULT 3600,
|
|
primary_ns TEXT NOT NULL,
|
|
admin_email TEXT NOT NULL,
|
|
serial INTEGER NOT NULL DEFAULT 1,
|
|
refresh INTEGER NOT NULL DEFAULT 7200,
|
|
retry INTEGER NOT NULL DEFAULT 3600,
|
|
expire INTEGER NOT NULL DEFAULT 1209600,
|
|
minimum INTEGER NOT NULL DEFAULT 3600,
|
|
auto_serial INTEGER NOT NULL DEFAULT 1,
|
|
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
);
|
|
CREATE INDEX idx_zones_enabled ON zones (enabled);
|
|
CREATE INDEX idx_zones_kind ON zones (kind);
|
|
|
|
CREATE TABLE records (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
zone_id INTEGER NOT NULL REFERENCES zones (id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL, -- relative to the apex; '@' is the apex itself
|
|
type TEXT NOT NULL,
|
|
data TEXT NOT NULL, -- rdata in zone-file presentation format
|
|
ttl INTEGER, -- NULL inherits zones.default_ttl
|
|
enabled INTEGER NOT NULL DEFAULT 1,
|
|
comment TEXT NOT NULL DEFAULT '',
|
|
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
);
|
|
CREATE INDEX idx_records_zone ON records (zone_id);
|
|
CREATE INDEX idx_records_zone_name ON records (zone_id, name);
|
|
CREATE INDEX idx_records_zone_name_type ON records (zone_id, name, type);
|
|
CREATE INDEX idx_records_type ON records (type);
|
|
|
|
CREATE TABLE networks (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL UNIQUE,
|
|
cidr TEXT NOT NULL,
|
|
description TEXT NOT NULL DEFAULT '',
|
|
enabled INTEGER NOT NULL DEFAULT 1,
|
|
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
);
|
|
CREATE INDEX idx_networks_enabled ON networks (enabled);
|
|
|
|
CREATE TABLE policies (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL UNIQUE,
|
|
description TEXT NOT NULL DEFAULT '',
|
|
enabled INTEGER NOT NULL DEFAULT 1,
|
|
block_action TEXT NOT NULL DEFAULT 'nxdomain'
|
|
CHECK (block_action IN ('nxdomain', 'refused', 'sinkhole')),
|
|
sinkhole_ipv4 TEXT NOT NULL DEFAULT '0.0.0.0',
|
|
sinkhole_ipv6 TEXT NOT NULL DEFAULT '::',
|
|
block_ttl INTEGER NOT NULL DEFAULT 60,
|
|
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
);
|
|
|
|
CREATE TABLE domain_lists (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
kind TEXT NOT NULL CHECK (kind IN ('blacklist', 'allowlist')),
|
|
name TEXT NOT NULL,
|
|
description TEXT NOT NULL DEFAULT '',
|
|
enabled INTEGER NOT NULL DEFAULT 1,
|
|
source_url TEXT NOT NULL DEFAULT '',
|
|
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
UNIQUE (kind, name)
|
|
);
|
|
|
|
CREATE TABLE domain_entries (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
list_id INTEGER NOT NULL REFERENCES domain_lists (id) ON DELETE CASCADE,
|
|
domain TEXT NOT NULL, -- normalised: lowercase, no trailing dot
|
|
match_subdomains INTEGER NOT NULL DEFAULT 1,
|
|
enabled INTEGER NOT NULL DEFAULT 1,
|
|
comment TEXT NOT NULL DEFAULT '',
|
|
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
UNIQUE (list_id, domain)
|
|
);
|
|
CREATE INDEX idx_domain_entries_list ON domain_entries (list_id);
|
|
CREATE INDEX idx_domain_entries_domain ON domain_entries (domain);
|
|
|
|
-- Policy assignments.
|
|
CREATE TABLE network_policies (
|
|
network_id INTEGER NOT NULL REFERENCES networks (id) ON DELETE CASCADE,
|
|
policy_id INTEGER NOT NULL REFERENCES policies (id) ON DELETE CASCADE,
|
|
PRIMARY KEY (network_id, policy_id)
|
|
);
|
|
CREATE INDEX idx_network_policies_policy ON network_policies (policy_id);
|
|
|
|
CREATE TABLE policy_lists (
|
|
policy_id INTEGER NOT NULL REFERENCES policies (id) ON DELETE CASCADE,
|
|
list_id INTEGER NOT NULL REFERENCES domain_lists (id) ON DELETE CASCADE,
|
|
PRIMARY KEY (policy_id, list_id)
|
|
);
|
|
CREATE INDEX idx_policy_lists_list ON policy_lists (list_id);
|
|
|
|
CREATE TABLE query_logs (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
ts INTEGER NOT NULL, -- unix milliseconds
|
|
client_ip TEXT NOT NULL,
|
|
network_id INTEGER,
|
|
network_name TEXT NOT NULL DEFAULT '',
|
|
qname TEXT NOT NULL,
|
|
qtype TEXT NOT NULL,
|
|
rcode TEXT NOT NULL,
|
|
source TEXT NOT NULL,
|
|
cache_hit INTEGER NOT NULL DEFAULT 0,
|
|
blocked INTEGER NOT NULL DEFAULT 0,
|
|
policy_id INTEGER,
|
|
policy_name TEXT NOT NULL DEFAULT '',
|
|
blacklist_id INTEGER,
|
|
blacklist_name TEXT NOT NULL DEFAULT '',
|
|
matched_rule TEXT NOT NULL DEFAULT '',
|
|
protocol TEXT NOT NULL DEFAULT 'udp',
|
|
duration_us INTEGER NOT NULL DEFAULT 0,
|
|
answer_count INTEGER NOT NULL DEFAULT 0
|
|
);
|
|
CREATE INDEX idx_query_logs_ts ON query_logs (ts);
|
|
CREATE INDEX idx_query_logs_client ON query_logs (client_ip, ts);
|
|
CREATE INDEX idx_query_logs_qname ON query_logs (qname, ts);
|
|
CREATE INDEX idx_query_logs_blocked ON query_logs (blocked, ts);
|
|
|
|
CREATE TABLE audit_logs (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
ts INTEGER NOT NULL, -- unix milliseconds
|
|
actor TEXT NOT NULL DEFAULT '',
|
|
source TEXT NOT NULL DEFAULT 'web',
|
|
client_ip TEXT NOT NULL DEFAULT '',
|
|
action TEXT NOT NULL,
|
|
object_type TEXT NOT NULL DEFAULT '',
|
|
object_id TEXT NOT NULL DEFAULT '',
|
|
object_name TEXT NOT NULL DEFAULT '',
|
|
details TEXT NOT NULL DEFAULT ''
|
|
);
|
|
CREATE INDEX idx_audit_logs_ts ON audit_logs (ts);
|
|
CREATE INDEX idx_audit_logs_object ON audit_logs (object_type, ts);
|