Skip to content
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
158 changes: 157 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,166 @@ The ACK service controller for AWS Certificate Manager is free of charge. If you
[samples]: https://github.com/aws-controllers-k8s/acmpca-controller/tree/main/samples

### Kubernetes Secrets
The ACK service controller for AWS Certificate Manager uses Kubernetes TLS Secrets to store the certificate chain and decrypted private key of the exported ACM certificate. Users are expected to create Secrets before creating Certificate resources. As these resources are created, the Secrets' `tls.crt` will be injected with the base64-encoded certificate and `tls.key` will be injected with the base64-encoded private key associated with the certificate. Users are responsible for deleting Secrets.
The ACK service controller for AWS Certificate Manager uses Kubernetes TLS Secrets in two ways:

* **Export** — write an ACM-issued certificate and private key into a Secret (`exportTo`).
* **Import** — read an existing certificate and private key from a Secret and import them into ACM (`importFrom`, or opaque secret import via `certificate` / `privateKey`).

For export, users are expected to create Secrets before creating Certificate resources. As these resources are created, the Secrets' `tls.crt` will be injected with the base64-encoded certificate and `tls.key` will be injected with the base64-encoded private key associated with the certificate. Users are responsible for deleting Secrets.

In addition, after a certificate is successfully renewed by ACM, the ACK service controller for AWS Certificate Manager will automatically export the renewed certificate again so that the Kubernetes TLS Secret `exportTo` contains the certificate data and private key data of the renewed certificate.

For import, the Secret must already contain valid PEM data in `tls.crt` (certificate) and `tls.key` (private key). If `tls.crt` contains multiple PEM blocks (leaf certificate followed by intermediate certificates), the controller automatically splits them for the ACM `ImportCertificate` API. Secrets may be type `Opaque` or `kubernetes.io/tls`.

#### Import Certificate

There are two ways to import an existing certificate into ACM from Kubernetes Secrets:

| Approach | Fields | Best for |
|----------|--------|----------|
| **TLS secret import** | `importFrom` | Standard TLS Secrets (`tls.crt` / `tls.key`) |
| **Opaque secret import** | `certificate`, `privateKey`, optional `certificateChain` | Custom secret keys or separate chain Secret |

