0x01. MongoDB搭建

Ubuntu 一键安装:

1
apt-get install mongodb

安装后默认开启mongodb服务,这里先关掉服务。
自定义MongoDB配置文件:

1
2
3
4
5
6
7
8
9
fork=true
dbpath = ./db/
logpath = ./log/mongodb.log
logappend = true
bind_ip = 127.0.0.1
port = 27018
journal = true
nohints = true
nohttpinterface = true

然后直接mongod --config mongodb.conf开启MongoDB服务。
客户端用mongo --port 27018测试是否正常运行。

0x02. Golang 语言环境

Ubuntu 一键安装:

1
apt-get install golang

也可以参考官方文档golang.org下载二进制包安装(需扶墙)。
执行go env查看go环境变量是否正常。

0x03. CoreDNS 安装

coredns.io 官方给出的安装说明:

1
2
3
4
5
6
$ export GOPATH=${GOPATH-~/go}
$ mkdir -p $GOPATH/src/github.com/coredns
$ cd $GOPATH/src/github.com/coredns/
$ git clone https://github.com/coredns/coredns.git
$ cd coredns
$ make CHECKS= godeps all

这里注意我们在执行 make CHECKS= godeps all 之前要修改代码才能解决上篇文章留下的几个问题。

参考官方文档,这里直接修改plugin/whoami/whoami.go插件,代码内容为对每条应用whoami插件的dns请求,返回一个record,其内容为客户端来源ip,并将整个记录存储在MongoDB服务器中。修改完整的 whoami.go 代码如下:

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
// Package whoami implements a plugin that returns details about the resolving
// querying it.
package whoami
import (
"net"
// "strconv"
"gopkg.in/mgo.v2"
"fmt"
"time"
// add
"github.com/coredns/coredns/request"
"github.com/miekg/dns"
"golang.org/x/net/context"
)
// Whoami is a plugin that returns your IP address, port and the protocol used for connecting
// to CoreDNS.
type Whoami struct{}
//mongodb data struct
type DNSQueryData struct {
Qip string
Qaddress string
Qtime time.Time
}
//init db session with mongodb config
var (
session, err = mgo.Dial("127.0.0.1:27018")
db = session.DB("coredns").C("Qrecoders")
)
// ServeDNS implements the plugin.Handler interface.
func (wh Whoami) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
state := request.Request{W: w, Req: r}
//add
//Standard out info
//and use MongoDB
fmt.Println("Source Ip:", state.IP(), "Address:", state.QName(), "Time:", time.Now())
err = db.Insert(&DNSQueryData{state.IP(), state.QName(), time.Now()})
if err != nil {
println(err)
}
//
a := new(dns.Msg)
a.SetReply(r)
a.Compress = true
a.Authoritative = true
ip := state.IP()
var rr dns.RR
switch state.Family() {
case 1:
rr = new(dns.A)
rr.(*dns.A).Hdr = dns.RR_Header{Name: state.QName(), Rrtype: dns.TypeA, Class: dns.ClassINET}
rr.(*dns.A).A = net.ParseIP(ip).To4()
case 2:
rr = new(dns.AAAA)
rr.(*dns.AAAA).Hdr = dns.RR_Header{Name: state.QName(), Rrtype: dns.TypeAAAA, Class: dns.ClassINET}
rr.(*dns.AAAA).AAAA = net.ParseIP(ip)
}
/*
srv := new(dns.SRV)
srv.Hdr = dns.RR_Header{Name: "_" + state.Proto() + "." + state.QName(), Rrtype: dns.TypeSRV, Class: state.QClass()}
if state.QName() == "." {
srv.Hdr.Name = "_" + state.Proto() + state.QName()
}
port, _ := strconv.Atoi(state.Port())
srv.Port = uint16(port)
srv.Target = "."
*/
//a.Extra = []dns.RR{rr, srv}
a.Answer = []dns.RR{rr}
state.SizeAndDo(a)
a, _ = state.Scrub(a)
w.WriteMsg(a)
//return 0, nil
return dns.RcodeSuccess, nil
}
// Name implements the Handler interface.
func (wh Whoami) Name() string { return "whoami" }

代码为了尽量少的修改原始插件,这里将mongodb配置信息直接写死在文件中了,也可以对whoami插件添加参数来引用配置信息。

执行 make CHECKS= godeps all 编译我们修改过的项目代码,并在当前目录成功生成coredns二进制文件。

这里遇到两个坑。

  • go get 需要扶墙,不然等死你也不成功,挂代理什么的也不行,直接在VPS上编译吧。
  • 开始试了几个VPS均失败,发现单核CPU会编译出错。

0x04. 测试 CoreDNS

OK,到这一步前面环境搭建没问题的话,接下来测试coredns。

添加coredns 配置文件:

1
2
3
dns.bypass.world:53 {
whoami
}

启动mongodb:

1
mongod --config mongodb.conf

执行coredns:

1
./coredns -conf coredns.conf

在上篇文章中我们做了如下DNS解析:

Type Name Value
A ns.bypass.world 119.23.236.88
NS dns.bypass.world ns.bypass.world

现在我们同样用这个NS记录,然后执行如下两条命令进行测试:

1
2
nslookup qqqq.dns.bypass.world
ping -c 1 asdf.dns.bypass.world

可以看到DNS Server日志如下:

1
2
3
4
5
6
7
8
9
10
11
./coredns -conf conf/coredns.conf
dns.bypass.world.:53
2018/03/21 16:40:03 [INFO] CoreDNS-1.1.0
2018/03/21 16:40:03 [INFO] linux/amd64, go1.9.2, 93ade7c-dirty
CoreDNS-1.1.0
linux/amd64, go1.9.2, 93ade7c-dirty
Source Ip: 58.22.*.* Address: qqqq.dns.bypass.world. Time: 2018-03-21 16:40:16.401635432 +0800 CST m=+13.074043412
Source Ip: 58.22.*.* Address: asdf.dns.bypass.world. Time: 2018-03-21 16:40:31.946179174 +0800 CST m=+28.618587169

用mongodb客户端查看数据库信息,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mongo --port 27018
MongoDB shell version: 2.6.10
connecting to: 127.0.0.1:27018/test
> use coredns
switched to db coredns
> db.Qrecoders.find().pretty()
{
"_id" : ObjectId("5ab208bfb21405522b18634a"),
"qip" : "58.22.*.*",
"qaddress" : "qqqq.dns.bypass.world.",
"qtime" : ISODate("2018-03-21T16:40:16.816Z")
}
{
"_id" : ObjectId("5ab208c2b21405522b18634b"),
"qip" : "58.22.*.*",
"qaddress" : "asdf.dns.bypass.world.",
"qtime" : ISODate("2018-03-21T16:40:31.062Z")
}
Type "it" for more

0x05. 后续

到此上篇文章留下的几个坑算是完美的填上。只需要写个简单的Django,把MongoBDB数据库查询api开放出来,整个环境算是组建完成。在写这篇文章之前,看到原来ceye.io 后端dns server 调用的是twisted组件,twisted 由python编写,经过测试发现twisted在这种场景下不如coredns更有优势,于是放弃熟悉的Python转学一段时间Golang,才算是读懂了coredns代码,并修改插件,填上了上篇文章留下的坑。