Skip to content

Commit

Permalink
Initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
wadahiro committed Feb 20, 2018
1 parent 0874432 commit 75e1bd5
Show file tree
Hide file tree
Showing 6 changed files with 529 additions and 1 deletion.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@

# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/

.tmp/
*.tar.gz
coredns
113 changes: 112 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,113 @@
# coredns-amazondns
Amazon DNS plugin for CoreDNS
The *amazondns* plugin behaves **Authoritative name server** using [Amazon DNS Server](https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_DHCP_Options.html#AmazonDNS) as the backend.

The Amazon DNS server is used to resolve the DNS domain names that you specify in a private hosted zone in Route 53. However, the server acts as **Caching name server**. Although CoreDNS has [proxy plugin](https://github.com/coredns/coredns/tree/master/plugin/proxy) and we can configure Amazon DNS server as the backend, it can't be Authoritative name server. In my case, Authoritative name server is required to handle delegated responsibility for the subdomain. That's why I created this plugin.

## Name

*amazondns* - enables serving Authoritative name server using Amazon DNS Server as the backend.

## Syntax

```txt
amazondns ZONE [ADDRESS] {
soa RR
ns RR
nsa RR
}
```

* **ZONE** the zone scope for this plugin.
* **ADDRESS** defines the Amazon DNS server address specifically.
If no **ADDRESS** entry, this plugin resovles it automatically using [Instance Metadata](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html).
* **soa** **RR** SOA record with [RFC 1035](https://tools.ietf.org/html/rfc1035#section-5) style.
* **ns** **RR** NS record(s) with [RFC 1035](https://tools.ietf.org/html/rfc1035#section-5) style.
* **nsa** **RR** A record(s) for the NS(s) with [RFC 1035](https://tools.ietf.org/html/rfc1035#section-5) style.

## Examples

Create your Route 53 private hostead zone with `sub.example.org` and attach your VPC. Then add A record for `test.sub.example.org` into the zone.

Next, boot EC2 instance and deploy CoreDNS binary, and configure CoreDNS config file as below.

```txt
. {
amazondns sub.example.org {
soa "sub.example.org 60 IN SOA ns1.sub.example.org hostmaster.sub.example.org (1 7200 900 1209600 86400)"
ns "sub.example.org 60 IN NS ns1.sub.example.org"
ns "sub.example.org 60 IN NS ns2.sub.example.org"
nsa "ns1.sub.example.org 60 IN A 192.168.0.1"
nsa "ns2.sub.example.org 60 IN A 192.168.0.2"
}
}
```

Boot CoreDNS and check it how it works.

The `test.sub.example.org` is resolved with *AUTHORITY SECTION* and *ADDITIONAL SECTION* as below.

```bash
> dig @localhost test.sub.example.org +norecurse

; <<>> DiG 9.11.1 <<>> @localhost test.sub.example.org +norecurse
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28681
;; flags: qr aa ra; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 3
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 23246de45b4a3601 (echoed)
;; QUESTION SECTION:
;test.sub.example.org. IN A
;; ANSWER SECTION:
test.sub.example.org. 60 IN A 10.0.0.10
;; AUTHORITY SECTION:
sub.example.org. 60 IN NS ns1.sub.example.org.
sub.example.org. 60 IN NS ns2.sub.example.org.
;; ADDITIONAL SECTION:
ns1.sub.example.org. 60 IN A 192.168.0.1
ns2.sub.example.org. 60 IN A 192.168.0.2
;; Query time: 12 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Tue Feb 20 15:11:55 JST 2018
;; MSG SIZE rcvd: 146
```
Also it can return NS record(s) for subdomain as below.
```bash
> dig @localhost sub.example.org ns
; <<>> DiG 9.11.1 <<>> @localhost sub.example.org ns +norecurse
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 2719
;; flags: qr aa ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 3
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: c1c3332966dba8fd (echoed)
;; QUESTION SECTION:
;sub.example.org. IN NS
;; ANSWER SECTION:
sub.example.org. 60 IN NS ns1.sub.example.org.
sub.example.org. 60 IN NS ns2.sub.example.org.
;; ADDITIONAL SECTION:
ns1.sub.example.org. 60 IN A 192.168.0.1
ns2.sub.example.org. 60 IN A 192.168.0.2
;; Query time: 1 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Tue Feb 20 15:08:27 JST 2018
;; MSG SIZE rcvd: 125
```
131 changes: 131 additions & 0 deletions amazondns.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package amazondns

import (
"github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/request"

"github.com/miekg/dns"
"golang.org/x/net/context"
)

// AmazonDNS represents a plugin instance that can proxy requests to AmazonDNS.
type AmazonDNS struct {
Next plugin.Handler

client *dns.Client
zones []string
zoneMap map[string]*Zone
}

type Zone struct {
zone string
dns string
soa dns.RR
ns []dns.RR
nsa []dns.RR
}

// ServeDNS implements plugin.Handler.
func (ad AmazonDNS) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
state := request.Request{W: w, Req: r}
name := state.Name()

key := plugin.Zones(ad.zones).Matches(name)
if key == "" {
return plugin.NextOrFailure(ad.Name(), ad.Next, ctx, w, r)
}
zone := ad.zoneMap[key]

qtype := state.QType()

m := new(dns.Msg)
m.SetReply(r)
m.Authoritative, m.RecursionAvailable, m.Compress = true, true, true

L:
switch qtype {
case dns.TypeA:
for _, nsa := range zone.nsa {
if name == nsa.Header().Name {
m.Answer = []dns.RR{nsa}
m.Ns = zone.ns
m.Rcode = dns.RcodeSuccess
break L
}
}
fallthrough
case dns.TypeAAAA:
fallthrough
case dns.TypeCNAME:
// Need recursive mode for getting record from AmazonDNS
r.MsgHdr.RecursionDesired = true
resp, _, err := ad.client.Exchange(r, zone.dns)

if err != nil {
return dns.RcodeServerFailure, err
}

m.Answer = resp.Answer
m.Rcode = resp.Rcode

// Overwrite authority and additional section
if len(m.Answer) > 0 {
m.Ns = zone.ns
m.Extra = zone.nsa
} else {
handleNotFound(zone, name, m)
}
case dns.TypeNS:
if name == zone.soa.Header().Name {
m.Answer = zone.ns
m.Extra = zone.nsa
m.Rcode = dns.RcodeSuccess
} else {
handleNotFound(zone, name, m)
}
case dns.TypeSOA:
if name == zone.soa.Header().Name {
m.Answer = []dns.RR{zone.soa}
m.Ns = zone.ns
} else {
handleNotFound(zone, name, m)
}
default:
handleNotFound(zone, name, m)
}

state.SizeAndDo(m)
m, _ = state.Scrub(m)
w.WriteMsg(m)
return dns.RcodeSuccess, nil
}

func handleNotFound(zone *Zone, name string, m *dns.Msg) {
m.Ns = []dns.RR{zone.soa}

if name == zone.soa.Header().Name {
m.Rcode = dns.RcodeSuccess
return
}
for _, ns := range zone.ns {
if name == ns.Header().Name {
m.Rcode = dns.RcodeSuccess
return
}
}
for _, nsa := range zone.nsa {
if name == nsa.Header().Name {
m.Rcode = dns.RcodeSuccess
return
}
}

// Error
m.Rcode = dns.RcodeNameError
}

func (ad *AmazonDNS) fillResponse(state request.Request, m *dns.Msg) {
}

// Name implements the Handler interface.
func (ad AmazonDNS) Name() string { return "amazondns" }
44 changes: 44 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/bin/sh

VERSION=0.1
TAG=master

if [ "$1" = "" ]; then
docker run --rm \
-v $(pwd):/go/src/github.com/wadahiro/coredns-amazondns \
-v $(pwd)/.tmp:/go \
-w /go/src/github.com/wadahiro/coredns-amazondns \
golang:1.9 ./build.sh $TAG
else
echo "Building CoreDNS:$1 with amazondns..."

go get github.com/coredns/coredns
cd /go/src/github.com/coredns/coredns

git reset --hard HEAD
git clean -f

git checkout $1
if [ "$?" -ne 0 ]; then
echo "Invalid tag: $1"
exit 1
fi

sed -i -e "/^route53:route53$/i amazondns:github.com/wadahiro/coredns-amazondns" plugin.cfg

if [ "$?" -ne 0 ]; then
echo "Failed"
exit 1
fi

cat plugin.cfg

go generate
#make
go build

cp coredns /go/src/github.com/wadahiro/coredns-amazondns/
tar cvzf coredns-amazondns-$VERSION.tar.gz coredns
mv coredns-*.tar.gz /go/src/github.com/wadahiro/coredns-amazondns/
fi

Loading

0 comments on commit 75e1bd5

Please sign in to comment.