readwriter.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. /*
  2. Copyright 2019 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package renewal
  14. import (
  15. "crypto"
  16. "crypto/x509"
  17. "path/filepath"
  18. "github.com/pkg/errors"
  19. "k8s.io/client-go/tools/clientcmd"
  20. clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
  21. certutil "k8s.io/client-go/util/cert"
  22. "k8s.io/client-go/util/keyutil"
  23. pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
  24. )
  25. // certificateReadWriter defines the behavior of a component that
  26. // read or write a certificate stored/embedded in a file
  27. type certificateReadWriter interface {
  28. // Read a certificate stored/embedded in a file
  29. Read() (*x509.Certificate, error)
  30. // Write (update) a certificate stored/embedded in a file
  31. Write(*x509.Certificate, crypto.Signer) error
  32. }
  33. // pkiCertificateReadWriter defines a certificateReadWriter for certificate files
  34. // in the K8s pki managed by kubeadm
  35. type pkiCertificateReadWriter struct {
  36. baseName string
  37. certificateDir string
  38. }
  39. // newPKICertificateReadWriter return a new pkiCertificateReadWriter
  40. func newPKICertificateReadWriter(certificateDir string, baseName string) *pkiCertificateReadWriter {
  41. return &pkiCertificateReadWriter{
  42. baseName: baseName,
  43. certificateDir: certificateDir,
  44. }
  45. }
  46. // Read a certificate from a file the K8s pki managed by kubeadm
  47. func (rw *pkiCertificateReadWriter) Read() (*x509.Certificate, error) {
  48. certificatePath, _ := pkiutil.PathsForCertAndKey(rw.certificateDir, rw.baseName)
  49. certs, err := certutil.CertsFromFile(certificatePath)
  50. if err != nil {
  51. return nil, errors.Wrapf(err, "failed to load existing certificate %s", rw.baseName)
  52. }
  53. if len(certs) != 1 {
  54. return nil, errors.Errorf("wanted exactly one certificate, got %d", len(certs))
  55. }
  56. return certs[0], nil
  57. }
  58. // Write a certificate to files in the K8s pki managed by kubeadm
  59. func (rw *pkiCertificateReadWriter) Write(newCert *x509.Certificate, newKey crypto.Signer) error {
  60. if err := pkiutil.WriteCertAndKey(rw.certificateDir, rw.baseName, newCert, newKey); err != nil {
  61. return errors.Wrapf(err, "failed to write new certificate %s", rw.baseName)
  62. }
  63. return nil
  64. }
  65. // kubeConfigReadWriter defines a certificateReadWriter for certificate files
  66. // embedded in the kubeConfig files managed by kubeadm, and more specifically
  67. // for the client certificate of the AuthInfo
  68. type kubeConfigReadWriter struct {
  69. kubernetesDir string
  70. kubeConfigFileName string
  71. kubeConfigFilePath string
  72. kubeConfig *clientcmdapi.Config
  73. }
  74. // newKubeconfigReadWriter return a new kubeConfigReadWriter
  75. func newKubeconfigReadWriter(kubernetesDir string, kubeConfigFileName string) *kubeConfigReadWriter {
  76. return &kubeConfigReadWriter{
  77. kubernetesDir: kubernetesDir,
  78. kubeConfigFileName: kubeConfigFileName,
  79. kubeConfigFilePath: filepath.Join(kubernetesDir, kubeConfigFileName),
  80. }
  81. }
  82. // Read a certificate embedded in kubeConfig file managed by kubeadm.
  83. // Please note that the kubeConfig file itself is kept in the ReadWriter state thus allowing
  84. // to preserve the attributes (Context, Servers, AuthInfo etc.)
  85. func (rw *kubeConfigReadWriter) Read() (*x509.Certificate, error) {
  86. // try to load the kubeConfig file
  87. kubeConfig, err := clientcmd.LoadFromFile(rw.kubeConfigFilePath)
  88. if err != nil {
  89. return nil, errors.Wrapf(err, "failed to load kubeConfig file %s", rw.kubeConfigFilePath)
  90. }
  91. // get current context
  92. if _, ok := kubeConfig.Contexts[kubeConfig.CurrentContext]; !ok {
  93. return nil, errors.Errorf("invalid kubeConfig file %s: missing context %s", rw.kubeConfigFilePath, kubeConfig.CurrentContext)
  94. }
  95. // get cluster info for current context and ensure a server certificate is embedded in it
  96. clusterName := kubeConfig.Contexts[kubeConfig.CurrentContext].Cluster
  97. if _, ok := kubeConfig.Clusters[clusterName]; !ok {
  98. return nil, errors.Errorf("invalid kubeConfig file %s: missing cluster %s", rw.kubeConfigFilePath, clusterName)
  99. }
  100. cluster := kubeConfig.Clusters[clusterName]
  101. if len(cluster.CertificateAuthorityData) == 0 {
  102. return nil, errors.Errorf("kubeConfig file %s does not have and embedded server certificate", rw.kubeConfigFilePath)
  103. }
  104. // get auth info for current context and ensure a client certificate is embedded in it
  105. authInfoName := kubeConfig.Contexts[kubeConfig.CurrentContext].AuthInfo
  106. if _, ok := kubeConfig.AuthInfos[authInfoName]; !ok {
  107. return nil, errors.Errorf("invalid kubeConfig file %s: missing authInfo %s", rw.kubeConfigFilePath, authInfoName)
  108. }
  109. authInfo := kubeConfig.AuthInfos[authInfoName]
  110. if len(authInfo.ClientCertificateData) == 0 {
  111. return nil, errors.Errorf("kubeConfig file %s does not have and embedded client certificate", rw.kubeConfigFilePath)
  112. }
  113. // parse the client certificate, retrive the cert config and then renew it
  114. certs, err := certutil.ParseCertsPEM(authInfo.ClientCertificateData)
  115. if err != nil {
  116. return nil, errors.Wrapf(err, "kubeConfig file %s does not contain a valid client certificate", rw.kubeConfigFilePath)
  117. }
  118. rw.kubeConfig = kubeConfig
  119. return certs[0], nil
  120. }
  121. // Write a certificate embedded in kubeConfig file managed by kubeadm
  122. // Please note that all the other attribute of the kubeConfig file are preserved, but this
  123. // requires to call Read before Write
  124. func (rw *kubeConfigReadWriter) Write(newCert *x509.Certificate, newKey crypto.Signer) error {
  125. // check if Read was called before Write
  126. if rw.kubeConfig == nil {
  127. return errors.Errorf("failed to Write kubeConfig file with renewd certs. It is necessary to call Read before Write")
  128. }
  129. // encodes the new key
  130. encodedClientKey, err := keyutil.MarshalPrivateKeyToPEM(newKey)
  131. if err != nil {
  132. return errors.Wrapf(err, "failed to marshal private key to PEM")
  133. }
  134. // get auth info for current context and ensure a client certificate is embedded in it
  135. authInfoName := rw.kubeConfig.Contexts[rw.kubeConfig.CurrentContext].AuthInfo
  136. // create a kubeConfig copy with the new client certs
  137. newConfig := rw.kubeConfig.DeepCopy()
  138. newConfig.AuthInfos[authInfoName].ClientKeyData = encodedClientKey
  139. newConfig.AuthInfos[authInfoName].ClientCertificateData = pkiutil.EncodeCertPEM(newCert)
  140. // writes the kubeConfig to disk
  141. return clientcmd.WriteToFile(*newConfig, rw.kubeConfigFilePath)
  142. }