Files
k3s/pkg/daemons/control/deps/deps.go
Hussein Galal 763188d642 V1.32.0+k3s1 (#11478)
* Update libraries and codegen for k8s 1.32

Signed-off-by: galal-hussein <hussein.galal.ahmed.11@gmail.com>
Signed-off-by: Brad Davidson <brad.davidson@rancher.com>

* Fixes for 1.32

Signed-off-by: galal-hussein <hussein.galal.ahmed.11@gmail.com>
Signed-off-by: Brad Davidson <brad.davidson@rancher.com>

* Disable tests with down-rev agents

These are broken by AuthorizeNodeWithSelectors being on by default. All
agents must be upgraded to v1.32 or newer to work properly, until we
backport RBAC changes to older branches.

Signed-off-by: Brad Davidson <brad.davidson@rancher.com>

---------

Signed-off-by: galal-hussein <hussein.galal.ahmed.11@gmail.com>
Signed-off-by: Brad Davidson <brad.davidson@rancher.com>
Co-authored-by: Brad Davidson <brad.davidson@rancher.com>
2024-12-20 23:17:14 +02:00

848 lines
26 KiB
Go

package deps
import (
"bytes"
"crypto"
cryptorand "crypto/rand"
"crypto/sha256"
"crypto/x509"
b64 "encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io/fs"
"net"
"os"
"path/filepath"
"strings"
"text/template"
"time"
"github.com/k3s-io/k3s/pkg/clientaccess"
"github.com/k3s-io/k3s/pkg/cloudprovider"
"github.com/k3s-io/k3s/pkg/daemons/config"
"github.com/k3s-io/k3s/pkg/passwd"
"github.com/k3s-io/k3s/pkg/util"
"github.com/k3s-io/k3s/pkg/version"
certutil "github.com/rancher/dynamiclistener/cert"
"github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
apiserverconfigv1 "k8s.io/apiserver/pkg/apis/apiserver/v1"
apiserverv1beta1 "k8s.io/apiserver/pkg/apis/apiserver/v1beta1"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/client-go/util/keyutil"
)
const (
ipsecTokenSize = 48
aescbcKeySize = 32
RequestHeaderCN = "system:auth-proxy"
)
var kubeconfigTemplate = template.Must(template.New("kubeconfig").Parse(`apiVersion: v1
clusters:
- cluster:
server: {{.URL}}
certificate-authority: {{.CACert}}
name: local
contexts:
- context:
cluster: local
namespace: default
user: user
name: Default
current-context: Default
kind: Config
preferences: {}
users:
- name: user
user:
client-certificate: {{.ClientCert}}
client-key: {{.ClientKey}}
`))
func migratePassword(p *passwd.Passwd) error {
server, _ := p.Pass("server")
node, _ := p.Pass("node")
if server == "" && node != "" {
return p.EnsureUser("server", version.Program+":server", node)
}
return nil
}
func KubeConfig(dest, url, caCert, clientCert, clientKey string) error {
data := struct {
URL string
CACert string
ClientCert string
ClientKey string
}{
URL: url,
CACert: caCert,
ClientCert: clientCert,
ClientKey: clientKey,
}
// cis-1.24 and newer require kubeconfigs to be 0600
output, err := os.OpenFile(dest, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return err
}
defer output.Close()
return kubeconfigTemplate.Execute(output, &data)
}
// CreateRuntimeCertFiles is responsible for filling out all the
// .crt and .key filenames for a ControlRuntime.
func CreateRuntimeCertFiles(config *config.Control) {
runtime := config.Runtime
runtime.ClientCA = filepath.Join(config.DataDir, "tls", "client-ca.crt")
runtime.ClientCAKey = filepath.Join(config.DataDir, "tls", "client-ca.key")
runtime.ServerCA = filepath.Join(config.DataDir, "tls", "server-ca.crt")
runtime.ServerCAKey = filepath.Join(config.DataDir, "tls", "server-ca.key")
runtime.RequestHeaderCA = filepath.Join(config.DataDir, "tls", "request-header-ca.crt")
runtime.RequestHeaderCAKey = filepath.Join(config.DataDir, "tls", "request-header-ca.key")
runtime.IPSECKey = filepath.Join(config.DataDir, "cred", "ipsec.psk")
runtime.ServiceKey = filepath.Join(config.DataDir, "tls", "service.key")
runtime.PasswdFile = filepath.Join(config.DataDir, "cred", "passwd")
runtime.NodePasswdFile = filepath.Join(config.DataDir, "cred", "node-passwd")
runtime.SigningClientCA = filepath.Join(config.DataDir, "tls", "client-ca.nochain.crt")
runtime.SigningServerCA = filepath.Join(config.DataDir, "tls", "server-ca.nochain.crt")
runtime.ServiceCurrentKey = filepath.Join(config.DataDir, "tls", "service.current.key")
runtime.KubeConfigAdmin = filepath.Join(config.DataDir, "cred", "admin.kubeconfig")
runtime.KubeConfigSupervisor = filepath.Join(config.DataDir, "cred", "supervisor.kubeconfig")
runtime.KubeConfigController = filepath.Join(config.DataDir, "cred", "controller.kubeconfig")
runtime.KubeConfigScheduler = filepath.Join(config.DataDir, "cred", "scheduler.kubeconfig")
runtime.KubeConfigAPIServer = filepath.Join(config.DataDir, "cred", "api-server.kubeconfig")
runtime.KubeConfigCloudController = filepath.Join(config.DataDir, "cred", "cloud-controller.kubeconfig")
runtime.ClientAdminCert = filepath.Join(config.DataDir, "tls", "client-admin.crt")
runtime.ClientAdminKey = filepath.Join(config.DataDir, "tls", "client-admin.key")
runtime.ClientSupervisorCert = filepath.Join(config.DataDir, "tls", "client-supervisor.crt")
runtime.ClientSupervisorKey = filepath.Join(config.DataDir, "tls", "client-supervisor.key")
runtime.ClientControllerCert = filepath.Join(config.DataDir, "tls", "client-controller.crt")
runtime.ClientControllerKey = filepath.Join(config.DataDir, "tls", "client-controller.key")
runtime.ClientCloudControllerCert = filepath.Join(config.DataDir, "tls", "client-"+version.Program+"-cloud-controller.crt")
runtime.ClientCloudControllerKey = filepath.Join(config.DataDir, "tls", "client-"+version.Program+"-cloud-controller.key")
runtime.ClientSchedulerCert = filepath.Join(config.DataDir, "tls", "client-scheduler.crt")
runtime.ClientSchedulerKey = filepath.Join(config.DataDir, "tls", "client-scheduler.key")
runtime.ClientKubeAPICert = filepath.Join(config.DataDir, "tls", "client-kube-apiserver.crt")
runtime.ClientKubeAPIKey = filepath.Join(config.DataDir, "tls", "client-kube-apiserver.key")
runtime.ClientKubeProxyCert = filepath.Join(config.DataDir, "tls", "client-kube-proxy.crt")
runtime.ClientKubeProxyKey = filepath.Join(config.DataDir, "tls", "client-kube-proxy.key")
runtime.ClientK3sControllerCert = filepath.Join(config.DataDir, "tls", "client-"+version.Program+"-controller.crt")
runtime.ClientK3sControllerKey = filepath.Join(config.DataDir, "tls", "client-"+version.Program+"-controller.key")
runtime.ServingKubeAPICert = filepath.Join(config.DataDir, "tls", "serving-kube-apiserver.crt")
runtime.ServingKubeAPIKey = filepath.Join(config.DataDir, "tls", "serving-kube-apiserver.key")
runtime.ClientKubeletKey = filepath.Join(config.DataDir, "tls", "client-kubelet.key")
runtime.ServingKubeletKey = filepath.Join(config.DataDir, "tls", "serving-kubelet.key")
runtime.EgressSelectorConfig = filepath.Join(config.DataDir, "etc", "egress-selector-config.yaml")
runtime.CloudControllerConfig = filepath.Join(config.DataDir, "etc", "cloud-config.yaml")
runtime.ClientAuthProxyCert = filepath.Join(config.DataDir, "tls", "client-auth-proxy.crt")
runtime.ClientAuthProxyKey = filepath.Join(config.DataDir, "tls", "client-auth-proxy.key")
runtime.ETCDServerCA = filepath.Join(config.DataDir, "tls", "etcd", "server-ca.crt")
runtime.ETCDServerCAKey = filepath.Join(config.DataDir, "tls", "etcd", "server-ca.key")
runtime.ETCDPeerCA = filepath.Join(config.DataDir, "tls", "etcd", "peer-ca.crt")
runtime.ETCDPeerCAKey = filepath.Join(config.DataDir, "tls", "etcd", "peer-ca.key")
runtime.ServerETCDCert = filepath.Join(config.DataDir, "tls", "etcd", "server-client.crt")
runtime.ServerETCDKey = filepath.Join(config.DataDir, "tls", "etcd", "server-client.key")
runtime.PeerServerClientETCDCert = filepath.Join(config.DataDir, "tls", "etcd", "peer-server-client.crt")
runtime.PeerServerClientETCDKey = filepath.Join(config.DataDir, "tls", "etcd", "peer-server-client.key")
runtime.ClientETCDCert = filepath.Join(config.DataDir, "tls", "etcd", "client.crt")
runtime.ClientETCDKey = filepath.Join(config.DataDir, "tls", "etcd", "client.key")
if config.EncryptSecrets {
runtime.EncryptionConfig = filepath.Join(config.DataDir, "cred", "encryption-config.json")
runtime.EncryptionHash = filepath.Join(config.DataDir, "cred", "encryption-state.json")
}
}
// GenServerDeps is responsible for generating the cluster dependencies
// needed to successfully bootstrap a cluster.
func GenServerDeps(config *config.Control) error {
runtime := config.Runtime
if err := genCerts(config); err != nil {
return err
}
if err := genServiceAccount(runtime); err != nil {
return err
}
if err := genUsers(config); err != nil {
return err
}
if err := genEncryptedNetworkInfo(config); err != nil {
return err
}
if err := genEncryptionConfigAndState(config); err != nil {
return err
}
if err := genEgressSelectorConfig(config); err != nil {
return err
}
if err := genCloudConfig(config); err != nil {
return err
}
return readTokens(runtime)
}
func readTokens(runtime *config.ControlRuntime) error {
tokens, err := passwd.Read(runtime.PasswdFile)
if err != nil {
return err
}
if nodeToken, ok := tokens.Pass("node"); ok {
runtime.AgentToken = "node:" + nodeToken
}
if serverToken, ok := tokens.Pass("server"); ok {
runtime.ServerToken = "server:" + serverToken
}
return nil
}
func getNodePass(config *config.Control, serverPass string) string {
if config.AgentToken == "" {
if _, passwd, ok := clientaccess.ParseUsernamePassword(serverPass); ok {
return passwd
}
return serverPass
}
return config.AgentToken
}
func genUsers(config *config.Control) error {
runtime := config.Runtime
passwd, err := passwd.Read(runtime.PasswdFile)
if err != nil {
return err
}
if err := migratePassword(passwd); err != nil {
return err
}
// if no token is provided on bootstrap, we generate a random token
serverPass, err := getServerPass(passwd, config)
if err != nil {
return err
}
nodePass := getNodePass(config, serverPass)
if err := passwd.EnsureUser("node", version.Program+":agent", nodePass); err != nil {
return err
}
if err := passwd.EnsureUser("server", version.Program+":server", serverPass); err != nil {
return err
}
return passwd.Write(runtime.PasswdFile)
}
func genEncryptedNetworkInfo(controlConfig *config.Control) error {
runtime := controlConfig.Runtime
if s, err := os.Stat(runtime.IPSECKey); err == nil && s.Size() > 0 {
psk, err := os.ReadFile(runtime.IPSECKey)
if err != nil {
return err
}
controlConfig.IPSECPSK = strings.TrimSpace(string(psk))
return nil
}
psk, err := util.Random(ipsecTokenSize)
if err != nil {
return err
}
controlConfig.IPSECPSK = psk
return os.WriteFile(runtime.IPSECKey, []byte(psk+"\n"), 0600)
}
func getServerPass(passwd *passwd.Passwd, config *config.Control) (string, error) {
var err error
serverPass := config.Token
if serverPass == "" {
serverPass, _ = passwd.Pass("server")
}
if serverPass == "" {
serverPass, err = util.Random(16)
if err != nil {
return "", err
}
}
return serverPass, nil
}
func genCerts(config *config.Control) error {
if err := genClientCerts(config); err != nil {
return err
}
if err := genServerCerts(config); err != nil {
return err
}
if err := genRequestHeaderCerts(config); err != nil {
return err
}
return genETCDCerts(config)
}
func getSigningCertFactory(regen bool, altNames *certutil.AltNames, extKeyUsage []x509.ExtKeyUsage, caCertFile, caKeyFile string) signedCertFactory {
return func(commonName string, organization []string, certFile, keyFile string) (bool, error) {
return createClientCertKey(regen, commonName, organization, altNames, extKeyUsage, caCertFile, caKeyFile, certFile, keyFile)
}
}
func genClientCerts(config *config.Control) error {
runtime := config.Runtime
regen, err := createSigningCertKey(version.Program+"-client", runtime.ClientCA, runtime.ClientCAKey)
if err != nil {
return err
}
certs, err := certutil.CertsFromFile(runtime.ClientCA)
if err != nil {
return err
}
// If our CA certs are signed by a root or intermediate CA, ClientCA will contain a chain.
// The controller-manager's signer wants just a single cert, not a full chain; so create a file
// that is guaranteed to contain only a single certificate.
if err := certutil.WriteCert(runtime.SigningClientCA, certutil.EncodeCertPEM(certs[0])); err != nil {
return err
}
factory := getSigningCertFactory(regen, nil, []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, runtime.ClientCA, runtime.ClientCAKey)
var certGen bool
apiEndpoint := fmt.Sprintf("https://%s:%d", config.Loopback(true), config.APIServerPort)
certGen, err = factory("system:admin", []string{user.SystemPrivilegedGroup}, runtime.ClientAdminCert, runtime.ClientAdminKey)
if err != nil {
return err
}
if certGen {
if err := KubeConfig(runtime.KubeConfigAdmin, apiEndpoint, runtime.ServerCA, runtime.ClientAdminCert, runtime.ClientAdminKey); err != nil {
return err
}
}
certGen, err = factory("system:"+version.Program+"-supervisor", []string{user.SystemPrivilegedGroup}, runtime.ClientSupervisorCert, runtime.ClientSupervisorKey)
if err != nil {
return err
}
if certGen {
if err := KubeConfig(runtime.KubeConfigSupervisor, apiEndpoint, runtime.ServerCA, runtime.ClientSupervisorCert, runtime.ClientSupervisorKey); err != nil {
return err
}
}
certGen, err = factory(user.KubeControllerManager, nil, runtime.ClientControllerCert, runtime.ClientControllerKey)
if err != nil {
return err
}
if certGen {
if err := KubeConfig(runtime.KubeConfigController, apiEndpoint, runtime.ServerCA, runtime.ClientControllerCert, runtime.ClientControllerKey); err != nil {
return err
}
}
certGen, err = factory(user.KubeScheduler, nil, runtime.ClientSchedulerCert, runtime.ClientSchedulerKey)
if err != nil {
return err
}
if certGen {
if err := KubeConfig(runtime.KubeConfigScheduler, apiEndpoint, runtime.ServerCA, runtime.ClientSchedulerCert, runtime.ClientSchedulerKey); err != nil {
return err
}
}
certGen, err = factory(user.APIServerUser, []string{user.SystemPrivilegedGroup}, runtime.ClientKubeAPICert, runtime.ClientKubeAPIKey)
if err != nil {
return err
}
if certGen {
if err := KubeConfig(runtime.KubeConfigAPIServer, apiEndpoint, runtime.ServerCA, runtime.ClientKubeAPICert, runtime.ClientKubeAPIKey); err != nil {
return err
}
}
if _, err = factory(user.KubeProxy, nil, runtime.ClientKubeProxyCert, runtime.ClientKubeProxyKey); err != nil {
return err
}
// This user (system:k3s-controller by default) must be bound to a role in rolebindings.yaml or the downstream equivalent
if _, err = factory("system:"+version.Program+"-controller", nil, runtime.ClientK3sControllerCert, runtime.ClientK3sControllerKey); err != nil {
return err
}
if _, _, err := certutil.LoadOrGenerateKeyFile(runtime.ClientKubeletKey, regen); err != nil {
return err
}
certGen, err = factory(version.Program+"-cloud-controller-manager", nil, runtime.ClientCloudControllerCert, runtime.ClientCloudControllerKey)
if err != nil {
return err
}
if certGen {
if err := KubeConfig(runtime.KubeConfigCloudController, apiEndpoint, runtime.ServerCA, runtime.ClientCloudControllerCert, runtime.ClientCloudControllerKey); err != nil {
return err
}
}
return nil
}
func genServerCerts(config *config.Control) error {
runtime := config.Runtime
regen, err := createServerSigningCertKey(config)
if err != nil {
return err
}
altNames := &certutil.AltNames{
DNSNames: []string{"kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc." + config.ClusterDomain},
}
addSANs(altNames, config.SANs)
if _, err := createClientCertKey(regen, "kube-apiserver", nil,
altNames, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
runtime.ServerCA, runtime.ServerCAKey,
runtime.ServingKubeAPICert, runtime.ServingKubeAPIKey); err != nil {
return err
}
if _, _, err := certutil.LoadOrGenerateKeyFile(runtime.ServingKubeletKey, regen); err != nil {
return err
}
return nil
}
func genETCDCerts(config *config.Control) error {
runtime := config.Runtime
regen, err := createSigningCertKey("etcd-server", runtime.ETCDServerCA, runtime.ETCDServerCAKey)
if err != nil {
return err
}
altNames := &certutil.AltNames{
DNSNames: []string{"kine.sock"},
}
addSANs(altNames, config.SANs)
if _, err := createClientCertKey(regen, "etcd-client", nil,
nil, []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
runtime.ETCDServerCA, runtime.ETCDServerCAKey,
runtime.ClientETCDCert, runtime.ClientETCDKey); err != nil {
return err
}
regen, err = createSigningCertKey("etcd-peer", runtime.ETCDPeerCA, runtime.ETCDPeerCAKey)
if err != nil {
return err
}
if _, err := createClientCertKey(regen, "etcd-peer", nil,
altNames, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
runtime.ETCDPeerCA, runtime.ETCDPeerCAKey,
runtime.PeerServerClientETCDCert, runtime.PeerServerClientETCDKey); err != nil {
return err
}
if config.DisableETCD {
return nil
}
if _, err := createClientCertKey(regen, "etcd-server", nil,
altNames, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
runtime.ETCDServerCA, runtime.ETCDServerCAKey,
runtime.ServerETCDCert, runtime.ServerETCDKey); err != nil {
return err
}
return nil
}
func genRequestHeaderCerts(config *config.Control) error {
runtime := config.Runtime
regen, err := createSigningCertKey(version.Program+"-request-header", runtime.RequestHeaderCA, runtime.RequestHeaderCAKey)
if err != nil {
return err
}
if _, err := createClientCertKey(regen, RequestHeaderCN, nil,
nil, []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
runtime.RequestHeaderCA, runtime.RequestHeaderCAKey,
runtime.ClientAuthProxyCert, runtime.ClientAuthProxyKey); err != nil {
return err
}
return nil
}
type signedCertFactory = func(commonName string, organization []string, certFile, keyFile string) (bool, error)
func createServerSigningCertKey(config *config.Control) (bool, error) {
runtime := config.Runtime
TokenCA := filepath.Join(config.DataDir, "tls", "token-ca.crt")
TokenCAKey := filepath.Join(config.DataDir, "tls", "token-ca.key")
if exists(TokenCA, TokenCAKey) && !exists(runtime.ServerCA) && !exists(runtime.ServerCAKey) {
logrus.Infof("Upgrading token-ca files to server-ca")
if err := os.Link(TokenCA, runtime.ServerCA); err != nil {
return false, err
}
if err := os.Link(TokenCAKey, runtime.ServerCAKey); err != nil {
return false, err
}
return true, nil
}
regen, err := createSigningCertKey(version.Program+"-server", runtime.ServerCA, runtime.ServerCAKey)
if err != nil {
return regen, err
}
// If our CA certs are signed by a root or intermediate CA, ServerCA will contain a chain.
// The controller-manager's signer wants just a single cert, not a full chain; so create a file
// that is guaranteed to contain only a single certificate.
certs, err := certutil.CertsFromFile(runtime.ServerCA)
if err != nil {
return regen, err
}
if err := certutil.WriteCert(runtime.SigningServerCA, certutil.EncodeCertPEM(certs[0])); err != nil {
return regen, err
}
return regen, nil
}
func addSANs(altNames *certutil.AltNames, sans []string) {
for _, san := range sans {
ip := net.ParseIP(san)
if ip == nil {
altNames.DNSNames = append(altNames.DNSNames, san)
} else {
altNames.IPs = append(altNames.IPs, ip)
}
}
}
func fieldsChanged(certFile string, commonName string, organization []string, sans *certutil.AltNames, caCertFile string) bool {
if sans == nil {
sans = &certutil.AltNames{}
}
certificates, err := certutil.CertsFromFile(certFile)
if err != nil || len(certificates) == 0 {
return false
}
if certificates[0].Subject.CommonName != commonName {
return true
}
if !sets.NewString(certificates[0].Subject.Organization...).Equal(sets.NewString(organization...)) {
return true
}
if !sets.NewString(certificates[0].DNSNames...).HasAll(sans.DNSNames...) {
return true
}
ips := sets.NewString()
for _, ip := range certificates[0].IPAddresses {
ips.Insert(ip.String())
}
for _, ip := range sans.IPs {
if !ips.Has(ip.String()) {
return true
}
}
caCertificates, err := certutil.CertsFromFile(caCertFile)
if err != nil || len(caCertificates) == 0 {
return false
}
return !bytes.Equal(certificates[0].AuthorityKeyId, caCertificates[0].SubjectKeyId)
}
func createClientCertKey(regen bool, commonName string, organization []string, altNames *certutil.AltNames, extKeyUsage []x509.ExtKeyUsage, caCertFile, caKeyFile, certFile, keyFile string) (bool, error) {
// check for reasons to renew the certificate even if not manually requested.
regen = regen || expired(certFile) || fieldsChanged(certFile, commonName, organization, altNames, caCertFile)
if !regen {
if exists(certFile, keyFile) {
return false, nil
}
}
caKey, err := certutil.PrivateKeyFromFile(caKeyFile)
if err != nil {
return false, err
}
caCerts, err := certutil.CertsFromFile(caCertFile)
if err != nil {
return false, err
}
keyBytes, _, err := certutil.LoadOrGenerateKeyFile(keyFile, regen)
if err != nil {
return false, err
}
key, err := certutil.ParsePrivateKeyPEM(keyBytes)
if err != nil {
return false, err
}
cfg := certutil.Config{
CommonName: commonName,
Organization: organization,
Usages: extKeyUsage,
}
if altNames != nil {
cfg.AltNames = *altNames
}
cert, err := certutil.NewSignedCert(cfg, key.(crypto.Signer), caCerts[0], caKey.(crypto.Signer))
if err != nil {
return false, err
}
return true, certutil.WriteCert(certFile, util.EncodeCertsPEM(cert, caCerts))
}
func exists(files ...string) bool {
for _, file := range files {
if _, err := os.Stat(file); err != nil {
return false
}
}
return true
}
func genServiceAccount(runtime *config.ControlRuntime) error {
if _, err := os.Stat(runtime.ServiceKey); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return err
}
key, err := certutil.NewPrivateKey()
if err != nil {
return err
}
if err := certutil.WriteKey(runtime.ServiceKey, certutil.EncodePrivateKeyPEM(key)); err != nil {
return err
}
}
// When rotating the ServiceAccount signing key, it is necessary to keep the old keys in ServiceKey so that
// old ServiceAccount tokens can be validated during the switchover process. The first key in the file
// should be the current key used to sign ServiceAccount tokens; others are old keys used for verification
// only. Create a file containing just the first key in the list, which will be used to configure the
// signing controller.
key, err := keyutil.PrivateKeyFromFile(runtime.ServiceKey)
if err != nil {
return err
}
keyData, err := keyutil.MarshalPrivateKeyToPEM(key)
if err != nil {
return err
}
return certutil.WriteKey(runtime.ServiceCurrentKey, keyData)
}
func createSigningCertKey(prefix, certFile, keyFile string) (bool, error) {
if exists(certFile, keyFile) {
return false, nil
}
caKeyBytes, _, err := certutil.LoadOrGenerateKeyFile(keyFile, false)
if err != nil {
return false, err
}
caKey, err := certutil.ParsePrivateKeyPEM(caKeyBytes)
if err != nil {
return false, err
}
cfg := certutil.Config{
CommonName: fmt.Sprintf("%s-ca@%d", prefix, time.Now().Unix()),
}
cert, err := certutil.NewSelfSignedCACert(cfg, caKey.(crypto.Signer))
if err != nil {
return false, err
}
if err := certutil.WriteCert(certFile, certutil.EncodeCertPEM(cert)); err != nil {
return false, err
}
return true, nil
}
func expired(certFile string) bool {
certificates, err := certutil.CertsFromFile(certFile)
if err != nil {
return false
}
return certutil.IsCertExpired(certificates[0], config.CertificateRenewDays)
}
func genEncryptionConfigAndState(controlConfig *config.Control) error {
runtime := controlConfig.Runtime
if !controlConfig.EncryptSecrets {
return nil
}
if s, err := os.Stat(runtime.EncryptionConfig); err == nil && s.Size() > 0 {
// On upgrade from older versions, the encryption hash may not exist, create it
if _, err := os.Stat(runtime.EncryptionHash); errors.Is(err, os.ErrNotExist) {
curEncryptionByte, err := os.ReadFile(runtime.EncryptionConfig)
if err != nil {
return err
}
encryptionConfigHash := sha256.Sum256(curEncryptionByte)
ann := "start-" + hex.EncodeToString(encryptionConfigHash[:])
return os.WriteFile(controlConfig.Runtime.EncryptionHash, []byte(ann), 0600)
}
return nil
}
aescbcKey := make([]byte, aescbcKeySize)
_, err := cryptorand.Read(aescbcKey)
if err != nil {
return err
}
encodedKey := b64.StdEncoding.EncodeToString(aescbcKey)
encConfig := apiserverconfigv1.EncryptionConfiguration{
TypeMeta: metav1.TypeMeta{
Kind: "EncryptionConfiguration",
APIVersion: "apiserver.config.k8s.io/v1",
},
Resources: []apiserverconfigv1.ResourceConfiguration{
{
Resources: []string{"secrets"},
Providers: []apiserverconfigv1.ProviderConfiguration{
{
AESCBC: &apiserverconfigv1.AESConfiguration{
Keys: []apiserverconfigv1.Key{
{
Name: "aescbckey",
Secret: encodedKey,
},
},
},
},
{
Identity: &apiserverconfigv1.IdentityConfiguration{},
},
},
},
},
}
b, err := json.Marshal(encConfig)
if err != nil {
return err
}
if err := util.AtomicWrite(runtime.EncryptionConfig, b, 0600); err != nil {
return err
}
encryptionConfigHash := sha256.Sum256(b)
ann := "start-" + hex.EncodeToString(encryptionConfigHash[:])
return os.WriteFile(controlConfig.Runtime.EncryptionHash, []byte(ann), 0600)
}
func genEgressSelectorConfig(controlConfig *config.Control) error {
var clusterConn apiserverv1beta1.Connection
if controlConfig.EgressSelectorMode == config.EgressSelectorModeDisabled {
clusterConn = apiserverv1beta1.Connection{
ProxyProtocol: apiserverv1beta1.ProtocolDirect,
}
} else {
clusterConn = apiserverv1beta1.Connection{
ProxyProtocol: apiserverv1beta1.ProtocolHTTPConnect,
Transport: &apiserverv1beta1.Transport{
TCP: &apiserverv1beta1.TCPTransport{
URL: fmt.Sprintf("https://%s:%d", controlConfig.BindAddressOrLoopback(false, true), controlConfig.SupervisorPort),
TLSConfig: &apiserverv1beta1.TLSConfig{
CABundle: controlConfig.Runtime.ServerCA,
ClientKey: controlConfig.Runtime.ClientKubeAPIKey,
ClientCert: controlConfig.Runtime.ClientKubeAPICert,
},
},
},
}
}
egressConfig := apiserverv1beta1.EgressSelectorConfiguration{
TypeMeta: metav1.TypeMeta{
Kind: "EgressSelectorConfiguration",
APIVersion: "apiserver.k8s.io/v1beta1",
},
EgressSelections: []apiserverv1beta1.EgressSelection{
{
Name: "cluster",
Connection: clusterConn,
},
},
}
b, err := json.Marshal(egressConfig)
if err != nil {
return err
}
return os.WriteFile(controlConfig.Runtime.EgressSelectorConfig, b, 0600)
}
func genCloudConfig(controlConfig *config.Control) error {
cloudConfig := cloudprovider.Config{
LBDefaultPriorityClassName: cloudprovider.DefaultLBPriorityClassName,
LBEnabled: !controlConfig.DisableServiceLB,
LBNamespace: controlConfig.ServiceLBNamespace,
LBImage: cloudprovider.DefaultLBImage,
Rootless: controlConfig.Rootless,
NodeEnabled: !controlConfig.DisableCCM,
}
if controlConfig.SystemDefaultRegistry != "" {
cloudConfig.LBImage = controlConfig.SystemDefaultRegistry + "/" + cloudConfig.LBImage
}
b, err := json.Marshal(cloudConfig)
if err != nil {
return err
}
return os.WriteFile(controlConfig.Runtime.CloudControllerConfig, b, 0600)
}