1
0
mirror of https://github.com/getsops/sops.git synced 2026-02-05 12:45:21 +01:00
Files
sops/azkv/keysource_test.go
Hidde Beydals b80c0d1eba azkv: update SDK to latest, add tests, tidy
This updates the Azure SDK to latest[1], while dropping the custom
authentication flow in favor of the SDK default[2]. It includes
integration tests, which require the `integration` Go build tag and
a set of environmental variables to be configured to be run:

```
PASS
coverage: 81.2% of statements
ok  	go.mozilla.org/sops/v3/azkv	5.376s	coverage: 81.2% of statements
```

The improvements are based on a fork of the key source in the Flux
project's kustomize-controller, built due to SOPS' limitation around
credential managment without relying on runtime environment variables.

- Azure SDK has been updated to latest, including integration test
  coverage.
- Custom authentication flow has been dropped in favor of the SDK
  default[2]. This should work well on almost any system and is
  generally the go-to way of setting this up, including on cloud
  environments, etc.
- It introduces a `TokenCredential` type which holds an
  `azcore.TokenCredential`, and can be applied to a `MasterKey`.
  When applied, the token is used instead of the SDK default. This is
  most useful when working with SOPS as an SDK, in combination with
  e.g. a local key service server implementation.
- Extensive test coverage.

The forked version of this has compatibility tests to ensure it works
with current SOPS:

- 327a3560b3/internal/sops/azkv/keysource_integration_test.go (L89)
- 327a3560b3/internal/sops/azkv/keysource_integration_test.go (L117)

[1]: https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/keyvault/azkeys
[2]: https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#DefaultAzureCredential

Signed-off-by: Hidde Beydals <hello@hidde.co>
2022-06-02 22:26:07 +02:00

217 lines
6.0 KiB
Go

