manager.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  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/x509"
  16. "sort"
  17. "github.com/pkg/errors"
  18. clientset "k8s.io/client-go/kubernetes"
  19. certutil "k8s.io/client-go/util/cert"
  20. kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
  21. kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
  22. certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
  23. "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
  24. )
  25. // Manager can be used to coordinate certificate renewal and related processes,
  26. // like CSR generation or checking certificate expiration
  27. type Manager struct {
  28. // cfg holds the kubeadm ClusterConfiguration
  29. cfg *kubeadmapi.ClusterConfiguration
  30. // kubernetesDir holds the directory where kubeConfig files are stored
  31. kubernetesDir string
  32. // certificates contains the certificateRenewHandler controlled by this manager
  33. certificates map[string]*CertificateRenewHandler
  34. }
  35. // CertificateRenewHandler defines required info for renewing a certificate
  36. type CertificateRenewHandler struct {
  37. // Name of the certificate to be used for UX.
  38. // This value can be used to trigger operations on this certificate
  39. Name string
  40. // LongName of the certificate to be used for UX
  41. LongName string
  42. // FileName defines the name (or the BaseName) of the certificate file
  43. FileName string
  44. // CABaseName define the base name for the CA that should be used for certificate renewal
  45. CABaseName string
  46. // readwriter define a CertificateReadWriter to be used for certificate renewal
  47. readwriter certificateReadWriter
  48. }
  49. // NewManager return a new certificate renewal manager ready for handling certificates in the cluster
  50. func NewManager(cfg *kubeadmapi.ClusterConfiguration, kubernetesDir string) (*Manager, error) {
  51. rm := &Manager{
  52. cfg: cfg,
  53. kubernetesDir: kubernetesDir,
  54. certificates: map[string]*CertificateRenewHandler{},
  55. }
  56. // gets the list of certificates that are expected according to the current cluster configuration
  57. certListFunc := certsphase.GetDefaultCertList
  58. if cfg.Etcd.External != nil {
  59. certListFunc = certsphase.GetCertsWithoutEtcd
  60. }
  61. certTree, err := certListFunc().AsMap().CertTree()
  62. if err != nil {
  63. return nil, err
  64. }
  65. // create a CertificateRenewHandler for each signed certificate in the certificate tree;
  66. // NB. we are not offering support for renewing CAs; this would cause serious consequences
  67. for ca, certs := range certTree {
  68. for _, cert := range certs {
  69. // create a ReadWriter for certificates stored in the K8s local PKI
  70. pkiReadWriter := newPKICertificateReadWriter(rm.cfg.CertificatesDir, cert.BaseName)
  71. // adds the certificateRenewHandler.
  72. // PKI certificates are indexed by name, that is a well know constant defined
  73. // in the certsphase package and that can be reused across all the kubeadm codebase
  74. rm.certificates[cert.Name] = &CertificateRenewHandler{
  75. Name: cert.Name,
  76. LongName: cert.LongName,
  77. FileName: cert.BaseName,
  78. CABaseName: ca.BaseName, //Nb. this is a path for etcd certs (they are stored in a subfolder)
  79. readwriter: pkiReadWriter,
  80. }
  81. }
  82. }
  83. // gets the list of certificates that should be considered for renewal
  84. kubeConfigs := []struct {
  85. longName string
  86. fileName string
  87. }{
  88. {
  89. longName: "certificate embedded in the kubeconfig file for the admin to use and for kubeadm itself",
  90. fileName: kubeadmconstants.AdminKubeConfigFileName,
  91. },
  92. {
  93. longName: "certificate embedded in the kubeconfig file for the controller manager to use",
  94. fileName: kubeadmconstants.ControllerManagerKubeConfigFileName,
  95. },
  96. {
  97. longName: "certificate embedded in the kubeconfig file for the scheduler manager to use",
  98. fileName: kubeadmconstants.SchedulerKubeConfigFileName,
  99. },
  100. //NB. we are escluding KubeletKubeConfig from renewal because management of this certificate is delegated to kubelet
  101. }
  102. // create a CertificateRenewHandler for each kubeConfig file
  103. for _, kubeConfig := range kubeConfigs {
  104. // create a ReadWriter for certificates embedded in kubeConfig files
  105. kubeConfigReadWriter := newKubeconfigReadWriter(kubernetesDir, kubeConfig.fileName)
  106. // adds the certificateRenewHandler.
  107. // Certificates embedded kubeConfig files in are indexed by fileName, that is a well know constant defined
  108. // in the kubeadm constants package and that can be reused across all the kubeadm codebase
  109. rm.certificates[kubeConfig.fileName] = &CertificateRenewHandler{
  110. Name: kubeConfig.fileName, // we are using fileName as name, because there is nothing similar outside
  111. LongName: kubeConfig.longName,
  112. FileName: kubeConfig.fileName,
  113. CABaseName: kubeadmconstants.CACertAndKeyBaseName, // all certificates in kubeConfig files are signed by the Kubernetes CA
  114. readwriter: kubeConfigReadWriter,
  115. }
  116. }
  117. return rm, nil
  118. }
  119. // Certificates return the list of certificates controlled by this Manager
  120. func (rm *Manager) Certificates() []*CertificateRenewHandler {
  121. certificates := []*CertificateRenewHandler{}
  122. for _, h := range rm.certificates {
  123. certificates = append(certificates, h)
  124. }
  125. sort.Slice(certificates, func(i, j int) bool { return certificates[i].Name < certificates[j].Name })
  126. return certificates
  127. }
  128. // RenewUsingLocalCA executes certificate renewal using local certificate authorities for generating new certs.
  129. // For PKI certificates, use the name defined in the certsphase package, while for certificates
  130. // embedded in the kubeConfig files, use the kubeConfig file name defined in the kubeadm constants package.
  131. // If you use the CertificateRenewHandler returned by Certificates func, handler.Name already contains the right value.
  132. func (rm *Manager) RenewUsingLocalCA(name string) (bool, error) {
  133. handler, ok := rm.certificates[name]
  134. if !ok {
  135. return false, errors.Errorf("%s is not a valid certificate for this cluster", name)
  136. }
  137. // checks if the certificate is externally managed (CA certificate provided without the certificate key)
  138. externallyManaged, err := rm.IsExternallyManaged(handler)
  139. if err != nil {
  140. return false, err
  141. }
  142. // in case of external CA it is not possible to renew certificates, then return early
  143. if externallyManaged {
  144. return false, nil
  145. }
  146. // reads the current certificate
  147. cert, err := handler.readwriter.Read()
  148. if err != nil {
  149. return false, err
  150. }
  151. // extract the certificate config
  152. cfg := certToConfig(cert)
  153. // reads the CA
  154. caCert, caKey, err := certsphase.LoadCertificateAuthority(rm.cfg.CertificatesDir, handler.CABaseName)
  155. if err != nil {
  156. return false, err
  157. }
  158. // create a new certificate with the same config
  159. newCert, newKey, err := NewFileRenewer(caCert, caKey).Renew(cfg)
  160. if err != nil {
  161. return false, errors.Wrapf(err, "failed to renew certificate %s", name)
  162. }
  163. // writes the new certificate to disk
  164. err = handler.readwriter.Write(newCert, newKey)
  165. if err != nil {
  166. return false, err
  167. }
  168. return true, nil
  169. }
  170. // RenewUsingCSRAPI executes certificate renewal uses the K8s certificate API.
  171. // For PKI certificates, use the name defined in the certsphase package, while for certificates
  172. // embedded in the kubeConfig files, use the kubeConfig file name defined in the kubeadm constants package.
  173. // If you use the CertificateRenewHandler returned by Certificates func, handler.Name already contains the right value.
  174. func (rm *Manager) RenewUsingCSRAPI(name string, client clientset.Interface) error {
  175. handler, ok := rm.certificates[name]
  176. if !ok {
  177. return errors.Errorf("%s is not a valid certificate for this cluster", name)
  178. }
  179. // reads the current certificate
  180. cert, err := handler.readwriter.Read()
  181. if err != nil {
  182. return err
  183. }
  184. // extract the certificate config
  185. cfg := certToConfig(cert)
  186. // create a new certificate with the same config
  187. newCert, newKey, err := NewAPIRenewer(client).Renew(cfg)
  188. if err != nil {
  189. return errors.Wrapf(err, "failed to renew certificate %s", name)
  190. }
  191. // writes the new certificate to disk
  192. err = handler.readwriter.Write(newCert, newKey)
  193. if err != nil {
  194. return err
  195. }
  196. return nil
  197. }
  198. // CreateRenewCSR generates CSR request for certificate renewal.
  199. // For PKI certificates, use the name defined in the certsphase package, while for certificates
  200. // embedded in the kubeConfig files, use the kubeConfig file name defined in the kubeadm constants package.
  201. // If you use the CertificateRenewHandler returned by Certificates func, handler.Name already contains the right value.
  202. func (rm *Manager) CreateRenewCSR(name, outdir string) error {
  203. handler, ok := rm.certificates[name]
  204. if !ok {
  205. return errors.Errorf("%s is not a known certificate", name)
  206. }
  207. // reads the current certificate
  208. cert, err := handler.readwriter.Read()
  209. if err != nil {
  210. return err
  211. }
  212. // extracts the certificate config
  213. cfg := certToConfig(cert)
  214. // generates the CSR request and save it
  215. csr, key, err := pkiutil.NewCSRAndKey(cfg)
  216. if err := pkiutil.WriteKey(outdir, name, key); err != nil {
  217. return errors.Wrapf(err, "failure while saving %s key", name)
  218. }
  219. if err := pkiutil.WriteCSR(outdir, name, csr); err != nil {
  220. return errors.Wrapf(err, "failure while saving %s CSR", name)
  221. }
  222. return nil
  223. }
  224. // GetExpirationInfo returns certificate expiration info.
  225. // For PKI certificates, use the name defined in the certsphase package, while for certificates
  226. // embedded in the kubeConfig files, use the kubeConfig file name defined in the kubeadm constants package.
  227. // If you use the CertificateRenewHandler returned by Certificates func, handler.Name already contains the right value.
  228. func (rm *Manager) GetExpirationInfo(name string) (*ExpirationInfo, error) {
  229. handler, ok := rm.certificates[name]
  230. if !ok {
  231. return nil, errors.Errorf("%s is not a known certificate", name)
  232. }
  233. // checks if the certificate is externally managed (CA certificate provided without the certificate key)
  234. externallyManaged, err := rm.IsExternallyManaged(handler)
  235. if err != nil {
  236. return nil, err
  237. }
  238. // reads the current certificate
  239. cert, err := handler.readwriter.Read()
  240. if err != nil {
  241. return nil, err
  242. }
  243. // returns the certificate expiration info
  244. return newExpirationInfo(name, cert, externallyManaged), nil
  245. }
  246. // IsExternallyManaged checks if we are in the external CA case (CA certificate provided without the certificate key)
  247. func (rm *Manager) IsExternallyManaged(h *CertificateRenewHandler) (bool, error) {
  248. switch h.CABaseName {
  249. case kubeadmconstants.CACertAndKeyBaseName:
  250. externallyManaged, err := certsphase.UsingExternalCA(rm.cfg)
  251. if err != nil {
  252. return false, errors.Wrapf(err, "Error checking external CA condition for %s certificate authority", h.CABaseName)
  253. }
  254. return externallyManaged, nil
  255. case kubeadmconstants.FrontProxyCACertAndKeyBaseName:
  256. externallyManaged, err := certsphase.UsingExternalFrontProxyCA(rm.cfg)
  257. if err != nil {
  258. return false, errors.Wrapf(err, "Error checking external CA condition for %s certificate authority", h.CABaseName)
  259. }
  260. return externallyManaged, nil
  261. case kubeadmconstants.EtcdCACertAndKeyBaseName:
  262. return false, nil
  263. default:
  264. return false, errors.Errorf("unknown certificate authority %s", h.CABaseName)
  265. }
  266. }
  267. func certToConfig(cert *x509.Certificate) *certutil.Config {
  268. return &certutil.Config{
  269. CommonName: cert.Subject.CommonName,
  270. Organization: cert.Subject.Organization,
  271. AltNames: certutil.AltNames{
  272. IPs: cert.IPAddresses,
  273. DNSNames: cert.DNSNames,
  274. },
  275. Usages: cert.ExtKeyUsage,
  276. }
  277. }