// Package classification Magpie // // Documentation for Edge Decryption API // // Schemes: https // BasePath: / // Version: 1.0.0 // Host: edge.ncr.com/crypto/ // // Consumes: // - text/plain // // Produces: // - text/plain // // swagger:meta package magpie import ( "context" "errors" "fmt" "hash/crc32" "path/filepath" "strings" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir" "sigs.k8s.io/controller-runtime/pkg/client" "edge-infra.dev/pkg/edge/edgeencrypt" kms "cloud.google.com/go/kms/apiv1" "cloud.google.com/go/kms/apiv1/kmspb" "github.com/go-logr/logr" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/wrapperspb" ) // +kubebuilder:rbac:groups="",namespace=magpie,resources=secrets,verbs=get;list;watch func Run(log logr.Logger) error { cfg, err := NewConfig() if err != nil { return fmt.Errorf("failed to load config: %w", err) } config, err := rest.InClusterConfig() if errors.Is(err, rest.ErrNotInCluster) { config, err = clientcmd.BuildConfigFromFlags("", filepath.Join(homedir.HomeDir(), ".kube", "config")) if err != nil { return fmt.Errorf("failed to get local config: %w", err) } } else if err != nil { return fmt.Errorf("failed to get in-cluster config: %w", err) } cl, err := client.New(config, client.Options{Scheme: createScheme()}) if err != nil { return fmt.Errorf("failed to create client: %w", err) } kmsClient, err := kms.NewKeyManagementClient(context.Background()) if err != nil { return fmt.Errorf("failed to create kms client: %w", err) } defer kmsClient.Close() crc32c := func(data []byte) uint32 { t := crc32.MakeTable(crc32.Castagnoli) return crc32.Checksum(data, t) } decrypt := func(ctx context.Context, bannerId, channelID, keyVersion string, ciphertext []byte) ([]byte, error) { ciphertextCRC32C := crc32c(ciphertext) req := &kmspb.AsymmetricDecryptRequest{ Name: cfg.KeyPath(bannerId, fmt.Sprintf(edgeencrypt.EncryptionSecret, channelID), keyVersion), Ciphertext: ciphertext, CiphertextCrc32C: wrapperspb.Int64(int64(ciphertextCRC32C)), } res, err := kmsClient.AsymmetricDecrypt(ctx, req) if err != nil { if status.Code(err) == codes.FailedPrecondition && strings.Contains(err.Error(), "DISABLED") { return nil, fmt.Errorf("kms key is disabled: %w", err) } return nil, fmt.Errorf("kms client failed to decrypt data: %w", err) } if !res.VerifiedCiphertextCrc32C { return nil, fmt.Errorf("AsymmetricDecrypt: request corrupted in-transit") } if int64(crc32c(res.Plaintext)) != res.PlaintextCrc32C.Value { return nil, fmt.Errorf("AsymmetricDecrypt: response corrupted in-transit") } return res.Plaintext, nil } server := NewDecryptionServer(cfg, cl, log, decrypt) return server.Start(fmt.Sprintf(":%s", cfg.Port), fmt.Sprintf(":%s", cfg.MetricPort)) } func createScheme() *runtime.Scheme { scheme := runtime.NewScheme() utilruntime.Must(clientgoscheme.AddToScheme(scheme)) return scheme }