mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
Merge pull request #90143 from neolit123/1.19-remove-cert-renew-api
kubeadm: remove usage of the "certificates" API for cert renewal
This commit is contained in:
commit
b8b4186a14
@ -122,7 +122,6 @@ type renewFlags struct {
|
||||
cfgPath string
|
||||
kubeconfigPath string
|
||||
cfg kubeadmapiv1beta2.ClusterConfiguration
|
||||
useAPI bool
|
||||
csrOnly bool
|
||||
csrPath string
|
||||
}
|
||||
@ -210,12 +209,6 @@ func addRenewFlags(cmd *cobra.Command, flags *renewFlags) {
|
||||
options.AddKubeConfigFlag(cmd.Flags(), &flags.kubeconfigPath)
|
||||
options.AddCSRFlag(cmd.Flags(), &flags.csrOnly)
|
||||
options.AddCSRDirFlag(cmd.Flags(), &flags.csrPath)
|
||||
// TODO: remove the flag and related logic once legacy signers are removed,
|
||||
// potentially with the release of certificates.k8s.io/v1:
|
||||
// https://github.com/kubernetes/kubeadm/issues/2047
|
||||
cmd.Flags().BoolVar(&flags.useAPI, "use-api", flags.useAPI, "Use the Kubernetes certificate API to renew certificates")
|
||||
cmd.Flags().MarkDeprecated("use-api", "certificate renewal from kubeadm using the Kubernetes API "+
|
||||
"is deprecated and will be removed when 'certificates.k8s.io/v1' releases.")
|
||||
}
|
||||
|
||||
func renewCert(flags *renewFlags, kdir string, internalcfg *kubeadmapi.InitConfiguration, handler *renewal.CertificateRenewHandler) error {
|
||||
@ -241,29 +234,15 @@ func renewCert(flags *renewFlags, kdir string, internalcfg *kubeadmapi.InitConfi
|
||||
|
||||
// otherwise, the renewal operation has to actually renew a certificate
|
||||
|
||||
// renew the certificate using the requested renew method
|
||||
if flags.useAPI {
|
||||
// renew using K8s certificate API
|
||||
kubeConfigPath := cmdutil.GetKubeConfigPath(flags.kubeconfigPath)
|
||||
client, err := kubeconfigutil.ClientSetFromFile(kubeConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := rm.RenewUsingCSRAPI(handler.Name, client); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// renew using local certificate authorities.
|
||||
// this operation can't complete in case the certificate key is not provided (external CA)
|
||||
renewed, err := rm.RenewUsingLocalCA(handler.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !renewed {
|
||||
fmt.Printf("Detected external %s, %s can't be renewed\n", handler.CABaseName, handler.LongName)
|
||||
return nil
|
||||
}
|
||||
// renew using local certificate authorities.
|
||||
// this operation can't complete in case the certificate key is not provided (external CA)
|
||||
renewed, err := rm.RenewUsingLocalCA(handler.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !renewed {
|
||||
fmt.Printf("Detected external %s, %s can't be renewed\n", handler.CABaseName, handler.LongName)
|
||||
return nil
|
||||
}
|
||||
fmt.Printf("%s renewed\n", handler.LongName)
|
||||
return nil
|
||||
|
@ -39,7 +39,6 @@ func TestCommandsGenerated(t *testing.T) {
|
||||
expectedFlags := []string{
|
||||
"cert-dir",
|
||||
"config",
|
||||
"use-api",
|
||||
}
|
||||
|
||||
expectedCommands := []string{
|
||||
|
@ -3,7 +3,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"apirenewer.go",
|
||||
"expiration.go",
|
||||
"filerenewer.go",
|
||||
"manager.go",
|
||||
@ -16,14 +15,9 @@ go_library(
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//cmd/kubeadm/app/phases/certs:go_default_library",
|
||||
"//cmd/kubeadm/app/util/pkiutil:go_default_library",
|
||||
"//staging/src/k8s.io/api/certificates/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/kubernetes/typed/certificates/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/tools/clientcmd/api:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/util/cert:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/util/certificate/csr:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/util/keyutil:go_default_library",
|
||||
"//vendor/github.com/pkg/errors:go_default_library",
|
||||
],
|
||||
@ -32,7 +26,6 @@ go_library(
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"apirenewer_test.go",
|
||||
"expiration_test.go",
|
||||
"filerenewer_test.go",
|
||||
"manager_test.go",
|
||||
@ -46,12 +39,6 @@ go_test(
|
||||
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
|
||||
"//cmd/kubeadm/app/util/pkiutil:go_default_library",
|
||||
"//cmd/kubeadm/test:go_default_library",
|
||||
"//staging/src/k8s.io/api/certificates/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/kubernetes/typed/certificates/v1beta1/fake:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/testing:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/util/cert:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/util/keyutil:go_default_library",
|
||||
|
@ -1,134 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package renewal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
certsapi "k8s.io/api/certificates/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
certstype "k8s.io/client-go/kubernetes/typed/certificates/v1beta1"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
csrutil "k8s.io/client-go/util/certificate/csr"
|
||||
pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
|
||||
)
|
||||
|
||||
const certAPIPrefixName = "kubeadm-cert"
|
||||
|
||||
var watchTimeout = 5 * time.Minute
|
||||
|
||||
// APIRenewer define a certificate renewer implementation that uses the K8s certificate API
|
||||
type APIRenewer struct {
|
||||
client certstype.CertificatesV1beta1Interface
|
||||
}
|
||||
|
||||
// NewAPIRenewer a new certificate renewer implementation that uses the K8s certificate API
|
||||
func NewAPIRenewer(client clientset.Interface) *APIRenewer {
|
||||
return &APIRenewer{
|
||||
client: client.CertificatesV1beta1(),
|
||||
}
|
||||
}
|
||||
|
||||
// Renew a certificate using the K8s certificate API
|
||||
func (r *APIRenewer) Renew(cfg *pkiutil.CertConfig) (*x509.Certificate, crypto.Signer, error) {
|
||||
reqTmp := &x509.CertificateRequest{
|
||||
Subject: pkix.Name{
|
||||
CommonName: cfg.CommonName,
|
||||
Organization: cfg.Organization,
|
||||
},
|
||||
DNSNames: cfg.AltNames.DNSNames,
|
||||
IPAddresses: cfg.AltNames.IPs,
|
||||
}
|
||||
|
||||
key, err := pkiutil.NewPrivateKey(cfg.PublicKeyAlgorithm)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "couldn't create new private key")
|
||||
}
|
||||
|
||||
csr, err := certutil.MakeCSRFromTemplate(key, reqTmp)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "couldn't create certificate signing request")
|
||||
}
|
||||
|
||||
usages := make([]certsapi.KeyUsage, len(cfg.Usages))
|
||||
for i, usage := range cfg.Usages {
|
||||
certsAPIUsage, ok := usageMap[usage]
|
||||
if !ok {
|
||||
return nil, nil, errors.Errorf("unknown key usage: %v", usage)
|
||||
}
|
||||
usages[i] = certsAPIUsage
|
||||
}
|
||||
|
||||
k8sCSR := &certsapi.CertificateSigningRequest{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: fmt.Sprintf("%s-%s-", certAPIPrefixName, cfg.CommonName),
|
||||
},
|
||||
Spec: certsapi.CertificateSigningRequestSpec{
|
||||
Request: csr,
|
||||
Usages: usages,
|
||||
},
|
||||
}
|
||||
|
||||
req, err := r.client.CertificateSigningRequests().Create(context.TODO(), k8sCSR, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "couldn't create certificate signing request")
|
||||
}
|
||||
|
||||
fmt.Printf("[certs] Certificate request %q created\n", req.Name)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), watchTimeout)
|
||||
defer cancel()
|
||||
|
||||
certData, err := csrutil.WaitForCertificate(ctx, r.client.CertificateSigningRequests(), req)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "certificate failed to appear")
|
||||
}
|
||||
|
||||
cert, err := certutil.ParseCertsPEM(certData)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "couldn't parse issued certificate")
|
||||
}
|
||||
|
||||
if len(cert) != 1 {
|
||||
return nil, nil, errors.Errorf("certificate request %q has %d certificates, wanted exactly 1", req.Name, len(cert))
|
||||
}
|
||||
|
||||
return cert[0], key, nil
|
||||
}
|
||||
|
||||
var usageMap = map[x509.ExtKeyUsage]certsapi.KeyUsage{
|
||||
x509.ExtKeyUsageAny: certsapi.UsageAny,
|
||||
x509.ExtKeyUsageServerAuth: certsapi.UsageServerAuth,
|
||||
x509.ExtKeyUsageClientAuth: certsapi.UsageClientAuth,
|
||||
x509.ExtKeyUsageCodeSigning: certsapi.UsageCodeSigning,
|
||||
x509.ExtKeyUsageEmailProtection: certsapi.UsageEmailProtection,
|
||||
x509.ExtKeyUsageIPSECEndSystem: certsapi.UsageIPsecEndSystem,
|
||||
x509.ExtKeyUsageIPSECTunnel: certsapi.UsageIPsecTunnel,
|
||||
x509.ExtKeyUsageIPSECUser: certsapi.UsageIPsecUser,
|
||||
x509.ExtKeyUsageTimeStamping: certsapi.UsageTimestamping,
|
||||
x509.ExtKeyUsageOCSPSigning: certsapi.UsageOCSPSigning,
|
||||
x509.ExtKeyUsageMicrosoftServerGatedCrypto: certsapi.UsageMicrosoftSGC,
|
||||
x509.ExtKeyUsageNetscapeServerGatedCrypto: certsapi.UsageNetscapeSGC,
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package renewal
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
certsapi "k8s.io/api/certificates/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
fakecerts "k8s.io/client-go/kubernetes/typed/certificates/v1beta1/fake"
|
||||
k8stesting "k8s.io/client-go/testing"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
|
||||
)
|
||||
|
||||
func TestAPIRenewer(t *testing.T) {
|
||||
caCertCfg := &pkiutil.CertConfig{
|
||||
Config: certutil.Config{CommonName: "kubernetes"},
|
||||
}
|
||||
caCert, caKey, err := pkiutil.NewCertificateAuthority(caCertCfg)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't create CA: %v", err)
|
||||
}
|
||||
|
||||
client := &fakecerts.FakeCertificatesV1beta1{
|
||||
Fake: &k8stesting.Fake{},
|
||||
}
|
||||
certReq := getCertReq(t, caCert, caKey)
|
||||
certReqNoCert := certReq.DeepCopy()
|
||||
certReqNoCert.Status.Certificate = nil
|
||||
client.AddReactor("get", "certificatesigningrequests", defaultReactionFunc(certReq))
|
||||
watcher := watch.NewFakeWithChanSize(3, false)
|
||||
watcher.Add(certReqNoCert)
|
||||
watcher.Modify(certReqNoCert)
|
||||
watcher.Modify(certReq)
|
||||
client.AddWatchReactor("certificatesigningrequests", k8stesting.DefaultWatchReactor(watcher, nil))
|
||||
|
||||
// override the timeout so tests are faster
|
||||
watchTimeout = time.Second
|
||||
|
||||
certCfg := &pkiutil.CertConfig{
|
||||
Config: certutil.Config{
|
||||
CommonName: "test-certs",
|
||||
AltNames: certutil.AltNames{
|
||||
DNSNames: []string{"test-domain.space"},
|
||||
},
|
||||
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
},
|
||||
}
|
||||
|
||||
renewer := &APIRenewer{
|
||||
client: client,
|
||||
}
|
||||
|
||||
cert, _, err := renewer.Renew(certCfg)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error renewing cert: %v", err)
|
||||
}
|
||||
|
||||
pool := x509.NewCertPool()
|
||||
pool.AddCert(caCert)
|
||||
|
||||
_, err = cert.Verify(x509.VerifyOptions{
|
||||
DNSName: "test-domain.space",
|
||||
Roots: pool,
|
||||
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("couldn't verify new cert: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func defaultReactionFunc(obj runtime.Object) k8stesting.ReactionFunc {
|
||||
return func(act k8stesting.Action) (bool, runtime.Object, error) {
|
||||
return true, obj, nil
|
||||
}
|
||||
}
|
||||
|
||||
func getCertReq(t *testing.T, caCert *x509.Certificate, caKey crypto.Signer) *certsapi.CertificateSigningRequest {
|
||||
cert, _, err := pkiutil.NewCertAndKey(caCert, caKey, &pkiutil.CertConfig{
|
||||
Config: certutil.Config{
|
||||
CommonName: "testcert",
|
||||
AltNames: certutil.AltNames{
|
||||
DNSNames: []string{"test-domain.space"},
|
||||
},
|
||||
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't generate cert: %v", err)
|
||||
}
|
||||
|
||||
return &certsapi.CertificateSigningRequest{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "testcert",
|
||||
},
|
||||
Status: certsapi.CertificateSigningRequestStatus{
|
||||
Conditions: []certsapi.CertificateSigningRequestCondition{
|
||||
{
|
||||
Type: certsapi.CertificateApproved,
|
||||
},
|
||||
},
|
||||
Certificate: pkiutil.EncodeCertPEM(cert),
|
||||
},
|
||||
}
|
||||
}
|
@ -21,7 +21,6 @@ import (
|
||||
"sort"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
@ -250,43 +249,6 @@ func (rm *Manager) RenewUsingLocalCA(name string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// RenewUsingCSRAPI executes certificate renewal uses the K8s certificate API.
|
||||
// For PKI certificates, use the name defined in the certsphase package, while for certificates
|
||||
// embedded in the kubeConfig files, use the kubeConfig file name defined in the kubeadm constants package.
|
||||
// If you use the CertificateRenewHandler returned by Certificates func, handler.Name already contains the right value.
|
||||
func (rm *Manager) RenewUsingCSRAPI(name string, client clientset.Interface) error {
|
||||
handler, ok := rm.certificates[name]
|
||||
if !ok {
|
||||
return errors.Errorf("%s is not a valid certificate for this cluster", name)
|
||||
}
|
||||
|
||||
// reads the current certificate
|
||||
cert, err := handler.readwriter.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// extract the certificate config
|
||||
cfg := &pkiutil.CertConfig{
|
||||
Config: certToConfig(cert),
|
||||
PublicKeyAlgorithm: rm.cfg.PublicKeyAlgorithm(),
|
||||
}
|
||||
|
||||
// create a new certificate with the same config
|
||||
newCert, newKey, err := NewAPIRenewer(client).Renew(cfg)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to renew certificate %s", name)
|
||||
}
|
||||
|
||||
// writes the new certificate to disk
|
||||
err = handler.readwriter.Write(newCert, newKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateRenewCSR generates CSR request for certificate renewal.
|
||||
// For PKI certificates, use the name defined in the certsphase package, while for certificates
|
||||
// embedded in the kubeConfig files, use the kubeConfig file name defined in the kubeadm constants package.
|
||||
|
Loading…
Reference in New Issue
Block a user