...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package magpie
18
19 import (
20 "context"
21 "errors"
22 "fmt"
23 "hash/crc32"
24 "path/filepath"
25 "strings"
26
27 "k8s.io/apimachinery/pkg/runtime"
28 utilruntime "k8s.io/apimachinery/pkg/util/runtime"
29 clientgoscheme "k8s.io/client-go/kubernetes/scheme"
30 "k8s.io/client-go/rest"
31 "k8s.io/client-go/tools/clientcmd"
32 "k8s.io/client-go/util/homedir"
33 "sigs.k8s.io/controller-runtime/pkg/client"
34
35 "edge-infra.dev/pkg/edge/edgeencrypt"
36
37 kms "cloud.google.com/go/kms/apiv1"
38 "cloud.google.com/go/kms/apiv1/kmspb"
39 "github.com/go-logr/logr"
40 "google.golang.org/grpc/codes"
41 "google.golang.org/grpc/status"
42 "google.golang.org/protobuf/types/known/wrapperspb"
43 )
44
45
46
47 func Run(log logr.Logger) error {
48 cfg, err := NewConfig()
49 if err != nil {
50 return fmt.Errorf("failed to load config: %w", err)
51 }
52
53 config, err := rest.InClusterConfig()
54 if errors.Is(err, rest.ErrNotInCluster) {
55 config, err = clientcmd.BuildConfigFromFlags("", filepath.Join(homedir.HomeDir(), ".kube", "config"))
56 if err != nil {
57 return fmt.Errorf("failed to get local config: %w", err)
58 }
59 } else if err != nil {
60 return fmt.Errorf("failed to get in-cluster config: %w", err)
61 }
62
63 cl, err := client.New(config, client.Options{Scheme: createScheme()})
64 if err != nil {
65 return fmt.Errorf("failed to create client: %w", err)
66 }
67
68 kmsClient, err := kms.NewKeyManagementClient(context.Background())
69 if err != nil {
70 return fmt.Errorf("failed to create kms client: %w", err)
71 }
72 defer kmsClient.Close()
73
74 crc32c := func(data []byte) uint32 {
75 t := crc32.MakeTable(crc32.Castagnoli)
76 return crc32.Checksum(data, t)
77 }
78
79 decrypt := func(ctx context.Context, bannerId, channelID, keyVersion string, ciphertext []byte) ([]byte, error) {
80 ciphertextCRC32C := crc32c(ciphertext)
81 req := &kmspb.AsymmetricDecryptRequest{
82 Name: cfg.KeyPath(bannerId, fmt.Sprintf(edgeencrypt.EncryptionSecret, channelID), keyVersion),
83 Ciphertext: ciphertext,
84 CiphertextCrc32C: wrapperspb.Int64(int64(ciphertextCRC32C)),
85 }
86 res, err := kmsClient.AsymmetricDecrypt(ctx, req)
87 if err != nil {
88 if status.Code(err) == codes.FailedPrecondition && strings.Contains(err.Error(), "DISABLED") {
89 return nil, fmt.Errorf("kms key is disabled: %w", err)
90 }
91 return nil, fmt.Errorf("kms client failed to decrypt data: %w", err)
92 }
93 if !res.VerifiedCiphertextCrc32C {
94 return nil, fmt.Errorf("AsymmetricDecrypt: request corrupted in-transit")
95 }
96 if int64(crc32c(res.Plaintext)) != res.PlaintextCrc32C.Value {
97 return nil, fmt.Errorf("AsymmetricDecrypt: response corrupted in-transit")
98 }
99 return res.Plaintext, nil
100 }
101
102 server := NewDecryptionServer(cfg, cl, log, decrypt)
103
104 return server.Start(fmt.Sprintf(":%s", cfg.Port), fmt.Sprintf(":%s", cfg.MetricPort))
105 }
106
107 func createScheme() *runtime.Scheme {
108 scheme := runtime.NewScheme()
109
110 utilruntime.Must(clientgoscheme.AddToScheme(scheme))
111 return scheme
112 }
113
View as plain text