Use Certificate Requests to issue Kubelet Certificates and set CA (#261)

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2022-07-15 09:33:11 +02:00 committed by GitHub
parent 49e98286a9
commit c6ff34f4d2
13 changed files with 451 additions and 159 deletions

View File

@ -11,6 +11,7 @@ import (
"time"
"github.com/edgelesssys/constellation/bootstrapper/internal/diskencryption"
"github.com/edgelesssys/constellation/bootstrapper/internal/kubelet"
"github.com/edgelesssys/constellation/bootstrapper/nodestate"
"github.com/edgelesssys/constellation/bootstrapper/role"
"github.com/edgelesssys/constellation/internal/cloud/metadata"
@ -39,6 +40,7 @@ type JoinClient struct {
diskUUID string
nodeName string
role role.Role
validIPs []net.IP
disk encryptedDisk
fileHandler file.Handler
@ -190,6 +192,11 @@ func (c *JoinClient) join(serviceEndpoint string) error {
ctx, cancel := c.timeoutCtx()
defer cancel()
certificateRequest, kubeletKey, err := kubelet.GetCertificateRequest(c.nodeName, c.validIPs)
if err != nil {
return err
}
conn, err := c.dialer.Dial(ctx, serviceEndpoint)
if err != nil {
c.log.With(zap.String("endpoint", serviceEndpoint), zap.Error(err)).Errorf("Join service unreachable")
@ -199,9 +206,9 @@ func (c *JoinClient) join(serviceEndpoint string) error {
protoClient := joinproto.NewAPIClient(conn)
req := &joinproto.IssueJoinTicketRequest{
DiskUuid: c.diskUUID,
NodeName: c.nodeName,
IsControlPlane: c.role == role.ControlPlane,
DiskUuid: c.diskUUID,
CertificateRequest: certificateRequest,
IsControlPlane: c.role == role.ControlPlane,
}
ticket, err := protoClient.IssueJoinTicket(ctx, req)
if err != nil {
@ -209,10 +216,10 @@ func (c *JoinClient) join(serviceEndpoint string) error {
return fmt.Errorf("issuing join ticket: %w", err)
}
return c.startNodeAndJoin(ticket)
return c.startNodeAndJoin(ticket, kubeletKey)
}
func (c *JoinClient) startNodeAndJoin(ticket *joinproto.IssueJoinTicketResponse) (retErr error) {
func (c *JoinClient) startNodeAndJoin(ticket *joinproto.IssueJoinTicketResponse, kubeletKey []byte) (retErr error) {
ctx, cancel := context.WithTimeout(context.Background(), c.joinTimeout)
defer cancel()
@ -244,6 +251,12 @@ func (c *JoinClient) startNodeAndJoin(ticket *joinproto.IssueJoinTicketResponse)
return fmt.Errorf("writing control plane files: %w", err)
}
}
if err := c.fileHandler.Write(kubelet.CertificateFilename, ticket.KubeletCert, file.OptMkdirAll); err != nil {
return fmt.Errorf("writing kubelet certificate: %w", err)
}
if err := c.fileHandler.Write(kubelet.KeyFilename, kubeletKey, file.OptMkdirAll); err != nil {
return fmt.Errorf("writing kubelet key: %w", err)
}
state := nodestate.NodeState{
Role: c.role,
@ -285,8 +298,17 @@ func (c *JoinClient) getNodeMetadata() error {
return errors.New("got instance metadata with unknown role")
}
var ips []net.IP
for _, ip := range inst.PrivateIPs {
ips = append(ips, net.ParseIP(ip))
}
for _, ip := range inst.PublicIPs {
ips = append(ips, net.ParseIP(ip))
}
c.nodeName = inst.Name
c.role = inst.Role
c.validIPs = ips
return nil
}

View File

@ -0,0 +1,49 @@
package kubelet
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"net"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
)
const (
// CertificateFilename is the path to the kubelets certificate.
CertificateFilename = "/run/state/kubelet/pki/kubelet-client-crt.pem"
// KeyFilename is the path to the kubelets private key.
KeyFilename = "/run/state/kubelet/pki/kubelet-client-key.pem"
)
// GetCertificateRequest returns a certificate request and macthing private key for the kubelet.
func GetCertificateRequest(nodeName string, ips []net.IP) (certificateRequest []byte, privateKey []byte, err error) {
privK, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, nil, err
}
keyBytes, err := x509.MarshalECPrivateKey(privK)
if err != nil {
return nil, nil, err
}
kubeletKey := pem.EncodeToMemory(&pem.Block{
Type: "EC PRIVATE KEY",
Bytes: keyBytes,
})
csrTemplate := &x509.CertificateRequest{
Subject: pkix.Name{
Organization: []string{constants.NodesGroup},
CommonName: constants.NodesUserPrefix + nodeName,
},
IPAddresses: ips,
}
certificateRequest, err = x509.CreateCertificateRequest(rand.Reader, csrTemplate, privK)
if err != nil {
return nil, nil, err
}
return certificateRequest, kubeletKey, nil
}

View File

@ -3,12 +3,14 @@ package k8sapi
import (
"path/filepath"
"github.com/edgelesssys/constellation/bootstrapper/internal/kubelet"
"github.com/edgelesssys/constellation/bootstrapper/internal/kubernetes/k8sapi/resources"
"github.com/edgelesssys/constellation/internal/constants"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kubeletconf "k8s.io/kubelet/config/v1beta1"
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
kubeconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
)
// Uses types defined here: https://kubernetes.io/docs/reference/config-api/kubeadm-config.v1beta3/
@ -62,6 +64,11 @@ func (c *CoreOSConfiguration) InitConfiguration(externalCloudProvider bool) Kube
"audit-log-maxbackup": "10", // CIS benchmark - Default value of Rancher
"audit-log-maxsize": "100", // CIS benchmark - Default value of Rancher
"profiling": "false", // CIS benchmark
"kubelet-certificate-authority": filepath.Join(
kubeconstants.KubernetesDir,
kubeconstants.DefaultCertificateDir,
kubeconstants.CACertName,
),
"tls-cipher-suites": "TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256," +
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256," +
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384," +
@ -134,6 +141,8 @@ func (c *CoreOSConfiguration) InitConfiguration(externalCloudProvider bool) Kube
Effect: corev1.TaintEffectPreferNoSchedule,
},
},
TLSCertFile: kubelet.CertificateFilename,
TLSPrivateKeyFile: kubelet.KeyFilename,
},
}
}

View File

@ -2,18 +2,28 @@ package k8sapi
import (
"context"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"net"
"net/http"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/edgelesssys/constellation/bootstrapper/internal/kubelet"
"github.com/edgelesssys/constellation/bootstrapper/internal/kubernetes/k8sapi/resources"
"github.com/edgelesssys/constellation/bootstrapper/util"
"github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/logger"
"github.com/spf13/afero"
"go.uber.org/zap"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
)
const (
@ -35,12 +45,14 @@ type Client interface {
// KubernetesUtil provides low level management of the kubernetes cluster.
type KubernetesUtil struct {
inst installer
file file.Handler
}
// NewKubernetesUtil creates a new KubernetesUtil.
func NewKubernetesUtil() *KubernetesUtil {
return &KubernetesUtil{
inst: newOSInstaller(),
file: file.NewHandler(afero.NewOsFs()),
}
}
@ -58,7 +70,9 @@ func (k *KubernetesUtil) InstallComponents(ctx context.Context, version string)
return enableSystemdUnit(ctx, kubeletServiceEtcPath)
}
func (k *KubernetesUtil) InitCluster(ctx context.Context, initConfig []byte, log *logger.Logger) error {
func (k *KubernetesUtil) InitCluster(
ctx context.Context, initConfig []byte, nodeName string, ips []net.IP, log *logger.Logger,
) error {
// TODO: audit policy should be user input
auditPolicy, err := resources.NewDefaultAuditPolicy().Marshal()
if err != nil {
@ -77,8 +91,40 @@ func (k *KubernetesUtil) InitCluster(ctx context.Context, initConfig []byte, log
return fmt.Errorf("writing kubeadm init yaml config %v: %w", initConfigFile.Name(), err)
}
cmd := exec.CommandContext(ctx, kubeadmPath, "init", "-v=5", "--config", initConfigFile.Name())
// preflight
log.Infof("Running kubeadm preflight checks")
cmd := exec.CommandContext(ctx, kubeadmPath, "init", "phase", "preflight", "-v=5", "--config", initConfigFile.Name())
out, err := cmd.CombinedOutput()
if err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
return fmt.Errorf("kubeadm init phase preflight failed (code %v) with: %s", exitErr.ExitCode(), out)
}
return fmt.Errorf("kubeadm init: %w", err)
}
// create CA certs
log.Infof("Creating Kubernetes control-plane certificates and keys")
cmd = exec.CommandContext(ctx, kubeadmPath, "init", "phase", "certs", "all", "-v=5", "--config", initConfigFile.Name())
out, err = cmd.CombinedOutput()
if err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
return fmt.Errorf("kubeadm init phase certs all failed (code %v) with: %s", exitErr.ExitCode(), out)
}
return fmt.Errorf("kubeadm init: %w", err)
}
// create kubelet key and CA signed certificate for the node
log.Infof("Creating signed kubelet certificate")
if err := k.createSignedKubeletCert(nodeName, ips); err != nil {
return err
}
// initialize the cluster
log.Infof("Initializing the cluster using kubeadm init")
cmd = exec.CommandContext(ctx, kubeadmPath, "init", "-v=5", "--skip-phases=preflight,certs", "--config", initConfigFile.Name())
out, err = cmd.CombinedOutput()
if err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
@ -312,3 +358,92 @@ func (k *KubernetesUtil) RestartKubelet() error {
defer cancel()
return restartSystemdUnit(ctx, "kubelet.service")
}
// createSignedKubeletCert manually creates a Kubernetes CA signed kubelet certificate for the bootstrapper node.
// This is necessary because this node does not request a certificate from the join service.
func (k *KubernetesUtil) createSignedKubeletCert(nodeName string, ips []net.IP) error {
certRequestRaw, kubeletKey, err := kubelet.GetCertificateRequest(nodeName, ips)
if err != nil {
return err
}
if err := k.file.Write(kubelet.KeyFilename, kubeletKey, file.OptMkdirAll); err != nil {
return err
}
parentCertRaw, err := k.file.Read(filepath.Join(
constants.KubernetesDir,
constants.DefaultCertificateDir,
constants.CACertName,
))
if err != nil {
return err
}
parentCertPEM, _ := pem.Decode(parentCertRaw)
parentCert, err := x509.ParseCertificate(parentCertPEM.Bytes)
if err != nil {
return err
}
parentKeyRaw, err := k.file.Read(filepath.Join(
constants.KubernetesDir,
constants.DefaultCertificateDir,
constants.CAKeyName,
))
if err != nil {
return err
}
parentKeyPEM, _ := pem.Decode(parentKeyRaw)
var parentKey any
switch parentKeyPEM.Type {
case "EC PRIVATE KEY":
parentKey, err = x509.ParseECPrivateKey(parentKeyPEM.Bytes)
case "RSA PRIVATE KEY":
parentKey, err = x509.ParsePKCS1PrivateKey(parentKeyPEM.Bytes)
case "PRIVATE KEY":
parentKey, err = x509.ParsePKCS8PrivateKey(parentKeyPEM.Bytes)
default:
err = fmt.Errorf("unsupported key type %q", parentCertPEM.Type)
}
if err != nil {
return err
}
certRequest, err := x509.ParseCertificateRequest(certRequestRaw)
if err != nil {
return err
}
serialNumber, err := util.GenerateCertificateSerialNumber()
if err != nil {
return err
}
now := time.Now()
// Create the kubelet certificate
// For a reference on the certificate fields, see: https://kubernetes.io/docs/setup/best-practices/certificates/
certTmpl := &x509.Certificate{
SerialNumber: serialNumber,
NotBefore: now.Add(-2 * time.Hour),
NotAfter: now.Add(24 * 365 * time.Hour),
Subject: certRequest.Subject,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{
x509.ExtKeyUsageClientAuth,
x509.ExtKeyUsageServerAuth,
},
IsCA: false,
BasicConstraintsValid: true,
IPAddresses: certRequest.IPAddresses,
}
certRaw, err := x509.CreateCertificate(rand.Reader, certTmpl, parentCert, certRequest.PublicKey, parentKey)
if err != nil {
return err
}
kubeletCert := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: certRaw,
})
return k.file.Write(kubelet.CertificateFilename, kubeletCert, file.OptMkdirAll)
}

