crds_controller.go 15 KB

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