crds_controller.go 13 KB

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