Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
C
Coredns
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Railgun
Coredns
Commits
8432f142
Commit
8432f142
authored
Oct 10, 2018
by
Can Yucel
Committed by
corbot[bot]
Oct 10, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
plugin/route53: add split zone support (#2160)
Automatically submitted.
parent
49c776df
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
123 additions
and
60 deletions
+123
-60
plugin/route53/README.md
plugin/route53/README.md
+11
-1
plugin/route53/route53.go
plugin/route53/route53.go
+57
-40
plugin/route53/route53_test.go
plugin/route53/route53_test.go
+29
-13
plugin/route53/setup.go
plugin/route53/setup.go
+9
-6
plugin/route53/setup_test.go
plugin/route53/setup_test.go
+17
-0
No files found.
plugin/route53/README.md
View file @
8432f142
...
@@ -21,7 +21,9 @@ route53 [ZONE:HOSTED_ZONE_ID...] {
...
@@ -21,7 +21,9 @@ route53 [ZONE:HOSTED_ZONE_ID...] {
}
}
~~~
~~~
*
**ZONE**
the name of the domain to be accessed.
*
**ZONE**
the name of the domain to be accessed. When there are multiple zones with overlapping domains
(private vs. public hosted zone), CoreDNS does the lookup in the given order here. Therefore, for a
non-existing resource record, SOA response will be from the rightmost zone.
*
**HOSTED_ZONE_ID**
the ID of the hosted zone that contains the resource record sets to be accessed.
*
**HOSTED_ZONE_ID**
the ID of the hosted zone that contains the resource record sets to be accessed.
*
**AWS_ACCESS_KEY_ID**
and
**AWS_SECRET_ACCESS_KEY**
the AWS access key ID and secret access key
*
**AWS_ACCESS_KEY_ID**
and
**AWS_SECRET_ACCESS_KEY**
the AWS access key ID and secret access key
to be used when query AWS (optional). If they are not provided, then coredns tries to access
to be used when query AWS (optional). If they are not provided, then coredns tries to access
...
@@ -81,3 +83,11 @@ Enable route53 with AWS credentials file:
...
@@ -81,3 +83,11 @@ Enable route53 with AWS credentials file:
}
}
}
}
~~~
~~~
Enable route53 with multiple hosted zones with the same domain:
~~~
txt
. {
route53 example.org.:Z1Z2Z3Z4DZ5Z6Z7 example.org.:Z93A52145678156
}
~~~
plugin/route53/route53.go
View file @
8432f142
...
@@ -30,29 +30,40 @@ type Route53 struct {
...
@@ -30,29 +30,40 @@ type Route53 struct {
upstream
*
upstream
.
Upstream
upstream
*
upstream
.
Upstream
zMu
sync
.
RWMutex
zMu
sync
.
RWMutex
zones
map
[
string
]
*
zone
zones
zones
}
}
type
zone
struct
{
type
zone
struct
{
id
string
id
string
z
*
file
.
Zone
z
*
file
.
Zone
dns
string
}
}
// New returns new *Route53.
type
zones
map
[
string
][]
*
zone
func
New
(
ctx
context
.
Context
,
c
route53iface
.
Route53API
,
keys
map
[
string
]
string
,
up
*
upstream
.
Upstream
)
(
*
Route53
,
error
)
{
zones
:=
make
(
map
[
string
]
*
zone
,
len
(
keys
))
// New reads from the keys map which uses domain names as its key and hosted
// zone id lists as its values, validates that each domain name/zone id pair does
// exist, and returns a new *Route53. In addition to this, upstream is passed
// for doing recursive queries against CNAMEs.
// Returns error if it cannot verify any given domain name/zone id pair.
func
New
(
ctx
context
.
Context
,
c
route53iface
.
Route53API
,
keys
map
[
string
][]
string
,
up
*
upstream
.
Upstream
)
(
*
Route53
,
error
)
{
zones
:=
make
(
map
[
string
][]
*
zone
,
len
(
keys
))
zoneNames
:=
make
([]
string
,
0
,
len
(
keys
))
zoneNames
:=
make
([]
string
,
0
,
len
(
keys
))
for
dns
,
id
:=
range
keys
{
for
dns
,
hostedZoneIDs
:=
range
keys
{
for
_
,
hostedZoneID
:=
range
hostedZoneIDs
{
_
,
err
:=
c
.
ListHostedZonesByNameWithContext
(
ctx
,
&
route53
.
ListHostedZonesByNameInput
{
_
,
err
:=
c
.
ListHostedZonesByNameWithContext
(
ctx
,
&
route53
.
ListHostedZonesByNameInput
{
DNSName
:
aws
.
String
(
dns
),
DNSName
:
aws
.
String
(
dns
),
HostedZoneId
:
aws
.
String
(
id
),
HostedZoneId
:
aws
.
String
(
hostedZoneID
),
})
})
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
zones
[
dns
]
=
&
zone
{
id
:
id
,
z
:
file
.
NewZone
(
dns
,
""
)}
if
_
,
ok
:=
zones
[
dns
];
!
ok
{
zoneNames
=
append
(
zoneNames
,
dns
)
zoneNames
=
append
(
zoneNames
,
dns
)
}
}
zones
[
dns
]
=
append
(
zones
[
dns
],
&
zone
{
id
:
hostedZoneID
,
dns
:
dns
,
z
:
file
.
NewZone
(
dns
,
""
)})
}
}
return
&
Route53
{
return
&
Route53
{
client
:
c
,
client
:
c
,
zoneNames
:
zoneNames
,
zoneNames
:
zoneNames
,
...
@@ -101,9 +112,14 @@ func (h *Route53) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg
...
@@ -101,9 +112,14 @@ func (h *Route53) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg
m
.
SetReply
(
r
)
m
.
SetReply
(
r
)
m
.
Authoritative
,
m
.
RecursionAvailable
=
true
,
true
m
.
Authoritative
,
m
.
RecursionAvailable
=
true
,
true
var
result
file
.
Result
var
result
file
.
Result
for
_
,
hostedZone
:=
range
z
{
h
.
zMu
.
RLock
()
h
.
zMu
.
RLock
()
m
.
Answer
,
m
.
Ns
,
m
.
Extra
,
result
=
z
.
z
.
Lookup
(
state
,
qname
)
m
.
Answer
,
m
.
Ns
,
m
.
Extra
,
result
=
hostedZone
.
z
.
Lookup
(
state
,
qname
)
h
.
zMu
.
RUnlock
()
h
.
zMu
.
RUnlock
()
if
len
(
m
.
Answer
)
!=
0
{
break
}
}
if
len
(
m
.
Answer
)
==
0
&&
h
.
Fall
.
Through
(
qname
)
{
if
len
(
m
.
Answer
)
==
0
&&
h
.
Fall
.
Through
(
qname
)
{
return
plugin
.
NextOrFailure
(
h
.
Name
(),
h
.
Next
,
ctx
,
w
,
r
)
return
plugin
.
NextOrFailure
(
h
.
Name
(),
h
.
Next
,
ctx
,
w
,
r
)
...
@@ -146,17 +162,17 @@ func (h *Route53) updateZones(ctx context.Context) error {
...
@@ -146,17 +162,17 @@ func (h *Route53) updateZones(ctx context.Context) error {
errc
:=
make
(
chan
error
)
errc
:=
make
(
chan
error
)
defer
close
(
errc
)
defer
close
(
errc
)
for
zName
,
z
:=
range
h
.
zones
{
for
zName
,
z
:=
range
h
.
zones
{
go
func
(
zName
string
,
z
*
zone
)
{
go
func
(
zName
string
,
z
[]
*
zone
)
{
var
err
error
var
err
error
defer
func
()
{
defer
func
()
{
errc
<-
err
errc
<-
err
}()
}()
for
i
,
hostedZone
:=
range
z
{
newZ
:=
file
.
NewZone
(
zName
,
""
)
newZ
:=
file
.
NewZone
(
zName
,
""
)
newZ
.
Upstream
=
*
h
.
upstream
newZ
.
Upstream
=
*
h
.
upstream
in
:=
&
route53
.
ListResourceRecordSetsInput
{
in
:=
&
route53
.
ListResourceRecordSetsInput
{
HostedZoneId
:
aws
.
String
(
z
.
id
),
HostedZoneId
:
aws
.
String
(
hostedZone
.
id
),
}
}
err
=
h
.
client
.
ListResourceRecordSetsPagesWithContext
(
ctx
,
in
,
err
=
h
.
client
.
ListResourceRecordSetsPagesWithContext
(
ctx
,
in
,
func
(
out
*
route53
.
ListResourceRecordSetsOutput
,
last
bool
)
bool
{
func
(
out
*
route53
.
ListResourceRecordSetsOutput
,
last
bool
)
bool
{
...
@@ -169,13 +185,14 @@ func (h *Route53) updateZones(ctx context.Context) error {
...
@@ -169,13 +185,14 @@ func (h *Route53) updateZones(ctx context.Context) error {
return
true
return
true
})
})
if
err
!=
nil
{
if
err
!=
nil
{
err
=
fmt
.
Errorf
(
"failed to list resource records for %v:%v from route53: %v"
,
zName
,
z
.
id
,
err
)
err
=
fmt
.
Errorf
(
"failed to list resource records for %v:%v from route53: %v"
,
zName
,
hostedZone
.
id
,
err
)
return
return
}
}
h
.
zMu
.
Lock
()
h
.
zMu
.
Lock
()
z
.
z
=
newZ
(
*
z
[
i
])
.
z
=
newZ
h
.
zMu
.
Unlock
()
h
.
zMu
.
Unlock
()
}
}(
zName
,
z
)
}(
zName
,
z
)
}
}
// Collect errors (if any). This will also sync on all zones updates
// Collect errors (if any). This will also sync on all zones updates
...
...
plugin/route53/route53_test.go
View file @
8432f142
...
@@ -31,19 +31,26 @@ func (fakeRoute53) ListResourceRecordSetsPagesWithContext(_ aws.Context, in *rou
...
@@ -31,19 +31,26 @@ func (fakeRoute53) ListResourceRecordSetsPagesWithContext(_ aws.Context, in *rou
if
aws
.
StringValue
(
in
.
HostedZoneId
)
==
"0987654321"
{
if
aws
.
StringValue
(
in
.
HostedZoneId
)
==
"0987654321"
{
return
errors
.
New
(
"bad. zone is bad"
)
return
errors
.
New
(
"bad. zone is bad"
)
}
}
var
rrs
[]
*
route53
.
ResourceRecordSet
rrsResponse
:=
map
[
string
][]
*
route53
.
ResourceRecordSet
{}
for
_
,
r
:=
range
[]
struct
{
for
_
,
r
:=
range
[]
struct
{
rType
,
name
,
value
string
rType
,
name
,
value
,
hostedZoneID
string
}{
}{
{
"A"
,
"example.org."
,
"1.2.3.4"
},
{
"A"
,
"example.org."
,
"1.2.3.4"
,
"1234567890"
},
{
"AAAA"
,
"example.org."
,
"2001:db8:85a3::8a2e:370:7334"
},
{
"AAAA"
,
"example.org."
,
"2001:db8:85a3::8a2e:370:7334"
,
"1234567890"
},
{
"CNAME"
,
"sample.example.org."
,
"example.org"
},
{
"CNAME"
,
"sample.example.org."
,
"example.org"
,
"1234567890"
},
{
"PTR"
,
"example.org."
,
"ptr.example.org."
},
{
"PTR"
,
"example.org."
,
"ptr.example.org."
,
"1234567890"
},
{
"SOA"
,
"org."
,
"ns-1536.awsdns-00.co.uk. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400"
},
{
"SOA"
,
"org."
,
"ns-1536.awsdns-00.co.uk. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400"
,
"1234567890"
},
{
"NS"
,
"com."
,
"ns-1536.awsdns-00.co.uk."
},
{
"NS"
,
"com."
,
"ns-1536.awsdns-00.co.uk."
,
"1234567890"
},
// Unsupported type should be ignored.
// Unsupported type should be ignored.
{
"YOLO"
,
"swag."
,
"foobar"
},
{
"YOLO"
,
"swag."
,
"foobar"
,
"1234567890"
},
// hosted zone with the same name, but a different id
{
"A"
,
"other-example.org."
,
"3.5.7.9"
,
"1357986420"
},
{
"SOA"
,
"org."
,
"ns-15.awsdns-00.co.uk. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400"
,
"1357986420"
},
}
{
}
{
rrs
,
ok
:=
rrsResponse
[
r
.
hostedZoneID
]
if
!
ok
{
rrs
=
make
([]
*
route53
.
ResourceRecordSet
,
0
)
}
rrs
=
append
(
rrs
,
&
route53
.
ResourceRecordSet
{
Type
:
aws
.
String
(
r
.
rType
),
rrs
=
append
(
rrs
,
&
route53
.
ResourceRecordSet
{
Type
:
aws
.
String
(
r
.
rType
),
Name
:
aws
.
String
(
r
.
name
),
Name
:
aws
.
String
(
r
.
name
),
ResourceRecords
:
[]
*
route53
.
ResourceRecord
{
ResourceRecords
:
[]
*
route53
.
ResourceRecord
{
...
@@ -53,9 +60,11 @@ func (fakeRoute53) ListResourceRecordSetsPagesWithContext(_ aws.Context, in *rou
...
@@ -53,9 +60,11 @@ func (fakeRoute53) ListResourceRecordSetsPagesWithContext(_ aws.Context, in *rou
},
},
TTL
:
aws
.
Int64
(
300
),
TTL
:
aws
.
Int64
(
300
),
})
})
rrsResponse
[
r
.
hostedZoneID
]
=
rrs
}
}
if
ok
:=
fn
(
&
route53
.
ListResourceRecordSetsOutput
{
if
ok
:=
fn
(
&
route53
.
ListResourceRecordSetsOutput
{
ResourceRecordSets
:
rrs
,
ResourceRecordSets
:
rrs
Response
[
aws
.
StringValue
(
in
.
HostedZoneId
)]
,
},
true
);
!
ok
{
},
true
);
!
ok
{
return
errors
.
New
(
"paging function return false"
)
return
errors
.
New
(
"paging function return false"
)
}
}
...
@@ -65,7 +74,7 @@ func (fakeRoute53) ListResourceRecordSetsPagesWithContext(_ aws.Context, in *rou
...
@@ -65,7 +74,7 @@ func (fakeRoute53) ListResourceRecordSetsPagesWithContext(_ aws.Context, in *rou
func
TestRoute53
(
t
*
testing
.
T
)
{
func
TestRoute53
(
t
*
testing
.
T
)
{
ctx
:=
context
.
Background
()
ctx
:=
context
.
Background
()
r
,
err
:=
New
(
ctx
,
fakeRoute53
{},
map
[
string
]
string
{
"bad."
:
"0987654321"
},
&
upstream
.
Upstream
{})
r
,
err
:=
New
(
ctx
,
fakeRoute53
{},
map
[
string
]
[]
string
{
"bad."
:
[]
string
{
"0987654321"
}
},
&
upstream
.
Upstream
{})
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatalf
(
"Failed to create Route53: %v"
,
err
)
t
.
Fatalf
(
"Failed to create Route53: %v"
,
err
)
}
}
...
@@ -73,7 +82,7 @@ func TestRoute53(t *testing.T) {
...
@@ -73,7 +82,7 @@ func TestRoute53(t *testing.T) {
t
.
Fatalf
(
"Expected errors for zone bad."
)
t
.
Fatalf
(
"Expected errors for zone bad."
)
}
}
r
,
err
=
New
(
ctx
,
fakeRoute53
{},
map
[
string
]
string
{
"org."
:
"1234567890"
,
"gov."
:
"Z098765432"
},
&
upstream
.
Upstream
{})
r
,
err
=
New
(
ctx
,
fakeRoute53
{},
map
[
string
]
[]
string
{
"org."
:
[]
string
{
"1357986420"
,
"1234567890"
},
"gov"
:
[]
string
{
"Z098765432"
}
},
&
upstream
.
Upstream
{})
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatalf
(
"Failed to create Route53: %v"
,
err
)
t
.
Fatalf
(
"Failed to create Route53: %v"
,
err
)
}
}
...
@@ -158,7 +167,7 @@ func TestRoute53(t *testing.T) {
...
@@ -158,7 +167,7 @@ func TestRoute53(t *testing.T) {
qname
:
"example.org"
,
qname
:
"example.org"
,
qtype
:
dns
.
TypeSOA
,
qtype
:
dns
.
TypeSOA
,
expectedCode
:
dns
.
RcodeSuccess
,
expectedCode
:
dns
.
RcodeSuccess
,
wantAnswer
:
[]
string
{
"org. 300 IN SOA ns-15
36
.awsdns-00.co.uk. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400"
},
wantAnswer
:
[]
string
{
"org. 300 IN SOA ns-15.awsdns-00.co.uk. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400"
},
},
},
// 6. Explicit SOA query for example.org.
// 6. Explicit SOA query for example.org.
{
{
...
@@ -187,6 +196,13 @@ func TestRoute53(t *testing.T) {
...
@@ -187,6 +196,13 @@ func TestRoute53(t *testing.T) {
expectedCode
:
dns
.
RcodeSuccess
,
expectedCode
:
dns
.
RcodeSuccess
,
wantAnswer
:
[]
string
{
"example.gov. 300 IN A 2.4.6.8"
},
wantAnswer
:
[]
string
{
"example.gov. 300 IN A 2.4.6.8"
},
},
},
// 10. other-zone.example.org is stored in a different hosted zone. success
{
qname
:
"other-example.org"
,
qtype
:
dns
.
TypeA
,
expectedCode
:
dns
.
RcodeSuccess
,
wantAnswer
:
[]
string
{
"other-example.org. 300 IN A 3.5.7.9"
},
},
}
}
for
ti
,
tc
:=
range
tests
{
for
ti
,
tc
:=
range
tests
{
...
...
plugin/route53/setup.go
View file @
8432f142
...
@@ -35,7 +35,8 @@ func init() {
...
@@ -35,7 +35,8 @@ func init() {
}
}
func
setup
(
c
*
caddy
.
Controller
,
f
func
(
*
credentials
.
Credentials
)
route53iface
.
Route53API
)
error
{
func
setup
(
c
*
caddy
.
Controller
,
f
func
(
*
credentials
.
Credentials
)
route53iface
.
Route53API
)
error
{
keys
:=
map
[
string
]
string
{}
keyPairs
:=
map
[
string
]
struct
{}{}
keys
:=
map
[
string
][]
string
{}
// Route53 plugin attempts to find AWS credentials by using ChainCredentials.
// Route53 plugin attempts to find AWS credentials by using ChainCredentials.
// And the order of that provider chain is as follows:
// And the order of that provider chain is as follows:
...
@@ -56,14 +57,16 @@ func setup(c *caddy.Controller, f func(*credentials.Credentials) route53iface.Ro
...
@@ -56,14 +57,16 @@ func setup(c *caddy.Controller, f func(*credentials.Credentials) route53iface.Ro
if
len
(
parts
)
!=
2
{
if
len
(
parts
)
!=
2
{
return
c
.
Errf
(
"invalid zone '%s'"
,
args
[
i
])
return
c
.
Errf
(
"invalid zone '%s'"
,
args
[
i
])
}
}
if
parts
[
0
]
==
""
||
parts
[
1
]
==
""
{
dns
,
hostedZoneID
:=
parts
[
0
],
parts
[
1
]
if
dns
==
""
||
hostedZoneID
==
""
{
return
c
.
Errf
(
"invalid zone '%s'"
,
args
[
i
])
return
c
.
Errf
(
"invalid zone '%s'"
,
args
[
i
])
}
}
zone
:=
plugin
.
Host
(
parts
[
0
])
.
Normalize
()
if
_
,
ok
:=
keyPairs
[
args
[
i
]];
ok
{
if
v
,
ok
:=
keys
[
zone
];
ok
&&
v
!=
parts
[
1
]
{
return
c
.
Errf
(
"conflict zone '%s'"
,
args
[
i
])
return
c
.
Errf
(
"conflict zone '%s' ('%s' vs. '%s')"
,
zone
,
v
,
parts
[
1
])
}
}
keys
[
zone
]
=
parts
[
1
]
keyPairs
[
args
[
i
]]
=
struct
{}{}
keys
[
dns
]
=
append
(
keys
[
dns
],
hostedZoneID
)
}
}
for
c
.
NextBlock
()
{
for
c
.
NextBlock
()
{
...
...
plugin/route53/setup_test.go
View file @
8432f142
...
@@ -53,6 +53,10 @@ func TestSetupRoute53(t *testing.T) {
...
@@ -53,6 +53,10 @@ func TestSetupRoute53(t *testing.T) {
aws_access_key ACCESS_KEY_ID SEKRIT_ACCESS_KEY
aws_access_key ACCESS_KEY_ID SEKRIT_ACCESS_KEY
upstream 1.2.3.4
upstream 1.2.3.4
}`
)
}`
)
if
err
:=
setup
(
c
,
f
);
err
!=
nil
{
t
.
Fatalf
(
"Unexpected errors: %v"
,
err
)
}
c
=
caddy
.
NewTestController
(
"dns"
,
`route53 example.org:12345678 {
c
=
caddy
.
NewTestController
(
"dns"
,
`route53 example.org:12345678 {
fallthrough
fallthrough
}`
)
}`
)
...
@@ -91,4 +95,17 @@ func TestSetupRoute53(t *testing.T) {
...
@@ -91,4 +95,17 @@ func TestSetupRoute53(t *testing.T) {
if
err
:=
setup
(
c
,
f
);
err
==
nil
{
if
err
:=
setup
(
c
,
f
);
err
==
nil
{
t
.
Fatalf
(
"Expected errors, but got: %v"
,
err
)
t
.
Fatalf
(
"Expected errors, but got: %v"
,
err
)
}
}
c
=
caddy
.
NewTestController
(
"dns"
,
`route53 example.org:12345678 example.org:12345678 {
upstream 1.2.3.4
}`
)
if
err
:=
setup
(
c
,
f
);
err
==
nil
{
t
.
Fatalf
(
"Expected errors, but got: %v"
,
err
)
}
c
=
caddy
.
NewTestController
(
"dns"
,
`route53 example.org {
upstream 1.2.3.4
}`
)
if
err
:=
setup
(
c
,
f
);
err
==
nil
{
t
.
Fatalf
(
"Expected errors, but got: %v"
,
err
)
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment