Skip to content

Commit 225546e

Browse files
authored
Merge pull request #1919 from daogilvie/allow-azure-keyvault-empty-version
feat(azkv): Skipping key-version will get latest key
2 parents 193c269 + 92b6728 commit 225546e

File tree

2 files changed

+51
-3
lines changed

2 files changed

+51
-3
lines changed

README.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,11 @@ a key. This has the following form::
378378
379379
https://${VAULT_URL}/keys/${KEY_NAME}/${KEY_VERSION}
380380
381+
You can omit the version, and have just a trailing slash, and this will use
382+
whatever the latest version of the key is::
383+
384+
https://${VAULT_URL}/keys/${KEY_NAME}/
385+
381386
To create a Key Vault and assign your service principal permissions on it
382387
from the commandline:
383388
@@ -401,6 +406,10 @@ Now you can encrypt a file using::
401406
402407
$ sops encrypt --azure-kv https://sops.vault.azure.net/keys/sops-key/some-string test.yaml > test.enc.yaml
403408
409+
or, without the version::
410+
411+
$ sops encrypt --azure-kv https://sops.vault.azure.net/keys/sops-key/ test.yaml > test.enc.yaml
412+
404413
And decrypt it using::
405414
406415
$ sops decrypt test.enc.yaml

azkv/keysource.go

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,21 @@ func NewMasterKey(vaultURL string, keyName string, keyVersion string) *MasterKey
7979
// MasterKey. The URL format is {vaultUrl}/keys/{keyName}/{keyVersion}.
8080
func NewMasterKeyFromURL(url string) (*MasterKey, error) {
8181
url = strings.TrimSpace(url)
82-
re := regexp.MustCompile("^(https://[^/]+)/keys/([^/]+)/([^/]+)$")
82+
re := regexp.MustCompile("^(https://[^/]+)/keys/([^/]+)(/[^/]*)?$")
8383
parts := re.FindStringSubmatch(url)
8484
if len(parts) < 3 {
85-
return nil, fmt.Errorf("could not parse %q into a valid Azure Key Vault MasterKey", url)
85+
return nil, fmt.Errorf("could not parse %q into a valid Azure Key Vault MasterKey %v", url, parts)
8686
}
87-
return NewMasterKey(parts[1], parts[2], parts[3]), nil
87+
// Blank key versions are supported in Azure Key Vault, as they default to the latest
88+
// version of the key. We need to put the actual version in the sops metadata block though
89+
var key *MasterKey
90+
if len(parts[3]) > 1 {
91+
key = NewMasterKey(parts[1], parts[2], parts[3][1:])
92+
} else {
93+
key = NewMasterKey(parts[1], parts[2], "")
94+
}
95+
err := key.ensureKeyHasVersion(context.Background())
96+
return key, err
8897
}
8998

9099
// MasterKeysFromURLs takes a comma separated list of Azure Key Vault URLs,
@@ -145,6 +154,36 @@ func (key *MasterKey) Encrypt(dataKey []byte) error {
145154
return key.EncryptContext(context.Background(), dataKey)
146155
}
147156

157+
func (key *MasterKey) ensureKeyHasVersion(ctx context.Context) error {
158+
if (key.Version != "") {
159+
// Nothing to do
160+
return nil
161+
}
162+
163+
token, err := key.getTokenCredential()
164+
165+
if err != nil {
166+
log.WithFields(logrus.Fields{"key": key.Name, "version": key.Version}).Info("Encryption failed")
167+
return fmt.Errorf("failed to get Azure token credential to retrieve key version: %w", err)
168+
}
169+
170+
c, err := azkeys.NewClient(key.VaultURL, token, key.clientOptions)
171+
if err != nil {
172+
log.WithFields(logrus.Fields{"key": key.Name, "version": key.Version}).Info("Encryption failed")
173+
return fmt.Errorf("failed to construct Azure Key Vault client to retrieve key version: %w", err)
174+
}
175+
176+
kdetail, err := c.GetKey(ctx, key.Name, key.Version, nil)
177+
if err != nil {
178+
log.WithFields(logrus.Fields{"key": key.Name, "version": key.Version}).Info("Encryption failed")
179+
return fmt.Errorf("failed to fetch Azure Key to retrieve key version: %w", err)
180+
}
181+
key.Version = kdetail.Key.KID.Version()
182+
183+
log.WithFields(logrus.Fields{"key": key.Name, "version": key.Version}).Info("Version fetch succeeded")
184+
return nil
185+
}
186+
148187
// EncryptContext takes a SOPS data key, encrypts it with Azure Key Vault, and stores
149188
// the result in the EncryptedKey field.
150189
func (key *MasterKey) EncryptContext(ctx context.Context, dataKey []byte) error {

0 commit comments

Comments
 (0)