talos/internal/app/bootkube/recover.go
Andrew Rynhard 4eeef28e90 feat: add etcd API
This adds RPCs for basic etcd management tasks.

Signed-off-by: Andrew Rynhard <andrew@rynhard.io>
2020-10-06 11:30:04 -07:00

112 lines
2.7 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package main
import (
"bytes"
"context"
"crypto/aes"
"encoding/base64"
"errors"
"fmt"
"os"
"github.com/kubernetes-sigs/bootkube/pkg/recovery"
k8saes "k8s.io/apiserver/pkg/storage/value/encrypt/aes"
"github.com/talos-systems/talos/internal/pkg/etcd"
machineapi "github.com/talos-systems/talos/pkg/machinery/api/machine"
"github.com/talos-systems/talos/pkg/machinery/config"
"github.com/talos-systems/talos/pkg/machinery/constants"
)
//nolint: gocyclo
func recoverAssets(config config.Provider) error {
// Ensure assets directory does not exist / is left over from a failed install
if err := os.RemoveAll(constants.AssetsDirectory); err != nil {
// Ignore if the directory does not exist
if !errors.Is(err, os.ErrNotExist) {
return err
}
}
var (
backend recovery.Backend
err error
)
switch *recoverSource {
case machineapi.RecoverRequest_ETCD.String():
var client *etcd.Client
client, err = etcd.NewClient([]string{"127.0.0.1:2379"})
if err != nil {
return err
}
var transform recovery.TransformerFromStorage
transform, err = aesTransformer(config.Cluster())
if err != nil {
return err
}
backend = recovery.NewEtcdBackendWithTransformer(client.Client, "/registry", transform)
case machineapi.RecoverRequest_APISERVER.String():
backend, err = recovery.NewAPIServerBackend(constants.RecoveryKubeconfig)
if err != nil {
return err
}
}
as, err := recovery.Recover(context.Background(), backend, constants.RecoveryKubeconfig)
if err != nil {
return err
}
if err = os.MkdirAll(constants.AssetsDirectory, 0o600); err != nil {
return err
}
if err = as.WriteFiles(constants.AssetsDirectory); err != nil {
return fmt.Errorf("failed to write recovered assets: %w", err)
}
return nil
}
func aesTransformer(clusterConfig config.ClusterConfig) (recovery.TransformerFromStorage, error) {
key, err := base64.StdEncoding.DecodeString(clusterConfig.AESCBCEncryptionSecret())
if err != nil {
return nil, err
}
cipher, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
transformer := k8saes.NewCBCTransformer(cipher)
return func(value []byte) ([]byte, error) {
const (
aesCBCTransformerPrefixV1 = "k8s:enc:aescbc:v1:"
aesCBCKeyName = "key1:"
aesCBCPrefix = aesCBCTransformerPrefixV1 + aesCBCKeyName
)
if !bytes.HasPrefix(value, []byte(aesCBCPrefix)) {
return value, nil
}
value = value[len(aesCBCPrefix):]
value, _, e := transformer.TransformFromStorage(value, nil)
return value, e
}, nil
}