Skip to content

Commit 4b274f7

Browse files
committed
feat: support aws cert manager in imager
Add support for using certificates stored in AWS Certificate Manager to sign secureboot images in imager. Signed-off-by: Tim Jones <tim.jones@siderolabs.com>
1 parent 4172095 commit 4b274f7

File tree

6 files changed

+72
-12
lines changed

6 files changed

+72
-12
lines changed

go.mod

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ require (
5151
github.com/alexflint/go-filemutex v1.3.0
5252
github.com/aws/aws-sdk-go-v2/config v1.32.6
5353
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16
54+
github.com/aws/aws-sdk-go-v2/service/acm v1.37.19
5455
github.com/aws/aws-sdk-go-v2/service/kms v1.49.4
5556
github.com/aws/smithy-go v1.24.0
5657
github.com/beevik/ntp v1.5.0
@@ -214,10 +215,10 @@ require (
214215
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
215216
github.com/apparentlymart/go-cidr v1.1.0 // indirect
216217
github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 // indirect
217-
github.com/aws/aws-sdk-go-v2 v1.41.0 // indirect
218+
github.com/aws/aws-sdk-go-v2 v1.41.1 // indirect
218219
github.com/aws/aws-sdk-go-v2/credentials v1.19.6 // indirect
219-
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 // indirect
220-
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 // indirect
220+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 // indirect
221+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 // indirect
221222
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
222223
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect
223224
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 // indirect

go.sum

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,20 +61,22 @@ github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hC
6161
github.com/armon/go-proxyproto v0.0.0-20210323213023-7e956b284f0a/go.mod h1:QmP9hvJ91BbJmGVGSbutW19IC0Q9phDCLGaomwTJbgU=
6262
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
6363
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
64-
github.com/aws/aws-sdk-go-v2 v1.41.0 h1:tNvqh1s+v0vFYdA1xq0aOJH+Y5cRyZ5upu6roPgPKd4=
65-
github.com/aws/aws-sdk-go-v2 v1.41.0/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0=
64+
github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6cerhU=
65+
github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0=
6666
github.com/aws/aws-sdk-go-v2/config v1.32.6 h1:hFLBGUKjmLAekvi1evLi5hVvFQtSo3GYwi+Bx4lpJf8=
6767
github.com/aws/aws-sdk-go-v2/config v1.32.6/go.mod h1:lcUL/gcd8WyjCrMnxez5OXkO3/rwcNmvfno62tnXNcI=
6868
github.com/aws/aws-sdk-go-v2/credentials v1.19.6 h1:F9vWao2TwjV2MyiyVS+duza0NIRtAslgLUM0vTA1ZaE=
6969
github.com/aws/aws-sdk-go-v2/credentials v1.19.6/go.mod h1:SgHzKjEVsdQr6Opor0ihgWtkWdfRAIwxYzSJ8O85VHY=
7070
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 h1:80+uETIWS1BqjnN9uJ0dBUaETh+P1XwFy5vwHwK5r9k=
7171
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16/go.mod h1:wOOsYuxYuB/7FlnVtzeBYRcjSRtQpAW0hCP7tIULMwo=
72-
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 h1:rgGwPzb82iBYSvHMHXc8h9mRoOUBZIGFgKb9qniaZZc=
73-
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16/go.mod h1:L/UxsGeKpGoIj6DxfhOWHWQ/kGKcd4I1VncE4++IyKA=
74-
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 h1:1jtGzuV7c82xnqOVfx2F0xmJcOw5374L7N6juGW6x6U=
75-
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16/go.mod h1:M2E5OQf+XLe+SZGmmpaI2yy+J326aFf6/+54PoxSANc=
72+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 h1:xOLELNKGp2vsiteLsvLPwxC+mYmO6OZ8PYgiuPJzF8U=
73+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17/go.mod h1:5M5CI3D12dNOtH3/mk6minaRwI2/37ifCURZISxA/IQ=
74+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 h1:WWLqlh79iO48yLkj1v3ISRNiv+3KdQoZ6JWyfcsyQik=
75+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17/go.mod h1:EhG22vHRrvF8oXSTYStZhJc1aUgKtnJe+aOiFEV90cM=
7676
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
7777
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
78+
github.com/aws/aws-sdk-go-v2/service/acm v1.37.19 h1:6BPfgg/Y4Pmrdr8KDwHx2CYkw8qPEaGQ+aixjuAY/0U=
79+
github.com/aws/aws-sdk-go-v2/service/acm v1.37.19/go.mod h1:mhOStWeEa1xP99WNNPstX75qgqWgJycL5H7UwZQbqbo=
7880
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E=
7981
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow=
8082
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 h1:oHjJHeUy0ImIV0bsrX0X91GkV5nJAyv1l1CC9lnO0TI=

pkg/imager/profile/input.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,12 @@ type SigningKeyAndCertificate struct {
111111
AzureCertificateID string `yaml:"azureCertificateID,omitempty"`
112112
// AWS.
113113
//
114-
// AWS KMS Key ID and region.
115-
// AWS doesn't have a good way to store a certificate, so it's expected to be a file.
114+
// AWS KMS Key ID, ACM certificate ARN, and region.
115+
// Support local cert file for legacy use cases.
116116
AwsKMSKeyID string `yaml:"awsKMSKeyID,omitempty"`
117117
AwsRegion string `yaml:"awsRegion,omitempty"`
118118
AwsCertPath string `yaml:"awsCertPath,omitempty"`
119+
AwsCertARN string `yaml:"awsCertARN,omitempty"`
119120
}
120121

121122
// SigningKey describes a signing key.
@@ -159,6 +160,8 @@ func (keyAndCert SigningKeyAndCertificate) GetSigner(ctx context.Context) (pesig
159160
return file.NewSecureBootSigner(keyAndCert.CertPath, keyAndCert.KeyPath)
160161
case keyAndCert.AzureVaultURL != "" && keyAndCert.AzureCertificateID != "":
161162
return azure.NewSecureBootSigner(ctx, keyAndCert.AzureVaultURL, keyAndCert.AzureCertificateID, keyAndCert.AzureCertificateID)
163+
case keyAndCert.AwsKMSKeyID != "" && keyAndCert.AwsCertARN != "":
164+
return aws.NewSecureBootACMSigner(ctx, keyAndCert.AwsKMSKeyID, keyAndCert.AwsRegion, keyAndCert.AwsCertARN)
162165
case keyAndCert.AwsKMSKeyID != "" && keyAndCert.AwsCertPath != "":
163166
return aws.NewSecureBootSigner(ctx, keyAndCert.AwsKMSKeyID, keyAndCert.AwsRegion, keyAndCert.AwsCertPath)
164167
default:

pkg/imager/profile/internal/signer/aws/aws.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"fmt"
1111

1212
"github.com/aws/aws-sdk-go-v2/config"
13+
"github.com/aws/aws-sdk-go-v2/service/acm"
1314
"github.com/aws/aws-sdk-go-v2/service/kms"
1415
)
1516

@@ -21,3 +22,12 @@ func getKmsClient(ctx context.Context, awsRegion string) (*kms.Client, error) {
2122

2223
return kms.NewFromConfig(awsCfg), nil
2324
}
25+
26+
func getAcmClient(ctx context.Context, awsRegion string) (*acm.Client, error) {
27+
awsCfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(awsRegion))
28+
if err != nil {
29+
return nil, fmt.Errorf("error initializing AWS default config: %w", err)
30+
}
31+
32+
return acm.NewFromConfig(awsCfg), nil
33+
}

pkg/imager/profile/internal/signer/aws/aws_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
)
1616

