crds_controller.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. /*
  2. Copyright © 2025 ESO Maintainer Team
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. https://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package crds
  14. import (
  15. "bytes"
  16. "context"
  17. "crypto/rand"
  18. "crypto/rsa"
  19. "crypto/tls"
  20. "crypto/x509"
  21. "crypto/x509/pkix"
  22. "encoding/pem"
  23. "errors"
  24. "fmt"
  25. "math/big"
  26. "net/http"
  27. "os"
  28. "path/filepath"
  29. "slices"
  30. "sync"
  31. "time"
  32. "github.com/external-secrets/external-secrets/pkg/utils"
  33. "github.com/go-logr/logr"
  34. corev1 "k8s.io/api/core/v1"
  35. apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
  36. "k8s.io/apimachinery/pkg/api/equality"
  37. "k8s.io/apimachinery/pkg/runtime"
  38. "k8s.io/apimachinery/pkg/types"
  39. "k8s.io/client-go/tools/record"
  40. ctrl "sigs.k8s.io/controller-runtime"
  41. "sigs.k8s.io/controller-runtime/pkg/client"
  42. "sigs.k8s.io/controller-runtime/pkg/controller"
  43. )
  44. const (
  45. certName = "tls.crt"
  46. keyName = "tls.key"
  47. caCertName = "ca.crt"
  48. caKeyName = "ca.key"
  49. certValidityDuration = 10 * 365 * 24 * time.Hour
  50. LookaheadInterval = 90 * 24 * time.Hour
  51. errResNotReady = "resource not ready: %s"
  52. )
  53. type Reconciler struct {
  54. client.Client
  55. Log logr.Logger
  56. Scheme *runtime.Scheme
  57. recorder record.EventRecorder
  58. SvcName string
  59. SvcNamespace string
  60. SecretName string
  61. SecretNamespace string
  62. CrdResources []string
  63. dnsName string
  64. CAName string
  65. CAChainName string
  66. CAOrganization string
  67. RequeueInterval time.Duration
  68. // the controller is ready when all crds are injected
  69. // and the controller is elected as leader
  70. leaderChan <-chan struct{}
  71. leaderElected bool
  72. readyStatusMapMu *sync.Mutex
  73. readyStatusMap map[string]bool
  74. }
  75. type Opts struct {
  76. SvcName string
  77. SvcNamespace string
  78. SecretName string
  79. SecretNamespace string
  80. Resources []string
  81. }
  82. func New(k8sClient client.Client, scheme *runtime.Scheme, leaderChan <-chan struct{}, logger logr.Logger,
  83. interval time.Duration, opts Opts) *Reconciler {
  84. return &Reconciler{
  85. Client: k8sClient,
  86. Log: logger,
  87. Scheme: scheme,
  88. SvcName: opts.SvcName,
  89. SvcNamespace: opts.SvcNamespace,
  90. SecretName: opts.SecretName,
  91. SecretNamespace: opts.SecretNamespace,
  92. RequeueInterval: interval,
  93. CrdResources: opts.Resources,
  94. CAName: "external-secrets",
  95. CAOrganization: "external-secrets",
  96. leaderChan: leaderChan,
  97. readyStatusMapMu: &sync.Mutex{},
  98. readyStatusMap: map[string]bool{},
  99. }
  100. }
  101. type CertInfo struct {
  102. CertDir string
  103. CertName string
  104. KeyName string
  105. CAName string
  106. }
  107. func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
  108. log := r.Log.WithValues("CustomResourceDefinition", req.NamespacedName)
  109. if slices.Contains(r.CrdResources, req.NamespacedName.Name) {
  110. err := r.updateCRD(logr.NewContext(ctx, log), req)
  111. if err != nil {
  112. log.Error(err, "failed to inject conversion webhook")
  113. r.readyStatusMapMu.Lock()
  114. r.readyStatusMap[req.NamespacedName.Name] = false
  115. r.readyStatusMapMu.Unlock()
  116. return ctrl.Result{}, err
  117. }
  118. r.readyStatusMapMu.Lock()
  119. r.readyStatusMap[req.NamespacedName.Name] = true
  120. r.readyStatusMapMu.Unlock()
  121. }
  122. return ctrl.Result{RequeueAfter: r.RequeueInterval}, nil
  123. }
  124. // ReadyCheck reviews if all webhook configs have been injected into the CRDs
  125. // and if the referenced webhook service is ready.
  126. func (r *Reconciler) ReadyCheck(_ *http.Request) error {
  127. // skip readiness check if we're not leader
  128. // as we depend on caches and being able to reconcile Webhooks
  129. if !r.leaderElected {
  130. select {
  131. case <-r.leaderChan:
  132. r.leaderElected = true
  133. default:
  134. return nil
  135. }
  136. }
  137. if err := r.checkCRDs(); err != nil {
  138. return err
  139. }
  140. return utils.CheckEndpointSlicesReady(context.TODO(), r.Client, r.SvcName, r.SvcNamespace)
  141. }
  142. func (r *Reconciler) checkCRDs() error {
  143. for _, res := range r.CrdResources {
  144. r.readyStatusMapMu.Lock()
  145. rdy := r.readyStatusMap[res]
  146. r.readyStatusMapMu.Unlock()
  147. if !rdy {
  148. return fmt.Errorf(errResNotReady, res)
  149. }
  150. }
  151. return nil
  152. }
  153. func (r *Reconciler) SetupWithManager(mgr ctrl.Manager, opts controller.Options) error {
  154. r.recorder = mgr.GetEventRecorderFor("custom-resource-definition")
  155. return ctrl.NewControllerManagedBy(mgr).
  156. WithOptions(opts).
  157. For(&apiext.CustomResourceDefinition{}).
  158. Complete(r)
  159. }
  160. func (r *Reconciler) updateCRD(ctx context.Context, req ctrl.Request) error {
  161. log := logr.FromContextOrDiscard(ctx)
  162. secret := corev1.Secret{}
  163. secretName := types.NamespacedName{
  164. Name: r.SecretName,
  165. Namespace: r.SecretNamespace,
  166. }
  167. err := r.Get(context.Background(), secretName, &secret)
  168. if err != nil {
  169. return err
  170. }
  171. var updatedResource apiext.CustomResourceDefinition
  172. if err := r.Get(ctx, req.NamespacedName, &updatedResource); err != nil {
  173. return err
  174. }
  175. before := updatedResource.DeepCopyObject()
  176. svc := types.NamespacedName{
  177. Name: r.SvcName,
  178. Namespace: r.SvcNamespace,
  179. }
  180. if err := injectService(&updatedResource, svc); err != nil {
  181. return err
  182. }
  183. r.dnsName = fmt.Sprintf("%v.%v.svc", r.SvcName, r.SvcNamespace)
  184. need, err := r.refreshCertIfNeeded(&secret)
  185. if err != nil {
  186. return err
  187. }
  188. if need {
  189. artifacts, err := buildArtifactsFromSecret(&secret)
  190. if err != nil {
  191. return err
  192. }
  193. if err := injectCert(&updatedResource, artifacts.CertPEM); err != nil {
  194. return err
  195. }
  196. }
  197. if !equality.Semantic.DeepEqual(before, &updatedResource) {
  198. if err := r.Update(ctx, &updatedResource); err != nil {
  199. return err
  200. }
  201. log.Info("updated crd")
  202. return nil
  203. }
  204. log.V(1).Info("crd is unchanged")
  205. return nil
  206. }
  207. func injectService(crd *apiext.CustomResourceDefinition, svc types.NamespacedName) error {
  208. if crd.Spec.Conversion != nil && crd.Spec.Conversion.Strategy == apiext.NoneConverter {
  209. return nil
  210. }
  211. if crd.Spec.Conversion == nil ||
  212. crd.Spec.Conversion.Webhook == nil ||
  213. crd.Spec.Conversion.Webhook.ClientConfig == nil ||
  214. crd.Spec.Conversion.Webhook.ClientConfig.Service == nil {
  215. return errors.New("unexpected crd conversion webhook config")
  216. }
  217. crd.Spec.Conversion.Webhook.ClientConfig.Service.Namespace = svc.Namespace
  218. crd.Spec.Conversion.Webhook.ClientConfig.Service.Name = svc.Name
  219. return nil
  220. }
  221. func injectCert(crd *apiext.CustomResourceDefinition, certPem []byte) error {
  222. if crd.Spec.Conversion != nil && crd.Spec.Conversion.Strategy == apiext.NoneConverter {
  223. return nil
  224. }
  225. if crd.Spec.Conversion == nil ||
  226. crd.Spec.Conversion.Webhook == nil ||
  227. crd.Spec.Conversion.Webhook.ClientConfig == nil {
  228. return errors.New("unexpected crd conversion webhook config")
  229. }
  230. crd.Spec.Conversion.Webhook.ClientConfig.CABundle = certPem
  231. return nil
  232. }
  233. type KeyPairArtifacts struct {
  234. Cert *x509.Certificate
  235. Key *rsa.PrivateKey
  236. CertPEM []byte
  237. KeyPEM []byte
  238. }
  239. func populateSecret(cert, key []byte, caArtifacts *KeyPairArtifacts, secret *corev1.Secret) {
  240. if secret.Data == nil {
  241. secret.Data = make(map[string][]byte)
  242. }
  243. secret.Data[caCertName] = caArtifacts.CertPEM
  244. secret.Data[caKeyName] = caArtifacts.KeyPEM
  245. secret.Data[certName] = cert
  246. secret.Data[keyName] = key
  247. }
  248. func ValidCert(caCert, cert, key []byte, dnsName string, at time.Time) (bool, error) {
  249. if len(caCert) == 0 || len(cert) == 0 || len(key) == 0 {
  250. return false, errors.New("empty cert")
  251. }
  252. pool := x509.NewCertPool()
  253. caDer, _ := pem.Decode(caCert)
  254. if caDer == nil {
  255. return false, errors.New("bad CA cert")
  256. }
  257. cac, err := x509.ParseCertificate(caDer.Bytes)
  258. if err != nil {
  259. return false, err
  260. }
  261. pool.AddCert(cac)
  262. _, err = tls.X509KeyPair(cert, key)
  263. if err != nil {
  264. return false, err
  265. }
  266. b, rest := pem.Decode(cert)
  267. if b == nil {
  268. return false, err
  269. }
  270. if len(rest) > 0 {
  271. intermediate, _ := pem.Decode(rest)
  272. inter, err := x509.ParseCertificate(intermediate.Bytes)
  273. if err != nil {
  274. return false, err
  275. }
  276. pool.AddCert(inter)
  277. }
  278. crt, err := x509.ParseCertificate(b.Bytes)
  279. if err != nil {
  280. return false, err
  281. }
  282. _, err = crt.Verify(x509.VerifyOptions{
  283. DNSName: dnsName,
  284. Roots: pool,
  285. CurrentTime: at,
  286. })
  287. if err != nil {
  288. return false, err
  289. }
  290. return true, nil
  291. }
  292. func lookaheadTime() time.Time {
  293. return time.Now().Add(LookaheadInterval)
  294. }
  295. func (r *Reconciler) validServerCert(caCert, cert, key []byte) bool {
  296. valid, err := ValidCert(caCert, cert, key, r.dnsName, lookaheadTime())
  297. if err != nil {
  298. return false
  299. }
  300. return valid
  301. }
  302. func (r *Reconciler) validCACert(cert, key []byte) bool {
  303. valid, err := ValidCert(cert, cert, key, r.CAName, lookaheadTime())
  304. if err != nil {
  305. return false
  306. }
  307. return valid
  308. }
  309. func (r *Reconciler) refreshCertIfNeeded(secret *corev1.Secret) (bool, error) {
  310. if secret.Data == nil || !r.validCACert(secret.Data[caCertName], secret.Data[caKeyName]) {
  311. if err := r.refreshCerts(true, secret); err != nil {
  312. return false, err
  313. }
  314. return true, nil
  315. }
  316. if !r.validServerCert(secret.Data[caCertName], secret.Data[certName], secret.Data[keyName]) {
  317. if err := r.refreshCerts(false, secret); err != nil {
  318. return false, err
  319. }
  320. return true, nil
  321. }
  322. return true, nil
  323. }
  324. func (r *Reconciler) refreshCerts(refreshCA bool, secret *corev1.Secret) error {
  325. var caArtifacts *KeyPairArtifacts
  326. now := time.Now()
  327. begin := now.Add(-1 * time.Hour)
  328. end := now.Add(certValidityDuration)
  329. if refreshCA {
  330. var err error
  331. caArtifacts, err = r.CreateCACert(begin, end)
  332. if err != nil {
  333. return err
  334. }
  335. } else {
  336. var err error
  337. caArtifacts, err = buildArtifactsFromSecret(secret)
  338. if err != nil {
  339. return err
  340. }
  341. }
  342. cert, key, err := r.CreateCertPEM(caArtifacts, begin, end)
  343. if err != nil {
  344. return err
  345. }
  346. return r.writeSecret(cert, key, caArtifacts, secret)
  347. }
  348. func buildArtifactsFromSecret(secret *corev1.Secret) (*KeyPairArtifacts, error) {
  349. caPem, ok := secret.Data[caCertName]
  350. if !ok {
  351. return nil, fmt.Errorf("cert secret is not well-formed, missing %s", caCertName)
  352. }
  353. keyPem, ok := secret.Data[caKeyName]
  354. if !ok {
  355. return nil, fmt.Errorf("cert secret is not well-formed, missing %s", caKeyName)
  356. }
  357. caDer, _ := pem.Decode(caPem)
  358. if caDer == nil {
  359. return nil, errors.New("bad CA cert")
  360. }
  361. caCert, err := x509.ParseCertificate(caDer.Bytes)
  362. if err != nil {
  363. return nil, err
  364. }
  365. keyDer, _ := pem.Decode(keyPem)
  366. if keyDer == nil {
  367. return nil, err
  368. }
  369. key, err := x509.ParsePKCS1PrivateKey(keyDer.Bytes)
  370. if err != nil {
  371. return nil, err
  372. }
  373. return &KeyPairArtifacts{
  374. Cert: caCert,
  375. CertPEM: caPem,
  376. KeyPEM: keyPem,
  377. Key: key,
  378. }, nil
  379. }
  380. func (r *Reconciler) CreateCACert(begin, end time.Time) (*KeyPairArtifacts, error) {
  381. templ := &x509.Certificate{
  382. SerialNumber: big.NewInt(0),
  383. Subject: pkix.Name{
  384. CommonName: r.CAName,
  385. Organization: []string{r.CAOrganization},
  386. },
  387. DNSNames: []string{
  388. r.CAName,
  389. },
  390. NotBefore: begin,
  391. NotAfter: end,
  392. KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign,
  393. BasicConstraintsValid: true,
  394. IsCA: true,
  395. }
  396. key, err := rsa.GenerateKey(rand.Reader, 2048)
  397. if err != nil {
  398. return nil, err
  399. }
  400. der, err := x509.CreateCertificate(rand.Reader, templ, templ, key.Public(), key)
  401. if err != nil {
  402. return nil, err
  403. }
  404. certPEM, keyPEM, err := pemEncode(der, key)
  405. if err != nil {
  406. return nil, err
  407. }
  408. cert, err := x509.ParseCertificate(der)
  409. if err != nil {
  410. return nil, err
  411. }
  412. return &KeyPairArtifacts{Cert: cert, Key: key, CertPEM: certPEM, KeyPEM: keyPEM}, nil
  413. }
  414. func (r *Reconciler) CreateCAChain(ca *KeyPairArtifacts, begin, end time.Time) (*KeyPairArtifacts, error) {
  415. templ := &x509.Certificate{
  416. SerialNumber: big.NewInt(2),
  417. Subject: pkix.Name{
  418. CommonName: r.CAChainName,
  419. Organization: []string{r.CAOrganization},
  420. },
  421. DNSNames: []string{
  422. r.CAChainName,
  423. },
  424. NotBefore: begin,
  425. NotAfter: end,
  426. KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign,
  427. BasicConstraintsValid: true,
  428. IsCA: true,
  429. }
  430. key, err := rsa.GenerateKey(rand.Reader, 2048)
  431. if err != nil {
  432. return nil, err
  433. }
  434. der, err := x509.CreateCertificate(rand.Reader, templ, ca.Cert, key.Public(), ca.Key)
  435. if err != nil {
  436. return nil, err
  437. }
  438. certPEM, keyPEM, err := pemEncode(der, key)
  439. if err != nil {
  440. return nil, err
  441. }
  442. cert, err := x509.ParseCertificate(der)
  443. if err != nil {
  444. return nil, err
  445. }
  446. return &KeyPairArtifacts{Cert: cert, Key: key, CertPEM: certPEM, KeyPEM: keyPEM}, nil
  447. }
  448. func (r *Reconciler) CreateCertPEM(ca *KeyPairArtifacts, begin, end time.Time) ([]byte, []byte, error) {
  449. templ := &x509.Certificate{
  450. SerialNumber: big.NewInt(1),
  451. Subject: pkix.Name{
  452. CommonName: r.dnsName,
  453. },
  454. DNSNames: []string{
  455. r.dnsName,
  456. },
  457. NotBefore: begin,
  458. NotAfter: end,
  459. KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
  460. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
  461. BasicConstraintsValid: true,
  462. }
  463. key, err := rsa.GenerateKey(rand.Reader, 2048)
  464. if err != nil {
  465. return nil, nil, err
  466. }
  467. der, err := x509.CreateCertificate(rand.Reader, templ, ca.Cert, key.Public(), ca.Key)
  468. if err != nil {
  469. return nil, nil, err
  470. }
  471. certPEM, keyPEM, err := pemEncode(der, key)
  472. if err != nil {
  473. return nil, nil, err
  474. }
  475. return certPEM, keyPEM, nil
  476. }
  477. func pemEncode(certificateDER []byte, key *rsa.PrivateKey) ([]byte, []byte, error) {
  478. certBuf := &bytes.Buffer{}
  479. if err := pem.Encode(certBuf, &pem.Block{Type: "CERTIFICATE", Bytes: certificateDER}); err != nil {
  480. return nil, nil, err
  481. }
  482. keyBuf := &bytes.Buffer{}
  483. if err := pem.Encode(keyBuf, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}); err != nil {
  484. return nil, nil, err
  485. }
  486. return certBuf.Bytes(), keyBuf.Bytes(), nil
  487. }
  488. func (r *Reconciler) writeSecret(cert, key []byte, caArtifacts *KeyPairArtifacts, secret *corev1.Secret) error {
  489. populateSecret(cert, key, caArtifacts, secret)
  490. return r.Update(context.Background(), secret)
  491. }
  492. // CheckCerts verifies that certificates exist in a given fs location
  493. // and if they're valid.
  494. func CheckCerts(c CertInfo, dnsName string, at time.Time) error {
  495. certFile := filepath.Join(c.CertDir, c.CertName)
  496. _, err := os.Stat(certFile)
  497. if err != nil {
  498. return err
  499. }
  500. ca, err := os.ReadFile(filepath.Join(c.CertDir, c.CAName))
  501. if err != nil {
  502. return err
  503. }
  504. cert, err := os.ReadFile(filepath.Join(c.CertDir, c.CertName))
  505. if err != nil {
  506. return err
  507. }
  508. key, err := os.ReadFile(filepath.Join(c.CertDir, c.KeyName))
  509. if err != nil {
  510. return err
  511. }
  512. ok, err := ValidCert(ca, cert, key, dnsName, at)
  513. if err != nil {
  514. return err
  515. }
  516. if !ok {
  517. return errors.New("certificate is not valid")
  518. }
  519. return nil
  520. }