package secretsencrypt import ( "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "os" "github.com/k3s-io/k3s/pkg/daemons/config" "github.com/k3s-io/k3s/pkg/util" "github.com/k3s-io/k3s/pkg/version" corev1 "k8s.io/api/core/v1" "github.com/sirupsen/logrus" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" apiserverconfigv1 "k8s.io/apiserver/pkg/apis/config/v1" ) const ( EncryptionStart string = "start" EncryptionPrepare string = "prepare" EncryptionRotate string = "rotate" EncryptionRotateKeys string = "rotate_keys" EncryptionReencryptRequest string = "reencrypt_request" EncryptionReencryptActive string = "reencrypt_active" EncryptionReencryptFinished string = "reencrypt_finished" ) var EncryptionHashAnnotation = version.Program + ".io/encryption-config-hash" func GetEncryptionProviders(runtime *config.ControlRuntime) ([]apiserverconfigv1.ProviderConfiguration, error) { curEncryptionByte, err := os.ReadFile(runtime.EncryptionConfig) if err != nil { return nil, err } curEncryption := apiserverconfigv1.EncryptionConfiguration{} if err = json.Unmarshal(curEncryptionByte, &curEncryption); err != nil { return nil, err } return curEncryption.Resources[0].Providers, nil } func GetEncryptionKeys(runtime *config.ControlRuntime) ([]apiserverconfigv1.Key, error) { providers, err := GetEncryptionProviders(runtime) if err != nil { return nil, err } if len(providers) > 2 { return nil, fmt.Errorf("more than 2 providers (%d) found in secrets encryption", len(providers)) } var curKeys []apiserverconfigv1.Key for _, p := range providers { if p.AESCBC != nil { curKeys = append(curKeys, p.AESCBC.Keys...) } if p.AESGCM != nil || p.KMS != nil || p.Secretbox != nil { return nil, fmt.Errorf("non-standard encryption keys found") } } return curKeys, nil } func WriteEncryptionConfig(runtime *config.ControlRuntime, keys []apiserverconfigv1.Key, enable bool) error { // Placing the identity provider first disables encryption var providers []apiserverconfigv1.ProviderConfiguration if enable { providers = []apiserverconfigv1.ProviderConfiguration{ { AESCBC: &apiserverconfigv1.AESConfiguration{ Keys: keys, }, }, { Identity: &apiserverconfigv1.IdentityConfiguration{}, }, } } else { providers = []apiserverconfigv1.ProviderConfiguration{ { Identity: &apiserverconfigv1.IdentityConfiguration{}, }, { AESCBC: &apiserverconfigv1.AESConfiguration{ Keys: keys, }, }, } } encConfig := apiserverconfigv1.EncryptionConfiguration{ TypeMeta: metav1.TypeMeta{ Kind: "EncryptionConfiguration", APIVersion: "apiserver.config.k8s.io/v1", }, Resources: []apiserverconfigv1.ResourceConfiguration{ { Resources: []string{"secrets"}, Providers: providers, }, }, } jsonfile, err := json.Marshal(encConfig) if err != nil { return err } return util.AtomicWrite(runtime.EncryptionConfig, jsonfile, 0600) } func GenEncryptionConfigHash(runtime *config.ControlRuntime) (string, error) { curEncryptionByte, err := os.ReadFile(runtime.EncryptionConfig) if err != nil { return "", err } encryptionConfigHash := sha256.Sum256(curEncryptionByte) return hex.EncodeToString(encryptionConfigHash[:]), nil } // GenReencryptHash generates a sha256 hash from the existing secrets keys and // a new key based on the input arguments. func GenReencryptHash(runtime *config.ControlRuntime, keyName string) (string, error) { keys, err := GetEncryptionKeys(runtime) if err != nil { return "", err } newKey := apiserverconfigv1.Key{ Name: keyName, Secret: "12345", } keys = append(keys, newKey) b, err := json.Marshal(keys) if err != nil { return "", err } hash := sha256.Sum256(b) return hex.EncodeToString(hash[:]), nil } func getEncryptionHashFile(runtime *config.ControlRuntime) (string, error) { curEncryptionByte, err := os.ReadFile(runtime.EncryptionHash) if err != nil { return "", err } return string(curEncryptionByte), nil } func BootstrapEncryptionHashAnnotation(node *corev1.Node, runtime *config.ControlRuntime) error { existingAnn, err := getEncryptionHashFile(runtime) if err != nil { return err } node.Annotations[EncryptionHashAnnotation] = existingAnn return nil } func WriteEncryptionHashAnnotation(runtime *config.ControlRuntime, node *corev1.Node, stage string) error { encryptionConfigHash, err := GenEncryptionConfigHash(runtime) if err != nil { return err } if node.Annotations == nil { return fmt.Errorf("node annotations do not exist for %s", node.ObjectMeta.Name) } ann := stage + "-" + encryptionConfigHash node.Annotations[EncryptionHashAnnotation] = ann if _, err = runtime.Core.Core().V1().Node().Update(node); err != nil { return err } logrus.Debugf("encryption hash annotation set successfully on node: %s\n", node.ObjectMeta.Name) return os.WriteFile(runtime.EncryptionHash, []byte(ann), 0600) }