mirror of
https://github.com/rancher/rke.git
synced 2025-08-21 08:13:09 +00:00
Add Secret Encryption Provider Support
This commit is contained in:
parent
e9327b51f5
commit
372393ac1b
@ -50,7 +50,7 @@ func GetClusterCertsFromKubernetes(ctx context.Context, kubeCluster *Cluster) (m
|
|||||||
|
|
||||||
certMap := make(map[string]pki.CertificatePKI)
|
certMap := make(map[string]pki.CertificatePKI)
|
||||||
for _, certName := range certificatesNames {
|
for _, certName := range certificatesNames {
|
||||||
secret, err := k8s.GetSecret(k8sClient, certName)
|
secret, err := k8s.GetSystemSecret(k8sClient, certName)
|
||||||
if err != nil && !strings.HasPrefix(certName, "kube-etcd") &&
|
if err != nil && !strings.HasPrefix(certName, "kube-etcd") &&
|
||||||
!strings.Contains(certName, pki.RequestHeaderCACertName) &&
|
!strings.Contains(certName, pki.RequestHeaderCACertName) &&
|
||||||
!strings.Contains(certName, pki.APIProxyClientCertName) &&
|
!strings.Contains(certName, pki.APIProxyClientCertName) &&
|
||||||
|
@ -58,6 +58,13 @@ type Cluster struct {
|
|||||||
UseKubectlDeploy bool
|
UseKubectlDeploy bool
|
||||||
v3.RancherKubernetesEngineConfig `yaml:",inline"`
|
v3.RancherKubernetesEngineConfig `yaml:",inline"`
|
||||||
WorkerHosts []*hosts.Host
|
WorkerHosts []*hosts.Host
|
||||||
|
EncryptionConfig encryptionConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type encryptionConfig struct {
|
||||||
|
RewriteSecrets bool
|
||||||
|
RotateKey bool
|
||||||
|
EncryptionProviderFile string
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -147,14 +154,28 @@ func (c *Cluster) DeployWorkerPlane(ctx context.Context, svcOptionData map[strin
|
|||||||
func ParseConfig(clusterFile string) (*v3.RancherKubernetesEngineConfig, error) {
|
func ParseConfig(clusterFile string) (*v3.RancherKubernetesEngineConfig, error) {
|
||||||
logrus.Debugf("Parsing cluster file [%v]", clusterFile)
|
logrus.Debugf("Parsing cluster file [%v]", clusterFile)
|
||||||
var rkeConfig v3.RancherKubernetesEngineConfig
|
var rkeConfig v3.RancherKubernetesEngineConfig
|
||||||
|
|
||||||
|
// the customConfig is mapped to a k8s type, which doesn't unmarshal well because it has a
|
||||||
|
// nested struct and no yaml tags. Therefor, we have to re-parse it again and assign it correctly.
|
||||||
|
// this only affects rke cli. Since rkeConfig is passed from rancher directly in the rancher use case.
|
||||||
|
clusterFile, secretConfig, err := resolvCustomEncryptionConfig(clusterFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if err := yaml.Unmarshal([]byte(clusterFile), &rkeConfig); err != nil {
|
if err := yaml.Unmarshal([]byte(clusterFile), &rkeConfig); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isEncryptionEnabled(&rkeConfig) && secretConfig != nil {
|
||||||
|
rkeConfig.Services.KubeAPI.SecretsEncryptionConfig.CustomConfig = secretConfig
|
||||||
|
}
|
||||||
return &rkeConfig, nil
|
return &rkeConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitClusterObject(ctx context.Context, rkeConfig *v3.RancherKubernetesEngineConfig, flags ExternalFlags) (*Cluster, error) {
|
func InitClusterObject(ctx context.Context, rkeConfig *v3.RancherKubernetesEngineConfig, flags ExternalFlags, encryptConfig string) (*Cluster, error) {
|
||||||
// basic cluster object from rkeConfig
|
// basic cluster object from rkeConfig
|
||||||
|
var err error
|
||||||
c := &Cluster{
|
c := &Cluster{
|
||||||
AuthnStrategies: make(map[string]bool),
|
AuthnStrategies: make(map[string]bool),
|
||||||
RancherKubernetesEngineConfig: *rkeConfig,
|
RancherKubernetesEngineConfig: *rkeConfig,
|
||||||
@ -164,6 +185,9 @@ func InitClusterObject(ctx context.Context, rkeConfig *v3.RancherKubernetesEngin
|
|||||||
CertificateDir: flags.CertificateDir,
|
CertificateDir: flags.CertificateDir,
|
||||||
StateFilePath: GetStateFilePath(flags.ClusterFilePath, flags.ConfigDir),
|
StateFilePath: GetStateFilePath(flags.ClusterFilePath, flags.ConfigDir),
|
||||||
PrivateRegistriesMap: make(map[string]v3.PrivateRegistry),
|
PrivateRegistriesMap: make(map[string]v3.PrivateRegistry),
|
||||||
|
EncryptionConfig: encryptionConfig{
|
||||||
|
EncryptionProviderFile: encryptConfig,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if metadata.K8sVersionToRKESystemImages == nil {
|
if metadata.K8sVersionToRKESystemImages == nil {
|
||||||
metadata.InitMetadata(ctx)
|
metadata.InitMetadata(ctx)
|
||||||
@ -177,9 +201,19 @@ func InitClusterObject(ctx context.Context, rkeConfig *v3.RancherKubernetesEngin
|
|||||||
if len(c.CertificateDir) == 0 {
|
if len(c.CertificateDir) == 0 {
|
||||||
c.CertificateDir = GetCertificateDirPath(c.ConfigPath, c.ConfigDir)
|
c.CertificateDir = GetCertificateDirPath(c.ConfigPath, c.ConfigDir)
|
||||||
}
|
}
|
||||||
|
// We don't manage custom configuration, if it's there we just use it.
|
||||||
|
if isEncryptionCustomConfig(rkeConfig) {
|
||||||
|
if c.EncryptionConfig.EncryptionProviderFile, err = c.readEncryptionCustomConfig(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else if isEncryptionEnabled(rkeConfig) && c.EncryptionConfig.EncryptionProviderFile == "" {
|
||||||
|
if c.EncryptionConfig.EncryptionProviderFile, err = c.getEncryptionProviderFile(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Setting cluster Defaults
|
// Setting cluster Defaults
|
||||||
err := c.setClusterDefaults(ctx, flags)
|
err = c.setClusterDefaults(ctx, flags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -294,7 +328,7 @@ func getLocalAdminConfigWithNewAddress(localConfigPath, cpAddress string, cluste
|
|||||||
|
|
||||||
func ApplyAuthzResources(ctx context.Context, rkeConfig v3.RancherKubernetesEngineConfig, flags ExternalFlags, dailersOptions hosts.DialersOptions) error {
|
func ApplyAuthzResources(ctx context.Context, rkeConfig v3.RancherKubernetesEngineConfig, flags ExternalFlags, dailersOptions hosts.DialersOptions) error {
|
||||||
// dialer factories are not needed here since we are not uses docker only k8s jobs
|
// dialer factories are not needed here since we are not uses docker only k8s jobs
|
||||||
kubeCluster, err := InitClusterObject(ctx, &rkeConfig, flags)
|
kubeCluster, err := InitClusterObject(ctx, &rkeConfig, flags, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -466,7 +500,7 @@ func ConfigureCluster(
|
|||||||
data map[string]interface{},
|
data map[string]interface{},
|
||||||
useKubectl bool) error {
|
useKubectl bool) error {
|
||||||
// dialer factories are not needed here since we are not uses docker only k8s jobs
|
// dialer factories are not needed here since we are not uses docker only k8s jobs
|
||||||
kubeCluster, err := InitClusterObject(ctx, &rkeConfig, flags)
|
kubeCluster, err := InitClusterObject(ctx, &rkeConfig, flags, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
392
cluster/encryption.go
Normal file
392
cluster/encryption.go
Normal file
@ -0,0 +1,392 @@
|
|||||||
|
package cluster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
ghodssyaml "github.com/ghodss/yaml"
|
||||||
|
normantypes "github.com/rancher/norman/types"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
sigsyaml "sigs.k8s.io/yaml"
|
||||||
|
|
||||||
|
"github.com/rancher/rke/k8s"
|
||||||
|
"github.com/rancher/rke/log"
|
||||||
|
"github.com/rancher/rke/services"
|
||||||
|
"github.com/rancher/rke/templates"
|
||||||
|
"github.com/rancher/rke/util"
|
||||||
|
"github.com/rancher/types/apis/management.cattle.io/v3"
|
||||||
|
"k8s.io/api/core/v1"
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
|
apiserverconfig "k8s.io/apiserver/pkg/apis/config"
|
||||||
|
apiserverconfigv1 "k8s.io/apiserver/pkg/apis/config/v1"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
EncryptionProviderFilePath = "/etc/kubernetes/ssl/encryption.yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type encryptionKey struct {
|
||||||
|
Name string
|
||||||
|
Secret string
|
||||||
|
}
|
||||||
|
type keyList struct {
|
||||||
|
KeyList []*encryptionKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReconcileEncryptionProviderConfig(ctx context.Context, kubeCluster, currentCluster *Cluster) error {
|
||||||
|
log.Infof(ctx, "[%s] Reconciling cluster's encryption provider configuration..", services.ControlRole)
|
||||||
|
if len(kubeCluster.ControlPlaneHosts) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// New or existing cluster deployment with encryption enabled. We will rewrite the secrets after deploying the addons.
|
||||||
|
if (currentCluster == nil || !currentCluster.IsEncryptionEnabled()) &&
|
||||||
|
kubeCluster.IsEncryptionEnabled() {
|
||||||
|
kubeCluster.EncryptionConfig.RewriteSecrets = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// encryption is disabled
|
||||||
|
if !kubeCluster.IsEncryptionEnabled() && !currentCluster.IsEncryptionEnabled() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// disable encryption
|
||||||
|
if !kubeCluster.IsEncryptionEnabled() && currentCluster.IsEncryptionEnabled() {
|
||||||
|
if currentCluster.IsEncryptionCustomConfig() {
|
||||||
|
// KubeAPI will be restarted for the last time during controlplane redeployment, since the
|
||||||
|
// Configuration file is now empty, the Process Plan will change.
|
||||||
|
kubeCluster.EncryptionConfig.EncryptionProviderFile = ""
|
||||||
|
return kubeCluster.DeployEncryptionProviderFile(ctx)
|
||||||
|
}
|
||||||
|
return kubeCluster.DisableSecretsEncryption(ctx, currentCluster)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) DisableSecretsEncryption(ctx context.Context, currentCluster *Cluster) error {
|
||||||
|
log.Infof(ctx, "[%s] Disabling Secrets Encryption..", services.ControlRole)
|
||||||
|
|
||||||
|
if len(c.ControlPlaneHosts) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
c.EncryptionConfig.EncryptionProviderFile, err = currentCluster.generateDisabledEncryptionProviderFile()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logrus.Debugf("[%s] Deploying Identity first Encryption Provider Configuration", services.ControlRole)
|
||||||
|
if err := c.DeployEncryptionProviderFile(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := services.RestartKubeAPIWithHealthcheck(ctx, c.ControlPlaneHosts, c.LocalConnDialerFactory, c.Certificates); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := c.RewriteSecrets(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// KubeAPI will be restarted for the last time during controlplane redeployment, since the
|
||||||
|
// Configuration file is now empty, the Process Plan will change.
|
||||||
|
c.EncryptionConfig.EncryptionProviderFile = ""
|
||||||
|
if err := c.DeployEncryptionProviderFile(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Infof(ctx, "[%s] Secrets Encryption disabled successfully", services.ControlRole)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) RewriteSecrets(ctx context.Context) error {
|
||||||
|
log.Infof(ctx, "Rewriting cluster secrets")
|
||||||
|
var errgrp errgroup.Group
|
||||||
|
k8sClient, err := k8s.NewClient(c.LocalKubeConfigPath, c.K8sWrapTransport)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to initialize new kubernetes client: %v", err)
|
||||||
|
}
|
||||||
|
secretsList, err := k8s.GetSecretsList(k8sClient, "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
secretsQueue := util.GetObjectQueue(secretsList.Items)
|
||||||
|
for w := 0; w < SyncWorkers; w++ {
|
||||||
|
errgrp.Go(func() error {
|
||||||
|
var errList []error
|
||||||
|
for secret := range secretsQueue {
|
||||||
|
s := secret.(v1.Secret)
|
||||||
|
err := rewriteSecret(k8sClient, &s)
|
||||||
|
if err != nil {
|
||||||
|
errList = append(errList, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return util.ErrList(errList)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err := errgrp.Wait(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Infof(ctx, "Cluster secrets rewritten successfully")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) RotateEncryptionKey(ctx context.Context, fullState *FullState) error {
|
||||||
|
//generate new key
|
||||||
|
newKey, err := generateEncryptionKey()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
oldKey, err := c.extractActiveKey(c.EncryptionConfig.EncryptionProviderFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// reverse the keys order in the file, making newKey the Active Key
|
||||||
|
initialKeyList := []*encryptionKey{ // order is critical here!
|
||||||
|
newKey,
|
||||||
|
oldKey,
|
||||||
|
}
|
||||||
|
initialProviderConfig, err := providerFileFromKeyList(keyList{KeyList: initialKeyList})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.EncryptionConfig.EncryptionProviderFile = initialProviderConfig
|
||||||
|
if err := c.DeployEncryptionProviderFile(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// commit to state as soon as possible
|
||||||
|
logrus.Debugf("[%s] Updating cluster state", services.ControlRole)
|
||||||
|
if err := c.UpdateClusterCurrentState(ctx, fullState); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := services.RestartKubeAPIWithHealthcheck(ctx, c.ControlPlaneHosts, c.LocalConnDialerFactory, c.Certificates); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// rewrite secrets
|
||||||
|
if err := c.RewriteSecrets(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// At this point, all secrets have been rewritten using the newKey, so we remove the old one.
|
||||||
|
finalKeyList := []*encryptionKey{
|
||||||
|
newKey,
|
||||||
|
}
|
||||||
|
finalProviderConfig, err := providerFileFromKeyList(keyList{KeyList: finalKeyList})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.EncryptionConfig.EncryptionProviderFile = finalProviderConfig
|
||||||
|
if err := c.DeployEncryptionProviderFile(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// commit to state
|
||||||
|
logrus.Debugf("[%s] Updating cluster state", services.ControlRole)
|
||||||
|
if err := c.UpdateClusterCurrentState(ctx, fullState); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := services.RestartKubeAPIWithHealthcheck(ctx, c.ControlPlaneHosts, c.LocalConnDialerFactory, c.Certificates); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) DeployEncryptionProviderFile(ctx context.Context) error {
|
||||||
|
logrus.Debugf("[%s] Deploying Encryption Provider Configuration file on Control Plane nodes..", services.ControlRole)
|
||||||
|
return deployFile(ctx, c.ControlPlaneHosts, c.SystemImages.Alpine, c.PrivateRegistriesMap, EncryptionProviderFilePath, c.EncryptionConfig.EncryptionProviderFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReconcileDesiredStateEncryptionConfig We do the rotation outside of the cluster reconcile logic. When we are done,
|
||||||
|
// DesiredState needs to be updated to reflect the "new" configuration
|
||||||
|
func (c *Cluster) ReconcileDesiredStateEncryptionConfig(ctx context.Context, fullState *FullState) error {
|
||||||
|
fullState.DesiredState.EncryptionConfig = c.EncryptionConfig.EncryptionProviderFile
|
||||||
|
return fullState.WriteStateFile(ctx, c.StateFilePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) IsEncryptionEnabled() bool {
|
||||||
|
if c == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if c.Services.KubeAPI.SecretsEncryptionConfig != nil &&
|
||||||
|
c.Services.KubeAPI.SecretsEncryptionConfig.Enabled {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) IsEncryptionCustomConfig() bool {
|
||||||
|
if c.IsEncryptionEnabled() &&
|
||||||
|
c.Services.KubeAPI.SecretsEncryptionConfig.CustomConfig != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) getEncryptionProviderFile() (string, error) {
|
||||||
|
if c.EncryptionConfig.EncryptionProviderFile != "" {
|
||||||
|
return c.EncryptionConfig.EncryptionProviderFile, nil
|
||||||
|
}
|
||||||
|
key, err := generateEncryptionKey()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
c.EncryptionConfig.EncryptionProviderFile, err = providerFileFromKeyList(keyList{KeyList: []*encryptionKey{key}})
|
||||||
|
return c.EncryptionConfig.EncryptionProviderFile, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) extractActiveKey(s string) (*encryptionKey, error) {
|
||||||
|
config := apiserverconfig.EncryptionConfiguration{}
|
||||||
|
if err := k8s.DecodeYamlResource(&config, c.EncryptionConfig.EncryptionProviderFile); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resource := config.Resources[0]
|
||||||
|
provider := resource.Providers[0]
|
||||||
|
return &encryptionKey{
|
||||||
|
Name: provider.AESCBC.Keys[0].Name,
|
||||||
|
Secret: provider.AESCBC.Keys[0].Secret,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) generateDisabledEncryptionProviderFile() (string, error) {
|
||||||
|
key, err := c.extractActiveKey(c.EncryptionConfig.EncryptionProviderFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return disabledProviderFileFromKey(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func rewriteSecret(k8sClient *kubernetes.Clientset, secret *v1.Secret) error {
|
||||||
|
var err error
|
||||||
|
if err = k8s.UpdateSecret(k8sClient, secret); err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if apierrors.IsConflict(err) {
|
||||||
|
secret, err = k8s.GetSecret(k8sClient, secret.Name, secret.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = k8s.UpdateSecret(k8sClient, secret)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateEncryptionKey() (*encryptionKey, error) {
|
||||||
|
// TODO: do this in a better way
|
||||||
|
buf := make([]byte, 16)
|
||||||
|
if _, err := rand.Read(buf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &encryptionKey{
|
||||||
|
Name: normantypes.GenerateName("key"),
|
||||||
|
Secret: base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf("%X", buf))),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isEncryptionEnabled(rkeConfig *v3.RancherKubernetesEngineConfig) bool {
|
||||||
|
if rkeConfig.Services.KubeAPI.SecretsEncryptionConfig != nil &&
|
||||||
|
rkeConfig.Services.KubeAPI.SecretsEncryptionConfig.Enabled {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func isEncryptionCustomConfig(rkeConfig *v3.RancherKubernetesEngineConfig) bool {
|
||||||
|
if isEncryptionEnabled(rkeConfig) &&
|
||||||
|
rkeConfig.Services.KubeAPI.SecretsEncryptionConfig.CustomConfig != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func providerFileFromKeyList(keyList interface{}) (string, error) {
|
||||||
|
return templates.CompileTemplateFromMap(templates.MultiKeyEncryptionProviderFile, keyList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func disabledProviderFileFromKey(keyList interface{}) (string, error) {
|
||||||
|
return templates.CompileTemplateFromMap(templates.DisabledEncryptionProviderFile, keyList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cluster) readEncryptionCustomConfig() (string, error) {
|
||||||
|
// directly marshalling apiserverconfig.EncryptionConfiguration to yaml breaks things because TypeMeta
|
||||||
|
// is nested and all fields don't have tags. apiserverconfigv1 has json tags only. So we do this as a work around.
|
||||||
|
|
||||||
|
out := apiserverconfigv1.EncryptionConfiguration{}
|
||||||
|
err := apiserverconfigv1.Convert_config_EncryptionConfiguration_To_v1_EncryptionConfiguration(
|
||||||
|
c.RancherKubernetesEngineConfig.Services.KubeAPI.SecretsEncryptionConfig.CustomConfig, &out, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
jsonConfig, err := json.Marshal(out)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
yamlConfig, err := sigsyaml.JSONToYAML(jsonConfig)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return templates.CompileTemplateFromMap(templates.CustomEncryptionProviderFile,
|
||||||
|
struct{ CustomConfig string }{CustomConfig: string(yamlConfig)})
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolvCustomEncryptionConfig(clusterFile string) (string, *apiserverconfig.EncryptionConfiguration, error) {
|
||||||
|
var err error
|
||||||
|
var r map[string]interface{}
|
||||||
|
err = ghodssyaml.Unmarshal([]byte(clusterFile), &r)
|
||||||
|
if err != nil {
|
||||||
|
return clusterFile, nil, fmt.Errorf("error unmarshalling: %v", err)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, fmt.Errorf("error unmarshalling encryption custom config: %v", err)
|
||||||
|
}
|
||||||
|
services, ok := r["services"].(map[string]interface{})
|
||||||
|
if services == nil || !ok {
|
||||||
|
return clusterFile, nil, nil
|
||||||
|
}
|
||||||
|
kubeapi, ok := services["kube-api"].(map[string]interface{})
|
||||||
|
if kubeapi == nil || !ok {
|
||||||
|
return clusterFile, nil, nil
|
||||||
|
}
|
||||||
|
sec, ok := kubeapi["secrets_encryption_config"].(map[string]interface{})
|
||||||
|
if sec == nil || !ok {
|
||||||
|
return clusterFile, nil, nil
|
||||||
|
}
|
||||||
|
customConfig, ok := sec["custom_config"].(map[string]interface{})
|
||||||
|
|
||||||
|
if ok && customConfig != nil {
|
||||||
|
delete(sec, "custom_config")
|
||||||
|
newClusterFile, err := ghodssyaml.Marshal(r)
|
||||||
|
c, err := parseCustomConfig(customConfig)
|
||||||
|
return string(newClusterFile), c, err
|
||||||
|
}
|
||||||
|
return clusterFile, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseCustomConfig(customConfig map[string]interface{}) (*apiserverconfig.EncryptionConfiguration, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
data, err := json.Marshal(customConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error marshalling: %v", err)
|
||||||
|
}
|
||||||
|
scheme := runtime.NewScheme()
|
||||||
|
err = apiserverconfig.AddToScheme(scheme)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error adding to scheme: %v", err)
|
||||||
|
}
|
||||||
|
err = apiserverconfigv1.AddToScheme(scheme)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error adding to scheme: %v", err)
|
||||||
|
}
|
||||||
|
codecs := serializer.NewCodecFactory(scheme)
|
||||||
|
decoder := codecs.UniversalDecoder()
|
||||||
|
decodedObj, objType, err := decoder.Decode(data, nil, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error decoding data: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
decodedConfig, ok := decodedObj.(*apiserverconfig.EncryptionConfiguration)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unexpected type: %T", objType)
|
||||||
|
}
|
||||||
|
return decodedConfig, nil
|
||||||
|
}
|
@ -34,14 +34,26 @@ func doDeployFile(ctx context.Context, host *hosts.Host, fileName, fileContents,
|
|||||||
if err := docker.DoRemoveContainer(ctx, host.DClient, ContainerName, host.Address); err != nil {
|
if err := docker.DoRemoveContainer(ctx, host.DClient, ContainerName, host.Address); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
containerEnv := []string{ConfigEnv + "=" + fileContents}
|
var cmd, containerEnv []string
|
||||||
imageCfg := &container.Config{
|
|
||||||
Image: alpineImage,
|
if fileContents != "" {
|
||||||
Cmd: []string{
|
containerEnv = []string{ConfigEnv + "=" + fileContents}
|
||||||
|
cmd = []string{
|
||||||
"sh",
|
"sh",
|
||||||
"-c",
|
"-c",
|
||||||
fmt.Sprintf("t=$(mktemp); echo -e \"$%s\" > $t && mv $t %s && chmod 600 %s", ConfigEnv, fileName, fileName),
|
fmt.Sprintf("t=$(mktemp); echo -e \"$%s\" > $t && mv $t %s && chmod 600 %s", ConfigEnv, fileName, fileName),
|
||||||
},
|
}
|
||||||
|
} else {
|
||||||
|
cmd = []string{
|
||||||
|
"sh",
|
||||||
|
"-c",
|
||||||
|
fmt.Sprintf("rm -f %s", fileName),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
imageCfg := &container.Config{
|
||||||
|
Image: alpineImage,
|
||||||
|
Cmd: cmd,
|
||||||
Env: containerEnv,
|
Env: containerEnv,
|
||||||
}
|
}
|
||||||
hostCfg := &container.HostConfig{
|
hostCfg := &container.HostConfig{
|
||||||
|
@ -157,6 +157,11 @@ func (c *Cluster) SetUpHosts(ctx context.Context, flags ExternalFlags) error {
|
|||||||
}
|
}
|
||||||
log.Infof(ctx, "[%s] Successfully deployed authentication webhook config Cluster nodes", authnWebhookFileName)
|
log.Infof(ctx, "[%s] Successfully deployed authentication webhook config Cluster nodes", authnWebhookFileName)
|
||||||
}
|
}
|
||||||
|
if c.EncryptionConfig.EncryptionProviderFile != "" {
|
||||||
|
if err := c.DeployEncryptionProviderFile(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ func GetServiceOptionData(data map[string]interface{}) map[string]*v3.Kubernetes
|
|||||||
|
|
||||||
func GeneratePlan(ctx context.Context, rkeConfig *v3.RancherKubernetesEngineConfig, hostsInfoMap map[string]types.Info, data map[string]interface{}) (v3.RKEPlan, error) {
|
func GeneratePlan(ctx context.Context, rkeConfig *v3.RancherKubernetesEngineConfig, hostsInfoMap map[string]types.Info, data map[string]interface{}) (v3.RKEPlan, error) {
|
||||||
clusterPlan := v3.RKEPlan{}
|
clusterPlan := v3.RKEPlan{}
|
||||||
myCluster, err := InitClusterObject(ctx, rkeConfig, ExternalFlags{})
|
myCluster, err := InitClusterObject(ctx, rkeConfig, ExternalFlags{}, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return clusterPlan, err
|
return clusterPlan, err
|
||||||
}
|
}
|
||||||
@ -109,15 +109,23 @@ func BuildRKEConfigNodePlan(ctx context.Context, myCluster *Cluster, host *hosts
|
|||||||
|
|
||||||
portChecks = append(portChecks, BuildPortChecksFromPortList(host, EtcdPortList, ProtocolTCP)...)
|
portChecks = append(portChecks, BuildPortChecksFromPortList(host, EtcdPortList, ProtocolTCP)...)
|
||||||
}
|
}
|
||||||
cloudConfig := v3.File{
|
files := []v3.File{
|
||||||
|
v3.File{
|
||||||
Name: cloudConfigFileName,
|
Name: cloudConfigFileName,
|
||||||
Contents: b64.StdEncoding.EncodeToString([]byte(myCluster.CloudConfigFile)),
|
Contents: b64.StdEncoding.EncodeToString([]byte(myCluster.CloudConfigFile)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if myCluster.IsEncryptionEnabled() {
|
||||||
|
files = append(files, v3.File{
|
||||||
|
Name: EncryptionProviderFilePath,
|
||||||
|
Contents: b64.StdEncoding.EncodeToString([]byte(myCluster.EncryptionConfig.EncryptionProviderFile)),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return v3.RKEConfigNodePlan{
|
return v3.RKEConfigNodePlan{
|
||||||
Address: host.Address,
|
Address: host.Address,
|
||||||
Processes: osLimitationFilter(hostDockerInfo.OSType, processes),
|
Processes: osLimitationFilter(hostDockerInfo.OSType, processes),
|
||||||
PortChecks: portChecks,
|
PortChecks: portChecks,
|
||||||
Files: []v3.File{cloudConfig},
|
Files: files,
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
k8s.ExternalAddressAnnotation: host.Address,
|
k8s.ExternalAddressAnnotation: host.Address,
|
||||||
k8s.InternalAddressAnnotation: host.InternalAddress,
|
k8s.InternalAddressAnnotation: host.InternalAddress,
|
||||||
@ -210,6 +218,10 @@ func (c *Cluster) BuildKubeAPIProcess(host *hosts.Host, prefixPath string, svcOp
|
|||||||
c.Services.KubeAPI.ExtraEnv,
|
c.Services.KubeAPI.ExtraEnv,
|
||||||
fmt.Sprintf("%s=%s", CloudConfigSumEnv, getCloudConfigChecksum(c.CloudConfigFile)))
|
fmt.Sprintf("%s=%s", CloudConfigSumEnv, getCloudConfigChecksum(c.CloudConfigFile)))
|
||||||
}
|
}
|
||||||
|
if c.EncryptionConfig.EncryptionProviderFile != "" {
|
||||||
|
CommandArgs["experimental-encryption-provider-config"] = EncryptionProviderFilePath
|
||||||
|
}
|
||||||
|
|
||||||
serviceOptions := c.GetKubernetesServicesOptions(host.DockerInfo.OSType, svcOptionData)
|
serviceOptions := c.GetKubernetesServicesOptions(host.DockerInfo.OSType, svcOptionData)
|
||||||
if serviceOptions.KubeAPI != nil {
|
if serviceOptions.KubeAPI != nil {
|
||||||
for k, v := range serviceOptions.KubeAPI {
|
for k, v := range serviceOptions.KubeAPI {
|
||||||
|
@ -66,7 +66,6 @@ func ReconcileCluster(ctx context.Context, kubeCluster, currentCluster *Cluster,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof(ctx, "[reconcile] Reconciled cluster state successfully")
|
log.Infof(ctx, "[reconcile] Reconciled cluster state successfully")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -36,11 +36,13 @@ type FullState struct {
|
|||||||
type State struct {
|
type State struct {
|
||||||
RancherKubernetesEngineConfig *v3.RancherKubernetesEngineConfig `json:"rkeConfig,omitempty"`
|
RancherKubernetesEngineConfig *v3.RancherKubernetesEngineConfig `json:"rkeConfig,omitempty"`
|
||||||
CertificatesBundle map[string]pki.CertificatePKI `json:"certificatesBundle,omitempty"`
|
CertificatesBundle map[string]pki.CertificatePKI `json:"certificatesBundle,omitempty"`
|
||||||
|
EncryptionConfig string `json:"encryptionConfig,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cluster) UpdateClusterCurrentState(ctx context.Context, fullState *FullState) error {
|
func (c *Cluster) UpdateClusterCurrentState(ctx context.Context, fullState *FullState) error {
|
||||||
fullState.CurrentState.RancherKubernetesEngineConfig = c.RancherKubernetesEngineConfig.DeepCopy()
|
fullState.CurrentState.RancherKubernetesEngineConfig = c.RancherKubernetesEngineConfig.DeepCopy()
|
||||||
fullState.CurrentState.CertificatesBundle = c.Certificates
|
fullState.CurrentState.CertificatesBundle = c.Certificates
|
||||||
|
fullState.CurrentState.EncryptionConfig = c.EncryptionConfig.EncryptionProviderFile
|
||||||
return fullState.WriteStateFile(ctx, c.StateFilePath)
|
return fullState.WriteStateFile(ctx, c.StateFilePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,12 +54,12 @@ func (c *Cluster) GetClusterState(ctx context.Context, fullState *FullState) (*C
|
|||||||
|
|
||||||
// resetup external flags
|
// resetup external flags
|
||||||
flags := GetExternalFlags(false, false, false, c.ConfigDir, c.ConfigPath)
|
flags := GetExternalFlags(false, false, false, c.ConfigDir, c.ConfigPath)
|
||||||
currentCluster, err := InitClusterObject(ctx, fullState.CurrentState.RancherKubernetesEngineConfig, flags)
|
currentCluster, err := InitClusterObject(ctx, fullState.CurrentState.RancherKubernetesEngineConfig, flags, fullState.CurrentState.EncryptionConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
currentCluster.Certificates = fullState.CurrentState.CertificatesBundle
|
currentCluster.Certificates = fullState.CurrentState.CertificatesBundle
|
||||||
|
currentCluster.EncryptionConfig.EncryptionProviderFile = fullState.CurrentState.EncryptionConfig
|
||||||
// resetup dialers
|
// resetup dialers
|
||||||
dialerOptions := hosts.GetDialerOptions(c.DockerDialerFactory, c.LocalConnDialerFactory, c.K8sWrapTransport)
|
dialerOptions := hosts.GetDialerOptions(c.DockerDialerFactory, c.LocalConnDialerFactory, c.K8sWrapTransport)
|
||||||
if err := currentCluster.SetupDialers(ctx, dialerOptions); err != nil {
|
if err := currentCluster.SetupDialers(ctx, dialerOptions); err != nil {
|
||||||
@ -147,7 +149,8 @@ func GetK8sVersion(localConfigPath string, k8sWrapTransport transport.WrapperFun
|
|||||||
return fmt.Sprintf("%#v", *serverVersion), nil
|
return fmt.Sprintf("%#v", *serverVersion), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func RebuildState(ctx context.Context, rkeConfig *v3.RancherKubernetesEngineConfig, oldState *FullState, flags ExternalFlags) (*FullState, error) {
|
func RebuildState(ctx context.Context, kubeCluster *Cluster, oldState *FullState, flags ExternalFlags) (*FullState, error) {
|
||||||
|
rkeConfig := &kubeCluster.RancherKubernetesEngineConfig
|
||||||
newState := &FullState{
|
newState := &FullState{
|
||||||
DesiredState: State{
|
DesiredState: State{
|
||||||
RancherKubernetesEngineConfig: rkeConfig.DeepCopy(),
|
RancherKubernetesEngineConfig: rkeConfig.DeepCopy(),
|
||||||
@ -169,25 +172,14 @@ func RebuildState(ctx context.Context, rkeConfig *v3.RancherKubernetesEngineConf
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rebuilding the certificates of the desired state
|
// Rebuilding the certificates of the desired state
|
||||||
if oldState.DesiredState.CertificatesBundle == nil {
|
if oldState.DesiredState.CertificatesBundle == nil { // this is a fresh cluster
|
||||||
// Get the certificate Bundle
|
if err := buildFreshState(ctx, kubeCluster, newState); err != nil {
|
||||||
certBundle, err := pki.GenerateRKECerts(ctx, *rkeConfig, "", "")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to generate certificate bundle: %v", err)
|
|
||||||
}
|
|
||||||
newState.DesiredState.CertificatesBundle = certBundle
|
|
||||||
} else {
|
|
||||||
pkiCertBundle := oldState.DesiredState.CertificatesBundle
|
|
||||||
// check for legacy clusters prior to requestheaderca
|
|
||||||
if pkiCertBundle[pki.RequestHeaderCACertName].Certificate == nil {
|
|
||||||
if err := pki.GenerateRKERequestHeaderCACert(ctx, pkiCertBundle, flags.ClusterFilePath, flags.ConfigDir); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
} else { // This is an existing cluster with an old DesiredState
|
||||||
if err := pki.GenerateRKEServicesCerts(ctx, pkiCertBundle, *rkeConfig, flags.ClusterFilePath, flags.ConfigDir, false); err != nil {
|
if err := rebuildExistingState(ctx, kubeCluster, oldState, newState, flags); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
newState.DesiredState.CertificatesBundle = pkiCertBundle
|
|
||||||
}
|
}
|
||||||
newState.CurrentState = oldState.CurrentState
|
newState.CurrentState = oldState.CurrentState
|
||||||
return newState, nil
|
return newState, nil
|
||||||
@ -292,3 +284,46 @@ func GetStateFromNodes(ctx context.Context, kubeCluster *Cluster) *Cluster {
|
|||||||
log.Infof(ctx, "[state] Successfully fetched cluster state from Nodes")
|
log.Infof(ctx, "[state] Successfully fetched cluster state from Nodes")
|
||||||
return ¤tCluster
|
return ¤tCluster
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildFreshState(ctx context.Context, kubeCluster *Cluster, newState *FullState) error {
|
||||||
|
rkeConfig := &kubeCluster.RancherKubernetesEngineConfig
|
||||||
|
// Get the certificate Bundle
|
||||||
|
certBundle, err := pki.GenerateRKECerts(ctx, *rkeConfig, "", "")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to generate certificate bundle: %v", err)
|
||||||
|
}
|
||||||
|
newState.DesiredState.CertificatesBundle = certBundle
|
||||||
|
if isEncryptionEnabled(rkeConfig) {
|
||||||
|
if newState.DesiredState.EncryptionConfig, err = kubeCluster.getEncryptionProviderFile(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func rebuildExistingState(ctx context.Context, kubeCluster *Cluster, oldState, newState *FullState, flags ExternalFlags) error {
|
||||||
|
rkeConfig := &kubeCluster.RancherKubernetesEngineConfig
|
||||||
|
pkiCertBundle := oldState.DesiredState.CertificatesBundle
|
||||||
|
// check for legacy clusters prior to requestheaderca
|
||||||
|
if pkiCertBundle[pki.RequestHeaderCACertName].Certificate == nil {
|
||||||
|
if err := pki.GenerateRKERequestHeaderCACert(ctx, pkiCertBundle, flags.ClusterFilePath, flags.ConfigDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := pki.GenerateRKEServicesCerts(ctx, pkiCertBundle, *rkeConfig, flags.ClusterFilePath, flags.ConfigDir, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newState.DesiredState.CertificatesBundle = pkiCertBundle
|
||||||
|
if isEncryptionEnabled(rkeConfig) {
|
||||||
|
if oldState.DesiredState.EncryptionConfig != "" {
|
||||||
|
newState.DesiredState.EncryptionConfig = oldState.DesiredState.EncryptionConfig
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
if newState.DesiredState.EncryptionConfig, err = kubeCluster.getEncryptionProviderFile(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -136,7 +136,7 @@ func rebuildClusterWithRotatedCertificates(ctx context.Context,
|
|||||||
return APIURL, caCrt, clientCert, clientKey, nil, err
|
return APIURL, caCrt, clientCert, clientKey, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
kubeCluster, err := cluster.InitClusterObject(ctx, clusterState.DesiredState.RancherKubernetesEngineConfig.DeepCopy(), flags)
|
kubeCluster, err := cluster.InitClusterObject(ctx, clusterState.DesiredState.RancherKubernetesEngineConfig.DeepCopy(), flags, clusterState.DesiredState.EncryptionConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return APIURL, caCrt, clientCert, clientKey, nil, err
|
return APIURL, caCrt, clientCert, clientKey, nil, err
|
||||||
}
|
}
|
||||||
@ -231,7 +231,7 @@ func GenerateRKECSRs(ctx context.Context, rkeConfig *v3.RancherKubernetesEngineC
|
|||||||
}
|
}
|
||||||
|
|
||||||
// initialze the cluster object from the config file
|
// initialze the cluster object from the config file
|
||||||
kubeCluster, err := cluster.InitClusterObject(ctx, rkeConfig, flags)
|
kubeCluster, err := cluster.InitClusterObject(ctx, rkeConfig, flags, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -74,10 +74,8 @@ func ClusterInit(ctx context.Context, rkeConfig *v3.RancherKubernetesEngineConfi
|
|||||||
if len(flags.CertificateDir) == 0 {
|
if len(flags.CertificateDir) == 0 {
|
||||||
flags.CertificateDir = cluster.GetCertificateDirPath(flags.ClusterFilePath, flags.ConfigDir)
|
flags.CertificateDir = cluster.GetCertificateDirPath(flags.ClusterFilePath, flags.ConfigDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
rkeFullState, _ := cluster.ReadStateFile(ctx, stateFilePath)
|
rkeFullState, _ := cluster.ReadStateFile(ctx, stateFilePath)
|
||||||
|
kubeCluster, err := cluster.InitClusterObject(ctx, rkeConfig, flags, rkeFullState.DesiredState.EncryptionConfig)
|
||||||
kubeCluster, err := cluster.InitClusterObject(ctx, rkeConfig, flags)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -93,16 +91,21 @@ func ClusterInit(ctx context.Context, rkeConfig *v3.RancherKubernetesEngineConfi
|
|||||||
}
|
}
|
||||||
log.Warnf(ctx, "[state] can't fetch legacy cluster state from Kubernetes: %v", err)
|
log.Warnf(ctx, "[state] can't fetch legacy cluster state from Kubernetes: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if certificate rotate or normal init
|
// check if certificate rotate or normal init
|
||||||
if kubeCluster.RancherKubernetesEngineConfig.RotateCertificates != nil {
|
if kubeCluster.RancherKubernetesEngineConfig.RotateCertificates != nil {
|
||||||
fullState, err = rotateRKECertificates(ctx, kubeCluster, flags, rkeFullState)
|
fullState, err = rotateRKECertificates(ctx, kubeCluster, flags, rkeFullState)
|
||||||
} else {
|
} else {
|
||||||
fullState, err = cluster.RebuildState(ctx, &kubeCluster.RancherKubernetesEngineConfig, rkeFullState, flags)
|
fullState, err = cluster.RebuildState(ctx, kubeCluster, rkeFullState, flags)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fullState.DesiredState.EncryptionConfig != "" {
|
||||||
|
kubeCluster.EncryptionConfig.EncryptionProviderFile = fullState.DesiredState.EncryptionConfig
|
||||||
|
}
|
||||||
|
|
||||||
rkeState := cluster.FullState{
|
rkeState := cluster.FullState{
|
||||||
DesiredState: fullState.DesiredState,
|
DesiredState: fullState.DesiredState,
|
||||||
CurrentState: fullState.CurrentState,
|
CurrentState: fullState.CurrentState,
|
||||||
|
107
cmd/encryption.go
Normal file
107
cmd/encryption.go
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/rancher/rke/cluster"
|
||||||
|
"github.com/rancher/rke/hosts"
|
||||||
|
"github.com/rancher/rke/log"
|
||||||
|
"github.com/rancher/rke/pki"
|
||||||
|
"github.com/rancher/types/apis/management.cattle.io/v3"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
func EncryptionCommand() cli.Command {
|
||||||
|
encryptFlags := []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "config",
|
||||||
|
Usage: "Specify an alternate cluster YAML file",
|
||||||
|
Value: pki.ClusterConfig,
|
||||||
|
EnvVar: "RKE_CONFIG",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
encryptFlags = append(encryptFlags, commonFlags...)
|
||||||
|
return cli.Command{
|
||||||
|
Name: "encrypt",
|
||||||
|
Usage: "Manage cluster encryption provider keys",
|
||||||
|
Subcommands: cli.Commands{
|
||||||
|
cli.Command{
|
||||||
|
Name: "rotate-key",
|
||||||
|
Usage: "Rotate cluster encryption provider key",
|
||||||
|
Action: rotateEncryptionKeyFromCli,
|
||||||
|
Flags: encryptFlags,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rotateEncryptionKeyFromCli(ctx *cli.Context) error {
|
||||||
|
logrus.Infof("Running RKE version: %v", ctx.App.Version)
|
||||||
|
clusterFile, filePath, err := resolveClusterFile(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to resolve cluster file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rkeConfig, err := cluster.ParseConfig(clusterFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to parse cluster file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rkeConfig, err = setOptionsFromCLI(ctx, rkeConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// setting up the flags
|
||||||
|
flags := cluster.GetExternalFlags(false, false, false, "", filePath)
|
||||||
|
|
||||||
|
return RotateEncryptionKey(context.Background(), rkeConfig, hosts.DialersOptions{}, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RotateEncryptionKey(ctx context.Context, rkeConfig *v3.RancherKubernetesEngineConfig,
|
||||||
|
dialersOptions hosts.DialersOptions, flags cluster.ExternalFlags) error {
|
||||||
|
log.Infof(ctx, "Rotating cluster secrets encryption key..")
|
||||||
|
stateFilePath := cluster.GetStateFilePath(flags.ClusterFilePath, flags.ConfigDir)
|
||||||
|
rkeFullState, _ := cluster.ReadStateFile(ctx, stateFilePath)
|
||||||
|
// We generate the first encryption config in ClusterInit, to store it ASAP. It's written
|
||||||
|
// to the DesiredState
|
||||||
|
stateEncryptionConfig := rkeFullState.DesiredState.EncryptionConfig
|
||||||
|
|
||||||
|
// if CurrentState has EncryptionConfig, it means this is NOT the first time we enable encryption, we should use the _latest_ applied value from the current cluster
|
||||||
|
if rkeFullState.CurrentState.EncryptionConfig != "" {
|
||||||
|
stateEncryptionConfig = rkeFullState.CurrentState.EncryptionConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
kubeCluster, err := cluster.InitClusterObject(ctx, rkeConfig, flags, stateEncryptionConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if kubeCluster.IsEncryptionCustomConfig() {
|
||||||
|
return fmt.Errorf("can't rotate encryption keys: Key Rotation is not supported with custom configuration")
|
||||||
|
}
|
||||||
|
if !kubeCluster.IsEncryptionEnabled() {
|
||||||
|
return fmt.Errorf("can't rotate encryption keys: Encryption Configuration is disabled")
|
||||||
|
}
|
||||||
|
kubeCluster.Certificates = rkeFullState.DesiredState.CertificatesBundle
|
||||||
|
if err := kubeCluster.SetupDialers(ctx, dialersOptions); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := kubeCluster.TunnelHosts(ctx, flags); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = kubeCluster.RotateEncryptionKey(ctx, rkeFullState)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// make sure we have the latest state
|
||||||
|
rkeFullState, _ = cluster.ReadStateFile(ctx, stateFilePath)
|
||||||
|
log.Infof(ctx, "Reconciling cluster state")
|
||||||
|
if err := kubeCluster.ReconcileDesiredStateEncryptionConfig(ctx, rkeFullState); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Infof(ctx, "Cluster secrets encryption key rotated successfully")
|
||||||
|
return nil
|
||||||
|
}
|
12
cmd/etcd.go
12
cmd/etcd.go
@ -92,7 +92,7 @@ func SnapshotSaveEtcdHosts(
|
|||||||
flags cluster.ExternalFlags, snapshotName string) error {
|
flags cluster.ExternalFlags, snapshotName string) error {
|
||||||
|
|
||||||
log.Infof(ctx, "Starting saving snapshot on etcd hosts")
|
log.Infof(ctx, "Starting saving snapshot on etcd hosts")
|
||||||
kubeCluster, err := cluster.InitClusterObject(ctx, rkeConfig, flags)
|
kubeCluster, err := cluster.InitClusterObject(ctx, rkeConfig, flags, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -121,12 +121,14 @@ func RestoreEtcdSnapshot(
|
|||||||
snapshotName string) (string, string, string, string, map[string]pki.CertificatePKI, error) {
|
snapshotName string) (string, string, string, string, map[string]pki.CertificatePKI, error) {
|
||||||
var APIURL, caCrt, clientCert, clientKey string
|
var APIURL, caCrt, clientCert, clientKey string
|
||||||
log.Infof(ctx, "Restoring etcd snapshot %s", snapshotName)
|
log.Infof(ctx, "Restoring etcd snapshot %s", snapshotName)
|
||||||
kubeCluster, err := cluster.InitClusterObject(ctx, rkeConfig, flags)
|
|
||||||
|
stateFilePath := cluster.GetStateFilePath(flags.ClusterFilePath, flags.ConfigDir)
|
||||||
|
rkeFullState, _ := cluster.ReadStateFile(ctx, stateFilePath)
|
||||||
|
|
||||||
|
kubeCluster, err := cluster.InitClusterObject(ctx, rkeConfig, flags, rkeFullState.DesiredState.EncryptionConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return APIURL, caCrt, clientCert, clientKey, nil, err
|
return APIURL, caCrt, clientCert, clientKey, nil, err
|
||||||
}
|
}
|
||||||
stateFilePath := cluster.GetStateFilePath(flags.ClusterFilePath, flags.ConfigDir)
|
|
||||||
rkeFullState, _ := cluster.ReadStateFile(ctx, stateFilePath)
|
|
||||||
if err := checkLegacyCluster(ctx, kubeCluster, rkeFullState, flags); err != nil {
|
if err := checkLegacyCluster(ctx, kubeCluster, rkeFullState, flags); err != nil {
|
||||||
return APIURL, caCrt, clientCert, clientKey, nil, err
|
return APIURL, caCrt, clientCert, clientKey, nil, err
|
||||||
}
|
}
|
||||||
@ -240,7 +242,7 @@ func SnapshotRemoveFromEtcdHosts(
|
|||||||
flags cluster.ExternalFlags, snapshotName string) error {
|
flags cluster.ExternalFlags, snapshotName string) error {
|
||||||
|
|
||||||
log.Infof(ctx, "Starting snapshot remove on etcd hosts")
|
log.Infof(ctx, "Starting snapshot remove on etcd hosts")
|
||||||
kubeCluster, err := cluster.InitClusterObject(ctx, rkeConfig, flags)
|
kubeCluster, err := cluster.InitClusterObject(ctx, rkeConfig, flags, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ func ClusterRemove(
|
|||||||
|
|
||||||
log.Infof(ctx, "Tearing down Kubernetes cluster")
|
log.Infof(ctx, "Tearing down Kubernetes cluster")
|
||||||
|
|
||||||
kubeCluster, err := cluster.InitClusterObject(ctx, rkeConfig, flags)
|
kubeCluster, err := cluster.InitClusterObject(ctx, rkeConfig, flags, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
22
cmd/up.go
22
cmd/up.go
@ -84,7 +84,16 @@ func ClusterUp(ctx context.Context, dialersOptions hosts.DialersOptions, flags c
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return APIURL, caCrt, clientCert, clientKey, nil, err
|
return APIURL, caCrt, clientCert, clientKey, nil, err
|
||||||
}
|
}
|
||||||
kubeCluster, err := cluster.InitClusterObject(ctx, clusterState.DesiredState.RancherKubernetesEngineConfig.DeepCopy(), flags)
|
// We generate the first encryption config in ClusterInit, to store it ASAP. It's written
|
||||||
|
// to the DesiredState
|
||||||
|
stateEncryptionConfig := clusterState.DesiredState.EncryptionConfig
|
||||||
|
|
||||||
|
// if CurrentState has EncryptionConfig, it means this is NOT the first time we enable encryption, we should use the _latest_ applied value from the current cluster
|
||||||
|
if clusterState.CurrentState.EncryptionConfig != "" {
|
||||||
|
stateEncryptionConfig = clusterState.CurrentState.EncryptionConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
kubeCluster, err := cluster.InitClusterObject(ctx, clusterState.DesiredState.RancherKubernetesEngineConfig.DeepCopy(), flags, stateEncryptionConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return APIURL, caCrt, clientCert, clientKey, nil, err
|
return APIURL, caCrt, clientCert, clientKey, nil, err
|
||||||
}
|
}
|
||||||
@ -136,10 +145,14 @@ func ClusterUp(ctx context.Context, dialersOptions hosts.DialersOptions, flags c
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return APIURL, caCrt, clientCert, clientKey, nil, err
|
return APIURL, caCrt, clientCert, clientKey, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// update APIURL after reconcile
|
// update APIURL after reconcile
|
||||||
if len(kubeCluster.ControlPlaneHosts) > 0 {
|
if len(kubeCluster.ControlPlaneHosts) > 0 {
|
||||||
APIURL = fmt.Sprintf("https://%s:6443", kubeCluster.ControlPlaneHosts[0].Address)
|
APIURL = fmt.Sprintf("https://%s:6443", kubeCluster.ControlPlaneHosts[0].Address)
|
||||||
}
|
}
|
||||||
|
if err = cluster.ReconcileEncryptionProviderConfig(ctx, kubeCluster, currentCluster); err != nil {
|
||||||
|
return APIURL, caCrt, clientCert, clientKey, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if err := kubeCluster.PrePullK8sImages(ctx); err != nil {
|
if err := kubeCluster.PrePullK8sImages(ctx); err != nil {
|
||||||
return APIURL, caCrt, clientCert, clientKey, nil, err
|
return APIURL, caCrt, clientCert, clientKey, nil, err
|
||||||
@ -149,7 +162,6 @@ func ClusterUp(ctx context.Context, dialersOptions hosts.DialersOptions, flags c
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return APIURL, caCrt, clientCert, clientKey, nil, err
|
return APIURL, caCrt, clientCert, clientKey, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply Authz configuration after deploying controlplane
|
// Apply Authz configuration after deploying controlplane
|
||||||
err = cluster.ApplyAuthzResources(ctx, kubeCluster.RancherKubernetesEngineConfig, flags, dialersOptions)
|
err = cluster.ApplyAuthzResources(ctx, kubeCluster.RancherKubernetesEngineConfig, flags, dialersOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -184,6 +196,11 @@ func ClusterUp(ctx context.Context, dialersOptions hosts.DialersOptions, flags c
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return APIURL, caCrt, clientCert, clientKey, nil, err
|
return APIURL, caCrt, clientCert, clientKey, nil, err
|
||||||
}
|
}
|
||||||
|
if kubeCluster.EncryptionConfig.RewriteSecrets {
|
||||||
|
if err = kubeCluster.RewriteSecrets(ctx); err != nil {
|
||||||
|
return APIURL, caCrt, clientCert, clientKey, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := checkAllIncluded(kubeCluster); err != nil {
|
if err := checkAllIncluded(kubeCluster); err != nil {
|
||||||
return APIURL, caCrt, clientCert, clientKey, nil, err
|
return APIURL, caCrt, clientCert, clientKey, nil, err
|
||||||
@ -220,6 +237,7 @@ func clusterUpFromCli(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rkeConfig, err := cluster.ParseConfig(clusterFile)
|
rkeConfig, err := cluster.ParseConfig(clusterFile)
|
||||||
|
// logrus.Infof("melsayed---------clusterUpFromCli--------- %+v", rkeConfig.Services.KubeAPI.SecretsEncryptionConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to parse cluster file: %v", err)
|
return fmt.Errorf("Failed to parse cluster file: %v", err)
|
||||||
}
|
}
|
||||||
|
1
go.mod
1
go.mod
@ -18,6 +18,7 @@ require (
|
|||||||
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible
|
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible
|
||||||
github.com/docker/docker v0.7.3-0.20190808172531-150530564a14
|
github.com/docker/docker v0.7.3-0.20190808172531-150530564a14
|
||||||
github.com/docker/go-connections v0.3.0
|
github.com/docker/go-connections v0.3.0
|
||||||
|
github.com/ghodss/yaml v1.0.0
|
||||||
github.com/go-ini/ini v1.37.0
|
github.com/go-ini/ini v1.37.0
|
||||||
github.com/google/btree v1.0.0 // indirect
|
github.com/google/btree v1.0.0 // indirect
|
||||||
github.com/gorilla/mux v1.7.3 // indirect
|
github.com/gorilla/mux v1.7.3 // indirect
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
func UpdateClusterRoleBindingFromYaml(k8sClient *kubernetes.Clientset, clusterRoleBindingYaml string) error {
|
func UpdateClusterRoleBindingFromYaml(k8sClient *kubernetes.Clientset, clusterRoleBindingYaml string) error {
|
||||||
clusterRoleBinding := rbacv1.ClusterRoleBinding{}
|
clusterRoleBinding := rbacv1.ClusterRoleBinding{}
|
||||||
if err := decodeYamlResource(&clusterRoleBinding, clusterRoleBindingYaml); err != nil {
|
if err := DecodeYamlResource(&clusterRoleBinding, clusterRoleBindingYaml); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return retryTo(updateClusterRoleBinding, k8sClient, clusterRoleBinding, DefaultRetries, DefaultSleepSeconds)
|
return retryTo(updateClusterRoleBinding, k8sClient, clusterRoleBinding, DefaultRetries, DefaultSleepSeconds)
|
||||||
@ -29,7 +29,7 @@ func updateClusterRoleBinding(k8sClient *kubernetes.Clientset, crb interface{})
|
|||||||
|
|
||||||
func UpdateClusterRoleFromYaml(k8sClient *kubernetes.Clientset, clusterRoleYaml string) error {
|
func UpdateClusterRoleFromYaml(k8sClient *kubernetes.Clientset, clusterRoleYaml string) error {
|
||||||
clusterRole := rbacv1.ClusterRole{}
|
clusterRole := rbacv1.ClusterRole{}
|
||||||
if err := decodeYamlResource(&clusterRole, clusterRoleYaml); err != nil {
|
if err := DecodeYamlResource(&clusterRole, clusterRoleYaml); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ type JobStatus struct {
|
|||||||
|
|
||||||
func ApplyK8sSystemJob(jobYaml, kubeConfigPath string, k8sWrapTransport transport.WrapperFunc, timeout int, addonUpdated bool) error {
|
func ApplyK8sSystemJob(jobYaml, kubeConfigPath string, k8sWrapTransport transport.WrapperFunc, timeout int, addonUpdated bool) error {
|
||||||
job := v1.Job{}
|
job := v1.Job{}
|
||||||
if err := decodeYamlResource(&job, jobYaml); err != nil {
|
if err := DecodeYamlResource(&job, jobYaml); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if job.Namespace == metav1.NamespaceNone {
|
if job.Namespace == metav1.NamespaceNone {
|
||||||
@ -53,7 +53,7 @@ func ApplyK8sSystemJob(jobYaml, kubeConfigPath string, k8sWrapTransport transpor
|
|||||||
|
|
||||||
func DeleteK8sSystemJob(jobYaml string, k8sClient *kubernetes.Clientset, timeout int) error {
|
func DeleteK8sSystemJob(jobYaml string, k8sClient *kubernetes.Clientset, timeout int) error {
|
||||||
job := v1.Job{}
|
job := v1.Job{}
|
||||||
if err := decodeYamlResource(&job, jobYaml); err != nil {
|
if err := DecodeYamlResource(&job, jobYaml); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := deleteK8sJob(k8sClient, job.Name, job.Namespace); err != nil {
|
if err := deleteK8sJob(k8sClient, job.Name, job.Namespace); err != nil {
|
||||||
|
@ -2,9 +2,10 @@ package k8s
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"k8s.io/client-go/transport"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"k8s.io/client-go/transport"
|
||||||
|
|
||||||
yamlutil "k8s.io/apimachinery/pkg/util/yaml"
|
yamlutil "k8s.io/apimachinery/pkg/util/yaml"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
@ -36,7 +37,7 @@ func NewClient(kubeConfigPath string, k8sWrapTransport transport.WrapperFunc) (*
|
|||||||
return K8sClientSet, nil
|
return K8sClientSet, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeYamlResource(resource interface{}, yamlManifest string) error {
|
func DecodeYamlResource(resource interface{}, yamlManifest string) error {
|
||||||
decoder := yamlutil.NewYAMLToJSONDecoder(bytes.NewReader([]byte(yamlManifest)))
|
decoder := yamlutil.NewYAMLToJSONDecoder(bytes.NewReader([]byte(yamlManifest)))
|
||||||
return decoder.Decode(&resource)
|
return decoder.Decode(&resource)
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
func UpdatePodSecurityPolicyFromYaml(k8sClient *kubernetes.Clientset, pspYaml string) error {
|
func UpdatePodSecurityPolicyFromYaml(k8sClient *kubernetes.Clientset, pspYaml string) error {
|
||||||
psp := v1beta1.PodSecurityPolicy{}
|
psp := v1beta1.PodSecurityPolicy{}
|
||||||
if err := decodeYamlResource(&psp, pspYaml); err != nil {
|
if err := DecodeYamlResource(&psp, pspYaml); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return retryTo(updatePodSecurityPolicy, k8sClient, psp, DefaultRetries, DefaultSleepSeconds)
|
return retryTo(updatePodSecurityPolicy, k8sClient, psp, DefaultRetries, DefaultSleepSeconds)
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
func UpdateRoleBindingFromYaml(k8sClient *kubernetes.Clientset, roleBindingYaml, namespace string) error {
|
func UpdateRoleBindingFromYaml(k8sClient *kubernetes.Clientset, roleBindingYaml, namespace string) error {
|
||||||
roleBinding := rbacv1.RoleBinding{}
|
roleBinding := rbacv1.RoleBinding{}
|
||||||
if err := decodeYamlResource(&roleBinding, roleBindingYaml); err != nil {
|
if err := DecodeYamlResource(&roleBinding, roleBindingYaml); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
roleBinding.Namespace = namespace
|
roleBinding.Namespace = namespace
|
||||||
@ -30,7 +30,7 @@ func updateRoleBinding(k8sClient *kubernetes.Clientset, rb interface{}) error {
|
|||||||
|
|
||||||
func UpdateRoleFromYaml(k8sClient *kubernetes.Clientset, roleYaml, namespace string) error {
|
func UpdateRoleFromYaml(k8sClient *kubernetes.Clientset, roleYaml, namespace string) error {
|
||||||
role := rbacv1.Role{}
|
role := rbacv1.Role{}
|
||||||
if err := decodeYamlResource(&role, roleYaml); err != nil {
|
if err := DecodeYamlResource(&role, roleYaml); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
role.Namespace = namespace
|
role.Namespace = namespace
|
||||||
|
@ -2,31 +2,24 @@ package k8s
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetSecret(k8sClient *kubernetes.Clientset, secretName string) (*v1.Secret, error) {
|
func GetSystemSecret(k8sClient *kubernetes.Clientset, secretName string) (*v1.Secret, error) {
|
||||||
return k8sClient.CoreV1().Secrets(metav1.NamespaceSystem).Get(secretName, metav1.GetOptions{})
|
return GetSecret(k8sClient, secretName, metav1.NamespaceSystem)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateSecret(k8sClient *kubernetes.Clientset, secretDataMap map[string][]byte, secretName string) error {
|
func GetSecret(k8sClient *kubernetes.Clientset, secretName, namespace string) (*v1.Secret, error) {
|
||||||
secret := &v1.Secret{
|
return k8sClient.CoreV1().Secrets(namespace).Get(secretName, metav1.GetOptions{})
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
}
|
||||||
Name: secretName,
|
|
||||||
Namespace: metav1.NamespaceSystem,
|
func GetSecretsList(k8sClient *kubernetes.Clientset, namespace string) (*v1.SecretList, error) {
|
||||||
},
|
return k8sClient.CoreV1().Secrets("").List(metav1.ListOptions{})
|
||||||
Data: secretDataMap,
|
}
|
||||||
}
|
|
||||||
if _, err := k8sClient.CoreV1().Secrets(metav1.NamespaceSystem).Create(secret); err != nil {
|
func UpdateSecret(k8sClient *kubernetes.Clientset, secret *v1.Secret) error {
|
||||||
if !apierrors.IsAlreadyExists(err) {
|
var err error
|
||||||
return err
|
_, err = k8sClient.CoreV1().Secrets(secret.Namespace).Update(secret)
|
||||||
}
|
return err
|
||||||
// update secret if its already exist
|
|
||||||
if _, err := k8sClient.CoreV1().Secrets(metav1.NamespaceSystem).Update(secret); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
func UpdateServiceAccountFromYaml(k8sClient *kubernetes.Clientset, serviceAccountYaml string) error {
|
func UpdateServiceAccountFromYaml(k8sClient *kubernetes.Clientset, serviceAccountYaml string) error {
|
||||||
serviceAccount := v1.ServiceAccount{}
|
serviceAccount := v1.ServiceAccount{}
|
||||||
|
|
||||||
if err := decodeYamlResource(&serviceAccount, serviceAccountYaml); err != nil {
|
if err := DecodeYamlResource(&serviceAccount, serviceAccountYaml); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return retryTo(updateServiceAccount, k8sClient, serviceAccount, DefaultRetries, DefaultSleepSeconds)
|
return retryTo(updateServiceAccount, k8sClient, serviceAccount, DefaultRetries, DefaultSleepSeconds)
|
||||||
|
4
main.go
4
main.go
@ -1,11 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/rancher/rke/metadata"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/rancher/rke/metadata"
|
||||||
|
|
||||||
"github.com/mattn/go-colorable"
|
"github.com/mattn/go-colorable"
|
||||||
"github.com/rancher/rke/cmd"
|
"github.com/rancher/rke/cmd"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -52,6 +53,7 @@ func mainErr() error {
|
|||||||
cmd.ConfigCommand(),
|
cmd.ConfigCommand(),
|
||||||
cmd.EtcdCommand(),
|
cmd.EtcdCommand(),
|
||||||
cmd.CertificateCommand(),
|
cmd.CertificateCommand(),
|
||||||
|
cmd.EncryptionCommand(),
|
||||||
}
|
}
|
||||||
app.Flags = []cli.Flag{
|
app.Flags = []cli.Flag{
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
|
@ -5,8 +5,10 @@ import (
|
|||||||
|
|
||||||
"github.com/rancher/rke/docker"
|
"github.com/rancher/rke/docker"
|
||||||
"github.com/rancher/rke/hosts"
|
"github.com/rancher/rke/hosts"
|
||||||
|
"github.com/rancher/rke/log"
|
||||||
"github.com/rancher/rke/pki"
|
"github.com/rancher/rke/pki"
|
||||||
"github.com/rancher/types/apis/management.cattle.io/v3"
|
"github.com/rancher/types/apis/management.cattle.io/v3"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func runKubeAPI(ctx context.Context, host *hosts.Host, df hosts.DialerFactory, prsMap map[string]v3.PrivateRegistry, kubeAPIProcess v3.Process, alpineImage string, certMap map[string]pki.CertificatePKI) error {
|
func runKubeAPI(ctx context.Context, host *hosts.Host, df hosts.DialerFactory, prsMap map[string]v3.PrivateRegistry, kubeAPIProcess v3.Process, alpineImage string, certMap map[string]pki.CertificatePKI) error {
|
||||||
@ -28,3 +30,21 @@ func removeKubeAPI(ctx context.Context, host *hosts.Host) error {
|
|||||||
func RestartKubeAPI(ctx context.Context, host *hosts.Host) error {
|
func RestartKubeAPI(ctx context.Context, host *hosts.Host) error {
|
||||||
return docker.DoRestartContainer(ctx, host.DClient, KubeAPIContainerName, host.Address)
|
return docker.DoRestartContainer(ctx, host.DClient, KubeAPIContainerName, host.Address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RestartKubeAPIWithHealthcheck(ctx context.Context, hostList []*hosts.Host, df hosts.DialerFactory, certMap map[string]pki.CertificatePKI) error {
|
||||||
|
log.Infof(ctx, "[%s] Restarting %s on contorl plane nodes..", ControlRole, KubeAPIContainerName)
|
||||||
|
for _, runHost := range hostList {
|
||||||
|
logrus.Debugf("[%s] Restarting %s on node [%s]", ControlRole, KubeAPIContainerName, runHost.Address)
|
||||||
|
if err := RestartKubeAPI(ctx, runHost); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logrus.Debugf("[%s] Running healthcheck for %s on node [%s]", ControlRole, KubeAPIContainerName, runHost.Address)
|
||||||
|
if err := runHealthcheck(ctx, runHost, KubeAPIContainerName,
|
||||||
|
df, GetHealthCheckURL(true, KubeAPIPort),
|
||||||
|
certMap); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Infof(ctx, "[%s] Restarted %s on contorl plane nodes successfully", ControlRole, KubeAPIContainerName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
34
templates/encryption_provider.go
Normal file
34
templates/encryption_provider.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package templates
|
||||||
|
|
||||||
|
const (
|
||||||
|
DisabledEncryptionProviderFile = `apiVersion: apiserver.config.k8s.io/v1
|
||||||
|
kind: EncryptionConfiguration
|
||||||
|
resources:
|
||||||
|
- resources:
|
||||||
|
- secrets
|
||||||
|
providers:
|
||||||
|
- identity: {}
|
||||||
|
- aescbc:
|
||||||
|
keys:
|
||||||
|
- name: {{.Name}}
|
||||||
|
secret: {{.Secret}}`
|
||||||
|
|
||||||
|
MultiKeyEncryptionProviderFile = `apiVersion: apiserver.config.k8s.io/v1
|
||||||
|
kind: EncryptionConfiguration
|
||||||
|
resources:
|
||||||
|
- resources:
|
||||||
|
- secrets
|
||||||
|
providers:
|
||||||
|
- aescbc:
|
||||||
|
keys:
|
||||||
|
{{- range $i, $v:= .KeyList}}
|
||||||
|
- name: {{ $v.Name}}
|
||||||
|
secret: {{ $v.Secret -}}
|
||||||
|
{{end}}
|
||||||
|
- identity: {}`
|
||||||
|
|
||||||
|
CustomEncryptionProviderFile = `apiVersion: apiserver.config.k8s.io/v1
|
||||||
|
kind: EncryptionConfiguration
|
||||||
|
{{.CustomConfig}}
|
||||||
|
`
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user