Skip to content

Commit

Permalink
feat: move to ECDSA keys for all Kubernetes/etcd certs and keys
Browse files Browse the repository at this point in the history
ECDSA keys are smaller which decreases Talos config size, they are more
efficient in terms of key generation, signing, etc., so it makes boot
performance better (and config generation as well).

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
  • Loading branch information
smira authored and talos-bot committed Feb 2, 2021
1 parent 9947ec8 commit 2277ce8
Show file tree
Hide file tree
Showing 16 changed files with 95 additions and 110 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ require (
github.com/spf13/cobra v1.1.1
github.com/stretchr/testify v1.7.0
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2
github.com/talos-systems/crypto v0.2.1-0.20210125160556-cf75519cab82
github.com/talos-systems/crypto v0.2.1-0.20210202170911-39584f1b6e54
github.com/talos-systems/go-blockdevice v0.2.0
github.com/talos-systems/go-loadbalancer v0.1.0
github.com/talos-systems/go-procfs v0.0.0-20210108152626-8cbc42d3dc24
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -856,8 +856,8 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0TYG7HtkIgExQo+2RdLuwRft63jn2HWj8=
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/talos-systems/crypto v0.2.1-0.20210125160556-cf75519cab82 h1:5TsM3o/yJJF6kakHyPee88D0yWNNDNKZJ2NCX9MFsKk=
github.com/talos-systems/crypto v0.2.1-0.20210125160556-cf75519cab82/go.mod h1:OXCK52Q0dzm88YRG4VdTBdidkPUtqrCxCyW7bUs4DAw=
github.com/talos-systems/crypto v0.2.1-0.20210202170911-39584f1b6e54 h1:2IGs3f0qG7f33Fv37XuYzIMxLARl4dcpwahZXLmdT2A=
github.com/talos-systems/crypto v0.2.1-0.20210202170911-39584f1b6e54/go.mod h1:OXCK52Q0dzm88YRG4VdTBdidkPUtqrCxCyW7bUs4DAw=
github.com/talos-systems/go-blockdevice v0.2.0 h1:tTu0ak3GfF8iSxNsdsicVhTKebIcyBARQYxhRV86AF0=
github.com/talos-systems/go-blockdevice v0.2.0/go.mod h1:DGbop5CJa0PYdhQK9cNVF61pPJNedas1m7Gi/qAnrsM=
github.com/talos-systems/go-loadbalancer v0.1.0 h1:MQFONvSjoleU8RrKq1O1Z8CyTCJGd4SLqdAHDlR6o9s=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func (ctrl *RenderSecretsStaticPodController) Run(ctx context.Context, r control

secrets := secretsRes.(*secrets.Kubernetes).Secrets()

serviceAccountKey, err := secrets.ServiceAccount.GetRSAKey()
serviceAccountKey, err := secrets.ServiceAccount.GetKey()
if err != nil {
return fmt.Errorf("error parsing service account key: %w", err)
}
Expand Down Expand Up @@ -121,8 +121,8 @@ func (ctrl *RenderSecretsStaticPodController) Run(ctx context.Context, r control
{
getter: func() *x509.PEMEncodedCertificateAndKey {
return &x509.PEMEncodedCertificateAndKey{
Crt: serviceAccountKey.PublicKeyPEM,
Key: serviceAccountKey.KeyPEM,
Crt: serviceAccountKey.GetPublicKeyPEM(),
Key: serviceAccountKey.GetPrivateKeyPEM(),
}
},
certFilename: "service-account.pub",
Expand Down Expand Up @@ -161,8 +161,8 @@ func (ctrl *RenderSecretsStaticPodController) Run(ctx context.Context, r control
{
getter: func() *x509.PEMEncodedCertificateAndKey {
return &x509.PEMEncodedCertificateAndKey{
Crt: serviceAccountKey.PublicKeyPEM,
Key: serviceAccountKey.KeyPEM,
Crt: serviceAccountKey.GetPublicKeyPEM(),
Key: serviceAccountKey.GetPrivateKeyPEM(),
}
},
keyFilename: "service-account.key",
Expand Down
7 changes: 2 additions & 5 deletions internal/app/machined/pkg/controllers/secrets/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,12 @@ func (ctrl *KubernetesController) updateSecrets(cfgProvider talosconfig.Provider
"kubernetes.default.svc."+cfgProvider.Cluster().Network().DNSDomain(),
)

ca, err := x509.NewCertificateAuthorityFromCertificateAndKey(k8sSecrets.Secrets().CA, x509.RSA(true))
ca, err := x509.NewCertificateAuthorityFromCertificateAndKey(k8sSecrets.Secrets().CA)
if err != nil {
return fmt.Errorf("failed to parse CA certificate: %w", err)
}

apiServer, err := x509.NewKeyPair(ca,
x509.RSA(true),
x509.IPAddresses(altNames.IPs),
x509.DNSNames(altNames.DNSNames),
x509.CommonName("kube-apiserver"),
Expand All @@ -200,7 +199,6 @@ func (ctrl *KubernetesController) updateSecrets(cfgProvider talosconfig.Provider
k8sSecrets.Secrets().APIServer = x509.NewCertificateAndKeyFromKeyPair(apiServer)

apiServerKubeletClient, err := x509.NewKeyPair(ca,
x509.RSA(true),
x509.CommonName(constants.KubernetesAdminCertCommonName),
x509.Organization(constants.KubernetesAdminCertOrganization),
x509.NotAfter(time.Now().Add(constants.KubernetesDefaultCertificateValidityDuration)),
Expand All @@ -213,13 +211,12 @@ func (ctrl *KubernetesController) updateSecrets(cfgProvider talosconfig.Provider

k8sSecrets.Secrets().ServiceAccount = cfgProvider.Cluster().ServiceAccount()

aggregatorCA, err := x509.NewCertificateAuthorityFromCertificateAndKey(k8sSecrets.Secrets().AggregatorCA, x509.RSA(true))
aggregatorCA, err := x509.NewCertificateAuthorityFromCertificateAndKey(k8sSecrets.Secrets().AggregatorCA)
if err != nil {
return fmt.Errorf("failed to parse aggregator CA: %w", err)
}

frontProxy, err := x509.NewKeyPair(aggregatorCA,
x509.RSA(true),
x509.CommonName("front-proxy-client"),
x509.NotAfter(time.Now().Add(constants.KubernetesDefaultCertificateValidityDuration)),
)
Expand Down
6 changes: 6 additions & 0 deletions internal/integration/provision/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type upgradeSpec struct {

UpgradePreserve bool
UpgradeStage bool
UseRSAKeys bool
}

const (
Expand Down Expand Up @@ -105,6 +106,7 @@ func upgradeBetweenTwoLastReleases() upgradeSpec {

MasterNodes: DefaultSettings.MasterNodes,
WorkerNodes: DefaultSettings.WorkerNodes,
UseRSAKeys: true, // ECDSA is supported with Talos >= 0.9
}
}

Expand All @@ -125,6 +127,7 @@ func upgradeStableReleaseToCurrent() upgradeSpec {

MasterNodes: DefaultSettings.MasterNodes,
WorkerNodes: DefaultSettings.WorkerNodes,
UseRSAKeys: true, // ECDSA is supported with Talos >= 0.9
}
}

Expand All @@ -146,6 +149,7 @@ func upgradeSingeNodePreserve() upgradeSpec {
MasterNodes: 1,
WorkerNodes: 0,
UpgradePreserve: true,
UseRSAKeys: true, // ECDSA is supported with Talos >= 0.9
}
}

Expand All @@ -168,6 +172,7 @@ func upgradeSingeNodeStage() upgradeSpec {
WorkerNodes: 0,
UpgradePreserve: true,
UpgradeStage: true,
UseRSAKeys: true, // ECDSA is supported with Talos >= 0.9
}
}

Expand Down Expand Up @@ -319,6 +324,7 @@ func (suite *UpgradeSuite) setupCluster() {
ClusterName: clusterName,
Endpoint: suite.controlPlaneEndpoint,
KubeVersion: "", // keep empty so that default version is used per Talos version
UseRSAKeys: suite.spec.UseRSAKeys,
GenOptions: append(
genOptions,
generate.WithEndpointList(masterEndpoints),
Expand Down
2 changes: 1 addition & 1 deletion internal/pkg/configuration/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func Generate(ctx context.Context, in *machine.GenerateConfigurationRequest) (re

switch {
case os.IsNotExist(err):
secrets, err = generate.NewSecretsBundle(clock)
secrets, err = generate.NewSecretsBundle(clock, false)
if err != nil {
return nil, err
}
Expand Down
1 change: 0 additions & 1 deletion internal/pkg/etcd/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ func GeneratePeerCert(etcdCA *x509.PEMEncodedCertificateAndKey) (*x509.PEMEncode
opts := []x509.Option{
x509.CommonName(hostname),
x509.DNSNames(dnsNames),
x509.RSA(true),
x509.IPAddresses(ips),
x509.NotAfter(time.Now().Add(87600 * time.Hour)),
}
Expand Down
18 changes: 7 additions & 11 deletions internal/pkg/kubeconfig/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,25 +45,21 @@ func GenerateAdmin(config config.ClusterConfig, out io.Writer) error {
return fmt.Errorf("error parsing kubeconfig template: %w", err)
}

k8sCA, err := config.CA().GetCert()
k8sCA, err := x509.NewCertificateAuthorityFromCertificateAndKey(config.CA())
if err != nil {
return fmt.Errorf("error getting Kubernetes CA certificate: %w", err)
return fmt.Errorf("error getting Kubernetes CA: %w", err)
}

k8sKey, err := config.CA().GetRSAKey()
if err != nil {
return fmt.Errorf("error parsing Kubernetes key: %w", err)
}

adminCert, err := x509.NewCertficateAndKey(k8sCA, k8sKey,
x509.RSA(true),
adminCert, err := x509.NewKeyPair(k8sCA,
x509.CommonName(constants.KubernetesAdminCertCommonName),
x509.Organization(constants.KubernetesAdminCertOrganization),
x509.NotAfter(time.Now().Add(config.AdminKubeconfig().CertLifetime())))
if err != nil {
return fmt.Errorf("error generating admin certificate: %w", err)
}

adminCertPEM := x509.NewCertificateAndKeyFromKeyPair(adminCert)

input := struct {
Cluster string
CACert string
Expand All @@ -73,8 +69,8 @@ func GenerateAdmin(config config.ClusterConfig, out io.Writer) error {
}{
Cluster: config.Name(),
CACert: base64.StdEncoding.EncodeToString(config.CA().Crt),
AdminCert: base64.StdEncoding.EncodeToString(adminCert.Crt),
AdminKey: base64.StdEncoding.EncodeToString(adminCert.Key),
AdminCert: base64.StdEncoding.EncodeToString(adminCertPEM.Crt),
AdminKey: base64.StdEncoding.EncodeToString(adminCertPEM.Key),
Server: config.Endpoint().String(),
}

Expand Down
60 changes: 33 additions & 27 deletions internal/pkg/kubeconfig/admin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,37 +24,43 @@ type AdminSuite struct {
}

func (suite *AdminSuite) TestGenerate() {
ca, err := x509.NewSelfSignedCertificateAuthority(x509.RSA(true))
suite.Require().NoError(err)

u, err := url.Parse("http://localhost:3333/api")
suite.Require().NoError(err)

cfg := &v1alpha1.ClusterConfig{
ClusterName: "talos1",
ClusterCA: &x509.PEMEncodedCertificateAndKey{
Crt: ca.CrtPEM,
Key: ca.KeyPEM,
},
ControlPlane: &v1alpha1.ControlPlaneConfig{
Endpoint: &v1alpha1.Endpoint{
URL: u,
},
},
AdminKubeconfigConfig: v1alpha1.AdminKubeconfigConfig{
AdminKubeconfigCertLifetime: time.Hour,
},
}
for _, rsa := range []bool{true, false} {
rsa := rsa

suite.Run(fmt.Sprintf("RSA=%v", rsa), func() {
ca, err := x509.NewSelfSignedCertificateAuthority(x509.RSA(rsa))
suite.Require().NoError(err)

u, err := url.Parse("http://localhost:3333/api")
suite.Require().NoError(err)

var buf bytes.Buffer
cfg := &v1alpha1.ClusterConfig{
ClusterName: "talos1",
ClusterCA: &x509.PEMEncodedCertificateAndKey{
Crt: ca.CrtPEM,
Key: ca.KeyPEM,
},
ControlPlane: &v1alpha1.ControlPlaneConfig{
Endpoint: &v1alpha1.Endpoint{
URL: u,
},
},
AdminKubeconfigConfig: v1alpha1.AdminKubeconfigConfig{
AdminKubeconfigCertLifetime: time.Hour,
},
}

suite.Require().NoError(kubeconfig.GenerateAdmin(cfg, &buf))
var buf bytes.Buffer

// verify config via k8s client
config, err := clientcmd.Load(buf.Bytes())
suite.Require().NoError(err)
suite.Require().NoError(kubeconfig.GenerateAdmin(cfg, &buf))

suite.Assert().NoError(clientcmd.ConfirmUsable(*config, fmt.Sprintf("admin@%s", cfg.ClusterName)))
// verify config via k8s client
config, err := clientcmd.Load(buf.Bytes())
suite.Require().NoError(err)

suite.Assert().NoError(clientcmd.ConfirmUsable(*config, fmt.Sprintf("admin@%s", cfg.ClusterName)))
})
}
}

func TestAdminSuite(t *testing.T) {
Expand Down
28 changes: 5 additions & 23 deletions pkg/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ package kubernetes

import (
"context"
stdlibx509 "crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"log"
Expand Down Expand Up @@ -103,38 +101,22 @@ func NewClientFromPKI(ca, crt, key []byte, endpoint *url.URL) (client *Client, e
// with a TTL of 10 minutes.
func NewTemporaryClientFromPKI(ca *x509.PEMEncodedCertificateAndKey, endpoint *url.URL) (client *Client, err error) {
opts := []x509.Option{
x509.RSA(true),
x509.CommonName("admin"),
x509.Organization("system:masters"),
x509.NotAfter(time.Now().Add(10 * time.Minute)),
}

key, err := x509.NewRSAKey()
k8sCA, err := x509.NewCertificateAuthorityFromCertificateAndKey(ca)
if err != nil {
return nil, fmt.Errorf("failed to create RSA key: %w", err)
return nil, fmt.Errorf("failed decoding Kubernetes CA: %w", err)
}

keyBlock, _ := pem.Decode(key.KeyPEM)
if keyBlock == nil {
return nil, errors.New("failed to decode key")
}

keyRSA, err := stdlibx509.ParsePKCS1PrivateKey(keyBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse private key: %w", err)
}

csr, err := x509.NewCertificateSigningRequest(keyRSA, opts...)
if err != nil {
return nil, fmt.Errorf("failed to create CSR: %w", err)
}

crt, err := x509.NewCertificateFromCSRBytes(ca.Crt, ca.Key, csr.X509CertificateRequestPEM, opts...)
keyPair, err := x509.NewKeyPair(k8sCA, opts...)
if err != nil {
return nil, fmt.Errorf("failed to create certificate from CSR: %w", err)
return nil, fmt.Errorf("failed generating temporary cert: %w", err)
}

h, err := NewClientFromPKI(ca.Crt, crt.X509CertificatePEM, key.KeyPEM, endpoint)
h, err := NewClientFromPKI(ca.Crt, keyPair.CrtPEM, keyPair.KeyPEM, endpoint)
if err != nil {
return nil, fmt.Errorf("failed to create client: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/machinery/config/types/v1alpha1/bundle/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func NewConfigBundle(opts ...Option) (*v1alpha1.ConfigBundle, error) {
fmt.Println("generating PKI and tokens")
}

secrets, err := generate.NewSecretsBundle(generate.NewClock())
secrets, err := generate.NewSecretsBundle(generate.NewClock(), options.InputOptions.UseRSAKeys)
if err != nil {
return bundle, err
}
Expand Down
1 change: 1 addition & 0 deletions pkg/machinery/config/types/v1alpha1/bundle/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type InputOptions struct {
Endpoint string
KubeVersion string
GenOptions []generate.GenOption
UseRSAKeys bool
}

// Options describes generate parameters.
Expand Down
Loading

0 comments on commit 2277ce8

Please sign in to comment.