feat: dnssec load keys from AWS Secrets Manager (#6618)
feat: dnssec load keys from AWS Secrets Manager Signed-off-by: kcolemangt <20099734+kcolemangt@users.noreply.github.com>
This commit is contained in:
parent
04d00b0083
commit
7078f1576f
5 changed files with 170 additions and 1 deletions
14
go.mod
14
go.mod
|
@ -60,6 +60,20 @@ require (
|
||||||
github.com/DataDog/go-tuf v1.1.0-0.5.2 // indirect
|
github.com/DataDog/go-tuf v1.1.0-0.5.2 // indirect
|
||||||
github.com/DataDog/sketches-go v1.4.5 // indirect
|
github.com/DataDog/sketches-go v1.4.5 // indirect
|
||||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2 v1.31.0 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.27.39 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.37 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.33.3 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.23.3 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.3 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.31.3 // indirect
|
||||||
|
github.com/aws/smithy-go v1.21.0 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/coreos/go-semver v0.3.0 // indirect
|
github.com/coreos/go-semver v0.3.0 // indirect
|
||||||
|
|
36
go.sum
36
go.sum
|
@ -55,6 +55,42 @@ github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/Y
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
|
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
|
||||||
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||||
|
github.com/aws/aws-sdk-go-v2 v1.31.0 h1:3V05LbxTSItI5kUqNwhJrrrY1BAXxXt0sN0l72QmG5U=
|
||||||
|
github.com/aws/aws-sdk-go-v2 v1.31.0/go.mod h1:ztolYtaEUtdpf9Wftr31CJfLVjOnD/CVRkKOOYgF8hA=
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.27.39 h1:FCylu78eTGzW1ynHcongXK9YHtoXD5AiiUqq3YfJYjU=
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.27.39/go.mod h1:wczj2hbyskP4LjMKBEZwPRO1shXY+GsQleab+ZXT2ik=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.11/go.mod h1:AQtFPsDH9bI2O+71anW6EKL+NcD7LG3dpKGMV4SShgo=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.37 h1:G2aOH01yW8X373JK419THj5QVqu9vKEwxSEsGxihoW0=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.37/go.mod h1:0ecCjlb7htYCptRD45lXJ6aJDQac6D2NlKGpZqyTG6A=
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1/go.mod h1:zusuAeqezXzAB24LGuzuekqMAEgWkVYukBec3kr3jUg=
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 h1:C/d03NAmh8C4BZXhuRNboF/DqhBkBCeDiJDcaqIT5pA=
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14/go.mod h1:7I0Ju7p9mCIdlrfS+JCgqcYD0VXz/N4yozsox+0o078=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 h1:kYQ3H1u0ANr9KEKlGs/jTLrBFPo8P8NaH/w7A01NeeM=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18/go.mod h1:r506HmK5JDUh9+Mw4CfGJGSSoqIiLCndAuqXuhbv67Y=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 h1:Z7IdFUONvTcvS7YuhtVxN99v2cCoHRXOS4mTr0B/pUc=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18/go.mod h1:DkKMmksZVVyat+Y+r1dEOgJEfUeA7UngIHWeKsi0yNc=
|
||||||
|
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/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
|
||||||
|
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/accept-encoding v1.11.5 h1:QFASJGfT8wMXtuP3D5CRmMjARHv9ZmzFUMJznHDOY3w=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5/go.mod h1:QdZ3OmoIjSX+8D1OPAzPxDfjXASbBMDsz9qvtyIhtik=
|
||||||
|
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/internal/presigned-url v1.11.20 h1:Xbwbmk44URTiHNx6PNo0ujDE6ERlsCKJD3u1zfnzAPg=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20/go.mod h1:oAfOFzUB14ltPZj1rWwRc3d/6OgD76R8KlvU3EqM9Fg=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.33.3 h1:W2M3kQSuN1+FXgV2wMv1JMWPxw/37wBN87QHYDuTV0Y=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.33.3/go.mod h1:WyLS5qwXHtjKAONYZq/4ewdd+hcVsa3LBu77Ow5uj3k=
|
||||||
|
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/sso v1.23.3 h1:rs4JCczF805+FDv2tRhZ1NU0RB2H6ryAvsWPanAr72Y=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.23.3/go.mod h1:XRlMvmad0ZNL+75C5FYdMvbbLkd6qiqz6foR1nA1PXY=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.4/go.mod h1:mUYPBhaF2lGiukDEjJX2BLRRKTmoUSitGDUgM4tRxak=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.3 h1:S7EPdMVZod8BGKQQPTBK+FcX9g7bKR7c4+HxWqHP7Vg=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.3/go.mod h1:FnvDM4sfa+isJ3kDXIzAB9GAwVSzFzSy97uZ3IsHo4E=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.28.6/go.mod h1:FZf1/nKNEkHdGGJP/cI2MoIMquumuRK6ol3QQJNDxmw=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.31.3 h1:VzudTFrDCIDakXtemR7l6Qzt2+JYsVqo2MxBPt5k8T8=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.31.3/go.mod h1:yMWe0F+XG0DkRZK5ODZhG7BEFYhLXi2dqGsv6tX0cgI=
|
||||||
|
github.com/aws/smithy-go v1.21.0 h1:H7L8dtDRk0P1Qm6y0ji7MCYMQObJ5R9CRpyPhRUkLYA=
|
||||||
|
github.com/aws/smithy-go v1.21.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
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/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
|
|
|
@ -16,7 +16,7 @@ This plugin can only be used once per Server Block.
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
dnssec [ZONES... ] {
|
dnssec [ZONES... ] {
|
||||||
key file KEY...
|
key file|aws_secretsmanager KEY...
|
||||||
cache_capacity CAPACITY
|
cache_capacity CAPACITY
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
@ -49,6 +49,26 @@ used.
|
||||||
* generated public key `Kexample.org+013+45330.key`
|
* generated public key `Kexample.org+013+45330.key`
|
||||||
* generated private key `Kexample.org+013+45330.private`
|
* generated private key `Kexample.org+013+45330.private`
|
||||||
|
|
||||||
|
* `key aws_secretsmanager` 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 aws_secretsmanager` directive.
|
||||||
|
|
||||||
|
[AWS SDK for Go V2](https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/#specifying-credentials) is used
|
||||||
|
for authentication with AWS Secrets Manager. Make sure the provided AWS credentials have the necessary
|
||||||
|
permissions (e.g., `secretsmanager:GetSecretValue`) to access the specified secrets in AWS Secrets Manager.
|
||||||
|
|
||||||
* `cache_capacity` indicates the capacity of the cache. The dnssec plugin uses a cache to store
|
* `cache_capacity` indicates the capacity of the cache. The dnssec plugin uses a cache to store
|
||||||
RRSIGs. The default for **CAPACITY** is 10000.
|
RRSIGs. The default for **CAPACITY** is 10000.
|
||||||
|
|
||||||
|
@ -75,6 +95,18 @@ example.org {
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
|
Sign responses for `example.org` with the key stored in AWS Secrets Manager under the secret name
|
||||||
|
"Kexample.org.+013+45330".
|
||||||
|
|
||||||
|
~~~
|
||||||
|
example.org {
|
||||||
|
dnssec {
|
||||||
|
key aws_secretsmanager Kexample.org.+013+45330
|
||||||
|
}
|
||||||
|
whoami
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
Sign responses for a kubernetes zone with the key "Kcluster.local+013+45129.key".
|
Sign responses for a kubernetes zone with the key "Kcluster.local+013+45129.key".
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
package dnssec
|
package dnssec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coredns/coredns/request"
|
"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"
|
"github.com/miekg/dns"
|
||||||
"golang.org/x/crypto/ed25519"
|
"golang.org/x/crypto/ed25519"
|
||||||
)
|
)
|
||||||
|
@ -23,6 +28,12 @@ type DNSKEY struct {
|
||||||
tag uint16
|
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
|
// 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.
|
// utilities. It adds ".key" for the public key and ".private" for the private key.
|
||||||
func ParseKeyFile(pubFile, privFile string) (*DNSKEY, error) {
|
func ParseKeyFile(pubFile, privFile string) (*DNSKEY, error) {
|
||||||
|
@ -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")
|
return &DNSKEY{K: dk, D: dk.ToDS(dns.SHA256), s: nil, tag: 0}, errors.New("no private key found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseKeyFromAWSSecretsManager retrieves and parses a DNSSEC key pair from AWS Secrets Manager.
|
||||||
|
func ParseKeyFromAWSSecretsManager(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.
|
// 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 {
|
func (d Dnssec) getDNSKEY(state request.Request, zone string, do bool, server string) *dns.Msg {
|
||||||
keys := make([]dns.RR, len(d.keys))
|
keys := make([]dns.RR, len(d.keys))
|
||||||
|
|
|
@ -141,6 +141,19 @@ func keyParse(c *caddy.Controller) ([]*DNSKEY, error) {
|
||||||
}
|
}
|
||||||
keys = append(keys, k)
|
keys = append(keys, k)
|
||||||
}
|
}
|
||||||
|
} else if value == "aws_secretsmanager" {
|
||||||
|
ks := c.RemainingArgs()
|
||||||
|
if len(ks) == 0 {
|
||||||
|
return nil, c.ArgErr()
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, k := range ks {
|
||||||
|
k, err := ParseKeyFromAWSSecretsManager(k)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return keys, nil
|
return keys, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue