crds_controller.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  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. if err := r.Update(ctx, &updatedResource); err != nil {
  188. return err
  189. }
  190. return nil
  191. }
  192. func injectService(crd *apiext.CustomResourceDefinition, svc types.NamespacedName) error {
  193. if crd.Spec.Conversion == nil ||
  194. crd.Spec.Conversion.Webhook == nil ||
  195. crd.Spec.Conversion.Webhook.ClientConfig == nil ||
  196. crd.Spec.Conversion.Webhook.ClientConfig.Service == nil {
  197. return fmt.Errorf("unexpected crd conversion webhook config")
  198. }
  199. crd.Spec.Conversion.Webhook.ClientConfig.Service.Namespace = svc.Namespace
  200. crd.Spec.Conversion.Webhook.ClientConfig.Service.Name = svc.Name
  201. return nil
  202. }
  203. func injectCert(crd *apiext.CustomResourceDefinition, certPem []byte) error {
  204. if crd.Spec.Conversion == nil ||
  205. crd.Spec.Conversion.Webhook == nil ||
  206. crd.Spec.Conversion.Webhook.ClientConfig == nil {
  207. return fmt.Errorf("unexpected crd conversion webhook config")
  208. }
  209. crd.Spec.Conversion.Webhook.ClientConfig.CABundle = certPem
  210. return nil
  211. }
  212. type KeyPairArtifacts struct {
  213. Cert *x509.Certificate
  214. Key *rsa.PrivateKey
  215. CertPEM []byte
  216. KeyPEM []byte
  217. }
  218. func populateSecret(cert, key []byte, caArtifacts *KeyPairArtifacts, secret *corev1.Secret) {
  219. if secret.Data == nil {
  220. secret.Data = make(map[string][]byte)
  221. }
  222. secret.Data[caCertName] = caArtifacts.CertPEM
  223. secret.Data[caKeyName] = caArtifacts.KeyPEM
  224. secret.Data[certName] = cert
  225. secret.Data[keyName] = key
  226. }
  227. func ValidCert(caCert, cert, key []byte, dnsName string, at time.Time) (bool, error) {
  228. if len(caCert) == 0 || len(cert) == 0 || len(key) == 0 {
  229. return false, errors.New("empty cert")
  230. }
  231. pool := x509.NewCertPool()
  232. caDer, _ := pem.Decode(caCert)
  233. if caDer == nil {
  234. return false, errors.New("bad CA cert")
  235. }
  236. cac, err := x509.ParseCertificate(caDer.Bytes)
  237. if err != nil {
  238. return false, err
  239. }
  240. pool.AddCert(cac)
  241. _, err = tls.X509KeyPair(cert, key)
  242. if err != nil {
  243. return false, err
  244. }
  245. b, _ := pem.Decode(cert)
  246. if b == nil {
  247. return false, err
  248. }
  249. crt, err := x509.ParseCertificate(b.Bytes)
  250. if err != nil {
  251. return false, err
  252. }
  253. _, err = crt.Verify(x509.VerifyOptions{
  254. DNSName: dnsName,
  255. Roots: pool,
  256. CurrentTime: at,
  257. })
  258. if err != nil {
  259. return false, err
  260. }
  261. return true, nil
  262. }
  263. func lookaheadTime() time.Time {
  264. return time.Now().Add(LookaheadInterval)
  265. }
  266. func (r *Reconciler) validServerCert(caCert, cert, key []byte) bool {
  267. valid, err := ValidCert(caCert, cert, key, r.dnsName, lookaheadTime())
  268. if err != nil {
  269. return false
  270. }
  271. return valid
  272. }
  273. func (r *Reconciler) validCACert(cert, key []byte) bool {
  274. valid, err := ValidCert(cert, cert, key, r.CAName, lookaheadTime())
  275. if err != nil {
  276. return false
  277. }
  278. return valid
  279. }
  280. func (r *Reconciler) refreshCertIfNeeded(secret *corev1.Secret) (bool, error) {
  281. if secret.Data == nil || !r.validCACert(secret.Data[caCertName], secret.Data[caKeyName]) {
  282. if err := r.refreshCerts(true, secret); err != nil {
  283. return false, err
  284. }
  285. return true, nil
  286. }
  287. if !r.validServerCert(secret.Data[caCertName], secret.Data[certName], secret.Data[keyName]) {
  288. if err := r.refreshCerts(false, secret); err != nil {
  289. return false, err
  290. }
  291. return true, nil
  292. }
  293. return true, nil
  294. }
  295. func (r *Reconciler) refreshCerts(refreshCA bool, secret *corev1.Secret) error {
  296. var caArtifacts *KeyPairArtifacts
  297. now := time.Now()
  298. begin := now.Add(-1 * time.Hour)
  299. end := now.Add(certValidityDuration)
  300. if refreshCA {
  301. var err error
  302. caArtifacts, err = r.CreateCACert(begin, end)
  303. if err != nil {
  304. return err
  305. }
  306. } else {
  307. var err error
  308. caArtifacts, err = buildArtifactsFromSecret(secret)
  309. if err != nil {
  310. return err
  311. }
  312. }
  313. cert, key, err := r.CreateCertPEM(caArtifacts, begin, end)
  314. if err != nil {
  315. return err
  316. }
  317. if err := r.writeSecret(cert, key, caArtifacts, secret); err != nil {
  318. return err
  319. }
  320. return nil
  321. }
  322. func buildArtifactsFromSecret(secret *corev1.Secret) (*KeyPairArtifacts, error) {
  323. caPem, ok := secret.Data[caCertName]
  324. if !ok {
  325. return nil, fmt.Errorf("cert secret is not well-formed, missing %s", caCertName)
  326. }
  327. keyPem, ok := secret.Data[caKeyName]
  328. if !ok {
  329. return nil, fmt.Errorf("cert secret is not well-formed, missing %s", caKeyName)
  330. }
  331. caDer, _ := pem.Decode(caPem)
  332. if caDer == nil {
  333. return nil, errors.New("bad CA cert")
  334. }
  335. caCert, err := x509.ParseCertificate(caDer.Bytes)
  336. if err != nil {
  337. return nil, err
  338. }
  339. keyDer, _ := pem.Decode(keyPem)
  340. if keyDer == nil {
  341. return nil, err
  342. }
  343. key, err := x509.ParsePKCS1PrivateKey(keyDer.Bytes)
  344. if err != nil {
  345. return nil, err
  346. }
  347. return &KeyPairArtifacts{
  348. Cert: caCert,
  349. CertPEM: caPem,
  350. KeyPEM: keyPem,
  351. Key: key,
  352. }, nil
  353. }
  354. func (r *Reconciler) CreateCACert(begin, end time.Time) (*KeyPairArtifacts, error) {
  355. templ := &x509.Certificate{
  356. SerialNumber: big.NewInt(0),
  357. Subject: pkix.Name{
  358. CommonName: r.CAName,
  359. Organization: []string{r.CAOrganization},
  360. },
  361. DNSNames: []string{
  362. r.CAName,
  363. },
  364. NotBefore: begin,
  365. NotAfter: end,
  366. KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign,
  367. BasicConstraintsValid: true,
  368. IsCA: true,
  369. }
  370. key, err := rsa.GenerateKey(rand.Reader, 2048)
  371. if err != nil {
  372. return nil, err
  373. }
  374. der, err := x509.CreateCertificate(rand.Reader, templ, templ, key.Public(), key)
  375. if err != nil {
  376. return nil, err
  377. }
  378. certPEM, keyPEM, err := pemEncode(der, key)
  379. if err != nil {
  380. return nil, err
  381. }
  382. cert, err := x509.ParseCertificate(der)
  383. if err != nil {
  384. return nil, err
  385. }
  386. return &KeyPairArtifacts{Cert: cert, Key: key, CertPEM: certPEM, KeyPEM: keyPEM}, nil
  387. }
  388. func (r *Reconciler) CreateCertPEM(ca *KeyPairArtifacts, begin, end time.Time) ([]byte, []byte, error) {
  389. templ := &x509.Certificate{
  390. SerialNumber: big.NewInt(1),
  391. Subject: pkix.Name{
  392. CommonName: r.dnsName,
  393. },
  394. DNSNames: []string{
  395. r.dnsName,
  396. },
  397. NotBefore: begin,
  398. NotAfter: end,
  399. KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
  400. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
  401. BasicConstraintsValid: true,
  402. }
  403. key, err := rsa.GenerateKey(rand.Reader, 2048)
  404. if err != nil {
  405. return nil, nil, err
  406. }
  407. der, err := x509.CreateCertificate(rand.Reader, templ, ca.Cert, key.Public(), ca.Key)
  408. if err != nil {
  409. return nil, nil, err
  410. }
  411. certPEM, keyPEM, err := pemEncode(der, key)
  412. if err != nil {
  413. return nil, nil, err
  414. }
  415. return certPEM, keyPEM, nil
  416. }
  417. func pemEncode(certificateDER []byte, key *rsa.PrivateKey) ([]byte, []byte, error) {
  418. certBuf := &bytes.Buffer{}
  419. if err := pem.Encode(certBuf, &pem.Block{Type: "CERTIFICATE", Bytes: certificateDER}); err != nil {
  420. return nil, nil, err
  421. }
  422. keyBuf := &bytes.Buffer{}
  423. if err := pem.Encode(keyBuf, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}); err != nil {
  424. return nil, nil, err
  425. }
  426. return certBuf.Bytes(), keyBuf.Bytes(), nil
  427. }
  428. func (r *Reconciler) writeSecret(cert, key []byte, caArtifacts *KeyPairArtifacts, secret *corev1.Secret) error {
  429. populateSecret(cert, key, caArtifacts, secret)
  430. return r.Update(context.Background(), secret)
  431. }
  432. // CheckCerts verifies that certificates exist in a given fs location
  433. // and if they're valid.
  434. func CheckCerts(c CertInfo, dnsName string, at time.Time) error {
  435. certFile := filepath.Join(c.CertDir, c.CertName)
  436. _, err := os.Stat(certFile)
  437. if err != nil {
  438. return err
  439. }
  440. ca, err := os.ReadFile(filepath.Join(c.CertDir, c.CAName))
  441. if err != nil {
  442. return err
  443. }
  444. cert, err := os.ReadFile(filepath.Join(c.CertDir, c.CertName))
  445. if err != nil {
  446. return err
  447. }
  448. key, err := os.ReadFile(filepath.Join(c.CertDir, c.KeyName))
  449. if err != nil {
  450. return err
  451. }
  452. ok, err := ValidCert(ca, cert, key, dnsName, at)
  453. if err != nil {
  454. return err
  455. }
  456. if !ok {
  457. return errors.New("certificate is not valid")
  458. }
  459. return nil
  460. }