crds_controller.go 14 KB

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