crds_controller.go 12 KB

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