readwriter.go 7.1 KB

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