crds_controller.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  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/client-go/tools/record"
  34. ctrl "sigs.k8s.io/controller-runtime"
  35. "sigs.k8s.io/controller-runtime/pkg/client"
  36. "sigs.k8s.io/controller-runtime/pkg/controller"
  37. )
  38. const (
  39. certName = "tls.crt"
  40. keyName = "tls.key"
  41. caCertName = "ca.crt"
  42. caKeyName = "ca.key"
  43. certValidityDuration = 10 * 365 * 24 * time.Hour
  44. lookaheadInterval = 90 * 24 * time.Hour
  45. )
  46. type WebhookType int
  47. const (
  48. Validating WebhookType = iota
  49. Mutating
  50. CRDConversion
  51. )
  52. type Reconciler struct {
  53. client.Client
  54. Log logr.Logger
  55. Scheme *runtime.Scheme
  56. recorder record.EventRecorder
  57. SvcLabels map[string]string
  58. SecretLabels map[string]string
  59. CrdResources []string
  60. CertDir string
  61. dnsName string
  62. CAName string
  63. CAOrganization string
  64. RestartOnSecretRefresh bool
  65. }
  66. type WebhookInfo struct {
  67. Name string
  68. Type WebhookType
  69. }
  70. func contains(s []string, e string) bool {
  71. for _, a := range s {
  72. if a == e {
  73. return true
  74. }
  75. }
  76. return false
  77. }
  78. func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
  79. log := r.Log.WithValues("CustomResourceDefinition", req.NamespacedName)
  80. if contains(r.CrdResources, req.NamespacedName.Name) {
  81. err := r.updateCRD(ctx, req)
  82. if err != nil {
  83. log.Error(err, "failed to inject conversion webhook")
  84. return ctrl.Result{}, err
  85. }
  86. }
  87. return ctrl.Result{}, nil
  88. }
  89. func (r *Reconciler) ConvertToWebhookInfo() []WebhookInfo {
  90. info := make([]WebhookInfo, len(r.CrdResources))
  91. for p, v := range r.CrdResources {
  92. r := WebhookInfo{
  93. Name: v,
  94. Type: CRDConversion,
  95. }
  96. info[p] = r
  97. }
  98. return info
  99. }
  100. func (r *Reconciler) SetupWithManager(mgr ctrl.Manager, opts controller.Options) error {
  101. crdGVK := schema.GroupVersionKind{Group: "apiextensions.k8s.io", Version: "v1", Kind: "CustomResourceDefinition"}
  102. res := &unstructured.Unstructured{}
  103. res.SetGroupVersionKind(crdGVK)
  104. r.recorder = mgr.GetEventRecorderFor("custom-resource-definition")
  105. return ctrl.NewControllerManagedBy(mgr).
  106. WithOptions(opts).
  107. For(res).
  108. Complete(r)
  109. }
  110. func (r *Reconciler) updateCRD(ctx context.Context, req ctrl.Request) error {
  111. crdGVK := schema.GroupVersionKind{Group: "apiextensions.k8s.io", Version: "v1", Kind: "CustomResourceDefinition"}
  112. svcList := corev1.ServiceList{}
  113. err := r.List(context.Background(), &svcList, client.MatchingLabels(r.SvcLabels))
  114. if err != nil {
  115. return err
  116. }
  117. if len(svcList.Items) != 1 {
  118. return errors.New("multiple services match labels")
  119. }
  120. secretList := corev1.SecretList{}
  121. err = r.List(context.Background(), &secretList, client.MatchingLabels(r.SecretLabels))
  122. if err != nil {
  123. return err
  124. }
  125. if len(secretList.Items) != 1 {
  126. return errors.New("multiple secrets match labels")
  127. }
  128. updatedResource := &unstructured.Unstructured{}
  129. updatedResource.SetGroupVersionKind(crdGVK)
  130. if err := r.Get(ctx, req.NamespacedName, updatedResource); err != nil {
  131. return err
  132. }
  133. if err := injectSvcToConversionWebhook(updatedResource, &svcList.Items[0]); err != nil {
  134. return err
  135. }
  136. r.dnsName = fmt.Sprintf("%v.%v.svc", svcList.Items[0].Name, svcList.Items[0].Namespace)
  137. need, err := r.refreshCertIfNeeded(&secretList.Items[0])
  138. if err != nil {
  139. return err
  140. }
  141. if need {
  142. artifacts, err := buildArtifactsFromSecret(&secretList.Items[0])
  143. if err != nil {
  144. return err
  145. }
  146. if err := injectCertToConversionWebhook(updatedResource, artifacts.CertPEM); err != nil {
  147. return err
  148. }
  149. }
  150. if err := r.Update(ctx, updatedResource); err != nil {
  151. return err
  152. }
  153. return nil
  154. }
  155. func (r *Reconciler) EnsureCertsMounted() bool {
  156. certFile := r.CertDir + "/" + certName
  157. _, err := os.Stat(certFile)
  158. return err == nil
  159. }
  160. func injectSvcToConversionWebhook(crd *unstructured.Unstructured, service *corev1.Service) 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, service.Name, "spec", "conversion", "webhook", "clientConfig", "service", "name"); err != nil {
  169. return err
  170. }
  171. if err := unstructured.SetNestedField(crd.Object, service.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. }