crds_controller.go 14 KB

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