Skip to content

Commit 7b0d4ee

Browse files
author
Nick Perry
committed
Add support for explicit template selection.
- Added ExplicitTemplateSelection field to AWSPCAIssuerSpec to control the usage of the aws-privateca.cert-manager.io/template-arn annotation. - Added resolveTemplateArn function which uses aws-privateca.cert-manager.io/template-arn annotation if ExplicitTemplateSelection is true. - Updated PCAProvisioner to call resolveTemplateArn. - Added unit tests for the new behavior. Signed-off-by: Nick Perry <[email protected]>
1 parent d5d1780 commit 7b0d4ee

File tree

7 files changed

+94
-6
lines changed

7 files changed

+94
-6
lines changed

charts/aws-pca-issuer/crds/awspca.cert-manager.io_awspcaclusterissuers.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ spec:
4343
arn:
4444
description: Specifies the ARN of the PCA resource
4545
type: string
46+
explicitTemplateSelection:
47+
description: |-
48+
When set to true, Certificates can explicitly select a specific
49+
PCA template via the aws-privateca.cert-manager.io/template-arn annotation.
50+
When false, this Certifcate annotation will be ignored and template
51+
selection will be based on the Certificate spec.
52+
type: boolean
4653
region:
4754
description: Should contain the AWS region if it cannot be inferred
4855
type: string

charts/aws-pca-issuer/crds/awspca.cert-manager.io_awspcaissuers.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ spec:
4242
arn:
4343
description: Specifies the ARN of the PCA resource
4444
type: string
45+
explicitTemplateSelection:
46+
description: |-
47+
When set to true, Certificates can explicitly select a specific
48+
PCA template via the aws-privateca.cert-manager.io/template-arn annotation.
49+
When false, this Certifcate annotation will be ignored and template
50+
selection will be based on the Certificate spec.
51+
type: boolean
4552
region:
4653
description: Should contain the AWS region if it cannot be inferred
4754
type: string

config/crd/bases/awspca.cert-manager.io_awspcaclusterissuers.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ spec:
4343
arn:
4444
description: Specifies the ARN of the PCA resource
4545
type: string
46+
explicitTemplateSelection:
47+
description: |-
48+
When set to true, Certificates can explicitly select a specific
49+
PCA template via the aws-privateca.cert-manager.io/template-arn annotation.
50+
When false, this Certificate annotation will be ignored and template
51+
selection will be based on the Certificate spec.
52+
type: boolean
4653
region:
4754
description: Should contain the AWS region if it cannot be inferred
4855
type: string

config/crd/bases/awspca.cert-manager.io_awspcaissuers.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ spec:
4242
arn:
4343
description: Specifies the ARN of the PCA resource
4444
type: string
45+
explicitTemplateSelection:
46+
description: |-
47+
When set to true, Certificates can explicitly select a specific
48+
PCA template via the aws-privateca.cert-manager.io/template-arn annotation.
49+
When false, this Certifcate annotation will be ignored and template
50+
selection will be based on the Certificate spec.
4551
region:
4652
description: Should contain the AWS region if it cannot be inferred
4753
type: string

