github.com/percona/percona-xtradb-cluster-operator@v1.14.0/pkg/controller/pxc/tls.go (about) 1 package pxc 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 "github.com/pkg/errors" 9 10 cm "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" 11 cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1" 12 api "github.com/percona/percona-xtradb-cluster-operator/pkg/apis/pxc/v1" 13 "github.com/percona/percona-xtradb-cluster-operator/pkg/pxctls" 14 corev1 "k8s.io/api/core/v1" 15 k8serr "k8s.io/apimachinery/pkg/api/errors" 16 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 17 "k8s.io/apimachinery/pkg/types" 18 ) 19 20 func (r *ReconcilePerconaXtraDBCluster) reconcileSSL(cr *api.PerconaXtraDBCluster) error { 21 if cr.Spec.AllowUnsafeConfig && (cr.Spec.TLS == nil || cr.Spec.TLS.IssuerConf == nil) { 22 return nil 23 } 24 secretObj := corev1.Secret{} 25 secretInternalObj := corev1.Secret{} 26 errSecret := r.client.Get(context.TODO(), 27 types.NamespacedName{ 28 Namespace: cr.Namespace, 29 Name: cr.Spec.PXC.SSLSecretName, 30 }, 31 &secretObj, 32 ) 33 errInternalSecret := r.client.Get(context.TODO(), 34 types.NamespacedName{ 35 Namespace: cr.Namespace, 36 Name: cr.Spec.PXC.SSLInternalSecretName, 37 }, 38 &secretInternalObj, 39 ) 40 if errSecret == nil && errInternalSecret == nil { 41 return nil 42 } else if errSecret != nil && !k8serr.IsNotFound(errSecret) { 43 return fmt.Errorf("get secret: %v", errSecret) 44 } else if errInternalSecret != nil && !k8serr.IsNotFound(errInternalSecret) { 45 return fmt.Errorf("get internal secret: %v", errInternalSecret) 46 } 47 // don't create secret ssl-internal if secret ssl is not created by operator 48 if errSecret == nil && !metav1.IsControlledBy(&secretObj, cr) { 49 return nil 50 } 51 err := r.createSSLByCertManager(cr) 52 if err != nil { 53 if cr.Spec.TLS != nil && cr.Spec.TLS.IssuerConf != nil { 54 return fmt.Errorf("create ssl with cert manager %w", err) 55 } 56 err = r.createSSLManualy(cr) 57 if err != nil { 58 return fmt.Errorf("create ssl internally: %v", err) 59 } 60 } 61 return nil 62 } 63 64 func (r *ReconcilePerconaXtraDBCluster) createSSLByCertManager(cr *api.PerconaXtraDBCluster) error { 65 issuerName := cr.Name + "-pxc-issuer" 66 caIssuerName := cr.Name + "-pxc-ca-issuer" 67 issuerKind := "Issuer" 68 issuerGroup := "" 69 if cr.Spec.TLS != nil && cr.Spec.TLS.IssuerConf != nil { 70 issuerKind = cr.Spec.TLS.IssuerConf.Kind 71 issuerName = cr.Spec.TLS.IssuerConf.Name 72 issuerGroup = cr.Spec.TLS.IssuerConf.Group 73 } else { 74 if err := r.createIssuer(cr.Namespace, caIssuerName, ""); err != nil { 75 return err 76 } 77 78 caCert := &cm.Certificate{ 79 ObjectMeta: metav1.ObjectMeta{ 80 Name: cr.Name + "-ca-cert", 81 Namespace: cr.Namespace, 82 }, 83 Spec: cm.CertificateSpec{ 84 SecretName: cr.Name + "-ca-cert", 85 CommonName: cr.Name + "-ca", 86 IsCA: true, 87 IssuerRef: cmmeta.ObjectReference{ 88 Name: caIssuerName, 89 Kind: issuerKind, 90 Group: issuerGroup, 91 }, 92 Duration: &metav1.Duration{Duration: time.Hour * 24 * 365}, 93 RenewBefore: &metav1.Duration{Duration: 730 * time.Hour}, 94 }, 95 } 96 97 err := r.client.Create(context.TODO(), caCert) 98 if err != nil && !k8serr.IsAlreadyExists(err) { 99 return fmt.Errorf("create CA certificate: %v", err) 100 } 101 102 if err := r.waitForCerts(cr.Namespace, caCert.Spec.SecretName); err != nil { 103 return err 104 } 105 106 if err := r.createIssuer(cr.Namespace, issuerName, caCert.Spec.SecretName); err != nil { 107 return err 108 } 109 } 110 111 kubeCert := &cm.Certificate{ 112 ObjectMeta: metav1.ObjectMeta{ 113 Name: cr.Name + "-ssl", 114 Namespace: cr.Namespace, 115 }, 116 Spec: cm.CertificateSpec{ 117 SecretName: cr.Spec.PXC.SSLSecretName, 118 CommonName: cr.Name + "-proxysql", 119 DNSNames: []string{ 120 cr.Name + "-pxc", 121 cr.Name + "-proxysql", 122 "*." + cr.Name + "-pxc", 123 "*." + cr.Name + "-proxysql", 124 }, 125 IssuerRef: cmmeta.ObjectReference{ 126 Name: issuerName, 127 Kind: issuerKind, 128 Group: issuerGroup, 129 }, 130 }, 131 } 132 133 if cr.Spec.TLS != nil && len(cr.Spec.TLS.SANs) > 0 { 134 kubeCert.Spec.DNSNames = append(kubeCert.Spec.DNSNames, cr.Spec.TLS.SANs...) 135 } 136 137 err := r.client.Create(context.TODO(), kubeCert) 138 if err != nil && !k8serr.IsAlreadyExists(err) { 139 return fmt.Errorf("create certificate: %v", err) 140 } 141 142 if cr.Spec.PXC.SSLSecretName == cr.Spec.PXC.SSLInternalSecretName { 143 return r.waitForCerts(cr.Namespace, cr.Spec.PXC.SSLSecretName) 144 } 145 146 kubeCert = &cm.Certificate{ 147 ObjectMeta: metav1.ObjectMeta{ 148 Name: cr.Name + "-ssl-internal", 149 Namespace: cr.Namespace, 150 }, 151 Spec: cm.CertificateSpec{ 152 SecretName: cr.Spec.PXC.SSLInternalSecretName, 153 CommonName: cr.Name + "-pxc", 154 DNSNames: []string{ 155 cr.Name + "-pxc", 156 "*." + cr.Name + "-pxc", 157 cr.Name + "-haproxy-replicas." + cr.Namespace + ".svc.cluster.local", 158 cr.Name + "-haproxy-replicas." + cr.Namespace, 159 cr.Name + "-haproxy-replicas", 160 cr.Name + "-haproxy." + cr.Namespace + ".svc.cluster.local", 161 cr.Name + "-haproxy." + cr.Namespace, 162 cr.Name + "-haproxy", 163 }, 164 IssuerRef: cmmeta.ObjectReference{ 165 Name: issuerName, 166 Kind: issuerKind, 167 Group: issuerGroup, 168 }, 169 }, 170 } 171 if cr.Spec.TLS != nil && len(cr.Spec.TLS.SANs) > 0 { 172 kubeCert.Spec.DNSNames = append(kubeCert.Spec.DNSNames, cr.Spec.TLS.SANs...) 173 } 174 err = r.client.Create(context.TODO(), kubeCert) 175 if err != nil && !k8serr.IsAlreadyExists(err) { 176 return fmt.Errorf("create internal certificate: %v", err) 177 } 178 179 return r.waitForCerts(cr.Namespace, cr.Spec.PXC.SSLSecretName, cr.Spec.PXC.SSLInternalSecretName) 180 } 181 182 func (r *ReconcilePerconaXtraDBCluster) waitForCerts(namespace string, secretsList ...string) error { 183 ticker := time.NewTicker(3 * time.Second) 184 timeoutTimer := time.NewTimer(30 * time.Second) 185 defer timeoutTimer.Stop() 186 defer ticker.Stop() 187 for { 188 select { 189 case <-timeoutTimer.C: 190 return errors.Errorf("timeout: can't get tls certificates from certmanager, %s", secretsList) 191 case <-ticker.C: 192 sucessCount := 0 193 for _, secretName := range secretsList { 194 secret := &corev1.Secret{} 195 err := r.client.Get(context.TODO(), types.NamespacedName{ 196 Name: secretName, 197 Namespace: namespace, 198 }, secret) 199 if err != nil && !k8serr.IsNotFound(err) { 200 return err 201 } else if err == nil { 202 sucessCount++ 203 } 204 } 205 if sucessCount == len(secretsList) { 206 return nil 207 } 208 } 209 } 210 } 211 212 func (r *ReconcilePerconaXtraDBCluster) createIssuer(namespace, issuer string, caCertSecret string) error { 213 spec := cm.IssuerSpec{} 214 215 if caCertSecret == "" { 216 spec = cm.IssuerSpec{ 217 IssuerConfig: cm.IssuerConfig{ 218 SelfSigned: &cm.SelfSignedIssuer{}, 219 }, 220 } 221 } else { 222 spec = cm.IssuerSpec{ 223 IssuerConfig: cm.IssuerConfig{ 224 CA: &cm.CAIssuer{SecretName: caCertSecret}, 225 }, 226 } 227 } 228 229 err := r.client.Create(context.TODO(), &cm.Issuer{ 230 ObjectMeta: metav1.ObjectMeta{ 231 Name: issuer, 232 Namespace: namespace, 233 }, 234 Spec: spec, 235 }) 236 if err != nil && !k8serr.IsAlreadyExists(err) { 237 return fmt.Errorf("create issuer: %v", err) 238 } 239 return nil 240 } 241 242 func (r *ReconcilePerconaXtraDBCluster) createSSLManualy(cr *api.PerconaXtraDBCluster) error { 243 data := make(map[string][]byte) 244 proxyHosts := []string{ 245 cr.Name + "-pxc", 246 cr.Name + "-proxysql", 247 "*." + cr.Name + "-pxc", 248 "*." + cr.Name + "-proxysql", 249 } 250 if cr.Spec.TLS != nil && len(cr.Spec.TLS.SANs) > 0 { 251 proxyHosts = append(proxyHosts, cr.Spec.TLS.SANs...) 252 } 253 caCert, tlsCert, key, err := pxctls.Issue(proxyHosts) 254 if err != nil { 255 return fmt.Errorf("create proxy certificate: %v", err) 256 } 257 data["ca.crt"] = caCert 258 data["tls.crt"] = tlsCert 259 data["tls.key"] = key 260 owner, err := OwnerRef(cr, r.scheme) 261 if err != nil { 262 return err 263 } 264 ownerReferences := []metav1.OwnerReference{owner} 265 secretObj := corev1.Secret{ 266 ObjectMeta: metav1.ObjectMeta{ 267 Name: cr.Spec.PXC.SSLSecretName, 268 Namespace: cr.Namespace, 269 OwnerReferences: ownerReferences, 270 }, 271 Data: data, 272 Type: corev1.SecretTypeTLS, 273 } 274 err = r.client.Create(context.TODO(), &secretObj) 275 if err != nil && !k8serr.IsAlreadyExists(err) { 276 return fmt.Errorf("create TLS secret: %v", err) 277 } 278 pxcHosts := []string{ 279 cr.Name + "-pxc", 280 "*." + cr.Name + "-pxc", 281 cr.Name + "-haproxy-replicas." + cr.Namespace + ".svc.cluster.local", 282 cr.Name + "-haproxy-replicas." + cr.Namespace, 283 cr.Name + "-haproxy-replicas", 284 cr.Name + "-haproxy." + cr.Namespace + ".svc.cluster.local", 285 cr.Name + "-haproxy." + cr.Namespace, 286 cr.Name + "-haproxy", 287 } 288 if cr.Spec.TLS != nil && len(cr.Spec.TLS.SANs) > 0 { 289 pxcHosts = append(pxcHosts, cr.Spec.TLS.SANs...) 290 } 291 caCert, tlsCert, key, err = pxctls.Issue(pxcHosts) 292 if err != nil { 293 return fmt.Errorf("create pxc certificate: %v", err) 294 } 295 data["ca.crt"] = caCert 296 data["tls.crt"] = tlsCert 297 data["tls.key"] = key 298 secretObjInternal := corev1.Secret{ 299 ObjectMeta: metav1.ObjectMeta{ 300 Name: cr.Spec.PXC.SSLInternalSecretName, 301 Namespace: cr.Namespace, 302 OwnerReferences: ownerReferences, 303 }, 304 Data: data, 305 Type: corev1.SecretTypeTLS, 306 } 307 err = r.client.Create(context.TODO(), &secretObjInternal) 308 if err != nil && !k8serr.IsAlreadyExists(err) { 309 return fmt.Errorf("create TLS internal secret: %v", err) 310 } 311 return nil 312 }