Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: dnssec load keys from AWS Secrets Manager #6618

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ require (
github.com/antonmedv/expr v1.15.5
github.com/apparentlymart/go-cidr v1.1.0
github.com/aws/aws-sdk-go v1.51.16
github.com/aws/aws-sdk-go-v2/config v1.27.11
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.6
github.com/coredns/caddy v1.1.1
github.com/dnstap/golang-dnstap v0.4.0
github.com/farsightsec/golang-framestream v0.3.0
Expand Down Expand Up @@ -58,6 +60,18 @@ require (
github.com/DataDog/go-tuf v1.0.2-0.5.2 // indirect
github.com/DataDog/sketches-go v1.4.2 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/aws/aws-sdk-go-v2 v1.26.1 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.11 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 // indirect
github.com/aws/smithy-go v1.20.2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
Expand Down
28 changes: 28 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,34 @@ github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4t
github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
github.com/aws/aws-sdk-go v1.51.16 h1:vnWKK8KjbftEkuPX8bRj3WHsLy1uhotn0eXptpvrxJI=
github.com/aws/aws-sdk-go v1.51.16/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA=
github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
github.com/aws/aws-sdk-go-v2/config v1.27.11 h1:f47rANd2LQEYHda2ddSCKYId18/8BhSRM4BULGmfgNA=
github.com/aws/aws-sdk-go-v2/config v1.27.11/go.mod h1:SMsV78RIOYdve1vf36z8LmnszlRWkwMQtomCAI0/mIE=
github.com/aws/aws-sdk-go-v2/credentials v1.17.11 h1:YuIB1dJNf1Re822rriUOTxopaHHvIq0l/pX3fwO+Tzs=
github.com/aws/aws-sdk-go-v2/credentials v1.17.11/go.mod h1:AQtFPsDH9bI2O+71anW6EKL+NcD7LG3dpKGMV4SShgo=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 h1:FVJ0r5XTHSmIHJV6KuDmdYhEpvlHpiSd38RQWhut5J4=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1/go.mod h1:zusuAeqezXzAB24LGuzuekqMAEgWkVYukBec3kr3jUg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5/go.mod h1:FSaRudD0dXiMPK2UjknVwwTYyZMRsHv3TtkabsZih5I=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 h1:PG1F3OD1szkuQPzDw3CIQsRIrtTlUC3lP84taWzHlq0=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5/go.mod h1:jU1li6RFryMz+so64PpKtudI+QzbKoIEivqdf6LNpOc=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 h1:ogRAwT1/gxJBcSWDMZlgyFUM962F51A5CRhDLbxLdmo=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7/go.mod h1:YCsIZhXfRPLFFCl5xxY+1T9RKzOKjCut+28JSX2DnAk=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.6 h1:TIOEjw0i2yyhmhRry3Oeu9YtiiHWISZ6j/irS1W3gX4=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.6/go.mod h1:3Ba++UwWd154xtP4FRX5pUK3Gt4up5sDHCve6kVfE+g=
github.com/aws/aws-sdk-go-v2/service/sso v1.20.5 h1:vN8hEbpRnL7+Hopy9dzmRle1xmDc7o8tmY0klsr175w=
github.com/aws/aws-sdk-go-v2/service/sso v1.20.5/go.mod h1:qGzynb/msuZIE8I75DVRCUXw3o3ZyBmUvMwQ2t/BrGM=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4 h1:Jux+gDDyi1Lruk+KHF91tK2KCuY61kzoCpvtvJJBtOE=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4/go.mod h1:mUYPBhaF2lGiukDEjJX2BLRRKTmoUSitGDUgM4tRxak=
github.com/aws/aws-sdk-go-v2/service/sts v1.28.6 h1:cwIxeBttqPN3qkaAjcEcsh8NYr8n2HZPkcKgPAi1phU=
github.com/aws/aws-sdk-go-v2/service/sts v1.28.6/go.mod h1:FZf1/nKNEkHdGGJP/cI2MoIMquumuRK6ol3QQJNDxmw=
github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q=
github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
Expand Down
29 changes: 28 additions & 1 deletion plugin/dnssec/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ This plugin can only be used once per Server Block.

~~~
dnssec [ZONES... ] {
key file KEY...
key file|secret KEY...
cache_capacity CAPACITY
}
~~~
Expand Down Expand Up @@ -49,6 +49,22 @@ used.
* generated public key `Kexample.org+013+45330.key`
* generated private key `Kexample.org+013+45330.private`

* `key secret` indicates that **KEY** secret(s) should be read from AWS Secrets Manager. Secret names or
ARNs may be used. After generating the keys as described in the `key file` section, you can store them
in AWS Secrets Manager using the following AWS CLI v2 command:

```sh
aws secretsmanager create-secret --name "Kexample.org.+013+45330" \
--description "DNSSEC keys for example.org" \
--secret-string "$(jq -n --arg key "$(cat Kexample.org.+013+45330.key)" \
--arg private "$(cat Kexample.org.+013+45330.private)" \
'{key: $key, private: $private}')"
```

This command reads the contents of the `.key` and `.private` files, constructs a JSON object, and stores it
as a new secret in AWS Secrets Manager with the specified name and description. CoreDNS will then fetch
the key data from AWS Secrets Manager when using the `key secret` directive.

* `cache_capacity` indicates the capacity of the cache. The dnssec plugin uses a cache to store
RRSIGs. The default for **CAPACITY** is 10000.

Expand All @@ -75,6 +91,17 @@ example.org {
}
~~~

Sign responses for `example.org` with the key stored in AWS Secrets Manager under the secret name "Kexample.org.+013+45330".

~~~ corefile
example.org {
dnssec {
key secret Kexample.org.+013+45330
}
whoami
}
~~~

Sign responses for a kubernetes zone with the key "Kcluster.local+013+45129.key".

~~~
Expand Down
74 changes: 74 additions & 0 deletions plugin/dnssec/dnskey.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
package dnssec

import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"encoding/json"
"errors"
"os"
"path/filepath"
"strings"
"time"

"github.com/coredns/coredns/request"

"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
"github.com/miekg/dns"
"golang.org/x/crypto/ed25519"
)
Expand All @@ -23,6 +28,12 @@ type DNSKEY struct {
tag uint16
}

// SecretKeyData represents the structure of the DNS keys stored in AWS Secrets Manager.
type SecretKeyData struct {
Key string `json:"key"`
Private string `json:"private"`
}

// ParseKeyFile read a DNSSEC keyfile as generated by dnssec-keygen or other
// utilities. It adds ".key" for the public key and ".private" for the private key.
func ParseKeyFile(pubFile, privFile string) (*DNSKEY, error) {
Expand Down Expand Up @@ -63,6 +74,69 @@ func ParseKeyFile(pubFile, privFile string) (*DNSKEY, error) {
return &DNSKEY{K: dk, D: dk.ToDS(dns.SHA256), s: nil, tag: 0}, errors.New("no private key found")
}

// ParseKeySecret retrieves and parses a DNSSEC key pair from AWS Secrets Manager.
func ParseKeySecret(secretID string) (*DNSKEY, error) {
// Load the AWS SDK configuration
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
return nil, err
}

// Create a Secrets Manager client
client := secretsmanager.NewFromConfig(cfg)

// Retrieve the secret value
input := &secretsmanager.GetSecretValueInput{
SecretId: &secretID,
}
result, err := client.GetSecretValue(context.TODO(), input)
if err != nil {
return nil, err
}

// Parse the secret string into SecretKeyData
var secretData SecretKeyData
err = json.Unmarshal([]byte(*result.SecretString), &secretData)
if err != nil {
return nil, err
}

// Parse the public key
rr, err := dns.NewRR(secretData.Key)
if err != nil {
return nil, err
}
dk, ok := rr.(*dns.DNSKEY)
if !ok {
return nil, errors.New("invalid public key format")
}

// Parse the private key
p, err := dk.ReadPrivateKey(strings.NewReader(secretData.Private), secretID)
if err != nil {
return nil, err
}

// Create the DNSKEY structure
var s crypto.Signer
var tag uint16
switch key := p.(type) {
case *rsa.PrivateKey:
s = key
tag = dk.KeyTag()
case *ecdsa.PrivateKey:
s = key
tag = dk.KeyTag()
case ed25519.PrivateKey:
s = key
tag = dk.KeyTag()
default:
return nil, errors.New("unsupported key type")
}

return &DNSKEY{K: dk, D: dk.ToDS(dns.SHA256), s: s, tag: tag}, nil
}

// getDNSKEY returns the correct DNSKEY to the client. Signatures are added when do is true.
func (d Dnssec) getDNSKEY(state request.Request, zone string, do bool, server string) *dns.Msg {
keys := make([]dns.RR, len(d.keys))
Expand Down
13 changes: 13 additions & 0 deletions plugin/dnssec/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,19 @@ func keyParse(c *caddy.Controller) ([]*DNSKEY, error) {
}
keys = append(keys, k)
}
} else if value == "secret" {
ks := c.RemainingArgs()
if len(ks) == 0 {
return nil, c.ArgErr()
}

for _, k := range ks {
k, err := ParseKeySecret(k)
if err != nil {
return nil, err
}
keys = append(keys, k)
}
}
return keys, nil
}
Loading