readwriter.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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. "os"
  18. "path/filepath"
  19. "github.com/pkg/errors"
  20. "k8s.io/client-go/tools/clientcmd"
  21. clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
  22. certutil "k8s.io/client-go/util/cert"
  23. "k8s.io/client-go/util/keyutil"
  24. certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
  25. pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
  26. )
  27. // certificateReadWriter defines the behavior of a component that
  28. // read or write a certificate stored/embedded in a file
  29. type certificateReadWriter interface {
  30. //Exists return true if the certificate exists
  31. Exists() bool
  32. // Read a certificate stored/embedded in a file
  33. Read() (*x509.Certificate, error)
  34. // Write (update) a certificate stored/embedded in a file
  35. Write(*x509.Certificate, crypto.Signer) error
  36. }
  37. // pkiCertificateReadWriter defines a certificateReadWriter for certificate files
  38. // in the K8s pki managed by kubeadm
  39. type pkiCertificateReadWriter struct {
  40. baseName string
  41. certificateDir string
  42. }
  43. // newPKICertificateReadWriter return a new pkiCertificateReadWriter
  44. func newPKICertificateReadWriter(certificateDir string, baseName string) *pkiCertificateReadWriter {
  45. return &pkiCertificateReadWriter{
  46. baseName: baseName,
  47. certificateDir: certificateDir,
  48. }
  49. }
  50. // Exists checks if a certificate exist
  51. func (rw *pkiCertificateReadWriter) Exists() bool {
  52. certificatePath, _ := pkiutil.PathsForCertAndKey(rw.certificateDir, rw.baseName)
  53. return fileExists(certificatePath)
  54. }
  55. func fileExists(filename string) bool {
  56. info, err := os.Stat(filename)
  57. if os.IsNotExist(err) {
  58. return false
  59. }
  60. return !info.IsDir()
  61. }
  62. // Read a certificate from a file the K8s pki managed by kubeadm
  63. func (rw *pkiCertificateReadWriter) Read() (*x509.Certificate, error) {
  64. certificatePath, _ := pkiutil.PathsForCertAndKey(rw.certificateDir, rw.baseName)
  65. certs, err := certutil.CertsFromFile(certificatePath)
  66. if err != nil {
  67. return nil, errors.Wrapf(err, "failed to load existing certificate %s", rw.baseName)
  68. }
  69. if len(certs) != 1 {
  70. return nil, errors.Errorf("wanted exactly one certificate, got %d", len(certs))
  71. }
  72. return certs[0], nil
  73. }
  74. // Write a certificate to files in the K8s pki managed by kubeadm
  75. func (rw *pkiCertificateReadWriter) Write(newCert *x509.Certificate, newKey crypto.Signer) error {
  76. if err := pkiutil.WriteCertAndKey(rw.certificateDir, rw.baseName, newCert, newKey); err != nil {
  77. return errors.Wrapf(err, "failed to write new certificate %s", rw.baseName)
  78. }
  79. return nil
  80. }
  81. // kubeConfigReadWriter defines a certificateReadWriter for certificate files
  82. // embedded in the kubeConfig files managed by kubeadm, and more specifically
  83. // for the client certificate of the AuthInfo
  84. type kubeConfigReadWriter struct {
  85. kubernetesDir string
  86. kubeConfigFileName string
  87. kubeConfigFilePath string
  88. kubeConfig *clientcmdapi.Config
  89. baseName string
  90. certificateDir string
  91. caCert *x509.Certificate
  92. }
  93. // newKubeconfigReadWriter return a new kubeConfigReadWriter
  94. func newKubeconfigReadWriter(kubernetesDir string, kubeConfigFileName string, certificateDir, baseName string) *kubeConfigReadWriter {
  95. return &kubeConfigReadWriter{
  96. kubernetesDir: kubernetesDir,
  97. kubeConfigFileName: kubeConfigFileName,
  98. kubeConfigFilePath: filepath.Join(kubernetesDir, kubeConfigFileName),
  99. certificateDir: certificateDir,
  100. baseName: baseName,
  101. }
  102. }
  103. // Exists checks if a certificate embedded in kubeConfig file exists
  104. func (rw *kubeConfigReadWriter) Exists() bool {
  105. return fileExists(rw.kubeConfigFilePath)
  106. }
  107. // Read a certificate embedded in kubeConfig file managed by kubeadm.
  108. // Please note that the kubeConfig file itself is kept in the ReadWriter state thus allowing
  109. // to preserve the attributes (Context, Servers, AuthInfo etc.)
  110. func (rw *kubeConfigReadWriter) Read() (*x509.Certificate, error) {
  111. // try to load the kubeConfig file
  112. kubeConfig, err := clientcmd.LoadFromFile(rw.kubeConfigFilePath)
  113. if err != nil {
  114. return nil, errors.Wrapf(err, "failed to load kubeConfig file %s", rw.kubeConfigFilePath)
  115. }
  116. // The CA cert is required for updating kubeconfig files.
  117. // For local CA renewal, the local CA on disk could have changed, thus a reload is needed.
  118. // For CSR renewal we assume the same CA on disk is mounted for usage with KCM's
  119. // '--cluster-signing-cert-file' flag.
  120. caCert, _, err := certsphase.LoadCertificateAuthority(rw.certificateDir, rw.baseName)
  121. if err != nil {
  122. return nil, err
  123. }
  124. rw.caCert = caCert
  125. // get current context
  126. if _, ok := kubeConfig.Contexts[kubeConfig.CurrentContext]; !ok {
  127. return nil, errors.Errorf("invalid kubeConfig file %s: missing context %s", rw.kubeConfigFilePath, kubeConfig.CurrentContext)
  128. }
  129. // get cluster info for current context and ensure a server certificate is embedded in it
  130. clusterName := kubeConfig.Contexts[kubeConfig.CurrentContext].Cluster
  131. if _, ok := kubeConfig.Clusters[clusterName]; !ok {
  132. return nil, errors.Errorf("invalid kubeConfig file %s: missing cluster %s", rw.kubeConfigFilePath, clusterName)
  133. }
  134. cluster := kubeConfig.Clusters[clusterName]
  135. if len(cluster.CertificateAuthorityData) == 0 {
  136. return nil, errors.Errorf("kubeConfig file %s does not have an embedded server certificate", rw.kubeConfigFilePath)
  137. }
  138. // get auth info for current context and ensure a client certificate is embedded in it
  139. authInfoName := kubeConfig.Contexts[kubeConfig.CurrentContext].AuthInfo
  140. if _, ok := kubeConfig.AuthInfos[authInfoName]; !ok {
  141. return nil, errors.Errorf("invalid kubeConfig file %s: missing authInfo %s", rw.kubeConfigFilePath, authInfoName)
  142. }
  143. authInfo := kubeConfig.AuthInfos[authInfoName]
  144. if len(authInfo.ClientCertificateData) == 0 {
  145. return nil, errors.Errorf("kubeConfig file %s does not have an embedded client certificate", rw.kubeConfigFilePath)
  146. }
  147. // parse the client certificate, retrive the cert config and then renew it
  148. certs, err := certutil.ParseCertsPEM(authInfo.ClientCertificateData)
  149. if err != nil {
  150. return nil, errors.Wrapf(err, "kubeConfig file %s does not contain a valid client certificate", rw.kubeConfigFilePath)
  151. }
  152. rw.kubeConfig = kubeConfig
  153. return certs[0], nil
  154. }
  155. // Write a certificate embedded in kubeConfig file managed by kubeadm
  156. // Please note that all the other attribute of the kubeConfig file are preserved, but this
  157. // requires to call Read before Write
  158. func (rw *kubeConfigReadWriter) Write(newCert *x509.Certificate, newKey crypto.Signer) error {
  159. // check if Read was called before Write
  160. if rw.kubeConfig == nil {
  161. return errors.Errorf("failed to Write kubeConfig file with renewed certs. It is necessary to call Read before Write")
  162. }
  163. // encodes the new key
  164. encodedClientKey, err := keyutil.MarshalPrivateKeyToPEM(newKey)
  165. if err != nil {
  166. return errors.Wrapf(err, "failed to marshal private key to PEM")
  167. }
  168. // Update the embedded CA in the kubeconfig file.
  169. // This assumes that the user has kept the current context to the desired one.
  170. clusterName := rw.kubeConfig.Contexts[rw.kubeConfig.CurrentContext].Cluster
  171. cluster := rw.kubeConfig.Clusters[clusterName]
  172. cluster.CertificateAuthorityData = pkiutil.EncodeCertPEM(rw.caCert)
  173. // get auth info for current context and ensure a client certificate is embedded in it
  174. authInfoName := rw.kubeConfig.Contexts[rw.kubeConfig.CurrentContext].AuthInfo
  175. // create a kubeConfig copy with the new client certs
  176. newConfig := rw.kubeConfig.DeepCopy()
  177. newConfig.AuthInfos[authInfoName].ClientKeyData = encodedClientKey
  178. newConfig.AuthInfos[authInfoName].ClientCertificateData = pkiutil.EncodeCertPEM(newCert)
  179. // writes the kubeConfig to disk
  180. return clientcmd.WriteToFile(*newConfig, rw.kubeConfigFilePath)
  181. }