-> <-
The ultimate answer to all dns queries.
z42 is an Authoritative name server that serves zone data from redis database.
table of contents
Configuration
server
dns listening server configuration
{
"server": {
"ip": "127.0.0.1",
"port": 1053,
"protocol": "udp",
"count": 1
}
}
ip
: ip address to bind, default: 127.0.0.1
port
: port number to bind, default: 1053
protocol
: protocol; can be tcp or udp, default: udp
count
: number of listeners per address, default: 1
redis
we use two seperate redis connection. one read-only connection to get zone data, and one read-write connection for get/set stats
data
{
"redis_data": {
"zone_cache_size": 10000,
"zone_cache_timeout": 60,
"zone_reload": 60,
"record_cache_size": 1000000,
"record_cache_timeout": 60,
"redis": {
"address": "127.0.0.1:6379",
"net": "tcp",
"db": 0,
"password": "",
"prefix": "",
"suffix": "_dns2",
"connection": {
"max_idle_connections": 10,
"max_active_connections": 10,
"connect_timeout": 500,
"read_timeout": 500,
"idle_keep_alive": 30,
"max_keep_alive": 0,
"wait_for_connection": false
}
}
}
}
zone_cache_timeout
: time in seconds before cached responses expire
zone_reload
: time in seconds before zone data is reloaded from redis
stat
{
"redis_stat": {
"redis": {
"address": "127.0.0.1:6379",
"net": "tcp",
"db": 0,
"password": "",
"prefix": "z42_",
"suffix": "_z42",
"connection": {
"max_idle_connections": 10,
"max_active_connections": 10,
"connect_timeout": 500,
"read_timeout": 500,
"idle_keep_alive": 30,
"max_keep_alive": 0,
"wait_for_connection": false
}
}
}
}
redis config
redis configurations
{
"redis": {
"address": "127.0.0.1:6379",
"net": "tcp",
"db": 0,
"password": "",
"prefix": "test_",
"suffix": "_test",
"connection": {
"max_idle_connections": 10,
"max_active_connections": 10,
"connect_timeout": 500,
"read_timeout": 500,
"idle_keep_alive": 30,
"max_keep_alive": 0,
"wait_for_connection": false
},
"connect_timeout": 0,
"read_timeout": 0
}
}
address
: redis address: "ip:port" for "tcp" and "/path/to/unix/socket.sock" for "unix", default: "127.0.0.1:6379"
net
: connection protocol: "tcp" or "unix", default: "tcp"
db
: redis database to use, default: 0
password
: redis AUTH string, default is empty
prefix
, suffix
: strings to prepend/append to all redis queries, default is empty
max_idle_connections
: maximum number of idle connections that pool keeps, default: 10
max_active_connections
: maximum number of active connections, default: 10
connect_timeout
: time to wait for connecting to redis server in milliseconds, 0 for no timeout; default: 500
read_timeout
: time to wait for redis query results in milliseconds, 0 for no timeout; default: 500
idle_keep_alive
: time to keep idle connections in seconds, 0 for unlimited; default: 30
max_keep_alive
: maximum time to keep a connection in seconds, 0 for unlimited; default: 0
wait_for_connection
: whether or not wait for a connection to be available if connection pool is full, default: false
handler
dns query handler configuration
{
"handler": {
"max_ttl": 300,
"log_source_location": false,
"log": {
"enable": true,
"level": "info",
"target": "file",
"format": "json",
"path": "/tmp/z42.log"
},
"geoip": {
"enable": true,
"country_db": "geoCity.mmdb",
"asn_db": "geoIsp.mmdb"
},
"upstream": [{
"ip": "1.1.1.1",
"port": 53,
"protocol": "udp",
"timeout": 400
}]
}
}
max_ttl
: max ttl in seconds, default: 3600
log_source_location
: enable logging source location of every request
upstream_fallback
: enable using upstream for querying non-authoritative requests
log
: log configuration to use for handler
geoip
geoip configuration
{
"geoip": {
"enable": true,
"country_db": "geoCity.mmdb",
"asn_db": "geoIsp.mmdb"
}
}
enable
: enable/disable geoip calculations, default: disable
country_db
: maxminddb file for country codes to use, default: geoCity.mmdb
asn_db
: maxminddb file for autonomous system numbers to use, default: geoIsp.mmdb
upstream
{
"upstream": [{
"ip": "1.1.1.1",
"port": 53,
"protocol": "udp",
"timeout": 400
}]
}
ip
: upstream ip address, default: 1.1.1.1
port
: upstream port number, deafult: 53
protocol
: upstream protocol, default : udp
timeout
: request timeout in milliseconds, default: 400
healthcheck
healthcheck configuration
{
"healthcheck": {
"enable": true,
"max_requests": 10,
"max_pending_requests": 100,
"update_interval": 600,
"check_interval": 600,
"log": {
"enable": true,
"level": "info",
"target": "file",
"format": "json",
"path": "/tmp/healthcheck.log"
}
}
}
enable
: enable/disable healthcheck, default: disable
max_requests
: maximum number of simultanous healthcheck requests, deafult: 10
max_pending_requests
: maximum number of requests to queue, default: 100
update_interval
: time between checking for updated data from redis in seconds, default: 300
check_interval
: time between two healthcheck requests in seconds, default: 600
log
: log configuration to use for healthcheck logs
log
log configuration
{
"log": {
"enable": true,
"level": "info",
"target": "file",
"format": "json",
"time_format": "2006-01-02T15:04:05.999999-07:00",
"path": "/tmp/z42.log",
"sentry": {
"enable": false,
"dsn": ""
},
"syslog": {
"enable": false,
"protocol": "udp",
"address": "localhost:514"
},
"kafka": {
"enable": false,
"brokers": ["127.0.0.1:9092"],
"topic": "z42",
"format": "capnp_request",
"compression": "none",
"timeout": 3000,
"buffer_size": 1000
}
}
}
enable
: enable/disable this log resource, default: disable
level
: log level, can be debug, info, warning, error, default: info
target
: log target, can be stdout, stderr, file, udp default: stdout
format
: log format, can be text, json, default: text. an extra log format ("capnp_request") is also available for request logs
time_format
: timestamp format using example-based layout, reference time is Mon Jan 2 15:04:05 MST 2006
path
: log output file path, net address if target is udp
sentry
: sentry hook configurations
syslog
: syslog hook configurations
kafka
: kafka hook configurations
enable
: enable/disable kafka hook, default: disable
brokers
: list of brokers in "ip:port" format, default : "127.0.0.1:9092"
topic
: name of kafka topic, default : "z42"
format
: message format, default: "json"
compression
: compression format : "snappy", "gzip", "lz4", "zstd", "none", default: "none"
timeout
: kafka operation timeout (dial, read, write) in milliseconds, default : 3000
buffer_size
: kafka producer buffer size, default : 1000
rate limit
rate limit connfiguration
{
"ratelimit": {
"enable": true,
"rate": 60,
"burst": 10,
"blacklist": ["10.10.10.1"],
"whitelist": ["127.0.0.1"]
}
}
enable
: enable/disable rate limit
rate
: maximum allowed request per minute
burst
: number of burst requests
blacklist
: list of ips to refuse all request
whitelist
: list of ips to bypass rate limit
example
sample config:
{
"server": [
{
"ip": "127.0.0.1",
"port": 1053,
"protocol": "udp",
"count": 8
}
],
"error_log": {
"enable": true,
"target": "stdout",
"level": "info",
"path": "/tmp/error.log",
"format": "text",
"time_format": "2006-01-02T15:04:05Z07:00"
},
"redis_data": {
"zone_cache_size": 10000,
"zone_cache_timeout": 60,
"zone_reload": 60,
"record_cache_size": 1000000,
"record_cache_timeout": 60,
"redis": {
"address": "127.0.0.1:6379",
"net": "tcp",
"db": 0,
"password": "",
"prefix": "",
"suffix": "_dns2",
"connection": {
"max_idle_connections": 10,
"max_active_connections": 10,
"connect_timeout": 500,
"read_timeout": 500,
"idle_keep_alive": 30,
"max_keep_alive": 0,
"wait_for_connection": false
}
}
},
"redis_stat": {
"redis": {
"address": "127.0.0.1:6379",
"net": "tcp",
"db": 0,
"password": "",
"prefix": "z42_",
"suffix": "_z42",
"connection": {
"max_idle_connections": 10,
"max_active_connections": 10,
"connect_timeout": 500,
"read_timeout": 500,
"idle_keep_alive": 30,
"max_keep_alive": 0,
"wait_for_connection": false
}
}
},
"handler": {
"upstream": [
{
"ip": "1.1.1.1",
"port": 53,
"protocol": "udp",
"timeout": 400
}
],
"geoip": {
"enable": true,
"country_db": "geoCity.mmdb",
"asn_db": "geoIsp.mmdb"
},
"max_ttl": 3600,
"log_source_location": false,
"log": {
"enable": true,
"target": "file",
"level": "info",
"path": "/tmp/z42.log",
"format": "json",
"time_format": "2006-01-02T15:04:05Z07:00"
}
},
"healthcheck": {
"enable": false,
"max_requests": 10,
"max_pending_requests": 100,
"update_interval": 600,
"check_interval": 600,
"log": {
"enable": true,
"target": "file",
"level": "info",
"path": "/tmp/healthcheck.log",
"format": "json",
"time_format": "2006-01-02T15:04:05Z07:00"
}
},
"ratelimit": {
"enable": false,
"burst": 10,
"rate": 60,
"whitelist": [],
"blacklist": []
}
}
keys
- z42:zones is a set containing all active zones
redis-cli>SMEMBERS z42:zones
1) "example.com."
2) "example.net."
- z42:zones:XXXX.XXX. is a hash map containing dns RRs, @ is used for TLD records.
redis-cli>HKEYS z42:zones:example.com.
1) "@"
2) "www"
3) "ns"
4) "subdomain.www"
@ is a special case used for root data
- z42:zones:XXXX.XXX.:config is a string containing zone specific configurations
redis-cli>GET z42:zones:example.com.:config
"{\"soa\":{\"ttl\":300, \"minttl\":100, \"mbox\":\"hostmaster.example.com.\",\"ns\":\"ns1.example.com.\",\"refresh\":44,\"retry\":55,\"expire\":66, \"serial\":23232}}"
- z42:zones:XXXX.XXX.:pub and z42:zones:XXXX.XXX.:priv contains keypair for dnssec
redis-cli>GET z42:zones:XXXX.XXX.:pub
"dnssec_test.com. IN DNSKEY 256 3 5 AwEAAaKsF5vxBfKuqeUa4+ugW37ftFZOyo+k7r2aeJzZdIbYk//P/dpC HK4uYG8Z1dr/qeo12ECNVcf76j+XAdJD841ELiRVaZteH8TqfPQ+jdHz 10e8Sfkh7OZ4oBwSCXWj+Q=="
zones
dns RRs
dns RRs are stored in redis as json strings inside a hash map using address as field key.
@ is used for TLD records.
redis-cli>HGETALL example.com.
1) "@"
2) "www"
A
{
"a":{
"ttl" : 360,
"records":[
{
"ip" : "1.2.3.4",
"country" : ["US"],
"asn": [444],
"weight" : 10
},
{
"ip" : "2.2.3.4",
"country" : ["US"],
"asn": [444],
"weight" : 10
}
],
"filter": {
"count":"single",
"order": "rr",
"geo_filter":"country"
},
"health_check":{
"enable":true,
"uri": "/hc/test.html",
"port": 8080,
"protocol": "https",
"up_count":3,
"down_count":-3,
"timeout":1000
}
}
}
AAAA
{
"aaaa":{
"ttl" : 360,
"records":[
{
"ip" : "1.2.3.4",
"country" : ["US"],
"asn": [444],
"weight" : 10
},
{
"ip" : "1.2.3.4",
"country" : ["US"],
"asn": [444],
"weight" : 10
}
],
"filter": {
"count":"single",
"order": "rr",
"geo_filter":"country"
},
"health_check":{
"enable":true,
"uri": "/hc/test.html",
"port": 8080,
"protocol": "https",
"up_count":3,
"down_count":-3,
"timeout":1000
}
}
}
filter
: filtering mode:
count
: return single or multiple results. values : "multi", "single"
order
: order of result. values : "none" - saved order, "weighted" - weighted shuffle, "rr" - uniform shuffle
geo_filter
: geo filter. values : "country" - same country, "location" - nearest destination, "asn" - same isp, "asn+country" same isp then same country, "none"
health_check
: health check configuration
enable
: enable/disable healthcheck for this host:ip
uri
: uri to use in healthcheck request
port
: port to use in healthcheck request
protocol
: protocol to use in healthcheck request, can be http or https
up_count
: number of successful healthcheck requests to consider an ip valid
down_count
: number of unsuccessful healthcheck requests to consider an ip invalid
timeout time
: to wait for a healthcheck response
ANAME
{
"aname":{
"location": "x.example.com."
}
}
CNAME
{
"cname":{
"host" : "x.example.com.",
"ttl" : 360
}
}
TXT
{
"txt":{
"ttl" : 360,
"records":[
{"text" : "this is a text"},
{"text" : "this is another text"}
]
}
}
NS
{
"ns":{
"ttl" : 360,
"records":[
{"host" : "ns1.example.com."},
{"host" : "ns2.example.com."}
]
}
}
MX
{
"mx":{
"ttl" : 360,
"records":[
{
"host" : "mx1.example.com.",
"preference" : 10
},
{
"host" : "mx2.example.com.",
"preference" : 20
}
]
}
}
SRV
{
"srv":{
"ttl" : 360,
"records":[
{
"target" : "sip.example.com.",
"port" : 555,
"priority" : 10,
"weight" : 100
}
]
}
}
CAA
{
"caa":{
"ttl": 360,
"records":[
{
"tag": "issuewild;",
"value": "godaddy.com",
"flag": 0
}
]
}
}
PTR
{
"ptr":{
"ttl": 300,
"domain": "mail.example.com"
}
}
TLSA
{
"tlsa":{
"ttl": 300,
"records":[
{
"usage": 1,
"selector": 1,
"matching_type": 1,
"certificate": "1CFC98A706BCF3683015"
}
]
}
}
config
{
"soa":{
"ttl" : 100,
"mbox" : "hostmaster.example.com.",
"ns" : "ns1.example.com.",
"refresh" : 44,
"retry" : 55,
"expire" : 66,
"serial" : 25245235
},
"cname_flattening": true,
"dnssec": true,
"domain_id": "123456789"
}
cname_flattening
: enable/disable cname flattening, default: false
dnssec
: enable/disable dnssec, default: false
domain_id
: unique domain id for logging, optional
zone example
$ORIGIN example.net.
example.net. 300 IN SOA <SOA RDATA>
example.net. 300 NS ns1.example.net.
example.net. 300 NS ns2.example.net.
*.example.net. 300 TXT "this is a wildcard"
*.example.net. 300 MX 10 host1.example.net.
sub.*.example.net. 300 TXT "this is not a wildcard"
host1.example.net. 300 A 5.5.5.5
_ssh.tcp.host1.example.net. 300 SRV <SRV RDATA>
_ssh.tcp.host2.example.net. 300 SRV <SRV RDATA>
subdel.example.net. 300 NS ns1.subdel.example.net.
subdel.example.net. 300 NS ns2.subdel.example.net.
above zone data should be stored at redis as follow:
redis-cli> smembers z42:zones
1) "example.net."
redis-cli> hgetall z42:zones:example.net.
1) "_ssh._tcp.host1"
2) "{\"srv\":{\"ttl\":300, \"records\":[{\"target\":\"tcp.example.com.\",\"port\":123,\"priority\":10,\"weight\":100}]}}"
3) "*"
4) "{\"txt\":{\"ttl\":300, \"records\":[{\"text\":\"this is a wildcard\"}]},\"mx\":{\"ttl\":300, \"records\":[{\"host\":\"host1.example.net.\",\"preference\": 10}]}}"
5) "host1"
6) "{\"a\":{\"ttl\":300, \"records\":[{\"ip\":\"5.5.5.5\"}]}}"
7) "sub.*"
8) "{\"txt\":{\"ttl\":300, \"records\":[{\"text\":\"this is not a wildcard\"}]}}"
9) "_ssh._tcp.host2"
10) "{\"srv\":{\"ttl\":300, \"records\":[{\"target\":\"tcp.example.com.\",\"port\":123,\"priority\":10,\"weight\":100}]}}"
11) "subdel"
12) "{\"ns\":{\"ttl\":300, \"records\":[{\"host\":\"ns1.subdel.example.net.\"},{\"host\":\"ns2.subdel.example.net.\"}]}"
13) "@"
14) "{\"ns\":{\"ttl\":300, \"records\":[{\"host\":\"ns1.example.net.\"},{\"host\":\"ns2.example.net.\"}]}"
redis-cli> get z42:zones:example.net.:config
"{\"soa\":{\"ttl\":300, \"minttl\":100, \"mbox\":\"hostmaster.example.net.\",\"ns\":\"ns1.example.net.\",\"refresh\":44,\"retry\":55,\"expire\":66, \"serial\":32343}}"