pkg/api/v1beta1/awspcaissuer_types.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ type AWSPCAIssuerSpec struct {
3131

3232
// Specifies the ARN of the PCA resource
3333
Arn string `json:"arn,omitempty"`
34+
// ExplicitTemplateSelection controls whether the issuer will honor the
35+
// aws-privateca.cert-manager.io/template-arn annotation on Certificate
36+
// resources. When set to true, the annotation (if present) will be used
37+
// to select the PCA template ARN. When false, the annotation will be
38+
// ignored and the template will be selected based on the Certificate spec.
39+
// Defaults to false.
40+
// +optional
41+
ExplicitTemplateSelection bool `json:"explicitTemplateSelection,omitempty"`
3442
// Should contain the AWS region if it cannot be inferred
3543
// +optional
3644
Region string `json:"region,omitempty"`

pkg/aws/pca.go

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,11 @@ type acmPCAClient interface {
6868

6969
// PCAProvisioner contains logic for issuing PCA certificates
7070
type PCAProvisioner struct {
71-
pcaClient acmPCAClient
72-
arn string
73-
signingAlgorithm *acmpcatypes.SigningAlgorithm
74-
clock func() time.Time
71+
pcaClient acmPCAClient
72+
arn string
73+
signingAlgorithm *acmpcatypes.SigningAlgorithm
74+
clock func() time.Time
75+
explicitTemplateSelection bool
7576
}
7677

7778
func GetConfig(ctx context.Context, client client.Client, spec *api.AWSPCAIssuerSpec) (aws.Config, error) {
@@ -166,7 +167,8 @@ func GetProvisioner(ctx context.Context, client client.Client, name types.Namesp
166167
pcaClient: acmpca.NewFromConfig(config, acmpca.WithAPIOptions(
167168
middleware.AddUserAgentKeyValue(injections.UserAgent, injections.PlugInVersion),
168169
)),
169-
arn: spec.Arn,
170+
arn: spec.Arn,
171+
explicitTemplateSelection: spec.ExplicitTemplateSelection,
170172
}
171173
collection.Store(name, provisioner)
172174

@@ -193,7 +195,8 @@ func (p *PCAProvisioner) Sign(ctx context.Context, cr *cmapi.CertificateRequest,
193195
validityExpiration = int64(p.now().Unix()) + int64(cr.Spec.Duration.Seconds())
194196
}
195197

196-
tempArn := templateArn(p.arn, cr.Spec)
198+
// Determine template ARN to use
199+
tempArn := p.resolveTemplateArn(ctx, cr)
197200

198201
// Consider it a "retry" if we try to re-create a cert with the same name in the same namespace
199202
token := idempotencyToken(cr)
@@ -228,6 +231,27 @@ func (p *PCAProvisioner) Sign(ctx context.Context, cr *cmapi.CertificateRequest,
228231
return nil
229232
}
230233

234+
// resolveTemplateArn determines which PCA template ARN to use for a
235+
// CertificateRequest.
236+
//
237+
// If the CertificateRequest has the annotation
238+
// "aws-privateca.cert-manager.io/template-arn" and it is non-empty, the
239+
// value of the annotation is returned. Otherwise, fall back to inference logic
240+
// implemented by `templateArn`.
241+
func (p *PCAProvisioner) resolveTemplateArn(ctx context.Context, cr *cmapi.CertificateRequest) string {
242+
const annotationKey = "aws-privateca.cert-manager.io/template-arn"
243+
244+
// Only use the annotation when the Issuer permits it.
245+
if p.explicitTemplateSelection {
246+
if val, ok := cr.ObjectMeta.GetAnnotations()[annotationKey]; ok && strings.TrimSpace(val) != "" {
247+
return val
248+
}
249+
}
250+
251+
// Fall back to inference logic based on spec
252+
return templateArn(p.arn, cr.Spec)
253+
}
254+
231255
func (p *PCAProvisioner) Get(ctx context.Context, cr *cmapi.CertificateRequest, certArn string, log logr.Logger) ([]byte, []byte, error) {
232256
getParams := acmpca.GetCertificateInput{
233257
CertificateArn: aws.String(certArn),

pkg/aws/pca_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,35 @@ func TestPCASign(t *testing.T) {
818818
}
819819
}
820820

821+
func TestResolveTemplateArn_ExplicitSelection(t *testing.T) {
822+
// Set up a minimal CSR
823+
key, _ := rsa.GenerateKey(rand.Reader, 2048)
824+
csrBytes, _ := x509.CreateCertificateRequest(rand.Reader, &template, key)
825+
826+
// Case 1: Issuer enables explicit template selection; annotation should be used
827+
crWithAnnotation := &cmapi.CertificateRequest{
828+
ObjectMeta: metav1.ObjectMeta{
829+
Annotations: map[string]string{
830+
"aws-privateca.cert-manager.io/template-arn": "arn:custom:template/Custom/V1",
831+
},
832+
},
833+
Spec: cmapi.CertificateRequestSpec{
834+
Request: pem.EncodeToMemory(&pem.Block{Bytes: csrBytes, Type: "CERTIFICATE REQUEST"}),
835+
},
836+
}
837+
838+
p := PCAProvisioner{arn: arn, pcaClient: &workingACMPCAClient{}, explicitTemplateSelection: true}
839+
got := p.resolveTemplateArn(context.TODO(), crWithAnnotation)
840+
assert.Equal(t, "arn:custom:template/Custom/V1", got, "should return the annotation when explicit selection enabled")
841+
842+
// Case 2: Issuer disables explicit template selection; annotation should be ignored
843+
crWithAnnotation2 := crWithAnnotation.DeepCopy()
844+
p2 := PCAProvisioner{arn: arn, pcaClient: &workingACMPCAClient{}, explicitTemplateSelection: false}
845+
got2 := p2.resolveTemplateArn(context.TODO(), crWithAnnotation2)
846+
// Should fall back to inference based on usages (none -> BlankEndEntity...)
847+
assert.True(t, strings.HasSuffix(got2, ":acm-pca:::template/BlankEndEntityCertificate_APICSRPassthrough/V1"), "should fall back to inferred template when explicit selection disabled")
848+
}
849+
821850
func TestPCASignValidity(t *testing.T) {
822851
now := time.Now()
823852
client := &workingACMPCAClient{}

0 commit comments

Comments
 (0)