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/sketches-go v1.4.5 // 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/cespare/xxhash/v2 v2.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/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-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/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
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... ] {
|
||||
key file KEY...
|
||||
key file|aws_secretsmanager KEY...
|
||||
cache_capacity CAPACITY
|
||||
}
|
||||
~~~
|
||||
|
@ -49,6 +49,26 @@ used.
|
|||
* generated public key `Kexample.org+013+45330.key`
|
||||
* 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
|
||||
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".
|
||||
|
||||
~~~
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
@ -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) {
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (d Dnssec) getDNSKEY(state request.Request, zone string, do bool, server string) *dns.Msg {
|
||||
keys := make([]dns.RR, len(d.keys))
|
||||
|
|
|
@ -141,6 +141,19 @@ func keyParse(c *caddy.Controller) ([]*DNSKEY, error) {
|
|||
}
|
||||
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
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue