mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-06-21 04:34:22 -04:00
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:
parent
49e98286a9
commit
c6ff34f4d2
13 changed files with 451 additions and 159 deletions
|
@ -11,6 +11,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/bootstrapper/internal/diskencryption"
|
"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/nodestate"
|
||||||
"github.com/edgelesssys/constellation/bootstrapper/role"
|
"github.com/edgelesssys/constellation/bootstrapper/role"
|
||||||
"github.com/edgelesssys/constellation/internal/cloud/metadata"
|
"github.com/edgelesssys/constellation/internal/cloud/metadata"
|
||||||
|
@ -39,6 +40,7 @@ type JoinClient struct {
|
||||||
diskUUID string
|
diskUUID string
|
||||||
nodeName string
|
nodeName string
|
||||||
role role.Role
|
role role.Role
|
||||||
|
validIPs []net.IP
|
||||||
disk encryptedDisk
|
disk encryptedDisk
|
||||||
fileHandler file.Handler
|
fileHandler file.Handler
|
||||||
|
|
||||||
|
@ -190,6 +192,11 @@ func (c *JoinClient) join(serviceEndpoint string) error {
|
||||||
ctx, cancel := c.timeoutCtx()
|
ctx, cancel := c.timeoutCtx()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
certificateRequest, kubeletKey, err := kubelet.GetCertificateRequest(c.nodeName, c.validIPs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
conn, err := c.dialer.Dial(ctx, serviceEndpoint)
|
conn, err := c.dialer.Dial(ctx, serviceEndpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.log.With(zap.String("endpoint", serviceEndpoint), zap.Error(err)).Errorf("Join service unreachable")
|
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)
|
protoClient := joinproto.NewAPIClient(conn)
|
||||||
req := &joinproto.IssueJoinTicketRequest{
|
req := &joinproto.IssueJoinTicketRequest{
|
||||||
DiskUuid: c.diskUUID,
|
DiskUuid: c.diskUUID,
|
||||||
NodeName: c.nodeName,
|
CertificateRequest: certificateRequest,
|
||||||
IsControlPlane: c.role == role.ControlPlane,
|
IsControlPlane: c.role == role.ControlPlane,
|
||||||
}
|
}
|
||||||
ticket, err := protoClient.IssueJoinTicket(ctx, req)
|
ticket, err := protoClient.IssueJoinTicket(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -209,10 +216,10 @@ func (c *JoinClient) join(serviceEndpoint string) error {
|
||||||
return fmt.Errorf("issuing join ticket: %w", err)
|
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)
|
ctx, cancel := context.WithTimeout(context.Background(), c.joinTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
@ -244,6 +251,12 @@ func (c *JoinClient) startNodeAndJoin(ticket *joinproto.IssueJoinTicketResponse)
|
||||||
return fmt.Errorf("writing control plane files: %w", err)
|
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{
|
state := nodestate.NodeState{
|
||||||
Role: c.role,
|
Role: c.role,
|
||||||
|
@ -285,8 +298,17 @@ func (c *JoinClient) getNodeMetadata() error {
|
||||||
return errors.New("got instance metadata with unknown role")
|
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.nodeName = inst.Name
|
||||||
c.role = inst.Role
|
c.role = inst.Role
|
||||||
|
c.validIPs = ips
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
49
bootstrapper/internal/kubelet/kubelet.go
Normal file
49
bootstrapper/internal/kubelet/kubelet.go
Normal 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
|
||||||
|
}
|
|
@ -3,12 +3,14 @@ package k8sapi
|
||||||
import (
|
import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/bootstrapper/internal/kubelet"
|
||||||
"github.com/edgelesssys/constellation/bootstrapper/internal/kubernetes/k8sapi/resources"
|
"github.com/edgelesssys/constellation/bootstrapper/internal/kubernetes/k8sapi/resources"
|
||||||
"github.com/edgelesssys/constellation/internal/constants"
|
"github.com/edgelesssys/constellation/internal/constants"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
kubeletconf "k8s.io/kubelet/config/v1beta1"
|
kubeletconf "k8s.io/kubelet/config/v1beta1"
|
||||||
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
|
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/
|
// 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-maxbackup": "10", // CIS benchmark - Default value of Rancher
|
||||||
"audit-log-maxsize": "100", // CIS benchmark - Default value of Rancher
|
"audit-log-maxsize": "100", // CIS benchmark - Default value of Rancher
|
||||||
"profiling": "false", // CIS benchmark
|
"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-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_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," +
|
"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,
|
Effect: corev1.TaintEffectPreferNoSchedule,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
TLSCertFile: kubelet.CertificateFilename,
|
||||||
|
TLSPrivateKeyFile: kubelet.KeyFilename,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,28 @@ package k8sapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/bootstrapper/internal/kubelet"
|
||||||
"github.com/edgelesssys/constellation/bootstrapper/internal/kubernetes/k8sapi/resources"
|
"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/edgelesssys/constellation/internal/logger"
|
||||||
|
"github.com/spf13/afero"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -35,12 +45,14 @@ type Client interface {
|
||||||
// KubernetesUtil provides low level management of the kubernetes cluster.
|
// KubernetesUtil provides low level management of the kubernetes cluster.
|
||||||
type KubernetesUtil struct {
|
type KubernetesUtil struct {
|
||||||
inst installer
|
inst installer
|
||||||
|
file file.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewKubernetesUtil creates a new KubernetesUtil.
|
// NewKubernetesUtil creates a new KubernetesUtil.
|
||||||
func NewKubernetesUtil() *KubernetesUtil {
|
func NewKubernetesUtil() *KubernetesUtil {
|
||||||
return &KubernetesUtil{
|
return &KubernetesUtil{
|
||||||
inst: newOSInstaller(),
|
inst: newOSInstaller(),
|
||||||
|
file: file.NewHandler(afero.NewOsFs()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +70,9 @@ func (k *KubernetesUtil) InstallComponents(ctx context.Context, version string)
|
||||||
return enableSystemdUnit(ctx, kubeletServiceEtcPath)
|
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
|
// TODO: audit policy should be user input
|
||||||
auditPolicy, err := resources.NewDefaultAuditPolicy().Marshal()
|
auditPolicy, err := resources.NewDefaultAuditPolicy().Marshal()
|
||||||
if err != nil {
|
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)
|
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()
|
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 {
|
if err != nil {
|
||||||
var exitErr *exec.ExitError
|
var exitErr *exec.ExitError
|
||||||
if errors.As(err, &exitErr) {
|
if errors.As(err, &exitErr) {
|
||||||
|
@ -312,3 +358,92 @@ func (k *KubernetesUtil) RestartKubelet() error {
|
||||||
defer cancel()
|
defer cancel()
|
||||||
return restartSystemdUnit(ctx, "kubelet.service")
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package kubernetes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/bootstrapper/internal/kubernetes/k8sapi"
|
"github.com/edgelesssys/constellation/bootstrapper/internal/kubernetes/k8sapi"
|
||||||
"github.com/edgelesssys/constellation/bootstrapper/internal/kubernetes/k8sapi/resources"
|
"github.com/edgelesssys/constellation/bootstrapper/internal/kubernetes/k8sapi/resources"
|
||||||
|
@ -10,7 +11,7 @@ import (
|
||||||
|
|
||||||
type clusterUtil interface {
|
type clusterUtil interface {
|
||||||
InstallComponents(ctx context.Context, version string) error
|
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
|
JoinCluster(ctx context.Context, joinConfig []byte, log *logger.Logger) error
|
||||||
SetupPodNetwork(context.Context, k8sapi.SetupPodNetworkInput) error
|
SetupPodNetwork(context.Context, k8sapi.SetupPodNetworkInput) error
|
||||||
SetupAccessManager(kubectl k8sapi.Client, sshUsers resources.Marshaler) error
|
SetupAccessManager(kubectl k8sapi.Client, sshUsers resources.Marshaler) error
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -96,6 +97,7 @@ func (k *KubeWrapper) InitCluster(
|
||||||
var subnetworkPodCIDR string
|
var subnetworkPodCIDR string
|
||||||
var controlPlaneEndpointIP string // this is the IP in "kubeadm init --control-plane-endpoint=<IP/DNS>:<port>" hence the unfortunate name
|
var controlPlaneEndpointIP string // this is the IP in "kubeadm init --control-plane-endpoint=<IP/DNS>:<port>" hence the unfortunate name
|
||||||
var nodeIP string
|
var nodeIP string
|
||||||
|
var validIPs []net.IP
|
||||||
|
|
||||||
// Step 1: retrieve cloud metadata for Kubernetes configuration
|
// Step 1: retrieve cloud metadata for Kubernetes configuration
|
||||||
if k.providerMetadata.Supported() {
|
if k.providerMetadata.Supported() {
|
||||||
|
@ -104,6 +106,12 @@ func (k *KubeWrapper) InitCluster(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("retrieving own instance metadata failed: %w", err)
|
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)
|
nodeName = k8sCompliantHostname(instance.Name)
|
||||||
providerID = instance.ProviderID
|
providerID = instance.ProviderID
|
||||||
if len(instance.PrivateIPs) > 0 {
|
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)
|
return nil, fmt.Errorf("encoding kubeadm init configuration as YAML: %w", err)
|
||||||
}
|
}
|
||||||
log.Infof("Initializing Kubernetes cluster")
|
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)
|
return nil, fmt.Errorf("kubeadm init: %w", err)
|
||||||
}
|
}
|
||||||
kubeConfig, err := k.GetKubeconfig()
|
kubeConfig, err := k.GetKubeconfig()
|
||||||
|
|
|
@ -3,6 +3,7 @@ package kubernetes
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"net"
|
||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -496,7 +497,7 @@ func (s *stubClusterUtil) InstallComponents(ctx context.Context, version string)
|
||||||
return s.installComponentsErr
|
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)
|
s.initConfigs = append(s.initConfigs, initConfig)
|
||||||
return s.initClusterErr
|
return s.initClusterErr
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
package kubernetesca
|
package kubernetesca
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/bootstrapper/util"
|
"github.com/edgelesssys/constellation/bootstrapper/util"
|
||||||
"github.com/edgelesssys/constellation/internal/file"
|
"github.com/edgelesssys/constellation/internal/file"
|
||||||
"github.com/edgelesssys/constellation/internal/logger"
|
"github.com/edgelesssys/constellation/internal/logger"
|
||||||
|
kubeconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
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.
|
// 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")
|
c.log.Debugf("Loading Kubernetes CA certificate")
|
||||||
parentCertRaw, err := c.file.Read(caCertFilename)
|
parentCertRaw, err := c.file.Read(caCertFilename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
parentCertPEM, _ := pem.Decode(parentCertRaw)
|
parentCertPEM, _ := pem.Decode(parentCertRaw)
|
||||||
parentCert, err := x509.ParseCertificate(parentCertPEM.Bytes)
|
parentCert, err := x509.ParseCertificate(parentCertPEM.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.log.Debugf("Loading Kubernetes CA private key")
|
c.log.Debugf("Loading Kubernetes CA private key")
|
||||||
parentKeyRaw, err := c.file.Read(caKeyFilename)
|
parentKeyRaw, err := c.file.Read(caKeyFilename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
parentKeyPEM, _ := pem.Decode(parentKeyRaw)
|
parentKeyPEM, _ := pem.Decode(parentKeyRaw)
|
||||||
var parentKey any
|
var parentKey any
|
||||||
|
@ -62,30 +62,34 @@ func (c KubernetesCA) GetCertificate(nodeName string) (cert []byte, key []byte,
|
||||||
case "PRIVATE KEY":
|
case "PRIVATE KEY":
|
||||||
parentKey, err = x509.ParsePKCS8PrivateKey(parentKeyPEM.Bytes)
|
parentKey, err = x509.ParsePKCS8PrivateKey(parentKeyPEM.Bytes)
|
||||||
default:
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.log.Infof("Creating kubelet private key")
|
certRequest, err := x509.ParseCertificateRequest(csr)
|
||||||
privK, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
keyBytes, err := x509.MarshalECPrivateKey(privK)
|
if err := certRequest.CheckSignature(); err != nil {
|
||||||
if err != nil {
|
return nil, err
|
||||||
return nil, nil, err
|
|
||||||
}
|
}
|
||||||
kubeletKey := pem.EncodeToMemory(&pem.Block{
|
|
||||||
Type: "EC PRIVATE KEY",
|
|
||||||
Bytes: keyBytes,
|
|
||||||
})
|
|
||||||
|
|
||||||
c.log.Infof("Creating kubelet certificate")
|
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()
|
serialNumber, err := util.GenerateCertificateSerialNumber()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
@ -95,25 +99,25 @@ func (c KubernetesCA) GetCertificate(nodeName string) (cert []byte, key []byte,
|
||||||
SerialNumber: serialNumber,
|
SerialNumber: serialNumber,
|
||||||
NotBefore: now.Add(-2 * time.Hour),
|
NotBefore: now.Add(-2 * time.Hour),
|
||||||
NotAfter: now.Add(24 * 365 * time.Hour),
|
NotAfter: now.Add(24 * 365 * time.Hour),
|
||||||
Subject: pkix.Name{
|
Subject: certRequest.Subject,
|
||||||
Organization: []string{"system:nodes"},
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||||
CommonName: fmt.Sprintf("system:node:%s", nodeName),
|
|
||||||
},
|
|
||||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
|
||||||
ExtKeyUsage: []x509.ExtKeyUsage{
|
ExtKeyUsage: []x509.ExtKeyUsage{
|
||||||
x509.ExtKeyUsageClientAuth,
|
x509.ExtKeyUsageClientAuth,
|
||||||
|
x509.ExtKeyUsageServerAuth,
|
||||||
},
|
},
|
||||||
IsCA: false,
|
IsCA: false,
|
||||||
BasicConstraintsValid: true,
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
kubeletCert := pem.EncodeToMemory(&pem.Block{
|
kubeletCert := pem.EncodeToMemory(&pem.Block{
|
||||||
Type: "CERTIFICATE",
|
Type: "CERTIFICATE",
|
||||||
Bytes: certRaw,
|
Bytes: certRaw,
|
||||||
})
|
})
|
||||||
|
|
||||||
return kubeletCert, kubeletKey, nil
|
return kubeletCert, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -19,6 +20,7 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"go.uber.org/goleak"
|
"go.uber.org/goleak"
|
||||||
|
kubeconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
|
@ -38,45 +40,126 @@ Q29uc3RlbGxhdGlvbg==
|
||||||
invalidCert := []byte(`-----BEGIN CERTIFICATE-----
|
invalidCert := []byte(`-----BEGIN CERTIFICATE-----
|
||||||
Q29uc3RlbGxhdGlvbg==
|
Q29uc3RlbGxhdGlvbg==
|
||||||
-----END CERTIFICATE-----`)
|
-----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 {
|
testCases := map[string]struct {
|
||||||
caCert []byte
|
caCert []byte
|
||||||
caKey []byte
|
caKey []byte
|
||||||
wantErr bool
|
createSigningRequest func() ([]byte, error)
|
||||||
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
"success ec key": {
|
"success ec key": {
|
||||||
caCert: ecCert,
|
caCert: ecCert,
|
||||||
caKey: ecKey,
|
caKey: ecKey,
|
||||||
|
createSigningRequest: defaultSigningRequestFunc,
|
||||||
},
|
},
|
||||||
"success rsa key": {
|
"success rsa key": {
|
||||||
caCert: rsaCert,
|
caCert: rsaCert,
|
||||||
caKey: rsaKey,
|
caKey: rsaKey,
|
||||||
|
createSigningRequest: defaultSigningRequestFunc,
|
||||||
},
|
},
|
||||||
"success any key": {
|
"success any key": {
|
||||||
caCert: testCert,
|
caCert: testCert,
|
||||||
caKey: testKey,
|
caKey: testKey,
|
||||||
|
createSigningRequest: defaultSigningRequestFunc,
|
||||||
},
|
},
|
||||||
"unsupported key": {
|
"unsupported key": {
|
||||||
caCert: ecCert,
|
caCert: ecCert,
|
||||||
caKey: unsupportedKey,
|
caKey: unsupportedKey,
|
||||||
wantErr: true,
|
createSigningRequest: defaultSigningRequestFunc,
|
||||||
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"invalid key": {
|
"invalid key": {
|
||||||
caCert: ecCert,
|
caCert: ecCert,
|
||||||
caKey: invalidKey,
|
caKey: invalidKey,
|
||||||
wantErr: true,
|
createSigningRequest: defaultSigningRequestFunc,
|
||||||
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"invalid certificate": {
|
"invalid certificate": {
|
||||||
caCert: invalidCert,
|
caCert: invalidCert,
|
||||||
caKey: ecKey,
|
caKey: ecKey,
|
||||||
wantErr: true,
|
createSigningRequest: defaultSigningRequestFunc,
|
||||||
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"no ca certificate": {
|
"no ca certificate": {
|
||||||
caKey: ecKey,
|
caKey: ecKey,
|
||||||
wantErr: true,
|
createSigningRequest: defaultSigningRequestFunc,
|
||||||
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"no ca key": {
|
"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,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -100,8 +183,9 @@ Q29uc3RlbGxhdGlvbg==
|
||||||
file,
|
file,
|
||||||
)
|
)
|
||||||
|
|
||||||
nodeName := "test"
|
signingRequest, err := tc.createSigningRequest()
|
||||||
kubeCert, kubeKey, err := ca.GetCertificate(nodeName)
|
require.NoError(err)
|
||||||
|
kubeCert, err := ca.GetCertificate(signingRequest)
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
return
|
return
|
||||||
|
@ -112,19 +196,12 @@ Q29uc3RlbGxhdGlvbg==
|
||||||
require.NotNil(certPEM)
|
require.NotNil(certPEM)
|
||||||
cert, err := x509.ParseCertificate(certPEM.Bytes)
|
cert, err := x509.ParseCertificate(certPEM.Bytes)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
assert.Equal("system:node:"+nodeName, cert.Subject.CommonName)
|
assert.True(strings.HasPrefix(cert.Subject.CommonName, kubeconstants.NodesUserPrefix))
|
||||||
assert.Equal("system:nodes", cert.Subject.Organization[0])
|
assert.Equal(kubeconstants.NodesGroup, cert.Subject.Organization[0])
|
||||||
assert.Equal(x509.KeyUsageDigitalSignature|x509.KeyUsageKeyEncipherment, cert.KeyUsage)
|
assert.Equal(x509.KeyUsageDigitalSignature|x509.KeyUsageKeyEncipherment, cert.KeyUsage)
|
||||||
assert.Equal(x509.ExtKeyUsageClientAuth, cert.ExtKeyUsage[0])
|
assert.Equal(x509.ExtKeyUsageClientAuth, cert.ExtKeyUsage[0])
|
||||||
assert.False(cert.IsCA)
|
assert.False(cert.IsCA)
|
||||||
assert.True(cert.BasicConstraintsValid)
|
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))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,7 +93,7 @@ func (s *Server) IssueJoinTicket(ctx context.Context, req *joinproto.IssueJoinTi
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Creating signed kubelet certificate")
|
log.Infof("Creating signed kubelet certificate")
|
||||||
kubeletCert, kubeletKey, err := s.ca.GetCertificate(req.NodeName)
|
kubeletCert, err := s.ca.GetCertificate(req.CertificateRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "unable to generate kubelet certificate: %s", err)
|
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,
|
Token: kubeArgs.Token,
|
||||||
DiscoveryTokenCaCertHash: kubeArgs.CACertHashes[0],
|
DiscoveryTokenCaCertHash: kubeArgs.CACertHashes[0],
|
||||||
KubeletCert: kubeletCert,
|
KubeletCert: kubeletCert,
|
||||||
KubeletKey: kubeletKey,
|
|
||||||
ControlPlaneFiles: controlPlaneFiles,
|
ControlPlaneFiles: controlPlaneFiles,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -144,5 +143,5 @@ type dataKeyGetter interface {
|
||||||
|
|
||||||
type certificateAuthority interface {
|
type certificateAuthority interface {
|
||||||
// GetCertificate returns a certificate and private key, signed by the issuer.
|
// 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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,33 +49,33 @@ func TestIssueJoinTicket(t *testing.T) {
|
||||||
"worker node": {
|
"worker node": {
|
||||||
kubeadm: stubTokenGetter{token: testJoinToken},
|
kubeadm: stubTokenGetter{token: testJoinToken},
|
||||||
kms: stubKeyGetter{dataKey: testKey},
|
kms: stubKeyGetter{dataKey: testKey},
|
||||||
ca: stubCA{cert: testCert, key: testKey},
|
ca: stubCA{cert: testCert},
|
||||||
id: mustMarshalID(testID),
|
id: mustMarshalID(testID),
|
||||||
},
|
},
|
||||||
"GetDataKey fails": {
|
"GetDataKey fails": {
|
||||||
kubeadm: stubTokenGetter{token: testJoinToken},
|
kubeadm: stubTokenGetter{token: testJoinToken},
|
||||||
kms: stubKeyGetter{getDataKeyErr: someErr},
|
kms: stubKeyGetter{getDataKeyErr: someErr},
|
||||||
ca: stubCA{cert: testCert, key: testKey},
|
ca: stubCA{cert: testCert},
|
||||||
id: mustMarshalID(testID),
|
id: mustMarshalID(testID),
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"loading IDs fails": {
|
"loading IDs fails": {
|
||||||
kubeadm: stubTokenGetter{token: testJoinToken},
|
kubeadm: stubTokenGetter{token: testJoinToken},
|
||||||
kms: stubKeyGetter{dataKey: testKey},
|
kms: stubKeyGetter{dataKey: testKey},
|
||||||
ca: stubCA{cert: testCert, key: testKey},
|
ca: stubCA{cert: testCert},
|
||||||
id: []byte{0x1, 0x2, 0x3},
|
id: []byte{0x1, 0x2, 0x3},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"no ID file": {
|
"no ID file": {
|
||||||
kubeadm: stubTokenGetter{token: testJoinToken},
|
kubeadm: stubTokenGetter{token: testJoinToken},
|
||||||
kms: stubKeyGetter{dataKey: testKey},
|
kms: stubKeyGetter{dataKey: testKey},
|
||||||
ca: stubCA{cert: testCert, key: testKey},
|
ca: stubCA{cert: testCert},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
"GetJoinToken fails": {
|
"GetJoinToken fails": {
|
||||||
kubeadm: stubTokenGetter{getJoinTokenErr: someErr},
|
kubeadm: stubTokenGetter{getJoinTokenErr: someErr},
|
||||||
kms: stubKeyGetter{dataKey: testKey},
|
kms: stubKeyGetter{dataKey: testKey},
|
||||||
ca: stubCA{cert: testCert, key: testKey},
|
ca: stubCA{cert: testCert},
|
||||||
id: mustMarshalID(testID),
|
id: mustMarshalID(testID),
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
|
@ -93,14 +93,14 @@ func TestIssueJoinTicket(t *testing.T) {
|
||||||
files: map[string][]byte{"test": {0x1, 0x2, 0x3}},
|
files: map[string][]byte{"test": {0x1, 0x2, 0x3}},
|
||||||
},
|
},
|
||||||
kms: stubKeyGetter{dataKey: testKey},
|
kms: stubKeyGetter{dataKey: testKey},
|
||||||
ca: stubCA{cert: testCert, key: testKey},
|
ca: stubCA{cert: testCert},
|
||||||
id: mustMarshalID(testID),
|
id: mustMarshalID(testID),
|
||||||
},
|
},
|
||||||
"GetControlPlaneCertificateKey fails": {
|
"GetControlPlaneCertificateKey fails": {
|
||||||
isControlPlane: true,
|
isControlPlane: true,
|
||||||
kubeadm: stubTokenGetter{token: testJoinToken, certificateKeyErr: someErr},
|
kubeadm: stubTokenGetter{token: testJoinToken, certificateKeyErr: someErr},
|
||||||
kms: stubKeyGetter{dataKey: testKey},
|
kms: stubKeyGetter{dataKey: testKey},
|
||||||
ca: stubCA{cert: testCert, key: testKey},
|
ca: stubCA{cert: testCert},
|
||||||
id: mustMarshalID(testID),
|
id: mustMarshalID(testID),
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
|
@ -125,7 +125,6 @@ func TestIssueJoinTicket(t *testing.T) {
|
||||||
|
|
||||||
req := &joinproto.IssueJoinTicketRequest{
|
req := &joinproto.IssueJoinTicketRequest{
|
||||||
DiskUuid: "uuid",
|
DiskUuid: "uuid",
|
||||||
NodeName: "test",
|
|
||||||
IsControlPlane: tc.isControlPlane,
|
IsControlPlane: tc.isControlPlane,
|
||||||
}
|
}
|
||||||
resp, err := api.IssueJoinTicket(context.Background(), req)
|
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.CACertHashes[0], resp.DiscoveryTokenCaCertHash)
|
||||||
assert.Equal(tc.kubeadm.token.Token, resp.Token)
|
assert.Equal(tc.kubeadm.token.Token, resp.Token)
|
||||||
assert.Equal(tc.ca.cert, resp.KubeletCert)
|
assert.Equal(tc.ca.cert, resp.KubeletCert)
|
||||||
assert.Equal(tc.ca.key, resp.KubeletKey)
|
|
||||||
|
|
||||||
if tc.isControlPlane {
|
if tc.isControlPlane {
|
||||||
assert.Len(resp.ControlPlaneFiles, len(tc.kubeadm.files))
|
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 {
|
type stubCA struct {
|
||||||
cert []byte
|
cert []byte
|
||||||
key []byte
|
|
||||||
getCertErr error
|
getCertErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f stubCA) GetCertificate(string) ([]byte, []byte, error) {
|
func (f stubCA) GetCertificate(csr []byte) ([]byte, error) {
|
||||||
return f.cert, f.key, f.getCertErr
|
return f.cert, f.getCertErr
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,9 +25,9 @@ type IssueJoinTicketRequest struct {
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
DiskUuid string `protobuf:"bytes,1,opt,name=disk_uuid,json=diskUuid,proto3" json:"disk_uuid,omitempty"`
|
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"`
|
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"`
|
IsControlPlane bool `protobuf:"varint,3,opt,name=is_control_plane,json=isControlPlane,proto3" json:"is_control_plane,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *IssueJoinTicketRequest) Reset() {
|
func (x *IssueJoinTicketRequest) Reset() {
|
||||||
|
@ -69,11 +69,11 @@ func (x *IssueJoinTicketRequest) GetDiskUuid() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *IssueJoinTicketRequest) GetNodeName() string {
|
func (x *IssueJoinTicketRequest) GetCertificateRequest() []byte {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.NodeName
|
return x.CertificateRequest
|
||||||
}
|
}
|
||||||
return ""
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *IssueJoinTicketRequest) GetIsControlPlane() bool {
|
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"`
|
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"`
|
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"`
|
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,4,opt,name=kubelet_cert,json=kubeletCert,proto3" json:"kubelet_cert,omitempty"`
|
||||||
KubeletCert []byte `protobuf:"bytes,5,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"`
|
||||||
ApiServerEndpoint string `protobuf:"bytes,6,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"`
|
||||||
Token string `protobuf:"bytes,7,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"`
|
||||||
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,8,rep,name=control_plane_files,json=controlPlaneFiles,proto3" json:"control_plane_files,omitempty"`
|
||||||
ControlPlaneFiles []*ControlPlaneCertOrKey `protobuf:"bytes,9,rep,name=control_plane_files,json=controlPlaneFiles,proto3" json:"control_plane_files,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *IssueJoinTicketResponse) Reset() {
|
func (x *IssueJoinTicketResponse) Reset() {
|
||||||
|
@ -152,13 +151,6 @@ func (x *IssueJoinTicketResponse) GetClusterId() []byte {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *IssueJoinTicketResponse) GetKubeletKey() []byte {
|
|
||||||
if x != nil {
|
|
||||||
return x.KubeletKey
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *IssueJoinTicketResponse) GetKubeletCert() []byte {
|
func (x *IssueJoinTicketResponse) GetKubeletCert() []byte {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.KubeletCert
|
return x.KubeletCert
|
||||||
|
@ -253,54 +245,53 @@ var File_join_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_join_proto_rawDesc = []byte{
|
var file_join_proto_rawDesc = []byte{
|
||||||
0x0a, 0x0a, 0x6a, 0x6f, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x6a, 0x6f,
|
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, 0x6e, 0x22, 0x90, 0x01, 0x0a, 0x16, 0x49, 0x73, 0x73, 0x75, 0x65, 0x4a, 0x6f, 0x69, 0x6e,
|
||||||
0x69, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09,
|
0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a,
|
||||||
0x64, 0x69, 0x73, 0x6b, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||||
0x08, 0x64, 0x69, 0x73, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64,
|
0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x55, 0x75, 0x69, 0x64, 0x12, 0x2f, 0x0a, 0x13, 0x63, 0x65,
|
||||||
0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f,
|
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||||
0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x69, 0x73, 0x5f, 0x63, 0x6f, 0x6e,
|
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x12, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
|
||||||
0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08,
|
0x63, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x69,
|
||||||
0x52, 0x0e, 0x69, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50, 0x6c, 0x61, 0x6e, 0x65,
|
0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x18,
|
||||||
0x22, 0x94, 0x03, 0x0a, 0x17, 0x49, 0x73, 0x73, 0x75, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x54, 0x69,
|
0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c,
|
||||||
0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x0e,
|
0x50, 0x6c, 0x61, 0x6e, 0x65, 0x22, 0xf3, 0x02, 0x0a, 0x17, 0x49, 0x73, 0x73, 0x75, 0x65, 0x4a,
|
||||||
0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01,
|
0x6f, 0x69, 0x6e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x6b, 0x4b,
|
0x65, 0x12, 0x24, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x6b, 0x5f,
|
||||||
0x65, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02,
|
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x65,
|
||||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a,
|
0x44, 0x69, 0x73, 0x6b, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72,
|
||||||
0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28,
|
0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f, 0x77, 0x6e, 0x65, 0x72,
|
||||||
0x0c, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b,
|
0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64,
|
||||||
0x6b, 0x75, 0x62, 0x65, 0x6c, 0x65, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28,
|
0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49,
|
||||||
0x0c, 0x52, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x6c, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x0a,
|
0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6b, 0x75, 0x62, 0x65, 0x6c, 0x65, 0x74, 0x5f, 0x63, 0x65, 0x72,
|
||||||
0x0c, 0x6b, 0x75, 0x62, 0x65, 0x6c, 0x65, 0x74, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x05, 0x20,
|
0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6b, 0x75, 0x62, 0x65, 0x6c, 0x65, 0x74,
|
||||||
0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6b, 0x75, 0x62, 0x65, 0x6c, 0x65, 0x74, 0x43, 0x65, 0x72, 0x74,
|
0x43, 0x65, 0x72, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x61, 0x70, 0x69, 0x5f, 0x73, 0x65, 0x72, 0x76,
|
||||||
0x12, 0x2e, 0x0a, 0x13, 0x61, 0x70, 0x69, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x65,
|
0x65, 0x72, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28,
|
||||||
0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x61,
|
0x09, 0x52, 0x11, 0x61, 0x70, 0x69, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70,
|
||||||
0x70, 0x69, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
|
0x6f, 0x69, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x06, 0x20,
|
||||||
0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52,
|
0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x3e, 0x0a, 0x1c, 0x64, 0x69,
|
||||||
0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x3e, 0x0a, 0x1c, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76,
|
0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x63, 0x61,
|
||||||
0x65, 0x72, 0x79, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x63, 0x61, 0x5f, 0x63, 0x65, 0x72,
|
0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09,
|
||||||
0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x18, 0x64, 0x69,
|
0x52, 0x18, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
|
||||||
0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x61, 0x43, 0x65,
|
0x43, 0x61, 0x43, 0x65, 0x72, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x4f, 0x0a, 0x13, 0x63, 0x6f,
|
||||||
0x72, 0x74, 0x48, 0x61, 0x73, 0x68, 0x12, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
|
0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65,
|
||||||
0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x09, 0x20,
|
0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x2e, 0x63,
|
||||||
0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72,
|
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x63, 0x65, 0x72,
|
||||||
0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x6f, 0x72,
|
0x74, 0x5f, 0x6f, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
|
||||||
0x5f, 0x6b, 0x65, 0x79, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x50, 0x6c, 0x61,
|
0x6c, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x43, 0x0a, 0x19, 0x63,
|
||||||
0x6e, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x43, 0x0a, 0x19, 0x63, 0x6f, 0x6e, 0x74, 0x72,
|
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x63, 0x65, 0x72,
|
||||||
0x6f, 0x6c, 0x5f, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x6f, 0x72,
|
0x74, 0x5f, 0x6f, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
|
||||||
0x5f, 0x6b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04,
|
||||||
0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61,
|
0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61,
|
||||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x32, 0x55, 0x0a, 0x03,
|
0x32, 0x55, 0x0a, 0x03, 0x41, 0x50, 0x49, 0x12, 0x4e, 0x0a, 0x0f, 0x49, 0x73, 0x73, 0x75, 0x65,
|
||||||
0x41, 0x50, 0x49, 0x12, 0x4e, 0x0a, 0x0f, 0x49, 0x73, 0x73, 0x75, 0x65, 0x4a, 0x6f, 0x69, 0x6e,
|
0x4a, 0x6f, 0x69, 0x6e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x1c, 0x2e, 0x6a, 0x6f, 0x69,
|
||||||
0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x1c, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x2e, 0x49, 0x73,
|
0x6e, 0x2e, 0x49, 0x73, 0x73, 0x75, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x54, 0x69, 0x63, 0x6b, 0x65,
|
||||||
0x73, 0x75, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x71,
|
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x2e,
|
||||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6a, 0x6f, 0x69, 0x6e, 0x2e, 0x49, 0x73, 0x73, 0x75,
|
0x49, 0x73, 0x73, 0x75, 0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x52,
|
||||||
0x65, 0x4a, 0x6f, 0x69, 0x6e, 0x54, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75,
|
||||||
0x6e, 0x73, 0x65, 0x42, 0x3c, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
|
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x64, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x73, 0x79,
|
||||||
0x6d, 0x2f, 0x65, 0x64, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x73, 0x79, 0x73, 0x2f, 0x63, 0x6f,
|
0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f,
|
||||||
0x6e, 0x73, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x6f, 0x69, 0x6e,
|
0x6a, 0x6f, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x6a, 0x6f, 0x69, 0x6e,
|
||||||
0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x6a, 0x6f, 0x69, 0x6e, 0x70, 0x72, 0x6f, 0x74,
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -11,7 +11,7 @@ service API {
|
||||||
|
|
||||||
message IssueJoinTicketRequest {
|
message IssueJoinTicketRequest {
|
||||||
string disk_uuid = 1;
|
string disk_uuid = 1;
|
||||||
string node_name = 2;
|
bytes certificate_request = 2;
|
||||||
bool is_control_plane = 3;
|
bool is_control_plane = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,12 +19,11 @@ message IssueJoinTicketResponse {
|
||||||
bytes state_disk_key = 1;
|
bytes state_disk_key = 1;
|
||||||
bytes owner_id = 2;
|
bytes owner_id = 2;
|
||||||
bytes cluster_id = 3;
|
bytes cluster_id = 3;
|
||||||
bytes kubelet_key = 4;
|
bytes kubelet_cert = 4;
|
||||||
bytes kubelet_cert = 5;
|
string api_server_endpoint = 5;
|
||||||
string api_server_endpoint = 6;
|
string token = 6;
|
||||||
string token = 7;
|
string discovery_token_ca_cert_hash = 7;
|
||||||
string discovery_token_ca_cert_hash = 8;
|
repeated control_plane_cert_or_key control_plane_files = 8;
|
||||||
repeated control_plane_cert_or_key control_plane_files = 9;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message control_plane_cert_or_key {
|
message control_plane_cert_or_key {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue