z42

module
v0.0.0-...-4d64e5c Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 27, 2021 License: GPL-3.0

README

Build Status Code Coverage Go Report Card

-> logo <-

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": []
  }
}

zone format in redis db

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}}"

Directories

Path Synopsis
cmd
api
internal
test
Package dnstest allows for easy testing of DNS client against a test server.
Package dnstest allows for easy testing of DNS client against a test server.
pkg
tools

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL