The ultimate answer to all dns queries.
z42 is an Authoritative name server that serves zone data from redis database.
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.1port: port number to bind, default: 1053protocol: protocol; can be tcp or udp, default: udpcount: number of listeners per address, default: 1
we use two seperate redis connection. one read-only connection to get zone data, and one read-write connection for get/set stats
{
"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 expirezone_reload: time in seconds before zone data is reloaded from redis
{
"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 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: 0password: redis AUTH string, default is emptyprefix,suffix: strings to prepend/append to all redis queries, default is emptymax_idle_connections: maximum number of idle connections that pool keeps, default: 10max_active_connections: maximum number of active connections, default: 10connect_timeout: time to wait for connecting to redis server in milliseconds, 0 for no timeout; default: 500read_timeout: time to wait for redis query results in milliseconds, 0 for no timeout; default: 500idle_keep_alive: time to keep idle connections in seconds, 0 for unlimited; default: 30max_keep_alive: maximum time to keep a connection in seconds, 0 for unlimited; default: 0wait_for_connection: whether or not wait for a connection to be available if connection pool is full, default: false
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: 3600log_source_location: enable logging source location of every requestupstream_fallback: enable using upstream for querying non-authoritative requestslog: log configuration to use for handler
geoip configuration
{
"geoip": {
"enable": true,
"country_db": "geoCity.mmdb",
"asn_db": "geoIsp.mmdb"
}
}enable: enable/disable geoip calculations, default: disablecountry_db: maxminddb file for country codes to use, default: geoCity.mmdbasn_db: maxminddb file for autonomous system numbers to use, default: geoIsp.mmdb
{
"upstream": [{
"ip": "1.1.1.1",
"port": 53,
"protocol": "udp",
"timeout": 400
}]
}ip: upstream ip address, default: 1.1.1.1port: upstream port number, deafult: 53protocol: upstream protocol, default : udptimeout: request timeout in milliseconds, default: 400
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: disablemax_requests: maximum number of simultanous healthcheck requests, deafult: 10max_pending_requests: maximum number of requests to queue, default: 100update_interval: time between checking for updated data from redis in seconds, default: 300check_interval: time between two healthcheck requests in seconds, default: 600log: log configuration to use for healthcheck logs
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: disablelevel: log level, can be debug, info, warning, error, default: infotarget: log target, can be stdout, stderr, file, udp default: stdoutformat: log format, can be text, json, default: text. an extra log format ("capnp_request") is also available for request logstime_format: timestamp format using example-based layout, reference time is Mon Jan 2 15:04:05 MST 2006path: log output file path, net address if target is udpsentry: sentry hook configurationssyslog: syslog hook configurationskafka: kafka hook configurationsenable: enable/disable kafka hook, default: disablebrokers: 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 : 3000buffer_size: kafka producer buffer size, default : 1000
rate limit connfiguration
{
"ratelimit": {
"enable": true,
"rate": 60,
"burst": 10,
"blacklist": ["10.10.10.1"],
"whitelist": ["127.0.0.1"]
}
}enable: enable/disable rate limitrate: maximum allowed request per minuteburst: number of burst requestsblacklist: list of ips to refuse all requestwhitelist: list of ips to bypass rate limit
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": []
}
}- 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=="
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":{
"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":{
"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 shufflegeo_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:ipuri: uri to use in healthcheck requestport: port to use in healthcheck requestprotocol: protocol to use in healthcheck request, can be http or httpsup_count: number of successful healthcheck requests to consider an ip validdown_count: number of unsuccessful healthcheck requests to consider an ip invalidtimeout time: to wait for a healthcheck response
{
"aname":{
"location": "x.example.com."
}
}{
"cname":{
"host" : "x.example.com.",
"ttl" : 360
}
}{
"txt":{
"ttl" : 360,
"records":[
{"text" : "this is a text"},
{"text" : "this is another text"}
]
}
}{
"ns":{
"ttl" : 360,
"records":[
{"host" : "ns1.example.com."},
{"host" : "ns2.example.com."}
]
}
}{
"mx":{
"ttl" : 360,
"records":[
{
"host" : "mx1.example.com.",
"preference" : 10
},
{
"host" : "mx2.example.com.",
"preference" : 20
}
]
}
}{
"srv":{
"ttl" : 360,
"records":[
{
"target" : "sip.example.com.",
"port" : 555,
"priority" : 10,
"weight" : 100
}
]
}
}{
"caa":{
"ttl": 360,
"records":[
{
"tag": "issuewild;",
"value": "godaddy.com",
"flag": 0
}
]
}
}{
"ptr":{
"ttl": 300,
"domain": "mail.example.com"
}
}{
"tlsa":{
"ttl": 300,
"records":[
{
"usage": 1,
"selector": 1,
"matching_type": 1,
"certificate": "1CFC98A706BCF3683015"
}
]
}
}{
"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: falsednssec: enable/disable dnssec, default: falsedomain_id: unique domain id for logging, optional
$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}}"
