Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
plugin/sign: a plugin that signs zones
Sign is a plugin that signs zone data (on disk). The README.md details what exactly happens to should be accurate related to the code. Signs are signed with a CSK, resigning and first time signing is all handled by *sign* plugin. Signed-off-by: Miek Gieben <miek@miek.nl>
- Loading branch information
Showing
22 changed files
with
948 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -49,4 +49,5 @@ var Directives = []string{ | |
"erratic", | ||
"whoami", | ||
"on", | ||
"sign", | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -58,3 +58,4 @@ grpc:grpc | |
erratic:erratic | ||
whoami:whoami | ||
on:github.com/caddyserver/caddy/onevent | ||
sign:sign |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
# sign | ||
|
||
## Name | ||
|
||
*sign* - add DNSSEC records to zone files. | ||
|
||
## Description | ||
|
||
The *sign* plugin is used to sign (see RFC 6781) zones. In this process DNSSEC resource records | ||
are added. The signatures have a expiration date, so the signing process must be repeated every so | ||
often, otherwise the zone's data will go BAD (RFC 4035, Section 5.5). The *sign* plugin takes care | ||
of this. | ||
|
||
Only NSEC is supported, *sign* does not support NSEC3. | ||
|
||
*Signs* works in conjunction with the *file* and *auto* plugins; this plugin **signs** the zones | ||
*files*, *auto* and *file* **serve** the zones *data*. | ||
|
||
For this plugin to work at least one Common Signing Key, (see coredns-keygen(1)) is needed. This key | ||
(or keys) will be used to sign the entire zone. *Sign* does not support the ZSK/KSK split, nor will | ||
it do key rollovers - it just signs. | ||
|
||
*Sign* will: | ||
|
||
* (Re)-sign the zone with the CSK(s) every Thursday at 15:00 UTC (- generous jitter). | ||
The jitter will be applied to avoid a stampeding herd of zones waiting to be signed. | ||
This default to random amount between -5 to 0 days. | ||
* Create signatures that have an inception of -3H and expiration of +3W for every key given. | ||
* Add or replace *all* apex CDS/CDNSKEY records with the ones derived from the given keys. For each | ||
key two CDS are created one with SHA1 and another with SHA256. | ||
* Update the SOA's serial number to the *Unix epoch* of when the signing happens. This will | ||
overwrite *any* previous serial number. | ||
|
||
Keys are named (following BIND9): `K<name>+<alg>+<id>.key` and `K<name>+<alg>+<id>.private`. | ||
The keys **must not** be included in your zone; they will be added by *sign*. These keys can be | ||
generated with `coredns-keygen` or BIND9's `dnssec-keygen`. You don't have to adhere to this naming | ||
scheme, but then you need to name your keys explicitly, see the `keys files` directive. | ||
|
||
A generated zone is written out in a file named `db.<name>.signed`. | ||
|
||
When CoreDNS starts up (or is reloaded) a quick scan is done to see if the zone needs to be | ||
resigned; this happens by checking SOA's RRSIG expiration time. If within 2 weeks, the zone will be | ||
resigned. | ||
|
||
## Syntax | ||
|
||
~~~ | ||
sign DBFILE [ZONES...] { | ||
keys file|directory KEY...|DIR... | ||
directory DIR | ||
} | ||
~~~ | ||
|
||
* **DBFILE** the database file to read and parse. If the path is relative, the path from the | ||
*root* directive will be prepended to it. | ||
* **ZONES** zones it should be sign for. If empty, the zones from the configuration block are | ||
used. | ||
* `keys` specifies the keys (there can be multiple) to sign the zone. If `file` is | ||
used the **KEY**'s filenames are used as is. If `directory` is used, *sign* will look in **DIR** | ||
for `K<name>+<alg>+<id>` files. Any metadata in these files (Activate, Publish, etc.) is | ||
*ignored*. | ||
* `directory` specifies the **DIR** where CoreDNS should save zones that have been signed. | ||
If not given this defaults to `/var/lib/coredns`. The zones are saved under the name | ||
`db.<name>.signed`. If the path is relative the path from the *root* directive will be prepended | ||
to it. | ||
|
||
## Examples | ||
|
||
Sign the `example.org` zone contained in the file `db.example.org` and write to result to | ||
`/var/lib/db.example.org.signed` to let the *file* plugin pick it up and serve it. | ||
|
||
~~~ corefile | ||
example.org { | ||
file /var/lib/coredns/db.example.org.signed | ||
sign db.example.org { | ||
key file /etc/coredns/keys/Kexample.org | ||
} | ||
} | ||
~~~ | ||
|
||
Or use a single zone file for *multiple* zones, note that the **ZONES** are repeated for both plugins. | ||
Also note this outputs *multiple* signed output files. Here we use the default output directory | ||
`/var/lib/coredns`. | ||
|
||
~~~ corefile | ||
. { | ||
file /var/lib/coredns/db.example.org.signed example.org | ||
file /var/lib/coredns/db.example.net.signed example.net | ||
sign db.example.org example.org example.net { | ||
key directory /etc/coredns/keys | ||
} | ||
} | ||
~~~ | ||
|
||
This is the same configuration, but the zones are put in the server block, but note that you still | ||
need to specify what file is served for what zone in the *file* plugin: | ||
|
||
~~~ corefile | ||
example.org example.net { | ||
file var/lib/coredns/db.example.org.signed example.org | ||
file var/lib/coredns/db.example.net.signed example.net | ||
sign db.example.org { | ||
key directory /etc/coredns/keys | ||
} | ||
} | ||
~~~ | ||
|
||
## Also See | ||
|
||
The DNSSEC RFCs: RFC 4033, RFC 4034 and RFC 4035. And the BCP on DNSSEC, RFC 6781. Further more the | ||
manual pages coredns-keygen(1) and dnssec-keygen(8). And the *file* plugin's documentation. | ||
|
||
## Bugs | ||
|
||
`keys directory` is not implemented, nor is coredns-keygen. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
* glue, delegation | ||
* more tests, test nsec sorting, test zone signing apex only zone etc. | ||
resign test, key parse test | ||
* discard bad keys on default | ||
* empty-non-terminals |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package sign | ||
|
||
import ( | ||
"github.com/miekg/dns" | ||
) | ||
|
||
func (p Pair) signRRs(rrs []dns.RR, signerName string, ttl, incep, expir uint32) (*dns.RRSIG, error) { | ||
rrsig := &dns.RRSIG{ | ||
Hdr: dns.RR_Header{Rrtype: dns.TypeRRSIG, Ttl: ttl}, | ||
Algorithm: p.Public.Algorithm, | ||
SignerName: signerName, | ||
KeyTag: p.KeyTag, | ||
OrigTtl: ttl, | ||
Inception: incep, | ||
Expiration: expir, | ||
} | ||
|
||
e := rrsig.Sign(p.Private, rrs) | ||
return rrsig, e | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package sign | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/coredns/coredns/plugin/file" | ||
"github.com/coredns/coredns/plugin/file/tree" | ||
|
||
"github.com/miekg/dns" | ||
) | ||
|
||
// write writes out the zone file to a temporary file which is then move into the correct place. | ||
func (s Signer) write(z *file.Zone) error { | ||
f, err := ioutil.TempFile(s.directory, "signed-") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// need better sorting of these records, to make it slightly nicer | ||
fmt.Fprintln(f, z.Apex.SOA.String()) | ||
for _, rr := range z.Apex.SIGSOA { | ||
fmt.Fprintln(f, rr.String()) | ||
} | ||
for _, rr := range z.Apex.NS { | ||
fmt.Fprintln(f, rr.String()) | ||
} | ||
for _, rr := range z.Apex.SIGNS { | ||
fmt.Fprintln(f, rr.String()) | ||
} | ||
err = z.Walk(func(e *tree.Elem, _ map[uint16][]dns.RR) error { | ||
for _, r := range e.All() { | ||
fmt.Fprintln(f, r.String()) | ||
} | ||
return nil | ||
}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
f.Close() | ||
return os.Rename(f.Name(), filepath.Join(s.directory, s.signedfile)) | ||
} | ||
|
||
// Parse parses the zone in filename and returns a new Zone or an error. This | ||
// is similar to the Parse function in the *file* plugin. However when parsing the | ||
// record type RRSIG, DNSKEY, CDNSKEY and CDS are *not* included in the | ||
// returned zone (if encountered). | ||
func Parse(f io.Reader, origin, fileName string) (*file.Zone, error) { | ||
// make work on signer? | ||
zp := dns.NewZoneParser(f, dns.Fqdn(origin), fileName) | ||
zp.SetIncludeAllowed(true) | ||
z := file.NewZone(origin, fileName) | ||
seenSOA := false | ||
|
||
for rr, ok := zp.Next(); ok; rr, ok = zp.Next() { | ||
if err := zp.Err(); err != nil { | ||
return nil, err | ||
} | ||
|
||
switch rr.(type) { | ||
case *dns.RRSIG, *dns.DNSKEY, *dns.CDNSKEY, *dns.CDS: | ||
// drop | ||
case *dns.SOA: | ||
seenSOA = true | ||
if err := z.Insert(rr); err != nil { | ||
return nil, err | ||
} | ||
default: | ||
if err := z.Insert(rr); err != nil { | ||
return nil, err | ||
} | ||
} | ||
} | ||
if !seenSOA { | ||
return nil, fmt.Errorf("file %q has no SOA record", fileName) | ||
} | ||
|
||
return z, nil | ||
} |
Oops, something went wrong.