crds_controller.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  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/base64"
  22. "encoding/pem"
  23. "errors"
  24. "fmt"
  25. "math/big"
  26. "os"
  27. "time"
  28. "github.com/go-logr/logr"
  29. corev1 "k8s.io/api/core/v1"
  30. "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
  31. "k8s.io/apimachinery/pkg/runtime"
  32. "k8s.io/apimachinery/pkg/runtime/schema"
  33. "k8s.io/apimachinery/pkg/types"
  34. "k8s.io/client-go/tools/record"
  35. ctrl "sigs.k8s.io/controller-runtime"
  36. "sigs.k8s.io/controller-runtime/pkg/client"
  37. "sigs.k8s.io/controller-runtime/pkg/controller"
  38. )
  39. const (
  40. certName = "tls.crt"
  41. keyName = "tls.key"
  42. caCertName = "ca.crt"
  43. caKeyName = "ca.key"
  44. certValidityDuration = 10 * 365 * 24 * time.Hour
  45. LookaheadInterval = 90 * 24 * time.Hour
  46. )
  47. type WebhookType int
  48. const (
  49. Validating WebhookType = iota
  50. Mutating
  51. CRDConversion
  52. )
  53. type Reconciler struct {
  54. client.Client
  55. Log logr.Logger
  56. Scheme *runtime.Scheme
  57. recorder record.EventRecorder
  58. SvcName string
  59. SvcNamespace string
  60. SecretName string
  61. SecretNamespace string
  62. CrdResources []string
  63. dnsName string
  64. CAName string
  65. CAOrganization string
  66. RestartOnSecretRefresh bool
  67. }
  68. type CertInfo struct {
  69. CertDir string
  70. CertName string
  71. KeyName string
  72. CAName string
  73. }
  74. type WebhookInfo struct {
  75. Name string
  76. Type WebhookType
  77. }
  78. func contains(s []string, e string) bool {
  79. for _, a := range s {
  80. if a == e {
  81. return true
  82. }
  83. }
  84. return false
  85. }
  86. func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
  87. log := r.Log.WithValues("CustomResourceDefinition", req.NamespacedName)
  88. if contains(r.CrdResources, req.NamespacedName.Name) {
  89. err := r.updateCRD(ctx, req)
  90. if err != nil {
  91. log.Error(err, "failed to inject conversion webhook")
  92. return ctrl.Result{}, err
  93. }
  94. }
  95. return ctrl.Result{}, nil
  96. }
  97. func (r *Reconciler) ConvertToWebhookInfo() []WebhookInfo {
  98. info := make([]WebhookInfo, len(r.CrdResources))
  99. for p, v := range r.CrdResources {
  100. r := WebhookInfo{
  101. Name: v,
  102. Type: CRDConversion,
  103. }
  104. info[p] = r
  105. }
  106. return info
  107. }
  108. func (r *Reconciler) SetupWithManager(mgr ctrl.Manager, opts controller.Options) error {
  109. crdGVK := schema.GroupVersionKind{Group: "apiextensions.k8s.io", Version: "v1", Kind: "CustomResourceDefinition"}
  110. res := &unstructured.Unstructured{}
  111. res.SetGroupVersionKind(crdGVK)
  112. r.recorder = mgr.GetEventRecorderFor("custom-resource-definition")
  113. return ctrl.NewControllerManagedBy(mgr).
  114. WithOptions(opts).
  115. For(res).
  116. Complete(r)
  117. }
  118. func (r *Reconciler) updateCRD(ctx context.Context, req ctrl.Request) error {
  119. crdGVK := schema.GroupVersionKind{Group: "apiextensions.k8s.io", Version: "v1", Kind: "CustomResourceDefinition"}
  120. secret := corev1.Secret{}
  121. secretName := types.NamespacedName{
  122. Name: r.SecretName,
  123. Namespace: r.SecretNamespace,
  124. }
  125. err := r.Get(context.Background(), secretName, &secret)
  126. if err != nil {
  127. return err
  128. }
  129. updatedResource := &unstructured.Unstructured{}
  130. updatedResource.SetGroupVersionKind(crdGVK)
  131. if err := r.Get(ctx, req.NamespacedName, updatedResource); err != nil {
  132. return err
  133. }
  134. svc := types.NamespacedName{
  135. Name: r.SvcName,
  136. Namespace: r.SvcNamespace,
  137. }
  138. if err := injectSvcToConversionWebhook(updatedResource, svc); err != nil {
  139. return err
  140. }
  141. r.dnsName = fmt.Sprintf("%v.%v.svc", r.SvcName, r.SvcNamespace)
  142. need, err := r.refreshCertIfNeeded(&secret)
  143. if err != nil {
  144. return err
  145. }
  146. if need {
  147. artifacts, err := buildArtifactsFromSecret(&secret)
  148. if err != nil {
  149. return err
  150. }
  151. if err := injectCertToConversionWebhook(updatedResource, artifacts.CertPEM); err != nil {
  152. return err
  153. }
  154. }
  155. if err := r.Update(ctx, updatedResource); err != nil {
  156. return err
  157. }
  158. return nil
  159. }
  160. func injectSvcToConversionWebhook(crd *unstructured.Unstructured, svc types.NamespacedName) error {
  161. _, found, err := unstructured.NestedMap(crd.Object, "spec", "conversion", "webhook", "clientConfig")
  162. if err != nil {
  163. return err
  164. }
  165. if !found {
  166. return errors.New("`conversion.webhook.clientConfig` field not found in CustomResourceDefinition")
  167. }
  168. if err := unstructured.SetNestedField(crd.Object, svc.Name, "spec", "conversion", "webhook", "clientConfig", "service", "name"); err != nil {
  169. return err
  170. }
  171. if err := unstructured.SetNestedField(crd.Object, svc.Namespace, "spec", "conversion", "webhook", "clientConfig", "service", "namespace"); err != nil {
  172. return err
  173. }
  174. return nil
  175. }
  176. func injectCertToConversionWebhook(crd *unstructured.Unstructured, certPem []byte) error {
  177. _, found, err := unstructured.NestedMap(crd.Object, "spec", "conversion", "webhook", "clientConfig")
  178. if err != nil {
  179. return err
  180. }
  181. if !found {
  182. return errors.New("`conversion.webhook.clientConfig` field not found in CustomResourceDefinition")
  183. }
  184. if err := unstructured.SetNestedField(crd.Object, base64.StdEncoding.EncodeToString(certPem), "spec", "conversion", "webhook", "clientConfig", "caBundle"); err != nil {
  185. return err
  186. }
  187. return nil
  188. }
  189. type KeyPairArtifacts struct {
  190. Cert *x509.Certificate
  191. Key *rsa.PrivateKey
  192. CertPEM []byte
  193. KeyPEM []byte
  194. }
  195. func populateSecret(cert, key []byte, caArtifacts *KeyPairArtifacts, secret *corev1.Secret) {
  196. if secret.Data == nil {
  197. secret.Data = make(map[string][]byte)
  198. }
  199. secret.Data[caCertName] = caArtifacts.CertPEM
  200. secret.Data[caKeyName] = caArtifacts.KeyPEM
  201. secret.Data[certName] = cert
  202. secret.Data[keyName] = key
  203. }
  204. func ValidCert(caCert, cert, key []byte, dnsName string, at time.Time) (bool, error) {
  205. if len(caCert) == 0 || len(cert) == 0 || len(key) == 0 {
  206. return false, errors.New("empty cert")
  207. }
  208. pool := x509.NewCertPool()
  209. caDer, _ := pem.Decode(caCert)
  210. if caDer == nil {
  211. return false, errors.New("bad CA cert")
  212. }
  213. cac, err := x509.ParseCertificate(caDer.Bytes)
  214. if err != nil {
  215. return false, err
  216. }
  217. pool.AddCert(cac)
  218. _, err = tls.X509KeyPair(cert, key)
  219. if err != nil {
  220. return false, err
  221. }
  222. b, _ := pem.Decode(cert)
  223. if b == nil {
  224. return false, err
  225. }
  226. crt, err := x509.ParseCertificate(b.Bytes)
  227. if err != nil {
  228. return false, err
  229. }
  230. _, err = crt.Verify(x509.VerifyOptions{
  231. DNSName: dnsName,
  232. Roots: pool,
  233. CurrentTime: at,
  234. })
  235. if err != nil {
  236. return false, err
  237. }
  238. return true, nil
  239. }
  240. func lookaheadTime() time.Time {
  241. return time.Now().Add(LookaheadInterval)
  242. }
  243. func (r *Reconciler) validServerCert(caCert, cert, key []byte) bool {
  244. valid, err := ValidCert(caCert, cert, key, r.dnsName, lookaheadTime())
  245. if err != nil {
  246. return false
  247. }
  248. return valid
  249. }
  250. func (r *Reconciler) validCACert(cert, key []byte) bool {
  251. valid, err := ValidCert(cert, cert, key, r.CAName, lookaheadTime())
  252. if err != nil {
  253. return false
  254. }
  255. return valid
  256. }
  257. func (r *Reconciler) refreshCertIfNeeded(secret *corev1.Secret) (bool, error) {
  258. if secret.Data == nil || !r.validCACert(secret.Data[caCertName], secret.Data[caKeyName]) {
  259. if err := r.refreshCerts(true, secret); err != nil {
  260. return false, err
  261. }
  262. if r.RestartOnSecretRefresh {
  263. os.Exit(0)
  264. }
  265. return true, nil
  266. }
  267. if !r.validServerCert(secret.Data[caCertName], secret.Data[certName], secret.Data[keyName]) {
  268. if err := r.refreshCerts(false, secret); err != nil {
  269. return false, err
  270. }
  271. if r.RestartOnSecretRefresh {
  272. os.Exit(0)
  273. }
  274. return true, nil
  275. }
  276. return true, nil
  277. }
  278. func (r *Reconciler) refreshCerts(refreshCA bool, secret *corev1.Secret) error {
  279. var caArtifacts *KeyPairArtifacts
  280. now := time.Now()
  281. begin := now.Add(-1 * time.Hour)
  282. end := now.Add(certValidityDuration)
  283. if refreshCA {
  284. var err error
  285. caArtifacts, err = r.CreateCACert(begin, end)
  286. if err != nil {
  287. return err
  288. }
  289. } else {
  290. var err error
  291. caArtifacts, err = buildArtifactsFromSecret(secret)
  292. if err != nil {
  293. return err
  294. }
  295. }
  296. cert, key, err := r.CreateCertPEM(caArtifacts, begin, end)
  297. if err != nil {
  298. return err
  299. }
  300. if err := r.writeSecret(cert, key, caArtifacts, secret); err != nil {
  301. return err
  302. }
  303. return nil
  304. }
  305. func buildArtifactsFromSecret(secret *corev1.Secret) (*KeyPairArtifacts, error) {
  306. caPem, ok := secret.Data[caCertName]
  307. if !ok {
  308. return nil, fmt.Errorf("cert secret is not well-formed, missing %s", caCertName)
  309. }
  310. keyPem, ok := secret.Data[caKeyName]
  311. if !ok {
  312. return nil, fmt.Errorf("cert secret is not well-formed, missing %s", caKeyName)
  313. }
  314. caDer, _ := pem.Decode(caPem)
  315. if caDer == nil {
  316. return nil, errors.New("bad CA cert")
  317. }
  318. caCert, err := x509.ParseCertificate(caDer.Bytes)
  319. if err != nil {
  320. return nil, err
  321. }
  322. keyDer, _ := pem.Decode(keyPem)
  323. if keyDer == nil {
  324. return nil, err
  325. }
  326. key, err := x509.ParsePKCS1PrivateKey(keyDer.Bytes)
  327. if err != nil {
  328. return nil, err
  329. }
  330. return &KeyPairArtifacts{
  331. Cert: caCert,
  332. CertPEM: caPem,
  333. KeyPEM: keyPem,
  334. Key: key,
  335. }, nil
  336. }
  337. func (r *Reconciler) CreateCACert(begin, end time.Time) (*KeyPairArtifacts, error) {
  338. templ := &x509.Certificate{
  339. SerialNumber: big.NewInt(0),
  340. Subject: pkix.Name{
  341. CommonName: r.CAName,
  342. Organization: []string{r.CAOrganization},
  343. },
  344. DNSNames: []string{
  345. r.CAName,
  346. },
  347. NotBefore: begin,
  348. NotAfter: end,
  349. KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageCertSign,
  350. BasicConstraintsValid: true,
  351. IsCA: true,
  352. }
  353. key, err := rsa.GenerateKey(rand.Reader, 2048)
  354. if err != nil {
  355. return nil, err
  356. }
  357. der, err := x509.CreateCertificate(rand.Reader, templ, templ, key.Public(), key)
  358. if err != nil {
  359. return nil, err
  360. }
  361. certPEM, keyPEM, err := pemEncode(der, key)
  362. if err != nil {
  363. return nil, err
  364. }
  365. cert, err := x509.ParseCertificate(der)
  366. if err != nil {
  367. return nil, err
  368. }
  369. return &KeyPairArtifacts{Cert: cert, Key: key, CertPEM: certPEM, KeyPEM: keyPEM}, nil
  370. }
  371. func (r *Reconciler) CreateCertPEM(ca *KeyPairArtifacts, begin, end time.Time) ([]byte, []byte, error) {
  372. templ := &x509.Certificate{
  373. SerialNumber: big.NewInt(1),
  374. Subject: pkix.Name{
  375. CommonName: r.dnsName,
  376. },
  377. DNSNames: []string{
  378. r.dnsName,
  379. },
  380. NotBefore: begin,
  381. NotAfter: end,
  382. KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
  383. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
  384. BasicConstraintsValid: true,
  385. }
  386. key, err := rsa.GenerateKey(rand.Reader, 2048)
  387. if err != nil {
  388. return nil, nil, err
  389. }
  390. der, err := x509.CreateCertificate(rand.Reader, templ, ca.Cert, key.Public(), ca.Key)
  391. if err != nil {
  392. return nil, nil, err
  393. }
  394. certPEM, keyPEM, err := pemEncode(der, key)
  395. if err != nil {
  396. return nil, nil, err
  397. }
  398. return certPEM, keyPEM, nil
  399. }
  400. func pemEncode(certificateDER []byte, key *rsa.PrivateKey) ([]byte, []byte, error) {
  401. certBuf := &bytes.Buffer{}
  402. if err := pem.Encode(certBuf, &pem.Block{Type: "CERTIFICATE", Bytes: certificateDER}); err != nil {
  403. return nil, nil, err
  404. }
  405. keyBuf := &bytes.Buffer{}
  406. if err := pem.Encode(keyBuf, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}); err != nil {
  407. return nil, nil, err
  408. }
  409. return certBuf.Bytes(), keyBuf.Bytes(), nil
  410. }
  411. func (r *Reconciler) writeSecret(cert, key []byte, caArtifacts *KeyPairArtifacts, secret *corev1.Secret) error {
  412. populateSecret(cert, key, caArtifacts, secret)
  413. return r.Update(context.Background(), secret)
  414. }
  415. func CheckCerts(c CertInfo, dnsName string, at time.Time) error {
  416. certFile := c.CertDir + "/" + c.CertName
  417. _, err := os.Stat(certFile)
  418. if err != nil {
  419. return err
  420. }
  421. ca, err := os.ReadFile(c.CertDir + "/" + c.CAName)
  422. if err != nil {
  423. return err
  424. }
  425. cert, err := os.ReadFile(c.CertDir + "/" + c.CertName)
  426. if err != nil {
  427. return err
  428. }
  429. key, err := os.ReadFile(c.CertDir + "/" + c.KeyName)
  430. if err != nil {
  431. return err
  432. }
  433. ok, err := ValidCert(ca, cert, key, dnsName, at)
  434. if err != nil {
  435. return err
  436. }
  437. if !ok {
  438. return errors.New("certificate is not valid")
  439. }
  440. return nil
  441. }