@@ -14,6 +14,7 @@ import (
14
14
"path"
15
15
"regexp"
16
16
"strings"
17
+ "time"
17
18
18
19
"github.com/Azure/kubernetes-kms/pkg/auth"
19
20
"github.com/Azure/kubernetes-kms/pkg/config"
@@ -38,6 +39,8 @@ const (
38
39
keyvaultRegionAnnotationKey = "x-ms-keyvault-region.azure.akv.io"
39
40
versionAnnotationKey = "version.azure.akv.io"
40
41
algorithmAnnotationKey = "algorithm.azure.akv.io"
42
+ keyVersionAnnotationKey = "keyversion.azure.akv.io"
43
+ keyIDHashAnnotationKey = "keyidhash.azure.akv.io"
41
44
dateAnnotationValue = "Date"
42
45
requestIDAnnotationValue = "X-Ms-Request-Id"
43
46
keyvaultRegionAnnotationValue = "X-Ms-Keyvault-Region"
@@ -64,20 +67,22 @@ type Client interface {
64
67
65
68
// KeyVaultClient is a client for interacting with Keyvault.
66
69
type KeyVaultClient struct {
67
- baseClient kv.BaseClient
68
- config * config.AzureConfig
69
- vaultName string
70
- keyName string
71
- keyVersion string
72
- vaultURL string
73
- keyIDHash string
74
- azureEnvironment * azure.Environment
70
+ baseClient kv.BaseClient
71
+ config * config.AzureConfig
72
+ vaultName string
73
+ keyName string
74
+ keyVersion string
75
+ keyVersionlessEnabled bool
76
+ vaultURL string
77
+ keyIDHash string
78
+ azureEnvironment * azure.Environment
75
79
}
76
80
77
81
// NewKeyVaultClient returns a new key vault client to use for kms operations.
78
82
func NewKeyVaultClient (
79
83
config * config.AzureConfig ,
80
84
vaultName , keyName , keyVersion string ,
85
+ keyVersionlessEnabled bool ,
81
86
proxyMode bool ,
82
87
proxyAddress string ,
83
88
proxyPort int ,
@@ -90,9 +95,10 @@ func NewKeyVaultClient(
90
95
91
96
// this should be the case for bring your own key, clusters bootstrapped with
92
97
// aks-engine or aks and standalone kms plugin deployments
93
- if len (vaultName ) == 0 || len (keyName ) == 0 || len (keyVersion ) == 0 {
94
- return nil , fmt .Errorf ("key vault name, key name and key version are required" )
98
+ if len (vaultName ) == 0 || len (keyName ) == 0 || ( ! keyVersionlessEnabled && len (keyVersion ) == 0 ) {
99
+ return nil , fmt .Errorf ("key vault name, key name and key version (not key versionless enabled) are required" )
95
100
}
101
+
96
102
kvClient := kv .New ()
97
103
err := kvClient .AddToUserAgent (version .GetUserAgent ())
98
104
if err != nil {
@@ -121,9 +127,12 @@ func NewKeyVaultClient(
121
127
return nil , fmt .Errorf ("failed to get vault url, error: %+v" , err )
122
128
}
123
129
124
- keyIDHash , err := getKeyIDHash (* vaultURL , keyName , keyVersion )
125
- if err != nil {
126
- return nil , fmt .Errorf ("failed to get key id hash, error: %w" , err )
130
+ var keyIDHash string
131
+ if len (keyVersion ) != 0 {
132
+ keyIDHash , err = getKeyIDHash (* vaultURL , keyName , keyVersion )
133
+ if err != nil {
134
+ return nil , fmt .Errorf ("failed to get key id hash, error: %w" , err )
135
+ }
127
136
}
128
137
129
138
if proxyMode {
@@ -134,18 +143,51 @@ func NewKeyVaultClient(
134
143
mlog .Always ("using kms key for encrypt/decrypt" , "vaultURL" , * vaultURL , "keyName" , keyName , "keyVersion" , keyVersion )
135
144
136
145
client := & KeyVaultClient {
137
- baseClient : kvClient ,
138
- config : config ,
139
- vaultName : vaultName ,
140
- keyName : keyName ,
141
- keyVersion : keyVersion ,
142
- vaultURL : * vaultURL ,
143
- azureEnvironment : env ,
144
- keyIDHash : keyIDHash ,
146
+ baseClient : kvClient ,
147
+ config : config ,
148
+ vaultName : vaultName ,
149
+ keyName : keyName ,
150
+ keyVersion : keyVersion ,
151
+ keyVersionlessEnabled : keyVersionlessEnabled ,
152
+ vaultURL : * vaultURL ,
153
+ azureEnvironment : env ,
154
+ keyIDHash : keyIDHash ,
145
155
}
146
156
return client , nil
147
157
}
148
158
159
+ func (kvc * KeyVaultClient ) GetLatestKeyVersion (ctx context.Context ) (string , error ) {
160
+ keyVersionResultPage , err := kvc .baseClient .GetKeyVersions (ctx , kvc .vaultURL , kvc .keyName , nil )
161
+ if err != nil {
162
+ return "" , fmt .Errorf ("failed to get key versions, error: %+v" , err )
163
+ }
164
+ var latestKeyVersionItem kv.KeyItem
165
+ for keyVersionResultPage .NotDone () {
166
+ for _ , value := range keyVersionResultPage .Values () {
167
+ if latestKeyVersionItem .Kid == nil {
168
+ latestKeyVersionItem = value
169
+ } else {
170
+ updatedTimeCurrent := time .Time (* value .Attributes .Updated )
171
+ updatedTimeLatest := time .Time (* latestKeyVersionItem .Attributes .Updated )
172
+ if updatedTimeCurrent .After (updatedTimeLatest ) {
173
+ latestKeyVersionItem = value
174
+ }
175
+ }
176
+ }
177
+ keyVersionResultPage .Next ()
178
+ }
179
+
180
+ if latestKeyVersionItem .Kid == nil {
181
+ return "" , fmt .Errorf ("failed to get latest key version, key id is nil" )
182
+ }
183
+ kidSplitted := strings .Split (* latestKeyVersionItem .Kid , "/" )
184
+ if len (kidSplitted ) == 0 {
185
+ return "" , fmt .Errorf ("failed to get latest key version, key id is invalid %q" , * latestKeyVersionItem .Kid )
186
+ }
187
+ latestKeyVersion := kidSplitted [len (kidSplitted )- 1 ]
188
+ return latestKeyVersion , nil
189
+ }
190
+
149
191
// Encrypt encrypts the given plain text using the keyvault key.
150
192
func (kvc * KeyVaultClient ) Encrypt (
151
193
ctx context.Context ,
@@ -158,15 +200,29 @@ func (kvc *KeyVaultClient) Encrypt(
158
200
Algorithm : encryptionAlgorithm ,
159
201
Value : & value ,
160
202
}
161
- result , err := kvc .baseClient .Encrypt (ctx , kvc .vaultURL , kvc .keyName , kvc .keyVersion , params )
203
+
204
+ keyVersion := kvc .keyVersion
205
+ keyIDHash := kvc .keyIDHash
206
+ if kvc .keyVersionlessEnabled {
207
+ var err error
208
+ if keyVersion , err = kvc .GetLatestKeyVersion (ctx ); err != nil {
209
+ return nil , fmt .Errorf ("failed to get latest key version, error: %+v" , err )
210
+ }
211
+
212
+ if keyIDHash , err = getKeyIDHash (kvc .vaultURL , kvc .keyName , keyVersion ); err != nil {
213
+ return nil , fmt .Errorf ("failed to get key id hash, error: %w" , err )
214
+ }
215
+ }
216
+
217
+ result , err := kvc .baseClient .Encrypt (ctx , kvc .vaultURL , kvc .keyName , keyVersion , params )
162
218
if err != nil {
163
219
return nil , fmt .Errorf ("failed to encrypt, error: %+v" , err )
164
220
}
165
221
166
- if kvc . keyIDHash != fmt .Sprintf ("%x" , sha256 .Sum256 ([]byte (* result .Kid ))) {
222
+ if keyIDHash != fmt .Sprintf ("%x" , sha256 .Sum256 ([]byte (* result .Kid ))) {
167
223
return nil , fmt .Errorf (
168
224
"key id initialized does not match with the key id from encryption result, expected: %s, got: %s" ,
169
- kvc . keyIDHash ,
225
+ keyIDHash ,
170
226
* result .Kid ,
171
227
)
172
228
}
@@ -177,11 +233,14 @@ func (kvc *KeyVaultClient) Encrypt(
177
233
keyvaultRegionAnnotationKey : []byte (result .Header .Get (keyvaultRegionAnnotationValue )),
178
234
versionAnnotationKey : []byte (encryptionResponseVersion ),
179
235
algorithmAnnotationKey : []byte (encryptionAlgorithm ),
236
+ keyVersionAnnotationKey : []byte (keyVersion ),
237
+ keyIDHashAnnotationKey : []byte (keyIDHash ),
180
238
}
181
239
240
+ mlog .Info ("Encryption succeeded" , "vaultName" , kvc .vaultName , "keyName" , kvc .keyName , "keyVersion" , keyVersion )
182
241
return & service.EncryptResponse {
183
242
Ciphertext : []byte (* result .Result ),
184
- KeyID : kvc . keyIDHash ,
243
+ KeyID : keyIDHash ,
185
244
Annotations : annotations ,
186
245
}, nil
187
246
}
@@ -208,7 +267,12 @@ func (kvc *KeyVaultClient) Decrypt(
208
267
Value : & value ,
209
268
}
210
269
211
- result , err := kvc .baseClient .Decrypt (ctx , kvc .vaultURL , kvc .keyName , kvc .keyVersion , params )
270
+ keyVersion := kvc .keyVersion
271
+ if len (annotations [keyVersionAnnotationKey ]) != 0 {
272
+ keyVersion = string (annotations [keyVersionAnnotationKey ])
273
+ }
274
+
275
+ result , err := kvc .baseClient .Decrypt (ctx , kvc .vaultURL , kvc .keyName , keyVersion , params )
212
276
if err != nil {
213
277
return nil , fmt .Errorf ("failed to decrypt, error: %+v" , err )
214
278
}
@@ -217,6 +281,7 @@ func (kvc *KeyVaultClient) Decrypt(
217
281
return nil , fmt .Errorf ("failed to base64 decode result, error: %+v" , err )
218
282
}
219
283
284
+ mlog .Info ("Decryption succeeded" , "vaultName" , kvc .vaultName , "keyName" , kvc .keyName , "keyVersion" , keyVersion )
220
285
return bytes , nil
221
286
}
222
287
@@ -241,11 +306,15 @@ func (kvc *KeyVaultClient) validateAnnotations(
241
306
return fmt .Errorf ("invalid annotations, annotations cannot be empty" )
242
307
}
243
308
244
- if keyID != kvc .keyIDHash {
309
+ expectedKeyIDHash := kvc .keyIDHash
310
+ if len (annotations [keyIDHashAnnotationKey ]) != 0 {
311
+ expectedKeyIDHash = string (annotations [keyIDHashAnnotationKey ])
312
+ }
313
+ if keyID != expectedKeyIDHash {
245
314
return fmt .Errorf (
246
315
"key id %s does not match expected key id %s used for encryption" ,
247
316
keyID ,
248
- kvc . keyIDHash ,
317
+ expectedKeyIDHash ,
249
318
)
250
319
}
251
320
0 commit comments