github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/secrets/cloud/manager.go (about) 1 // Copyright 2016-2018, Pulumi Corporation. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package cloud implements support for a generic cloud secret manager. 16 package cloud 17 18 import ( 19 "context" 20 "crypto/rand" 21 "encoding/json" 22 "fmt" 23 netUrl "net/url" 24 25 gosecrets "gocloud.dev/secrets" 26 _ "gocloud.dev/secrets/awskms" // support for awskms:// 27 _ "gocloud.dev/secrets/azurekeyvault" // support for azurekeyvault:// 28 "gocloud.dev/secrets/gcpkms" // support for gcpkms:// 29 _ "gocloud.dev/secrets/hashivault" // support for hashivault:// 30 "google.golang.org/api/cloudkms/v1" 31 32 "github.com/pulumi/pulumi/pkg/v3/authhelpers" 33 "github.com/pulumi/pulumi/pkg/v3/secrets" 34 "github.com/pulumi/pulumi/sdk/v3/go/common/resource/config" 35 ) 36 37 // Type is the type of secrets managed by this secrets provider 38 const Type = "cloud" 39 40 type cloudSecretsManagerState struct { 41 URL string `json:"url"` 42 EncryptedKey []byte `json:"encryptedkey"` 43 } 44 45 // NewCloudSecretsManagerFromState deserialize configuration from state and returns a secrets 46 // manager that uses the target cloud key management service to encrypt/decrypt a data key used for 47 // envelope encryption of secrets values. 48 func NewCloudSecretsManagerFromState(state json.RawMessage) (secrets.Manager, error) { 49 var s cloudSecretsManagerState 50 if err := json.Unmarshal(state, &s); err != nil { 51 return nil, fmt.Errorf("unmarshalling state: %w", err) 52 } 53 54 return NewCloudSecretsManager(s.URL, s.EncryptedKey) 55 } 56 57 // openKeeper opens the keeper, handling pulumi-specifc cases in the URL. 58 func openKeeper(ctx context.Context, url string) (*gosecrets.Keeper, error) { 59 u, err := netUrl.Parse(url) 60 if err != nil { 61 return nil, fmt.Errorf("unable to parse the secrets provider URL: %w", err) 62 } 63 64 switch u.Scheme { 65 case gcpkms.Scheme: 66 credentials, err := authhelpers.ResolveGoogleCredentials(ctx, cloudkms.CloudkmsScope) 67 if err != nil { 68 return nil, fmt.Errorf("missing google credentials: %w", err) 69 } 70 71 kmsClient, _, err := gcpkms.Dial(ctx, credentials.TokenSource) 72 if err != nil { 73 return nil, fmt.Errorf("failed to connect to gcpkms: %w", err) 74 } 75 opener := gcpkms.URLOpener{ 76 Client: kmsClient, 77 } 78 79 return opener.OpenKeeperURL(ctx, u) 80 default: 81 return gosecrets.OpenKeeper(ctx, url) 82 } 83 } 84 85 // GenerateNewDataKey generates a new DataKey seeded by a fresh random 32-byte key and encrypted 86 // using the target cloud key management service. 87 func GenerateNewDataKey(url string) ([]byte, error) { 88 plaintextDataKey := make([]byte, 32) 89 _, err := rand.Read(plaintextDataKey) 90 if err != nil { 91 return nil, err 92 } 93 keeper, err := openKeeper(context.Background(), url) 94 if err != nil { 95 return nil, err 96 } 97 return keeper.Encrypt(context.Background(), plaintextDataKey) 98 } 99 100 // NewCloudSecretsManager returns a secrets manager that uses the target cloud key management 101 // service to encrypt/decrypt a data key used for envelope encryption of secrets values. 102 func NewCloudSecretsManager(url string, encryptedDataKey []byte) (*Manager, error) { 103 keeper, err := openKeeper(context.Background(), url) 104 if err != nil { 105 return nil, err 106 } 107 plaintextDataKey, err := keeper.Decrypt(context.Background(), encryptedDataKey) 108 if err != nil { 109 return nil, err 110 } 111 crypter := config.NewSymmetricCrypter(plaintextDataKey) 112 return &Manager{ 113 crypter: crypter, 114 state: cloudSecretsManagerState{ 115 URL: url, 116 EncryptedKey: encryptedDataKey, 117 }, 118 }, nil 119 } 120 121 // Manager is the secrets.Manager implementation for cloud key management services 122 type Manager struct { 123 state cloudSecretsManagerState 124 crypter config.Crypter 125 } 126 127 func (m *Manager) Type() string { return Type } 128 func (m *Manager) State() interface{} { return m.state } 129 func (m *Manager) Encrypter() (config.Encrypter, error) { return m.crypter, nil } 130 func (m *Manager) Decrypter() (config.Decrypter, error) { return m.crypter, nil } 131 func (m *Manager) EncryptedKey() []byte { return m.state.EncryptedKey }