mirror of
https://github.com/siderolabs/talos.git
synced 2025-08-28 10:01:14 +02:00
246 lines
6.2 KiB
Go
246 lines
6.2 KiB
Go
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
package generate
|
|
|
|
import (
|
|
"bytes"
|
|
stdlibx509 "crypto/x509"
|
|
"encoding/base64"
|
|
"encoding/pem"
|
|
"errors"
|
|
"math/rand"
|
|
"net"
|
|
"strings"
|
|
"text/template"
|
|
|
|
"github.com/talos-systems/talos/pkg/crypto/x509"
|
|
)
|
|
|
|
// CertStrings holds the string representation of a certificate and key.
|
|
type CertStrings struct {
|
|
Crt string
|
|
Key string
|
|
}
|
|
|
|
// Input holds info about certs, ips, and node type.
|
|
type Input struct {
|
|
Certs *Certs
|
|
MasterIPs []string
|
|
Index int
|
|
ClusterName string
|
|
ServiceDomain string
|
|
PodNet []string
|
|
ServiceNet []string
|
|
Endpoints string
|
|
KubeadmTokens *KubeadmTokens
|
|
TrustdInfo *TrustdInfo
|
|
}
|
|
|
|
// Certs holds the base64 encoded keys and certificates.
|
|
type Certs struct {
|
|
AdminCert string
|
|
AdminKey string
|
|
OsCert string
|
|
OsKey string
|
|
K8sCert string
|
|
K8sKey string
|
|
}
|
|
|
|
// KubeadmTokens holds the senesitve kubeadm data.
|
|
type KubeadmTokens struct {
|
|
BootstrapToken string
|
|
CertKey string
|
|
}
|
|
|
|
// TrustdInfo holds the trustd credentials.
|
|
type TrustdInfo struct {
|
|
Token string
|
|
}
|
|
|
|
// RandomString returns a string of length n.
|
|
func RandomString(n int) string {
|
|
var letter = []rune("abcdefghijklmnopqrstuvwxy0123456789")
|
|
|
|
b := make([]rune, n)
|
|
for i := range b {
|
|
b[i] = letter[rand.Intn(len(letter))]
|
|
}
|
|
return string(b)
|
|
}
|
|
|
|
// NewInput generates the sensitive data required to generate all userdata
|
|
// types.
|
|
// nolint: gocyclo
|
|
func NewInput(clustername string, masterIPs []string) (input *Input, err error) {
|
|
kubeadmTokens := &KubeadmTokens{
|
|
BootstrapToken: RandomString(6) + "." + RandomString(16),
|
|
CertKey: RandomString(26),
|
|
}
|
|
|
|
trustdInfo := &TrustdInfo{
|
|
Token: RandomString(6) + "." + RandomString(16),
|
|
}
|
|
|
|
// Generate Kubernetes CA.
|
|
opts := []x509.Option{x509.RSA(true), x509.Organization("talos-k8s")}
|
|
k8sCert, err := x509.NewSelfSignedCertificateAuthority(opts...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Generate Talos CA.
|
|
opts = []x509.Option{x509.RSA(false), x509.Organization("talos-os")}
|
|
osCert, err := x509.NewSelfSignedCertificateAuthority(opts...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Generate the admin talosconfig.
|
|
adminKey, err := x509.NewKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pemBlock, _ := pem.Decode(adminKey.KeyPEM)
|
|
if pemBlock == nil {
|
|
return nil, errors.New("failed to decode admin key pem")
|
|
}
|
|
adminKeyEC, err := stdlibx509.ParseECPrivateKey(pemBlock.Bytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ips := []net.IP{net.ParseIP("127.0.0.1")}
|
|
opts = []x509.Option{x509.IPAddresses(ips)}
|
|
csr, err := x509.NewCertificateSigningRequest(adminKeyEC, opts...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
csrPemBlock, _ := pem.Decode(csr.X509CertificateRequestPEM)
|
|
if csrPemBlock == nil {
|
|
return nil, errors.New("failed to decode csr pem")
|
|
}
|
|
ccsr, err := stdlibx509.ParseCertificateRequest(csrPemBlock.Bytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
caPemBlock, _ := pem.Decode(osCert.CrtPEM)
|
|
if caPemBlock == nil {
|
|
return nil, errors.New("failed to decode ca cert pem")
|
|
}
|
|
caCrt, err := stdlibx509.ParseCertificate(caPemBlock.Bytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
caKeyPemBlock, _ := pem.Decode(osCert.KeyPEM)
|
|
if caKeyPemBlock == nil {
|
|
return nil, errors.New("failed to decode ca key pem")
|
|
}
|
|
caKey, err := stdlibx509.ParseECPrivateKey(caKeyPemBlock.Bytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
adminCrt, err := x509.NewCertificateFromCSR(caCrt, caKey, ccsr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
certs := &Certs{
|
|
AdminCert: base64.StdEncoding.EncodeToString(adminCrt.X509CertificatePEM),
|
|
AdminKey: base64.StdEncoding.EncodeToString(adminKey.KeyPEM),
|
|
OsCert: base64.StdEncoding.EncodeToString(osCert.CrtPEM),
|
|
OsKey: base64.StdEncoding.EncodeToString(osCert.KeyPEM),
|
|
K8sCert: base64.StdEncoding.EncodeToString(k8sCert.CrtPEM),
|
|
K8sKey: base64.StdEncoding.EncodeToString(k8sCert.KeyPEM),
|
|
}
|
|
|
|
input = &Input{
|
|
Certs: certs,
|
|
MasterIPs: masterIPs,
|
|
PodNet: []string{"10.244.0.0/16"},
|
|
ServiceNet: []string{"10.96.0.0/12"},
|
|
ServiceDomain: "cluster.local",
|
|
ClusterName: clustername,
|
|
Endpoints: strings.Join(masterIPs[1:], ", "),
|
|
KubeadmTokens: kubeadmTokens,
|
|
TrustdInfo: trustdInfo,
|
|
}
|
|
|
|
return input, nil
|
|
}
|
|
|
|
// Type represents a userdata type.
|
|
type Type int
|
|
|
|
const (
|
|
// TypeInit indicates a userdata type should correspond to the kubeadm
|
|
// InitConfiguration type.
|
|
TypeInit Type = iota
|
|
// TypeControlPlane indicates a userdata type should correspond to the
|
|
// kubeadm JoinConfiguration type that has the ControlPlane field
|
|
// defined.
|
|
TypeControlPlane
|
|
// TypeJoin indicates a userdata type should correspond to the kubeadm
|
|
// JoinConfiguration type.
|
|
TypeJoin
|
|
)
|
|
|
|
// Sring returns the string representation of Type.
|
|
func (t Type) String() string {
|
|
return [...]string{"Init", "ControlPlane", "Join"}[t]
|
|
}
|
|
|
|
// Userdata returns the talos userdata for a given node type.
|
|
func Userdata(t Type, in *Input) (string, error) {
|
|
var template string
|
|
switch t {
|
|
case TypeInit:
|
|
template = initTempl
|
|
case TypeControlPlane:
|
|
template = controlPlaneTempl
|
|
case TypeJoin:
|
|
template = workerTempl
|
|
default:
|
|
return "", errors.New("failed to determine userdata type to generate")
|
|
}
|
|
|
|
var err error
|
|
var ud string
|
|
|
|
ud, err = renderTemplate(in, template)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// TODO: We cant implement this currently because of
|
|
// issues with kubeadm dependency mismatch between
|
|
// talos and clusterapi//kubebuilder.
|
|
// We should figure out way we can work around/through
|
|
// this
|
|
/*
|
|
// Create an actual userdata struct from the
|
|
// generated data so we can call validate
|
|
// and ensure we are providing proper data
|
|
data := &userdata.UserData{}
|
|
if err = yaml.Unmarshal([]byte(ud), data); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if err = data.Validate(); err != nil {
|
|
return "", err
|
|
}
|
|
*/
|
|
|
|
return ud, nil
|
|
}
|
|
|
|
// renderTemplate will output a templated string.
|
|
func renderTemplate(in *Input, udTemplate string) (string, error) {
|
|
templ := template.Must(template.New("udTemplate").Parse(udTemplate))
|
|
var buf bytes.Buffer
|
|
if err := templ.Execute(&buf, in); err != nil {
|
|
return "", err
|
|
}
|
|
return buf.String(), nil
|
|
}
|