@@ -2,6 +2,8 @@ package tls
22
33import (
44 "context"
5+ "crypto/x509"
6+ "encoding/pem"
57 "fmt"
68 "strings"
79
@@ -14,6 +16,8 @@ import (
1416 "github.com/sirupsen/logrus"
1517 corev1 "k8s.io/api/core/v1"
1618 apierrors "k8s.io/apimachinery/pkg/api/errors"
19+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
20+ klabels "k8s.io/apimachinery/pkg/labels"
1721 "k8s.io/apimachinery/pkg/util/validation"
1822 "k8s.io/apimachinery/pkg/util/validation/field"
1923 kclient "sigs.k8s.io/controller-runtime/pkg/client"
@@ -145,6 +149,56 @@ func ProvisionCerts(req router.Request, resp router.Response) error {
145149 return nil
146150}
147151
152+ // certFromSecret converts TLS secret data to a TLS certificate
153+ func certFromSecret (secret corev1.Secret ) (* x509.Certificate , error ) {
154+ tlsPEM , ok := secret .Data ["tls.crt" ]
155+ if ! ok {
156+ return nil , fmt .Errorf ("key tls.crt not found in secret %s/%s" , secret .Namespace , secret .Name )
157+ }
158+
159+ tlsCertBytes , _ := pem .Decode (tlsPEM )
160+ if tlsCertBytes == nil {
161+ return nil , fmt .Errorf ("failed to parse Cert PEM stored in secret %s/%s" , secret .Namespace , secret .Name )
162+ }
163+
164+ return x509 .ParseCertificate (tlsCertBytes .Bytes )
165+ }
166+
167+ // findExistingCert returns the first TLS secret that matches the given domain
168+ func findExistingCertSecret (ctx context.Context , client kclient.Client , target string ) (* corev1.Secret , error ) {
169+ // Find existing certificate if exists
170+ existingTLSSecrets := & corev1.SecretList {}
171+ if err := client .List (ctx , existingTLSSecrets , & kclient.ListOptions {LabelSelector : klabels .SelectorFromSet (map [string ]string {
172+ labels .AcornManaged : "true" ,
173+ })}); err != nil {
174+ logrus .Errorf ("Error listing existing TLS secrets: %v" , err )
175+ }
176+
177+ for _ , sec := range existingTLSSecrets .Items {
178+ logrus .Debugf ("Found existing TLS secret: %s/%s" , sec .Namespace , sec .Name )
179+ if sec .Type != corev1 .SecretTypeTLS {
180+ continue
181+ }
182+ domain , ok := sec .Annotations [labels .AcornDomain ]
183+ if ! ok {
184+ continue
185+ }
186+
187+ if domain == target {
188+ cert , err := certFromSecret (sec )
189+ if err != nil {
190+ logrus .Errorf ("Error parsing cert from secret: %v" , err )
191+ continue
192+ }
193+ if err := cert .VerifyHostname (target ); err == nil {
194+ return & sec , nil
195+ }
196+ }
197+
198+ }
199+ return nil , nil
200+ }
201+
148202func (u * LEUser ) provisionCertIfNotExists (ctx context.Context , client kclient.Client , domain string , namespace string , secretName string ) error {
149203 // Find existing secret if exists
150204 existingSecret := & corev1.Secret {}
@@ -158,6 +212,45 @@ func (u *LEUser) provisionCertIfNotExists(ctx context.Context, client kclient.Cl
158212 return findSecretErr
159213 }
160214
215+ // Let's see if we have some existing certificate that matches the domain
216+ existingSecret , err := findExistingCertSecret (ctx , client , domain )
217+ if err != nil {
218+ return err
219+ }
220+ if existingSecret != nil {
221+ // We found an existing certificate
222+ // 1. It's in the same namespace, so it will be picked up automatically
223+ if existingSecret .Namespace == namespace {
224+ logrus .Debugf ("Found existing TLS secret %s/%s for domain %s" , existingSecret .Namespace , existingSecret .Name , domain )
225+ // Skip: TLS secret already exists, nothing to do here, renewal will be handled elsewhere
226+ return nil
227+ }
228+
229+ logrus .Debugf ("Found existing TLS secret %s/%s for domain %s, copying to %s/%s" , existingSecret .Namespace , existingSecret .Name , domain , namespace , secretName )
230+
231+ // 2. Copy secret to new namespace
232+ copiedSecret := & corev1.Secret {
233+ ObjectMeta : metav1.ObjectMeta {
234+ Name : secretName ,
235+ Namespace : namespace ,
236+ Labels : map [string ]string {
237+ labels .AcornManaged : "true" ,
238+ },
239+ Annotations : map [string ]string {
240+ labels .AcornDomain : domain ,
241+ },
242+ },
243+ Data : existingSecret .Data ,
244+ Type : existingSecret .Type ,
245+ }
246+
247+ if err := client .Create (ctx , copiedSecret ); err != nil {
248+ logrus .Errorf ("Error creating TLS secret %s/%s: %v" , copiedSecret .Namespace , copiedSecret .Name , err )
249+ return err
250+ }
251+ return nil
252+ }
253+
161254 go func () {
162255 // Do not start a new challenge if we already have one in progress
163256 if ! lockDomain (domain ) {
0 commit comments