crds_controller.go 13 KB

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