crds_controller.go 12 KB

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