1717
func TestIntegration(t *testing.T) {
18-
for _, envVar := range []string{"AWS_KMS_KEY_ID", "AWS_REGION", "AWS_CERT_PATH"} {
18+
for _, envVar := range []string{"AWS_KMS_KEY_ID", "AWS_REGION", "AWS_CERT_PATH", "AWS_CERT_ARN"} {
1919
if os.Getenv(envVar) == "" {
2020
t.Skipf("%s not set", envVar)
2121
}
@@ -34,4 +34,10 @@ func TestIntegration(t *testing.T) {
3434

3535
_, err = sbSigner.Signer().Sign(nil, digest[:], nil)
3636
require.NoError(t, err)
37+
38+
sbAcmSigner, err := aws.NewSecureBootACMSigner(t.Context(), os.Getenv("AWS_KMS_KEY_ID"), os.Getenv("AWS_REGION"), os.Getenv("AWS_CERT_ARN"))
39+
require.NoError(t, err)
40+
41+
_, err = sbAcmSigner.Signer().Sign(nil, digest[:], nil)
42+
require.NoError(t, err)
3743
}

pkg/imager/profile/internal/signer/aws/secureboot.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import (
1212
"fmt"
1313
"os"
1414

15+
"github.com/aws/aws-sdk-go-v2/service/acm"
16+
"github.com/siderolabs/go-pointer"
17+
1518
"github.com/siderolabs/talos/internal/pkg/secureboot/pesign"
1619
)
1720

@@ -61,3 +64,38 @@ func NewSecureBootSigner(ctx context.Context, kmsKeyID, awsRegion, certPath stri
6164
cert: cert,
6265
}, nil
6366
}
67+
68+
// NewSecureBootACMSigner creates a new SecureBootSigner using an ACM certificate.
69+
func NewSecureBootACMSigner(ctx context.Context, kmsKeyID, awsRegion, acmCertificateARN string) (*SecureBootSigner, error) {
70+
keySigner, err := NewPCRSigner(ctx, kmsKeyID, awsRegion)
71+
if err != nil {
72+
return nil, fmt.Errorf("failed to initialize certificate key signer (kms): %w", err)
73+
}
74+
75+
acmClient, err := getAcmClient(ctx, awsRegion)
76+
if err != nil {
77+
return nil, fmt.Errorf("failed to build ACM client: %w", err)
78+
}
79+
80+
resp, err := acmClient.GetCertificate(ctx, &acm.GetCertificateInput{
81+
CertificateArn: &acmCertificateARN,
82+
})
83+
if err != nil {
84+
return nil, fmt.Errorf("failed to get certificate: %w", err)
85+
}
86+
87+
certBlock, _ := pem.Decode([]byte(pointer.SafeDeref(resp.Certificate)))
88+
if certBlock == nil {
89+
return nil, fmt.Errorf("failed to decode certificate")
90+
}
91+
92+
cert, err := x509.ParseCertificate(certBlock.Bytes)
93+
if err != nil {
94+
return nil, fmt.Errorf("failed to decode certificate: %w", err)
95+
}
96+
97+
return &SecureBootSigner{
98+
keySigner: keySigner,
99+
cert: cert,
100+
}, nil
101+
}

0 commit comments

Comments
 (0)