Both approaches call the ACM [ImportCertificate](https://docs.aws.amazon.com/acm/latest/userguide/import-certificate.html) API. Imported certificates cannot be used with certificate request fields such as `domainName`, or with `exportTo`. After import, the controller may populate fields such as `domainName`, `keyAlgorithm`, and `tags` in the resource spec from ACM.

##### Import with `importFrom`

To import from a standard TLS Secret, specify the Secret using the `importFrom` field. This field is **exclusive** with certificate request, export, and opaque secret import fields (`domainName`, `exportTo`, `certificate`, `privateKey`, etc.) and may be updated after creation. Set `certificateARN` with `importFrom` to replace an existing imported certificate.

```
apiVersion: v1
kind: Secret
type: kubernetes.io/tls
metadata:
name: my-tls-secret
namespace: demo-app
data:
tls.crt: <base64-encoded-certificate-pem>
tls.key: <base64-encoded-private-key-pem>
---
apiVersion: acm.services.k8s.aws/v1alpha1
kind: Certificate
metadata:
name: imported-cert
namespace: demo-app
spec:
importFrom:
name: my-tls-secret
```

To reference a Secret in a different namespace:

```
spec:
importFrom:
name: my-tls-secret
namespace: other-namespace
```

To replace an existing imported certificate at a known ARN:

```
spec:
importFrom:
name: my-tls-secret
certificateARN: arn:aws:acm:region:account:certificate/12345678-1234-1234-1234-123456789012
```

##### Opaque secret import

Opaque secret import remains supported for backward compatibility. Set `certificate` to trigger import mode, and provide `privateKey` referencing the matching private key PEM. Each field is a `SecretKeyReference` with `name`, optional `namespace`, and `key` for the data entry within the Secret.

Required fields:

* **`certificate`** — secret reference to the leaf certificate PEM
* **`privateKey`** — secret reference to the private key PEM

Optional fields:

* **`certificateChain`** — secret reference to intermediate certificate PEMs (when not included in the certificate PEM)
* **`certificateARN`** — ARN of an existing imported certificate to replace
* **`tags`** — tags to apply to the imported certificate

Opaque secret import is **exclusive** with `importFrom`, certificate request fields (`domainName`, `domainValidationOptions`, etc.), and `exportTo`. It cannot be combined with `importFrom`.

If the certificate PEM contains multiple PEM blocks (leaf followed by intermediates), the controller automatically splits them for the ACM import API, even when `certificateChain` is not set.

```
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: my-import-secret
namespace: demo-app
data:
tls.crt: <base64-encoded-certificate-pem>
tls.key: <base64-encoded-private-key-pem>
---
apiVersion: acm.services.k8s.aws/v1alpha1
kind: Certificate
metadata:
name: imported-cert-opaque
namespace: demo-app
spec:
certificate:
name: my-import-secret
key: tls.crt
privateKey:
name: my-import-secret
key: tls.key
tags:
- key: environment
value: dev
```

Certificate and chain in separate Secrets:

```
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: my-cert
namespace: demo-app
data:
cert.pem: <base64-encoded-leaf-certificate-pem>
---
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: my-key
namespace: demo-app
data:
key.pem: <base64-encoded-private-key-pem>
---
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: my-chain
namespace: demo-app
data:
chain.pem: <base64-encoded-intermediate-pems>
---
apiVersion: acm.services.k8s.aws/v1alpha1
kind: Certificate
metadata:
name: imported-cert-opaque
namespace: demo-app
spec:
certificate:
name: my-cert
key: cert.pem
privateKey:
name: my-key
key: key.pem
certificateChain:
name: my-chain
key: chain.pem
```

**Note:** Opaque secret import fields (`certificate`, `privateKey`, `certificateChain`) are immutable once set. For new deployments, prefer `importFrom` when importing from a standard TLS Secret.

#### Export Certificate
To export an ACM certificate to a Kubernetes TLS Secret, users must specify the namespace and the name of the Secret using the `exportTo` field of the Certificate resource, as shown below.
Expand Down
26 changes: 20 additions & 6 deletions apis/v1alpha1/certificate.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions apis/v1alpha1/generator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ operations:
operation_type: DELETE
resources:
Certificate:
spec_validations:
- rule: "!has(self.importFrom) || (!has(self.certificate) && !has(self.certificateAuthorityARN) && !has(self.certificateAuthorityRef) && !has(self.certificateChain) && !has(self.domainName) && !has(self.domainValidationOptions) && !has(self.exportTo) && !has(self.keyAlgorithm) && !has(self.options) && !has(self.privateKey) && !has(self.subjectAlternativeNames))"
message: "importFrom cannot be set with certificate request, export, or opaque secret import fields"
hooks:
delta_pre_compare:
template_path: hooks/certificate/delta_pre_compare.go.tpl
Expand All @@ -62,6 +65,8 @@ resources:
template_path: hooks/certificate/sdk_file_end.go.tpl
late_initialize_post_read_one:
template_path: hooks/certificate/late_initialize_post_read_one.go.tpl
sdk_read_one_post_set_output:
template_path: hooks/certificate/sdk_read_one_post_set_output.go.tpl
exceptions:
errors:
404:
Expand All @@ -82,10 +87,17 @@ resources:
is_secret: true
compare:
is_ignored: true
ImportFrom:
type: "bytes"
is_secret_reference: true
compare:
is_ignored: true
DomainName:
is_primary_key: false
is_required: false
is_immutable: true
compare:
is_ignored: true
Certificate:
type: "bytes"
is_secret: true
Expand Down
5 changes: 5 additions & 0 deletions apis/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 39 additions & 8 deletions config/crd/bases/acm.services.k8s.aws_certificates.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ spec:
properties:
certificate:
description: |-
The Certificate to import into AWS Certificate Manager (ACM) to use with services that are integrated with ACM.
This field is only valid when importing an existing certificate into ACM.
Opaque secret import field. Reference to a Kubernetes Secret key containing the leaf certificate PEM to
import into ACM. Requires privateKey. Mutually exclusive with importFrom and certificate request
fields. If the PEM contains multiple certificate blocks, the controller splits leaf and
intermediate certificates automatically. Immutable once set.
properties:
key:
description: Key is the key within the secret
Expand All @@ -64,8 +66,8 @@ spec:
rule: self == oldSelf
certificateARN:
description: |-
The Amazon Resource Name (ARN) of an imported certificate to replace. This field is only valid when importing
an existing certificate into ACM.
The Amazon Resource Name (ARN) of an imported certificate to replace. Valid with opaque secret
import (certificate/privateKey) or importFrom. Immutable once set.
type: string
x-kubernetes-validations:
- message: Value is immutable once set
Expand Down Expand Up @@ -105,8 +107,9 @@ spec:
type: object
certificateChain:
description: |-
SecretKeyReference combines a k8s corev1.SecretReference with a
specific key within the referred-to Secret
Opaque secret import field. Optional reference to a Kubernetes Secret key containing intermediate
certificate PEMs. Use when the chain is stored separately from certificate. Mutually exclusive
with importFrom and certificate request fields. Immutable once set.
properties:
key:
description: Key is the key within the secret
Expand Down Expand Up @@ -185,6 +188,25 @@ spec:
x-kubernetes-validations:
- message: Value is immutable once set
rule: self == oldSelf
importFrom:
description: |-
Reference to an existing Kubernetes TLS Secret to import into ACM. The certificate PEM is read from
tls.crt and the private key is read from tls.key in the Secret. If tls.crt contains multiple PEM
blocks (leaf certificate followed by intermediate certificates), they are automatically split for
ACM import. Mutually exclusive with opaque secret import fields (certificate, privateKey,
certificateChain) and certificate request fields. May be updated after creation. certificateARN
may be set to replace an existing imported certificate.
properties:
name:
description: name is unique within a namespace to reference a
secret resource.
type: string
namespace:
description: namespace defines the space within which the secret
name must be unique.
type: string
type: object
x-kubernetes-map-type: atomic
keyAlgorithm:
description: |-
Specifies the algorithm of the public and private key pair that your certificate
Expand Down Expand Up @@ -244,8 +266,9 @@ spec:
type: object
privateKey:
description: |-
The private key that matches the public key in the certificate. This field is only valid when importing
an existing certificate into ACM.
Opaque secret import field. Reference to a Kubernetes Secret key containing the private key PEM that
matches certificate. Required when certificate is set. Mutually exclusive with importFrom and
certificate request fields. Immutable once set.
properties:
key:
description: Key is the key within the secret
Expand Down Expand Up @@ -308,6 +331,14 @@ spec:
type: object
type: array
type: object
x-kubernetes-validations:
- message: importFrom cannot be set with certificate request, export,
or opaque secret import fields
rule: '!has(self.importFrom) || (!has(self.certificate) && !has(self.certificateAuthorityARN)
&& !has(self.certificateAuthorityRef) && !has(self.certificateChain)
&& !has(self.domainName) && !has(self.domainValidationOptions) &&
!has(self.exportTo) && !has(self.keyAlgorithm) && !has(self.options)
&& !has(self.privateKey) && !has(self.subjectAlternativeNames))'
status:
description: CertificateStatus defines the observed state of Certificate
properties:
Expand Down
1 change: 1 addition & 0 deletions config/iam/recommended-inline-policy
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"Effect": "Allow",
"Action": [
"acm:DescribeCertificate",
"acm:GetCertificate",
"acm:ImportCertificate",
"acm:RequestCertificate",
"acm:UpdateCertificateOptions",
Expand Down
Loading