Skip to content

Commit

Permalink
WIP: add azure keyvault based secret store
Browse files Browse the repository at this point in the history
  • Loading branch information
adohe committed Dec 6, 2023
1 parent 079b7c1 commit 318bf3b
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 2 deletions.
14 changes: 13 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ go 1.19

require (
github.com/AlecAivazis/survey/v2 v2.3.4
github.com/Azure/go-autorest/autorest/mocks v0.4.1
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible
github.com/Azure/go-autorest/autorest v0.11.29
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12
github.com/Azure/go-autorest/autorest/mocks v0.4.2
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
github.com/aliyun/aliyun-oss-go-sdk v2.1.8+incompatible
github.com/aws/aws-sdk-go v1.48.6
Expand Down Expand Up @@ -78,6 +81,13 @@ require (
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.22 // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
Expand Down Expand Up @@ -109,6 +119,7 @@ require (
github.com/containerd/console v1.0.3 // indirect
github.com/containerd/containerd v1.7.5 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/docker/cli v23.0.1+incompatible // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/docker v23.0.3+incompatible // indirect
Expand All @@ -132,6 +143,7 @@ require (
github.com/gofrs/uuid v4.2.0+incompatible // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/glog v1.1.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
Expand Down
34 changes: 33 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,35 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/AlecAivazis/survey/v2 v2.3.4 h1:pchTU9rsLUSvWEl2Aq9Pv3k0IE2fkqtGxazskAMd9Ng=
github.com/AlecAivazis/survey/v2 v2.3.4/go.mod h1:hrV6Y/kQCLhIZXGcriDCUBtB3wnN7156gMXJ3+b23xM=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw=
github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/adal v0.9.22 h1:/GblQdIudfEM3AWWZ0mrYJQSd7JS4S/Mbzh6F0ov0Xc=
github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk=
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 h1:wkAZRgT/pn8HhFyzfe9UnqOjJYqlembgCTi72Bm/xKk=
github.com/Azure/go-autorest/autorest/azure/auth v0.5.12/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg=
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 h1:0W/yGmFdTIT77fvdlGZ0LMISoLHFJ7Tx4U0yeB+uFs4=
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw=
github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU=
github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk=
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac=
github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
Expand Down Expand Up @@ -146,6 +169,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/didi/gendry v1.7.0 h1:dFR6+TVCnbjvLfNiGN53xInG/C5HqG7u0gfnkF5J/Vo=
github.com/didi/gendry v1.7.0/go.mod h1:cSLuShZ1Zbs1S05RIOLNQv616aBaOQ1BDrXJP9A3J+M=
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc=
github.com/djherbis/times v1.5.0 h1:79myA211VwPhFTqUk8xehWrsEO+zcIZj0zT8mXPVARU=
github.com/djherbis/times v1.5.0/go.mod h1:5q7FDLvbNg1L/KaBmPcWlVR9NmoKo3+ucqUA3ijQhA0=
Expand Down Expand Up @@ -232,6 +257,10 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1:zN2lZNZRflqFyxVaTIU61KNKQ9C0055u9CAfpmqUvo4=
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
Expand Down Expand Up @@ -641,8 +670,11 @@ golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
Expand Down
27 changes: 27 additions & 0 deletions pkg/apis/secrets/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ type ProviderSpec struct {
AWS *AWSProvider `yaml:"aws,omitempty" json:"aws,omitempty"`
// Vault configures a store to retrieve secrets from HashiCorp Vault.
Vault *VaultProvider `yaml:"vault,omitempty" json:"vault,omitempty"`
// Azure configures a store to retrieve secrets from Azure KeyVault.
Azure *AzureKVProvider `yaml:"azure,omitempty" json:"azure,omitempty"`
}

// AWSProvider configures a store to retrieve secrets from AWS Secrets Manager.
Expand All @@ -54,3 +56,28 @@ type VaultProvider struct {
// "v2", defaults to "v2".
Version VaultKVStoreVersion `yaml:"version" json:"version"`
}

// AzureEnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure.
type AzureEnvironmentType string

const (
AzureEnvironmentPublicCloud AzureEnvironmentType = "PublicCloud"
AzureEnvironmentUSGovernmentCloud AzureEnvironmentType = "USGovernmentCloud"
AzureEnvironmentChinaCloud AzureEnvironmentType = "ChinaCloud"
AzureEnvironmentGermanCloud AzureEnvironmentType = "GermanCloud"
)

// AzureKVProvider configures a store to retrieve secrets from Azure KeyVault
type AzureKVProvider struct {
// Vault Url from which the secrets to be fetched from.
VaultURL *string `yaml:"vaultUrl" json:"vaultUrl"`

// TenantID configures the Azure Tenant to send requests to.
TenantID *string `yaml:"tenantId,omitempty" json:"tenantId,omitempty"`

// EnvironmentType specifies the Azure cloud environment endpoints to use for connecting and authenticating with Azure.
// By-default it points to the public cloud AAD endpoint, and the following endpoints are available:
// PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud
// Ref: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152
EnvironmentType AzureEnvironmentType `yaml:"environmentType,omitempty" json:"environmentType,omitempty"`
}
1 change: 1 addition & 0 deletions pkg/secrets/providers/azure/keyvault/fake/fake.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package fake
12 changes: 12 additions & 0 deletions pkg/secrets/providers/azure/keyvault/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package keyvault

import (
"context"

"github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault"
)

// SecretClient is a testable interface for making operations call for Azure KeyVault.
type SecretClient interface {
GetSecret(ctx context.Context, vaultBaseURL string, secretName string, secretVersion string) (result keyvault.SecretBundle, err error)
}
119 changes: 119 additions & 0 deletions pkg/secrets/providers/azure/keyvault/keyvault.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package keyvault

import (
"context"
"fmt"
"os"
"strings"

"github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/azure/auth"

secretsapi "kusionstack.io/kusion/pkg/apis/secrets"
"kusionstack.io/kusion/pkg/secrets"
)

const (
errMissingProviderSpec = "store spec is missing provider"
errMissingAzureProvider = "invalid provider spec. Missing Azure field in store provider spec"
errMissingTenant = "missing tenantID in store provider spec"
errMissingClientIDSecret = "cannot read clientID/clientSecret from environment variables"
)

// DefaultFactory should implement the secrets.SecretStoreFactory interface
var _ secrets.SecretStoreFactory = &DefaultFactory{}

// kvSecretStore should implement the secrets.SecretStore interface
var _ secrets.SecretStore = &kvSecretStore{}

type DefaultFactory struct{}

// NewSecretStore constructs an Azure KeyVault based secret store with specific secret store spec.
func (p *DefaultFactory) NewSecretStore(spec secretsapi.SecretStoreSpec) (secrets.SecretStore, error) {
providerSpec := spec.Provider
if providerSpec == nil {
return nil, fmt.Errorf(errMissingProviderSpec)
}
if providerSpec.Azure == nil {
return nil, fmt.Errorf(errMissingAzureProvider)
}

secretClient, err := getSecretClient(providerSpec.Azure)
if err != nil {
return nil, err
}
return &kvSecretStore{secretClient}, nil
}

func getSecretClient(spec *secretsapi.AzureKVProvider) (SecretClient, error) {
authorizer, err := authorizerForServicePrincipal(spec)
if err != nil {
return nil, err
}
client := keyvault.New()
client.Authorizer = authorizer
return client, nil
}

// authorizerForServicePrincipal returns a service principal based authorizer used by clients to access to Azure.
// By-default it uses credentials from the environment;
// See https://docs.microsoft.com/en-us/go/azure/azure-sdk-go-authorization#use-environment-based-authentication.
func authorizerForServicePrincipal(spec *secretsapi.AzureKVProvider) (autorest.Authorizer, error) {
if spec.TenantID == nil {
return nil, fmt.Errorf(errMissingTenant)
}

clientID := os.Getenv("AZURE_CLIENT_ID")
clientSecret := os.Getenv("AZURE_CLIENT_SECRET")
if clientID == "" || clientSecret == "" {
return nil, fmt.Errorf(errMissingClientIDSecret)
}

clientCredentialsConfig := auth.NewClientCredentialsConfig(clientID, clientSecret, *spec.TenantID)
clientCredentialsConfig.Resource = kvResourceForProviderConfig(spec.EnvironmentType)
clientCredentialsConfig.AADEndpoint = adEndpointForEnvironmentType(spec.EnvironmentType)
return clientCredentialsConfig.Authorizer()
}

func adEndpointForEnvironmentType(t secretsapi.AzureEnvironmentType) string {
switch t {
case secretsapi.AzureEnvironmentPublicCloud:
return azure.PublicCloud.ActiveDirectoryEndpoint
case secretsapi.AzureEnvironmentChinaCloud:
return azure.ChinaCloud.ActiveDirectoryEndpoint
case secretsapi.AzureEnvironmentUSGovernmentCloud:
return azure.USGovernmentCloud.ActiveDirectoryEndpoint
case secretsapi.AzureEnvironmentGermanCloud:
return azure.GermanCloud.ActiveDirectoryEndpoint
default:
return azure.PublicCloud.ActiveDirectoryEndpoint
}
}

func kvResourceForProviderConfig(t secretsapi.AzureEnvironmentType) string {
var res string
switch t {
case secretsapi.AzureEnvironmentPublicCloud:
res = azure.PublicCloud.KeyVaultEndpoint
case secretsapi.AzureEnvironmentChinaCloud:
res = azure.ChinaCloud.KeyVaultEndpoint
case secretsapi.AzureEnvironmentUSGovernmentCloud:
res = azure.USGovernmentCloud.KeyVaultEndpoint
case secretsapi.AzureEnvironmentGermanCloud:
res = azure.GermanCloud.KeyVaultEndpoint
default:
res = azure.PublicCloud.KeyVaultEndpoint
}
return strings.TrimSuffix(res, "/")
}

type kvSecretStore struct {
baseClient SecretClient
}

// GetSecret retrieves ref secret value from Azure KeyVault.
func (k *kvSecretStore) GetSecret(ctx context.Context, ref secretsapi.ExternalSecretRef) ([]byte, error) {
return nil, nil
}
1 change: 1 addition & 0 deletions pkg/secrets/providers/azure/keyvault/keyvault_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package keyvault

0 comments on commit 318bf3b

Please sign in to comment.