crds_controller.go 13 KB

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