@@ -79,12 +79,21 @@ func NewMasterKey(vaultURL string, keyName string, keyVersion string) *MasterKey
7979// MasterKey. The URL format is {vaultUrl}/keys/{keyName}/{keyVersion}.
8080func 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.
150189func (key * MasterKey ) EncryptContext (ctx context.Context , dataKey []byte ) error {
0 commit comments