View File

@ -2,6 +2,7 @@ package kubernetes
import (
"context"
"net"
"github.com/edgelesssys/constellation/bootstrapper/internal/kubernetes/k8sapi"
"github.com/edgelesssys/constellation/bootstrapper/internal/kubernetes/k8sapi/resources"
@ -10,7 +11,7 @@ import (
type clusterUtil interface {
InstallComponents(ctx context.Context, version string) error
InitCluster(ctx context.Context, initConfig []byte, log *logger.Logger) error
InitCluster(ctx context.Context, initConfig []byte, nodeName string, ips []net.IP, log *logger.Logger) error
JoinCluster(ctx context.Context, joinConfig []byte, log *logger.Logger) error
SetupPodNetwork(context.Context, k8sapi.SetupPodNetworkInput) error
SetupAccessManager(kubectl k8sapi.Client, sshUsers resources.Marshaler) error

View File

@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"net"
"os/exec"
"strings"
@ -96,6 +97,7 @@ func (k *KubeWrapper) InitCluster(
var subnetworkPodCIDR string
var controlPlaneEndpointIP string // this is the IP in "kubeadm init --control-plane-endpoint=<IP/DNS>:<port>" hence the unfortunate name
var nodeIP string
var validIPs []net.IP
// Step 1: retrieve cloud metadata for Kubernetes configuration
if k.providerMetadata.Supported() {
@ -104,6 +106,12 @@ func (k *KubeWrapper) InitCluster(
if err != nil {
return nil, fmt.Errorf("retrieving own instance metadata failed: %w", err)
}
for _, ip := range instance.PrivateIPs {
validIPs = append(validIPs, net.ParseIP(ip))
}
for _, ip := range instance.PublicIPs {
validIPs = append(validIPs, net.ParseIP(ip))
}
nodeName = k8sCompliantHostname(instance.Name)
providerID = instance.ProviderID
if len(instance.PrivateIPs) > 0 {
@ -152,7 +160,7 @@ func (k *KubeWrapper) InitCluster(
return nil, fmt.Errorf("encoding kubeadm init configuration as YAML: %w", err)
}
log.Infof("Initializing Kubernetes cluster")
if err := k.clusterUtil.InitCluster(ctx, initConfigYAML, log); err != nil {
if err := k.clusterUtil.InitCluster(ctx, initConfigYAML, nodeName, validIPs, log); err != nil {
return nil, fmt.Errorf("kubeadm init: %w", err)
}
kubeConfig, err := k.GetKubeconfig()

View File

@ -3,6 +3,7 @@ package kubernetes
import (
"context"
"errors"
"net"
"regexp"
"testing"
@ -496,7 +497,7 @@ func (s *stubClusterUtil) InstallComponents(ctx context.Context, version string)
return s.installComponentsErr
}
func (s *stubClusterUtil) InitCluster(ctx context.Context, initConfig []byte, log *logger.Logger) error {
func (s *stubClusterUtil) InitCluster(ctx context.Context, initConfig []byte, nodeName string, ips []net.IP, log *logger.Logger) error {
s.initConfigs = append(s.initConfigs, initConfig)
return s.initClusterErr
}

View File

@ -1,18 +1,18 @@
package kubernetesca
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"fmt"
"strings"
"time"
"github.com/edgelesssys/constellation/bootstrapper/util"
"github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/logger"
kubeconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
)
const (
@ -35,22 +35,22 @@ func New(log *logger.Logger, fileHandler file.Handler) *KubernetesCA {
}
// GetCertificate creates a certificate for a node and signs it using the Kubernetes root CA.
func (c KubernetesCA) GetCertificate(nodeName string) (cert []byte, key []byte, err error) {
func (c KubernetesCA) GetCertificate(csr []byte) (cert []byte, err error) {
c.log.Debugf("Loading Kubernetes CA certificate")
parentCertRaw, err := c.file.Read(caCertFilename)
if err != nil {
return nil, nil, err
return nil, err
}
parentCertPEM, _ := pem.Decode(parentCertRaw)
parentCert, err := x509.ParseCertificate(parentCertPEM.Bytes)
if err != nil {
return nil, nil, err
return nil, err
}
c.log.Debugf("Loading Kubernetes CA private key")
parentKeyRaw, err := c.file.Read(caKeyFilename)
if err != nil {
return nil, nil, err
return nil, err
}
parentKeyPEM, _ := pem.Decode(parentKeyRaw)
var parentKey any
@ -62,30 +62,34 @@ func (c KubernetesCA) GetCertificate(nodeName string) (cert []byte, key []byte,
case "PRIVATE KEY":
parentKey, err = x509.ParsePKCS8PrivateKey(parentKeyPEM.Bytes)
default:
return nil, nil, fmt.Errorf("unsupported key type %q", parentCertPEM.Type)
return nil, fmt.Errorf("unsupported key type %q", parentCertPEM.Type)
}
if err != nil {
return nil, nil, err
return nil, err
}
c.log.Infof("Creating kubelet private key")
privK, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
certRequest, err := x509.ParseCertificateRequest(csr)
if err != nil {
return nil, nil, err
return nil, err
}
keyBytes, err := x509.MarshalECPrivateKey(privK)
if err != nil {
return nil, nil, err
if err := certRequest.CheckSignature(); err != nil {
return nil, err
}
kubeletKey := pem.EncodeToMemory(&pem.Block{
Type: "EC PRIVATE KEY",
Bytes: keyBytes,
})
c.log.Infof("Creating kubelet certificate")
if len(certRequest.Subject.Organization) != 1 {
return nil, errors.New("certificate request must have exactly one organization")
}
if certRequest.Subject.Organization[0] != kubeconstants.NodesGroup {
return nil, fmt.Errorf("certificate request must have organization %q but has %q", kubeconstants.NodesGroup, certRequest.Subject.Organization[0])
}
if !strings.HasPrefix(certRequest.Subject.CommonName, kubeconstants.NodesUserPrefix) {
return nil, fmt.Errorf("certificate request must have common name prefix %q but is %q", kubeconstants.NodesUserPrefix, certRequest.Subject.CommonName)
}
serialNumber, err := util.GenerateCertificateSerialNumber()
if err != nil {
return nil, nil, err
return nil, err
}
now := time.Now()
@ -95,25 +99,25 @@ func (c KubernetesCA) GetCertificate(nodeName string) (cert []byte, key []byte,
SerialNumber: serialNumber,
NotBefore: now.Add(-2 * time.Hour),
NotAfter: now.Add(24 * 365 * time.Hour),
Subject: pkix.Name{
Organization: []string{"system:nodes"},
CommonName: fmt.Sprintf("system:node:%s", nodeName),
},
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
Subject: certRequest.Subject,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{
x509.ExtKeyUsageClientAuth,
x509.ExtKeyUsageServerAuth,
},
IsCA: false,
BasicConstraintsValid: true,
IPAddresses: certRequest.IPAddresses,
}
certRaw, err := x509.CreateCertificate(rand.Reader, certTmpl, parentCert, &privK.PublicKey, parentKey)
certRaw, err := x509.CreateCertificate(rand.Reader, certTmpl, parentCert, certRequest.PublicKey, parentKey)
if err != nil {
return nil, nil, err
return nil, err
}
kubeletCert := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: certRaw,
})
return kubeletCert, kubeletKey, nil
return kubeletCert, nil
}

View File

@ -10,6 +10,7 @@ import (
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"strings"
"testing"
"time"
@ -19,6 +20,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
kubeconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
)
func TestMain(m *testing.M) {
@ -38,45 +40,126 @@ Q29uc3RlbGxhdGlvbg==
invalidCert := []byte(`-----BEGIN CERTIFICATE-----
Q29uc3RlbGxhdGlvbg==
-----END CERTIFICATE-----`)
defaultSigningRequestFunc := func() ([]byte, error) {
privK, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}
csrTemplate := &x509.CertificateRequest{
Subject: pkix.Name{
Organization: []string{kubeconstants.NodesGroup},
CommonName: kubeconstants.NodesUserPrefix + "test-node",
},
}
return x509.CreateCertificateRequest(rand.Reader, csrTemplate, privK)
}
testCases := map[string]struct {
caCert []byte
caKey []byte
wantErr bool
caCert []byte
caKey []byte
createSigningRequest func() ([]byte, error)
wantErr bool
}{
"success ec key": {
caCert: ecCert,
caKey: ecKey,
caCert: ecCert,
caKey: ecKey,
createSigningRequest: defaultSigningRequestFunc,
},
"success rsa key": {
caCert: rsaCert,
caKey: rsaKey,
caCert: rsaCert,
caKey: rsaKey,
createSigningRequest: defaultSigningRequestFunc,
},
"success any key": {
caCert: testCert,
caKey: testKey,
caCert: testCert,
caKey: testKey,
createSigningRequest: defaultSigningRequestFunc,
},
"unsupported key": {
caCert: ecCert,
caKey: unsupportedKey,
wantErr: true,
caCert: ecCert,
caKey: unsupportedKey,
createSigningRequest: defaultSigningRequestFunc,
wantErr: true,
},
"invalid key": {
caCert: ecCert,
caKey: invalidKey,
wantErr: true,
caCert: ecCert,
caKey: invalidKey,
createSigningRequest: defaultSigningRequestFunc,
wantErr: true,
},
"invalid certificate": {
caCert: invalidCert,
caKey: ecKey,
wantErr: true,
caCert: invalidCert,
caKey: ecKey,
createSigningRequest: defaultSigningRequestFunc,
wantErr: true,
},
"no ca certificate": {
caKey: ecKey,
wantErr: true,
caKey: ecKey,
createSigningRequest: defaultSigningRequestFunc,
wantErr: true,
},
"no ca key": {
caCert: ecCert,
caCert: ecCert,
createSigningRequest: defaultSigningRequestFunc,
wantErr: true,
},
"no signing request": {
caCert: ecCert,
caKey: ecKey,
createSigningRequest: func() ([]byte, error) { return nil, nil },
wantErr: true,
},
"incorrect common name format": {
caCert: ecCert,
caKey: ecKey,
createSigningRequest: func() ([]byte, error) {
privK, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}
csrTemplate := &x509.CertificateRequest{
Subject: pkix.Name{
Organization: []string{kubeconstants.NodesGroup},
CommonName: "test-node",
},
}
return x509.CreateCertificateRequest(rand.Reader, csrTemplate, privK)
},
wantErr: true,
},
"incorrect organization format": {
caCert: ecCert,
caKey: ecKey,
createSigningRequest: func() ([]byte, error) {
privK, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}
csrTemplate := &x509.CertificateRequest{
Subject: pkix.Name{
Organization: []string{"test"},
CommonName: kubeconstants.NodesUserPrefix + "test-node",
},
}
return x509.CreateCertificateRequest(rand.Reader, csrTemplate, privK)
},
wantErr: true,
},
"no organization": {
caCert: ecCert,
caKey: ecKey,
createSigningRequest: func() ([]byte, error) {
privK, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}
csrTemplate := &x509.CertificateRequest{
Subject: pkix.Name{
CommonName: kubeconstants.NodesUserPrefix + "test-node",
},
}
return x509.CreateCertificateRequest(rand.Reader, csrTemplate, privK)
},
wantErr: true,
},
}
@ -100,8 +183,9 @@ Q29uc3RlbGxhdGlvbg==
file,
)
nodeName := "test"
kubeCert, kubeKey, err := ca.GetCertificate(nodeName)
signingRequest, err := tc.createSigningRequest()
require.NoError(err)
kubeCert, err := ca.GetCertificate(signingRequest)
if tc.wantErr {
assert.Error(err)
return
@ -112,19 +196,12 @@ Q29uc3RlbGxhdGlvbg==
require.NotNil(certPEM)
cert, err := x509.ParseCertificate(certPEM.Bytes)
require.NoError(err)
assert.Equal("system:node:"+nodeName, cert.Subject.CommonName)
assert.Equal("system:nodes", cert.Subject.Organization[0])
assert.True(strings.HasPrefix(cert.Subject.CommonName, kubeconstants.NodesUserPrefix))
assert.Equal(kubeconstants.NodesGroup, cert.Subject.Organization[0])
assert.Equal(x509.KeyUsageDigitalSignature|x509.KeyUsageKeyEncipherment, cert.KeyUsage)
assert.Equal(x509.ExtKeyUsageClientAuth, cert.ExtKeyUsage[0])
assert.False(cert.IsCA)
assert.True(cert.BasicConstraintsValid)
keyPEM, _ := pem.Decode(kubeKey)
require.NotNil(keyPEM)
key, err := x509.ParseECPrivateKey(keyPEM.Bytes)
require.NoError(err)
require.IsType(&ecdsa.PublicKey{}, cert.PublicKey)
assert.Equal(&key.PublicKey, cert.PublicKey.(*ecdsa.PublicKey))
})
}
}

View File

@ -93,7 +93,7 @@ func (s *Server) IssueJoinTicket(ctx context.Context, req *joinproto.IssueJoinTi
}
log.Infof("Creating signed kubelet certificate")
kubeletCert, kubeletKey, err := s.ca.GetCertificate(req.NodeName)
kubeletCert, err := s.ca.GetCertificate(req.CertificateRequest)
if err != nil {
return nil, status.Errorf(codes.Internal, "unable to generate kubelet certificate: %s", err)
}
@ -124,7 +124,6 @@ func (s *Server) IssueJoinTicket(ctx context.Context, req *joinproto.IssueJoinTi
Token: kubeArgs.Token,
DiscoveryTokenCaCertHash: kubeArgs.CACertHashes[0],
KubeletCert: kubeletCert,
KubeletKey: kubeletKey,
ControlPlaneFiles: controlPlaneFiles,
}, nil
}
@ -144,5 +143,5 @@ type dataKeyGetter interface {
type certificateAuthority interface {
// GetCertificate returns a certificate and private key, signed by the issuer.
GetCertificate(nodeName string) (kubeletCert []byte, kubeletKey []byte, err error)
GetCertificate(certificateRequest []byte) (kubeletCert []byte, err error)
}

View File

@ -49,33 +49,33 @@ func TestIssueJoinTicket(t *testing.T) {
"worker node": {
kubeadm: stubTokenGetter{token: testJoinToken},
kms: stubKeyGetter{dataKey: testKey},
ca: stubCA{cert: testCert, key: testKey},
ca: stubCA{cert: testCert},
id: mustMarshalID(testID),
},
"GetDataKey fails": {
kubeadm: stubTokenGetter{token: testJoinToken},
kms: stubKeyGetter{getDataKeyErr: someErr},
ca: stubCA{cert: testCert, key: testKey},
ca: stubCA{cert: testCert},
id: mustMarshalID(testID),
wantErr: true,
},
"loading IDs fails": {
kubeadm: stubTokenGetter{token: testJoinToken},
kms: stubKeyGetter{dataKey: testKey},
ca: stubCA{cert: testCert, key: testKey},
ca: stubCA{cert: testCert},
id: []byte{0x1, 0x2, 0x3},
wantErr: true,
},
"no ID file": {
kubeadm: stubTokenGetter{token: testJoinToken},
kms: stubKeyGetter{dataKey: testKey},
ca: stubCA{cert: testCert, key: testKey},
ca: stubCA{cert: testCert},
wantErr: true,
},
"GetJoinToken fails": {
kubeadm: stubTokenGetter{getJoinTokenErr: someErr},
kms: stubKeyGetter{dataKey: testKey},
ca: stubCA{cert: testCert, key: testKey},
ca: stubCA{cert: testCert},
id: mustMarshalID(testID),
wantErr: true,
},
@ -93,14 +93,14 @@ func TestIssueJoinTicket(t *testing.T) {
files: map[string][]byte{"test": {0x1, 0x2, 0x3}},
},
kms: stubKeyGetter{dataKey: testKey},
ca: stubCA{cert: testCert, key: testKey},
ca: stubCA{cert: testCert},
id: mustMarshalID(testID),
},
"GetControlPlaneCertificateKey fails": {
isControlPlane: true,
kubeadm: stubTokenGetter{token: testJoinToken, certificateKeyErr: someErr},
kms: stubKeyGetter{dataKey: testKey},
ca: stubCA{cert: testCert, key: testKey},
ca: stubCA{cert: testCert},
id: mustMarshalID(testID),
wantErr: true,
},
@ -125,7 +125,6 @@ func TestIssueJoinTicket(t *testing.T) {
req := &joinproto.IssueJoinTicketRequest{
DiskUuid: "uuid",
NodeName: "test",
IsControlPlane: tc.isControlPlane,
}
resp, err := api.IssueJoinTicket(context.Background(), req)
@ -145,7 +144,6 @@ func TestIssueJoinTicket(t *testing.T) {
assert.Equal(tc.kubeadm.token.CACertHashes[0], resp.DiscoveryTokenCaCertHash)
assert.Equal(tc.kubeadm.token.Token, resp.Token)
assert.Equal(tc.ca.cert, resp.KubeletCert)
assert.Equal(tc.ca.key, resp.KubeletKey)
if tc.isControlPlane {
assert.Len(resp.ControlPlaneFiles, len(tc.kubeadm.files))
@ -188,10 +186,9 @@ func (f stubKeyGetter) GetDataKey(context.Context, string, int) ([]byte, error)
type stubCA struct {
cert []byte
key []byte
getCertErr error
}
func (f stubCA) GetCertificate(string) ([]byte, []byte, error) {
return f.cert, f.key, f.getCertErr
func (f stubCA) GetCertificate(csr []byte) ([]byte, error) {
return f.cert, f.getCertErr
}

View File

@ -25,9 +25,9 @@ type IssueJoinTicketRequest struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
DiskUuid string `protobuf:"bytes,1,opt,name=disk_uuid,json=diskUuid,proto3" json:"disk_uuid,omitempty"`
NodeName string `protobuf:"bytes,2,opt,name=node_name,json=nodeName,proto3" json:"node_name,omitempty"`
IsControlPlane bool `protobuf:"varint,3,opt,name=is_control_plane,json=isControlPlane,proto3" json:"is_control_plane,omitempty"`
DiskUuid string `protobuf:"bytes,1,opt,name=disk_uuid,json=diskUuid,proto3" json:"disk_uuid,omitempty"`
CertificateRequest []byte `protobuf:"bytes,2,opt,name=certificate_request,json=certificateRequest,proto3" json:"certificate_request,omitempty"`
IsControlPlane bool `protobuf:"varint,3,opt,name=is_control_plane,json=isControlPlane,proto3" json:"is_control_plane,omitempty"`
}
func (x *IssueJoinTicketRequest) Reset() {
@ -69,11 +69,11 @@ func (x *IssueJoinTicketRequest) GetDiskUuid() string {
return ""
}
func (x *IssueJoinTicketRequest) GetNodeName() string {
func (x *IssueJoinTicketRequest) GetCertificateRequest() []byte {
if x != nil {
return x.NodeName
return x.CertificateRequest
}
return ""
return nil
}
func (x *IssueJoinTicketRequest) GetIsControlPlane() bool {
@ -91,12 +91,11 @@ type IssueJoinTicketResponse struct {
StateDiskKey []byte `protobuf:"bytes,1,opt,name=state_disk_key,json=stateDiskKey,proto3" json:"state_disk_key,omitempty"`
OwnerId []byte `protobuf:"bytes,2,opt,name=owner_id,json=ownerId,proto3" json:"owner_id,omitempty"`
ClusterId []byte `protobuf:"bytes,3,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"`
KubeletKey []byte `protobuf:"bytes,4,opt,name=kubelet_key,json=kubeletKey,proto3" json:"kubelet_key,omitempty"`
KubeletCert []byte `protobuf:"bytes,5,opt,name=kubelet_cert,json=kubeletCert,proto3" json:"kubelet_cert,omitempty"`
ApiServerEndpoint string `protobuf:"bytes,6,opt,name=api_server_endpoint,json=apiServerEndpoint,proto3" json:"api_server_endpoint,omitempty"`
Token string `protobuf:"bytes,7,opt,name=token,proto3" json:"token,omitempty"`
DiscoveryTokenCaCertHash string `protobuf:"bytes,8,opt,name=discovery_token_ca_cert_hash,json=discoveryTokenCaCertHash,proto3" json:"discovery_token_ca_cert_hash,omitempty"`
ControlPlaneFiles []*ControlPlaneCertOrKey `protobuf:"bytes,9,rep,name=control_plane_files,json=controlPlaneFiles,proto3" json:"control_plane_files,omitempty"`
KubeletCert []byte `protobuf:"bytes,4,opt,name=kubelet_cert,json=kubeletCert,proto3" json:"kubelet_cert,omitempty"`
ApiServerEndpoint string `protobuf:"bytes,5,opt,name=api_server_endpoint,json=apiServerEndpoint,proto3" json:"api_server_endpoint,omitempty"`
Token string `protobuf:"bytes,6,opt,name=token,proto3" json:"token,omitempty"`
DiscoveryTokenCaCertHash string `protobuf:"bytes,7,opt,name=discovery_token_ca_cert_hash,json=discoveryTokenCaCertHash,proto3" json:"discovery_token_ca_cert_hash,omitempty"`
ControlPlaneFiles []*ControlPlaneCertOrKey `protobuf:"bytes,8,rep,name=control_plane_files,json=controlPlaneFiles,proto3" json:"control_plane_files,omitempty"`
}
func (x *IssueJoinTicketResponse) Reset() {
@ -152,13 +151,6 @@ func (x *IssueJoinTicketResponse) GetClusterId() []byte {
return nil
}
func (x *IssueJoinTicketResponse) GetKubeletKey() []byte {
if x != nil {
return x.KubeletKey
}
return nil
}
func (x *IssueJoinTicketResponse) GetKubeletCert() []byte {
if x != nil {
return x.KubeletCert
@ -253,54 +245,53 @@ var File_join_proto protoreflect.FileDescriptor
var file_join_proto_rawDesc = []byte{
0x0a, 0x0a, 0x6a, 0x6f, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x6a, 0x6f,
0x69, 0x6e, 0x22, 0x7c, 0x0a, 0x16, 0x49, 0x73, 0x73, 0x75, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x54,
0x69, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09,
0x64, 0x69, 0x73, 0x6b, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x64, 0x69, 0x73, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64,
0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f,
0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x69, 0x73, 0x5f, 0x63, 0x6f, 0x6e,
0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08,
0x52, 0x0e, 0x69, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50, 0x6c, 0x61, 0x6e, 0x65,
0x22, 0x94, 0x03, 0x0a, 0x17, 0x49, 0x73, 0x73, 0x75, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x54, 0x69,
0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x0e,
0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x6b, 0x4b,
0x65, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a,
0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b,
0x6b, 0x75, 0x62, 0x65, 0x6c, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x6c, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x0a,
0x0c, 0x6b, 0x75, 0x62, 0x65, 0x6c, 0x65, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x05, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6b, 0x75, 0x62, 0x65, 0x6c, 0x65, 0x74, 0x43, 0x65, 0x72, 0x74,
0x12, 0x2e, 0x0a, 0x13, 0x61, 0x70, 0x69, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x65,
0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x61,
0x70, 0x69, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52,
0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x3e, 0x0a, 0x1c, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76,
0x65, 0x72, 0x79, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x63, 0x61, 0x5f, 0x63, 0x65, 0x72,
0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x18, 0x64, 0x69,
0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x61, 0x43, 0x65,
0x72, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x09, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72,
0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x6f, 0x72,
0x5f, 0x6b, 0x65, 0x79, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50, 0x6c, 0x61,
0x6e, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x43, 0x0a, 0x19, 0x63, 0x6f, 0x6e, 0x74, 0x72,
0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x6f, 0x72,
0x5f, 0x6b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x32, 0x55, 0x0a, 0x03,
0x41, 0x50, 0x49, 0x12, 0x4e, 0x0a, 0x0f, 0x49, 0x73, 0x73, 0x75, 0x65, 0x4a, 0x6f, 0x69, 0x6e,
0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x1c, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x2e, 0x49, 0x73,
0x73, 0x75, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x2e, 0x49, 0x73, 0x73, 0x75,
0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x65, 0x64, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x73, 0x79, 0x73, 0x2f, 0x63, 0x6f,
0x6e, 0x73, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x6f, 0x69, 0x6e,
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x6a, 0x6f, 0x69, 0x6e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x69, 0x6e, 0x22, 0x90, 0x01, 0x0a, 0x16, 0x49, 0x73, 0x73, 0x75, 0x65, 0x4a, 0x6f, 0x69, 0x6e,
0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a,
0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x65,
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x12, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x69,
0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x18,
0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
0x50, 0x6c, 0x61, 0x6e, 0x65, 0x22, 0xf3, 0x02, 0x0a, 0x17, 0x49, 0x73, 0x73, 0x75, 0x65, 0x4a,
0x6f, 0x69, 0x6e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x24, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x6b, 0x5f,
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x65,
0x44, 0x69, 0x73, 0x6b, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72,
0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f, 0x77, 0x6e, 0x65, 0x72,
0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64,
0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49,
0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6b, 0x75, 0x62, 0x65, 0x6c, 0x65, 0x74, 0x5f, 0x63, 0x65, 0x72,
0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6b, 0x75, 0x62, 0x65, 0x6c, 0x65, 0x74,
0x43, 0x65, 0x72, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x61, 0x70, 0x69, 0x5f, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28,
0x09, 0x52, 0x11, 0x61, 0x70, 0x69, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70,
0x6f, 0x69, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x06, 0x20,
0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x3e, 0x0a, 0x1c, 0x64, 0x69,
0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x63, 0x61,
0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09,
0x52, 0x18, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
0x43, 0x61, 0x43, 0x65, 0x72, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x4f, 0x0a, 0x13, 0x63, 0x6f,
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65,
0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x2e, 0x63,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x63, 0x65, 0x72,
0x74, 0x5f, 0x6f, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
0x6c, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x43, 0x0a, 0x19, 0x63,
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x63, 0x65, 0x72,
0x74, 0x5f, 0x6f, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04,
0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61,
0x32, 0x55, 0x0a, 0x03, 0x41, 0x50, 0x49, 0x12, 0x4e, 0x0a, 0x0f, 0x49, 0x73, 0x73, 0x75, 0x65,
0x4a, 0x6f, 0x69, 0x6e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x1c, 0x2e, 0x6a, 0x6f, 0x69,
0x6e, 0x2e, 0x49, 0x73, 0x73, 0x75, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x54, 0x69, 0x63, 0x6b, 0x65,
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x2e,
0x49, 0x73, 0x73, 0x75, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75,
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x64, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x73, 0x79,
0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f,
0x6a, 0x6f, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x6a, 0x6f, 0x69, 0x6e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@ -11,7 +11,7 @@ service API {
message IssueJoinTicketRequest {
string disk_uuid = 1;
string node_name = 2;
bytes certificate_request = 2;
bool is_control_plane = 3;
}
@ -19,12 +19,11 @@ message IssueJoinTicketResponse {
bytes state_disk_key = 1;
bytes owner_id = 2;
bytes cluster_id = 3;
bytes kubelet_key = 4;
bytes kubelet_cert = 5;
string api_server_endpoint = 6;
string token = 7;
string discovery_token_ca_cert_hash = 8;
repeated control_plane_cert_or_key control_plane_files = 9;
bytes kubelet_cert = 4;
string api_server_endpoint = 5;
string token = 6;
string discovery_token_ca_cert_hash = 7;
repeated control_plane_cert_or_key control_plane_files = 8;
}
message control_plane_cert_or_key {