Manual client secrets on azure

This commit is contained in:
katexochen 2022-08-29 14:18:05 +02:00 committed by Paul Meyer
parent 1861dc2744
commit 10e5249631
11 changed files with 84 additions and 80 deletions

View File

@ -113,8 +113,8 @@ func (c *fakeAzureClient) CreateInstances(ctx context.Context, input azurecl.Cre
func (c *fakeAzureClient) CreateServicePrincipal(ctx context.Context) (string, error) {
c.adAppObjectID = "00000000-0000-0000-0000-000000000001"
return azureshared.ApplicationCredentials{
ClientID: "client-id",
ClientSecret: "client-secret",
AppClientID: "client-id",
ClientSecretValue: "client-secret",
}.ToCloudServiceAccountURI(), nil
}
@ -174,8 +174,8 @@ func (c *stubAzureClient) CreateInstances(ctx context.Context, input azurecl.Cre
func (c *stubAzureClient) CreateServicePrincipal(ctx context.Context) (string, error) {
return azureshared.ApplicationCredentials{
ClientID: "00000000-0000-0000-0000-000000000000",
ClientSecret: "secret",
AppClientID: "00000000-0000-0000-0000-000000000000",
ClientSecretValue: "secret",
}.ToCloudServiceAccountURI(), c.createServicePrincipalErr
}

View File

@ -17,6 +17,7 @@ import (
"github.com/edgelesssys/constellation/cli/internal/cloudcmd"
"github.com/edgelesssys/constellation/cli/internal/gcp"
"github.com/edgelesssys/constellation/cli/internal/helm"
"github.com/edgelesssys/constellation/internal/azureshared"
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/internal/cloud/cloudtypes"
"github.com/edgelesssys/constellation/internal/config"
@ -349,7 +350,13 @@ func getMarschaledServiceAccountURI(provider cloudprovider.Provider, config *con
return key.ToCloudServiceAccountURI(), nil
case cloudprovider.Azure:
return "", fmt.Errorf("TODO")
creds := azureshared.ApplicationCredentials{
TenantID: config.Provider.Azure.TenantID,
AppClientID: config.Provider.Azure.AppClientID,
ClientSecretValue: config.Provider.Azure.ClientSecretValue,
Location: config.Provider.Azure.Location,
}
return creds.ToCloudServiceAccountURI(), nil
case cloudprovider.QEMU:
return "", nil // QEMU does not use service account keys

View File

@ -87,12 +87,8 @@ func TestInitialize(t *testing.T) {
initServerAPI: &stubInitServer{initResp: testInitResp},
},
"initialize some azure instances": {
state: testAzureState,
idFile: &clusterIDsFile{IP: "192.0.2.1"},
configMutator: func(c *config.Config) {
c.Provider.Azure.ResourceGroup = "resourceGroup"
c.Provider.Azure.UserAssignedIdentity = "userAssignedIdentity"
},
state: testAzureState,
idFile: &clusterIDsFile{IP: "192.0.2.1"},
initServerAPI: &stubInitServer{initResp: testInitResp},
},
"initialize some qemu instances": {
@ -109,12 +105,8 @@ func TestInitialize(t *testing.T) {
setAutoscaleFlag: true,
},
"initialize azure with autoscaling": {
state: testAzureState,
idFile: &clusterIDsFile{IP: "192.0.2.1"},
configMutator: func(c *config.Config) {
c.Provider.Azure.ResourceGroup = "resourceGroup"
c.Provider.Azure.UserAssignedIdentity = "userAssignedIdentity"
},
state: testAzureState,
idFile: &clusterIDsFile{IP: "192.0.2.1"},
initServerAPI: &stubInitServer{initResp: testInitResp},
setAutoscaleFlag: true,
},
@ -557,6 +549,8 @@ func defaultConfigWithExpectedMeasurements(t *testing.T, conf *config.Config, cs
conf.Provider.Azure.UserAssignedIdentity = "test-identity"
conf.Provider.Azure.Image = "some/image/location"
conf.Provider.Azure.ResourceGroup = "test-resource-group"
conf.Provider.Azure.AppClientID = "test-client-secret-id"
conf.Provider.Azure.ClientSecretValue = "test-client-secret"
conf.Provider.Azure.Measurements[8] = []byte("00000000000000000000000000000000")
conf.Provider.Azure.Measurements[9] = []byte("11111111111111111111111111111111")
case cloudprovider.GCP:

View File

@ -60,7 +60,6 @@ func TestVerify(t *testing.T) {
someErr := errors.New("failed")
testCases := map[string]struct {
setupFs func(*require.Assertions) afero.Fs
provider cloudprovider.Provider
protoClient *stubVerifyClient
nodeEndpointFlag string
@ -72,7 +71,6 @@ func TestVerify(t *testing.T) {
wantErr bool
}{
"gcp": {
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
provider: cloudprovider.GCP,
nodeEndpointFlag: "192.0.2.1:1234",
ownerIDFlag: zeroBase64,
@ -80,7 +78,6 @@ func TestVerify(t *testing.T) {
wantEndpoint: "192.0.2.1:1234",
},
"azure": {
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
provider: cloudprovider.Azure,
nodeEndpointFlag: "192.0.2.1:1234",
ownerIDFlag: zeroBase64,
@ -88,7 +85,6 @@ func TestVerify(t *testing.T) {
wantEndpoint: "192.0.2.1:1234",
},
"default port": {
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
provider: cloudprovider.GCP,
nodeEndpointFlag: "192.0.2.1",
ownerIDFlag: zeroBase64,
@ -96,14 +92,12 @@ func TestVerify(t *testing.T) {
wantEndpoint: "192.0.2.1:" + strconv.Itoa(constants.VerifyServiceNodePortGRPC),
},
"endpoint not set": {
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
provider: cloudprovider.GCP,
ownerIDFlag: zeroBase64,
protoClient: &stubVerifyClient{},
wantErr: true,
},
"endpoint from id file": {
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
provider: cloudprovider.GCP,
ownerIDFlag: zeroBase64,
protoClient: &stubVerifyClient{},
@ -111,7 +105,6 @@ func TestVerify(t *testing.T) {
wantEndpoint: "192.0.2.1:" + strconv.Itoa(constants.VerifyServiceNodePortGRPC),
},
"override endpoint from details file": {
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
provider: cloudprovider.GCP,
nodeEndpointFlag: "192.0.2.2:1234",
ownerIDFlag: zeroBase64,
@ -120,7 +113,6 @@ func TestVerify(t *testing.T) {
wantEndpoint: "192.0.2.2:1234",
},
"invalid endpoint": {
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
provider: cloudprovider.GCP,
nodeEndpointFlag: ":::::",
ownerIDFlag: zeroBase64,
@ -128,13 +120,11 @@ func TestVerify(t *testing.T) {
wantErr: true,
},
"neither owner id nor cluster id set": {
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
provider: cloudprovider.GCP,
nodeEndpointFlag: "192.0.2.1:1234",
wantErr: true,
},
"use owner id from id file": {
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
provider: cloudprovider.GCP,
nodeEndpointFlag: "192.0.2.1:1234",
protoClient: &stubVerifyClient{},
@ -142,7 +132,6 @@ func TestVerify(t *testing.T) {
wantEndpoint: "192.0.2.1:1234",
},
"config file not existing": {
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
provider: cloudprovider.GCP,
ownerIDFlag: zeroBase64,
nodeEndpointFlag: "192.0.2.1:1234",
@ -150,7 +139,6 @@ func TestVerify(t *testing.T) {
wantErr: true,
},
"error protoClient GetState": {
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
provider: cloudprovider.Azure,
nodeEndpointFlag: "192.0.2.1:1234",
ownerIDFlag: zeroBase64,
@ -158,7 +146,6 @@ func TestVerify(t *testing.T) {
wantErr: true,
},
"error protoClient GetState not rpc": {
setupFs: func(require *require.Assertions) afero.Fs { return afero.NewMemMapFs() },
provider: cloudprovider.Azure,
nodeEndpointFlag: "192.0.2.1:1234",
ownerIDFlag: zeroBase64,
@ -189,7 +176,7 @@ func TestVerify(t *testing.T) {
if tc.nodeEndpointFlag != "" {
require.NoError(cmd.Flags().Set("node-endpoint", tc.nodeEndpointFlag))
}
fileHandler := file.NewHandler(tc.setupFs(require))
fileHandler := file.NewHandler(afero.NewMemMapFs())
config := defaultConfigWithExpectedMeasurements(t, config.Default(), tc.provider)
require.NoError(fileHandler.WriteYAML(constants.ConfigFilename, config))

View File

@ -8,10 +8,10 @@ import (
// ApplicationCredentials is a set of Azure AD application credentials.
// It is the equivalent of a service account key in other cloud providers.
type ApplicationCredentials struct {
TenantID string
ClientID string
ClientSecret string
Location string
TenantID string
AppClientID string
ClientSecretValue string
Location string
}
// ApplicationCredentialsFromURI converts a cloudServiceAccountURI into Azure ApplicationCredentials.
@ -28,10 +28,10 @@ func ApplicationCredentialsFromURI(cloudServiceAccountURI string) (ApplicationCr
}
query := uri.Query()
return ApplicationCredentials{
TenantID: query.Get("tenant_id"),
ClientID: query.Get("client_id"),
ClientSecret: query.Get("client_secret"),
Location: query.Get("location"),
TenantID: query.Get("tenant_id"),
AppClientID: query.Get("client_id"),
ClientSecretValue: query.Get("client_secret"),
Location: query.Get("location"),
}, nil
}
@ -39,8 +39,8 @@ func ApplicationCredentialsFromURI(cloudServiceAccountURI string) (ApplicationCr
func (c ApplicationCredentials) ToCloudServiceAccountURI() string {
query := url.Values{}
query.Add("tenant_id", c.TenantID)
query.Add("client_id", c.ClientID)
query.Add("client_secret", c.ClientSecret)
query.Add("client_id", c.AppClientID)
query.Add("client_secret", c.ClientSecretValue)
query.Add("location", c.Location)
uri := url.URL{
Scheme: "serviceaccount",

View File

@ -15,10 +15,10 @@ func TestMain(m *testing.M) {
func TestApplicationCredentialsFromURI(t *testing.T) {
creds := ApplicationCredentials{
TenantID: "tenant-id",
ClientID: "client-id",
ClientSecret: "client-secret",
Location: "location",
TenantID: "tenant-id",
AppClientID: "client-id",
ClientSecretValue: "client-secret",
Location: "location",
}
testCases := map[string]struct {
cloudServiceAccountURI string
@ -63,10 +63,10 @@ func TestToCloudServiceAccountURI(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
key := ApplicationCredentials{
TenantID: "tenant-id",
ClientID: "client-id",
ClientSecret: "client-secret",
Location: "location",
TenantID: "tenant-id",
AppClientID: "client-id",
ClientSecretValue: "client-secret",
Location: "location",
}
cloudServiceAccountURI := key.ToCloudServiceAccountURI()

View File

@ -36,8 +36,8 @@ func (a *Autoscaler) Secrets(providerID string, cloudServiceAccountURI string) (
Namespace: "kube-system",
},
Data: map[string][]byte{
"ClientID": []byte(creds.ClientID),
"ClientSecret": []byte(creds.ClientSecret),
"ClientID": []byte(creds.AppClientID),
"ClientSecret": []byte(creds.ClientSecretValue),
"ResourceGroup": []byte(resourceGroup),
"SubscriptionID": []byte(subscriptionID),
"TenantID": []byte(creds.TenantID),

View File

@ -100,8 +100,8 @@ func (c *CloudControllerManager) Secrets(ctx context.Context, providerID string,
UseInstanceMetadata: true,
VMType: vmType,
Location: creds.Location,
AADClientID: creds.ClientID,
AADClientSecret: creds.ClientSecret,
AADClientID: creds.AppClientID,
AADClientSecret: creds.ClientSecretValue,
}
rawConfig, err := json.Marshal(config)

View File

@ -149,18 +149,24 @@ type AzureConfig struct {
// Type of a node's state disk. The type influences boot time and I/O performance. See: https://docs.microsoft.com/en-us/azure/virtual-machines/disks-types#disk-type-comparison
StateDiskType string `yaml:"stateDiskType" validate:"oneof=Premium_LRS Premium_ZRS Standard_LRS StandardSSD_LRS StandardSSD_ZRS"`
// description: |
// Expected confidential VM measurements.
Measurements Measurements `yaml:"measurements"`
// description: |
// List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning.
EnforcedMeasurements []uint32 `yaml:"enforcedMeasurements"`
// description: |
// Authorize spawned VMs to access Azure API. See: https://docs.edgeless.systems/constellation/latest/#/getting-started/install?id=azure
UserAssignedIdentity string `yaml:"userAssignedIdentity" validate:"required"`
// description: |
// Resource group to use.
ResourceGroup string `yaml:"resourceGroup" validate:"required"`
// description: |
// Application client ID of the Active Directory app registration.
AppClientID string `yaml:"appClientID" validate:"required"`
// description: |
// Client secret value of the Active Directory app registration credentials.
ClientSecretValue string `yaml:"clientSecretValue" validate:"required"`
// description: |
// Expected confidential VM measurements.
Measurements Measurements `yaml:"measurements"`
// description: |
// List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning.
EnforcedMeasurements []uint32 `yaml:"enforcedMeasurements"`
// description: |
// Use VMs with security type Confidential VM. If set to false, Trusted Launch VMs will be used instead. See: https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-vm-overview
ConfidentialVM *bool `yaml:"confidentialVM" validate:"required"`
}

View File

@ -199,7 +199,7 @@ func init() {
FieldName: "azure",
},
}
AzureConfigDoc.Fields = make([]encoder.Doc, 10)
AzureConfigDoc.Fields = make([]encoder.Doc, 12)
AzureConfigDoc.Fields[0].Name = "subscription"
AzureConfigDoc.Fields[0].Type = "string"
AzureConfigDoc.Fields[0].Note = ""
@ -225,31 +225,41 @@ func init() {
AzureConfigDoc.Fields[4].Note = ""
AzureConfigDoc.Fields[4].Description = "Type of a node's state disk. The type influences boot time and I/O performance. See: https://docs.microsoft.com/en-us/azure/virtual-machines/disks-types#disk-type-comparison"
AzureConfigDoc.Fields[4].Comments[encoder.LineComment] = "Type of a node's state disk. The type influences boot time and I/O performance. See: https://docs.microsoft.com/en-us/azure/virtual-machines/disks-types#disk-type-comparison"
AzureConfigDoc.Fields[5].Name = "measurements"
AzureConfigDoc.Fields[5].Type = "Measurements"
AzureConfigDoc.Fields[5].Name = "userAssignedIdentity"
AzureConfigDoc.Fields[5].Type = "string"
AzureConfigDoc.Fields[5].Note = ""
AzureConfigDoc.Fields[5].Description = "Expected confidential VM measurements."
AzureConfigDoc.Fields[5].Comments[encoder.LineComment] = "Expected confidential VM measurements."
AzureConfigDoc.Fields[6].Name = "enforcedMeasurements"
AzureConfigDoc.Fields[6].Type = "[]uint32"
AzureConfigDoc.Fields[5].Description = "Authorize spawned VMs to access Azure API. See: https://docs.edgeless.systems/constellation/latest/#/getting-started/install?id=azure"
AzureConfigDoc.Fields[5].Comments[encoder.LineComment] = "Authorize spawned VMs to access Azure API. See: https://docs.edgeless.systems/constellation/latest/#/getting-started/install?id=azure"
AzureConfigDoc.Fields[6].Name = "resourceGroup"
AzureConfigDoc.Fields[6].Type = "string"
AzureConfigDoc.Fields[6].Note = ""
AzureConfigDoc.Fields[6].Description = "List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning."
AzureConfigDoc.Fields[6].Comments[encoder.LineComment] = "List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning."
AzureConfigDoc.Fields[7].Name = "userAssignedIdentity"
AzureConfigDoc.Fields[6].Description = "Resource group to use."
AzureConfigDoc.Fields[6].Comments[encoder.LineComment] = "Resource group to use."
AzureConfigDoc.Fields[7].Name = "appClientID"
AzureConfigDoc.Fields[7].Type = "string"
AzureConfigDoc.Fields[7].Note = ""
AzureConfigDoc.Fields[7].Description = "Authorize spawned VMs to access Azure API. See: https://docs.edgeless.systems/constellation/latest/#/getting-started/install?id=azure"
AzureConfigDoc.Fields[7].Comments[encoder.LineComment] = "Authorize spawned VMs to access Azure API. See: https://docs.edgeless.systems/constellation/latest/#/getting-started/install?id=azure"
AzureConfigDoc.Fields[8].Name = "resourceGroup"
AzureConfigDoc.Fields[7].Description = "Application client ID of the Active Directory app registration."
AzureConfigDoc.Fields[7].Comments[encoder.LineComment] = "Application client ID of the Active Directory app registration."
AzureConfigDoc.Fields[8].Name = "clientSecretValue"
AzureConfigDoc.Fields[8].Type = "string"
AzureConfigDoc.Fields[8].Note = ""
AzureConfigDoc.Fields[8].Description = "Resource group to use."
AzureConfigDoc.Fields[8].Comments[encoder.LineComment] = "Resource group to use."
AzureConfigDoc.Fields[8].Name = "confidentialVM"
AzureConfigDoc.Fields[8].Type = "bool"
AzureConfigDoc.Fields[8].Note = ""
AzureConfigDoc.Fields[8].Description = "Use VMs with security type Confidential VM. If set to false, Trusted Launch VMs will be used instead. See: https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-vm-overview"
AzureConfigDoc.Fields[8].Comments[encoder.LineComment] = "Use VMs with security type Confidential VM. If set to false, Trusted Launch VMs will be used instead. See: https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-vm-overview"
AzureConfigDoc.Fields[8].Description = "Client secret value of the Active Directory app registration credentials."
AzureConfigDoc.Fields[8].Comments[encoder.LineComment] = "Client secret value of the Active Directory app registration credentials."
AzureConfigDoc.Fields[9].Name = "measurements"
AzureConfigDoc.Fields[9].Type = "Measurements"
AzureConfigDoc.Fields[9].Note = ""
AzureConfigDoc.Fields[9].Description = "Expected confidential VM measurements."
AzureConfigDoc.Fields[9].Comments[encoder.LineComment] = "Expected confidential VM measurements."
AzureConfigDoc.Fields[10].Name = "enforcedMeasurements"
AzureConfigDoc.Fields[10].Type = "[]uint32"
AzureConfigDoc.Fields[10].Note = ""
AzureConfigDoc.Fields[10].Description = "List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning."
AzureConfigDoc.Fields[10].Comments[encoder.LineComment] = "List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning."
AzureConfigDoc.Fields[11].Name = "confidentialVM"
AzureConfigDoc.Fields[11].Type = "bool"
AzureConfigDoc.Fields[11].Note = ""
AzureConfigDoc.Fields[11].Description = "Use VMs with security type Confidential VM. If set to false, Trusted Launch VMs will be used instead. See: https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-vm-overview"
AzureConfigDoc.Fields[11].Comments[encoder.LineComment] = "Use VMs with security type Confidential VM. If set to false, Trusted Launch VMs will be used instead. See: https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-vm-overview"
GCPConfigDoc.Type = "GCPConfig"
GCPConfigDoc.Comments[encoder.LineComment] = "GCPConfig are GCP specific configuration values used by the CLI."

View File

@ -13,7 +13,7 @@ import (
"go.uber.org/goleak"
)
const defaultMsgCount = 9 // expect this number of error messages by default because user-specific values are not set
const defaultMsgCount = 12 // expect this number of error messages by default because user-specific values are not set
func TestMain(m *testing.M) {
goleak.VerifyTestMain(m)