package azkv
import (
"testing"
"time"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
"github.com/stretchr/testify/assert"
)
const (
mockAzureURL = "https://test.vault.azure.net/keys/test-key/a2a690a4fcc04166b739da342a912c90"
)
func TestNewMasterKeyFromURL(t *testing.T) {
tests := []struct {
name string
url string
expectErr bool
expectKey MasterKey
}{
{
name: "URL",
url: "https://test.vault.azure.net/keys/test-key/a2a690a4fcc04166b739da342a912c90",
expectKey: MasterKey{
VaultURL: "https://test.vault.azure.net",
Name: "test-key",
Version: "a2a690a4fcc04166b739da342a912c90",
},
},
{
name: "malformed URL",
url: "https://test.vault.azure.net/no-keys-here/test-key/a2a690a4fcc04166b739da342a912c90",
expectErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
key, err := NewMasterKeyFromURL(tt.url)
if tt.expectErr {
assert.Error(t, err)
assert.Nil(t, key)
return
}
assert.NoError(t, err)
assert.Equal(t, tt.expectKey.VaultURL, key.VaultURL)
assert.Equal(t, tt.expectKey.Name, key.Name)
assert.Equal(t, tt.expectKey.Version, key.Version)
assert.NotNil(t, key.CreationDate)
})
}
}
func TestMasterKeysFromURLs(t *testing.T) {
tests := []struct {
name string
urls string
expectErr bool
expectKeyCount int
expectKeys []MasterKey
}{
{
name: "single URL",
urls: "https://test.vault.azure.net/keys/test-key/a2a690a4fcc04166b739da342a912c90",
expectKeyCount: 1,
expectKeys: []MasterKey{
{
VaultURL: "https://test.vault.azure.net",
Name: "test-key",
Version: "a2a690a4fcc04166b739da342a912c90",
},
},
},
{
name: "multiple URLs",
urls: "https://test.vault.azure.net/keys/test-key/a2a690a4fcc04166b739da342a912c90,https://test2.vault.azure.net/keys/another-test-key/cf0021e8b743453bae758e7fbf71b60e",
expectKeyCount: 2,
expectKeys: []MasterKey{
{
VaultURL: "https://test.vault.azure.net",
Name: "test-key",
Version: "a2a690a4fcc04166b739da342a912c90",
},
{
VaultURL: "https://test2.vault.azure.net",
Name: "another-test-key",
Version: "cf0021e8b743453bae758e7fbf71b60e",
},
},
},
{
name: "multiple URLs, one malformed",
urls: "https://test.vault.azure.net/keys/test-key/a2a690a4fcc04166b739da342a912c90,https://test.vault.azure.net/no-keys-here/test-key/a2a690a4fcc04166b739da342a912c90",
expectErr: true,
},
{
name: "empty",
urls: "",
expectErr: false,
expectKeyCount: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
keys, err := MasterKeysFromURLs(tt.urls)
if tt.expectErr {
assert.Error(t, err)
assert.Nil(t, keys)
return
}
assert.NoError(t, err)
assert.Len(t, keys, tt.expectKeyCount)
for idx := range keys {
assert.Equal(t, tt.expectKeys[idx].VaultURL, keys[idx].VaultURL)
assert.Equal(t, tt.expectKeys[idx].Name, keys[idx].Name)
assert.Equal(t, tt.expectKeys[idx].Version, keys[idx].Version)
assert.NotNil(t, keys[idx].CreationDate)
}
})
}
}
func TestTokenCredential_ApplyToMasterKey(t *testing.T) {
credential, err := azidentity.NewUsernamePasswordCredential("tenant", "client", "username", "password", nil)
assert.NoError(t, err)
token := NewTokenCredential(credential)
key := &MasterKey{}
token.ApplyToMasterKey(key)
assert.Equal(t, credential, key.tokenCredential)
}
func TestMasterKey_EncryptedDataKey(t *testing.T) {
key := &MasterKey{EncryptedKey: "some key"}
assert.EqualValues(t, key.EncryptedKey, key.EncryptedDataKey())
}
func TestMasterKey_SetEncryptedDataKey(t *testing.T) {
encryptedKey := []byte("encrypted")
key := &MasterKey{}
key.SetEncryptedDataKey(encryptedKey)
assert.EqualValues(t, encryptedKey, key.EncryptedKey)
}
func TestMasterKey_EncryptIfNeeded(t *testing.T) {
t.Run("not encrypted", func(t *testing.T) {
key, err := NewMasterKeyFromURL(mockAzureURL)
assert.NoError(t, err)
err = key.Encrypt([]byte("some data"))
assert.Error(t, err)
assert.ErrorContains(t, err, "failed to encrypt sops data key with Azure Key Vault key")
})
t.Run("already encrypted", func(t *testing.T) {
encryptedKey := "encrypted"
key, err := NewMasterKeyFromURL(mockAzureURL)
assert.NoError(t, err)
key.EncryptedKey = encryptedKey
assert.NoError(t, key.EncryptIfNeeded([]byte("other data")))
assert.Equal(t, encryptedKey, key.EncryptedKey)
})
}
func TestMasterKey_NeedsRotation(t *testing.T) {
key := NewMasterKey("", "", "")
assert.False(t, key.NeedsRotation())
key.CreationDate = key.CreationDate.Add(-(azkvTTL + time.Second))
assert.True(t, key.NeedsRotation())
}
func TestMasterKey_ToString(t *testing.T) {
key := NewMasterKey("https://test.vault.azure.net", "key-name", "key-version")
assert.Equal(t, "https://test.vault.azure.net/keys/key-name/key-version", key.ToString())
}
func TestMasterKey_ToMap(t *testing.T) {
key := MasterKey{
CreationDate: time.Date(2016, time.October, 31, 10, 0, 0, 0, time.UTC),
VaultURL: "https://test.vault.azure.net",
Name: "test-key",
Version: "1",
EncryptedKey: "this is encrypted",
}
assert.Equal(t, map[string]interface{}{
"vaultUrl": key.VaultURL,
"key": key.Name,
"version": key.Version,
"enc": "this is encrypted",
"created_at": "2016-10-31T10:00:00Z",
}, key.ToMap())
}
func TestMasterKey_getTokenCredential(t *testing.T) {
t.Run("with TokenCredential", func(t *testing.T) {
credential, err := azidentity.NewUsernamePasswordCredential("tenant", "client", "username", "password", nil)
assert.NoError(t, err)
token := NewTokenCredential(credential)
key := &MasterKey{}
token.ApplyToMasterKey(key)
got, err := key.getTokenCredential()
assert.NoError(t, err)
assert.Equal(t, credential, got)
})
t.Run("default", func(t *testing.T) {
key := &MasterKey{}
got, err := key.getTokenCredential()
assert.NoError(t, err)
assert.IsType(t, &azidentity.DefaultAzureCredential{}, got)
})
}