CoreDNS の gRPC proxy サーバーを作ってみよう


CoreDNS とは?

CoreDNS: DNS and Service Discovery
2018 年現在 CNCF で Incubating な DNS サーバーです。
SkyDNS と互換性があり、 etcd などの多種のバックエンドを使って DNS をサービスすることができます。

ところで、社内で独自のカスタムを施した DNS サービスを作りたいと思ったことはないですか?
CoreDNS なら、 gRPC proxy 機能を用いて、独自のカスタマイズした DNS サービスに問合せを proxy することも可能です。
今回は簡単な例題を作成して、 CoreDNS gRPC proxy サービスの作成に入門してみましょう!

簡単な gRPC proxy サービスの例

今回は例題として、いかなるドメインの A 問合せに対しても単純に 127.0.0.1 を返すだけのサービスを作成してみましょう。
こちらが今回のサンプルです。

main.go

package main

import (
    "context"
    "fmt"
    "github.com/coredns/coredns/pb"
    "github.com/coredns/coredns/plugin/etcd/msg"
    "github.com/miekg/dns"
    "google.golang.org/grpc"
    "log"
    "net"
    "os"
)

type DnsSample struct {}

func (ds *DnsSample) Query(ctx context.Context, in *pb.DnsPacket) (*pb.DnsPacket, error) {
    m := new(dns.Msg)
    err := m.Unpack(in.Msg)
    if err != nil {
        return nil, fmt.Errorf("failed to unpack msg: %v", err)
    }

    r := new(dns.Msg)
    r.Question = m.Question

    q := m.Question[0]
    switch q.Qtype {
    case dns.TypeA:
        r.Id = m.Id
        r.Response = true
        serv := new(msg.Service)
        r.Answer = []dns.RR{serv.NewA(q.Name, net.IPv4(127, 0, 0, 1))}
    }

    out, err := r.Pack()
    if err != nil {
        return nil, fmt.Errorf("failed to pack msg: %v", err)
    }

    return &pb.DnsPacket{Msg: out}, nil
}

func main() {
    server := grpc.NewServer()
    pb.RegisterDnsServiceServer(server, &DnsSample{})
    listenPort, err := net.Listen("tcp", "0.0.0.0:1053")
    if err != nil {
        log.Println(err)
        os.Exit(1)
    }
    server.Serve(listenPort)
}

gRPC の定義は、 github.com/coredns/coredns/pb にありますので、これを import して用います。

dns.proto

syntax = "proto3";

package coredns.dns;
option go_package = "pb";

message DnsPacket {
    bytes msg = 1;
}

service DnsService {
    rpc Query (DnsPacket) returns (DnsPacket);
}

この rpc Query を自分で実装することで独自カスタムができます。
このように CoreDNS 側とやり取りできるデータはただの byte 列です。
dns.Msg を用いて、 Unpack(), Pack() することができるので、それで DNS のプロトコルをパースすることができます。
具体的にはコードを読んでみてください。

gRPC proxy を行う設定

CoreDNS は Corefile というファイルに設定を記述します。
下記のように記述することで、 DNS の問い合わせを今回作成する独自の DNS サービスに proxy することができるようになります。

Corefile

.:53 {
    proxy . 127.0.0.1:1053 {
        protocol grpc insecure
    }
    debug
    log
}

動作確認

CoreDNS を起動

設定ファイルであるCorefile が存在するディレクトリで起動します。

$ sudo coredns

gRPC proxy サービスを起動

今回作成した main.go を起動します。

$ go build main.go

問合せしてみる

適当なドメインへの A 問合せに対して、 127.0.0.1 を返しています!

$ dig +short A @127.0.0.1 "test.com"
127.0.0.1

Let's hack!

今回の記事では CoreDNS の gRPC proxy で動作する、独自 DNS サービスの例題を実装してみました。
これらの例題をもとに、バックエンドデータベース、レコードデータのフォーマット、アルゴリズム、お好きなようにカスタマイズしてみてください😉