cli: use uami for in-cluter authentication (#1820)

This commit is contained in:
3u13r 2023-05-26 11:45:03 +02:00 committed by GitHub
parent 9502bc8ff4
commit 661f084ffa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 65 additions and 117 deletions

View File

@ -201,11 +201,9 @@ func (c *IAMCreator) createAzure(ctx context.Context, cl terraformClient, opts *
return iamid.File{
CloudProvider: cloudprovider.Azure,
AzureOutput: iamid.AzureFile{
ApplicationID: iamOutput.Azure.ApplicationID,
ApplicationClientSecretValue: iamOutput.Azure.ApplicationClientSecretValue,
SubscriptionID: iamOutput.Azure.SubscriptionID,
TenantID: iamOutput.Azure.TenantID,
UAMIID: iamOutput.Azure.UAMIID,
SubscriptionID: iamOutput.Azure.SubscriptionID,
TenantID: iamOutput.Azure.TenantID,
UAMIID: iamOutput.Azure.UAMIID,
},
}, nil
}

View File

@ -50,21 +50,17 @@ func TestIAMCreator(t *testing.T) {
}
validAzureIAMOutput := terraform.IAMOutput{
Azure: terraform.AzureIAMOutput{
SubscriptionID: "test_subscription_id",
TenantID: "test_tenant_id",
ApplicationID: "test_application_id",
ApplicationClientSecretValue: "test_application_client_secret_value",
UAMIID: "test_uami_id",
SubscriptionID: "test_subscription_id",
TenantID: "test_tenant_id",
UAMIID: "test_uami_id",
},
}
validAzureIAMIDFile := iamid.File{
CloudProvider: cloudprovider.Azure,
AzureOutput: iamid.AzureFile{
SubscriptionID: "test_subscription_id",
TenantID: "test_tenant_id",
ApplicationID: "test_application_id",
ApplicationClientSecretValue: "test_application_client_secret_value",
UAMIID: "test_uami_id",
SubscriptionID: "test_subscription_id",
TenantID: "test_tenant_id",
UAMIID: "test_uami_id",
},
}

View File

@ -486,8 +486,6 @@ func (c *azureIAMCreator) printOutputValues(cmd *cobra.Command, flags iamFlags,
cmd.Printf("location:\t\t%s\n", flags.azure.region)
cmd.Printf("resourceGroup:\t\t%s\n", flags.azure.resourceGroup)
cmd.Printf("userAssignedIdentity:\t%s\n", iamFile.AzureOutput.UAMIID)
cmd.Printf("appClientID:\t\t%s\n", iamFile.AzureOutput.ApplicationID)
cmd.Printf("clientSecretValue:\t%s\n\n", iamFile.AzureOutput.ApplicationClientSecretValue)
}
func (c *azureIAMCreator) writeOutputValuesToConfig(conf *config.Config, flags iamFlags, iamFile iamid.File) {
@ -496,8 +494,6 @@ func (c *azureIAMCreator) writeOutputValuesToConfig(conf *config.Config, flags i
conf.Provider.Azure.Location = flags.azure.region
conf.Provider.Azure.ResourceGroup = flags.azure.resourceGroup
conf.Provider.Azure.UserAssignedIdentity = iamFile.AzureOutput.UAMIID
conf.Provider.Azure.AppClientID = iamFile.AzureOutput.ApplicationID
conf.Provider.Azure.ClientSecretValue = iamFile.AzureOutput.ApplicationClientSecretValue
}
func (c *azureIAMCreator) parseAndWriteIDFile(_ iamid.File, _ file.Handler) error {

View File

@ -358,11 +358,9 @@ func TestIAMCreateAzure(t *testing.T) {
validIAMIDFile := iamid.File{
CloudProvider: cloudprovider.Azure,
AzureOutput: iamid.AzureFile{
SubscriptionID: "test_subscription_id",
TenantID: "test_tenant_id",
ApplicationID: "test_application_id",
ApplicationClientSecretValue: "test_application_client_secret_value",
UAMIID: "test_uami_id",
SubscriptionID: "test_subscription_id",
TenantID: "test_tenant_id",
UAMIID: "test_uami_id",
},
}
@ -611,8 +609,6 @@ func TestIAMCreateAzure(t *testing.T) {
require.NoError(readErr)
assert.Equal(tc.creator.id.AzureOutput.SubscriptionID, readConfig.Provider.Azure.SubscriptionID)
assert.Equal(tc.creator.id.AzureOutput.TenantID, readConfig.Provider.Azure.TenantID)
assert.Equal(tc.creator.id.AzureOutput.ApplicationID, readConfig.Provider.Azure.AppClientID)
assert.Equal(tc.creator.id.AzureOutput.ApplicationClientSecretValue, readConfig.Provider.Azure.ClientSecretValue)
assert.Equal(tc.creator.id.AzureOutput.UAMIID, readConfig.Provider.Azure.UserAssignedIdentity)
assert.Equal(tc.regionFlag, readConfig.Provider.Azure.Location)
assert.Equal(tc.resourceGroupFlag, readConfig.Provider.Azure.ResourceGroup)

View File

@ -449,13 +449,19 @@ func (i *initCmd) getMarshaledServiceAccountURI(provider cloudprovider.Provider,
return "", nil // AWS does not need a service account URI
case cloudprovider.Azure:
i.log.Debugf("Handling case for Azure")
// TODO(3u13r): Remove this fallback and enforce assigned managed identity after the v2.8.0 but before the v2.9.0 release.
authMethod := azureshared.AuthMethodUserAssignedIdentity
if config.Provider.Azure.AppClientID != "" {
authMethod = azureshared.AuthMethodServicePrincipal
}
creds := azureshared.ApplicationCredentials{
TenantID: config.Provider.Azure.TenantID,
AppClientID: config.Provider.Azure.AppClientID,
ClientSecretValue: config.Provider.Azure.ClientSecretValue,
Location: config.Provider.Azure.Location,
// TODO(malt3): Switch preferred auth method to uami as planned by AB#2961
PreferredAuthMethod: azureshared.AuthMethodServicePrincipal,
TenantID: config.Provider.Azure.TenantID,
AppClientID: config.Provider.Azure.AppClientID,
ClientSecretValue: config.Provider.Azure.ClientSecretValue,
Location: config.Provider.Azure.Location,
PreferredAuthMethod: authMethod,
UamiResourceID: config.Provider.Azure.UserAssignedIdentity,
}
return creds.ToCloudServiceAccountURI(), nil

View File

@ -31,11 +31,9 @@ type GCPFile struct {
// AzureFile contains the output information of a Microsoft Azure IAM configuration.
type AzureFile struct {
SubscriptionID string `json:"subscriptionID,omitempty"`
TenantID string `json:"tenantID,omitempty"`
ApplicationID string `json:"applicationID,omitempty"`
UAMIID string `json:"uamiID,omitempty"`
ApplicationClientSecretValue string `json:"applicationClientSecretValue,omitempty"`
SubscriptionID string `json:"subscriptionID,omitempty"`
TenantID string `json:"tenantID,omitempty"`
UAMIID string `json:"uamiID,omitempty"`
}
// AWSFile contains the output information of an AWS IAM configuration.

View File

@ -185,11 +185,9 @@ type GCPIAMOutput struct {
// AzureIAMOutput contains the output information of the Terraform IAM operation on Microsoft Azure.
type AzureIAMOutput struct {
SubscriptionID string
TenantID string
ApplicationID string
UAMIID string
ApplicationClientSecretValue string
SubscriptionID string
TenantID string
UAMIID string
}
// AWSIAMOutput contains the output information of the Terraform IAM operation on GCP.
@ -249,14 +247,6 @@ func (c *Client) CreateIAMConfig(ctx context.Context, provider cloudprovider.Pro
if !ok {
return IAMOutput{}, errors.New("invalid type in tenant id output: not a string")
}
applicationIDRaw, ok := tfState.Values.Outputs["application_id"]
if !ok {
return IAMOutput{}, errors.New("no application id output found")
}
applicationIDOutput, ok := applicationIDRaw.Value.(string)
if !ok {
return IAMOutput{}, errors.New("invalid type in application id output: not a string")
}
uamiIDRaw, ok := tfState.Values.Outputs["uami_id"]
if !ok {
return IAMOutput{}, errors.New("no UAMI id output found")
@ -265,21 +255,11 @@ func (c *Client) CreateIAMConfig(ctx context.Context, provider cloudprovider.Pro
if !ok {
return IAMOutput{}, errors.New("invalid type in UAMI id output: not a string")
}
appClientSecretRaw, ok := tfState.Values.Outputs["application_client_secret_value"]
if !ok {
return IAMOutput{}, errors.New("no application client secret value output found")
}
appClientSecretOutput, ok := appClientSecretRaw.Value.(string)
if !ok {
return IAMOutput{}, errors.New("invalid type in application client secret valueoutput: not a string")
}
return IAMOutput{
Azure: AzureIAMOutput{
SubscriptionID: subscriptionIDOutput,
TenantID: tenantIDOutput,
ApplicationID: applicationIDOutput,
UAMIID: uamiIDOutput,
ApplicationClientSecretValue: appClientSecretOutput,
SubscriptionID: subscriptionIDOutput,
TenantID: tenantIDOutput,
UAMIID: uamiIDOutput,
},
}, nil
case cloudprovider.AWS:

View File

@ -68,31 +68,3 @@ resource "azurerm_role_assignment" "uami_owner_role" {
role_definition_name = "Owner"
principal_id = azurerm_user_assigned_identity.identity_uami.principal_id
}
# the app registration, application secrets
# and role assignments below will be removed in the future
# TODO(malt3): remove app registration as planned by AB#2961
# Create application registration
resource "azuread_application" "base_application" {
display_name = "${var.resource_group_name}-application"
owners = [data.azuread_client_config.current.object_id]
}
resource "azuread_service_principal" "application_principal" {
application_id = azuread_application.base_application.application_id
app_role_assignment_required = false
owners = [data.azuread_client_config.current.object_id]
}
# Set identity as base resource group owner
resource "azurerm_role_assignment" "app_registration_owner_role" {
scope = azurerm_resource_group.base_resource_group.id
role_definition_name = "Owner"
principal_id = azuread_service_principal.application_principal.object_id
}
# Create application secret (password)
resource "azuread_application_password" "base_application_secret" {
application_object_id = azuread_application.base_application.object_id
}

View File

@ -6,15 +6,6 @@ output "tenant_id" {
value = data.azurerm_subscription.current.tenant_id
}
output "application_id" {
value = azuread_application.base_application.application_id
}
output "uami_id" {
value = azurerm_user_assigned_identity.identity_uami.id
}
output "application_client_secret_value" {
value = azuread_application_password.base_application_secret.value
sensitive = true
}

View File

@ -116,9 +116,8 @@ func TestPrepareIAM(t *testing.T) {
ServiceAccountID: "const-test-case",
}
azureVars := &AzureIAMVariables{
Region: "westus",
ServicePrincipal: "constell-test-sp",
ResourceGroup: "constell-test-rg",
Region: "westus",
ResourceGroup: "constell-test-rg",
}
awsVars := &AWSIAMVariables{
Region: "eu-east-2a",
@ -480,9 +479,8 @@ func TestCreateIAM(t *testing.T) {
ServiceAccountID: "const-test-case",
}
azureVars := &AzureIAMVariables{
Region: "westus",
ServicePrincipal: "constell-test-sp",
ResourceGroup: "constell-test-rg",
Region: "westus",
ResourceGroup: "constell-test-rg",
}
awsVars := &AWSIAMVariables{
Region: "eu-east-2a",
@ -581,11 +579,9 @@ func TestCreateIAM(t *testing.T) {
tf: &stubTerraform{showState: newTestState()},
fs: afero.NewMemMapFs(),
want: IAMOutput{Azure: AzureIAMOutput{
SubscriptionID: "test_subscription_id",
TenantID: "test_tenant_id",
ApplicationID: "test_application_id",
ApplicationClientSecretValue: "test_application_client_secret_value",
UAMIID: "test_uami_id",
SubscriptionID: "test_subscription_id",
TenantID: "test_tenant_id",
UAMIID: "test_uami_id",
}},
},
"azure init fails": {

View File

@ -1,8 +1,19 @@
# Configuration migrations
# Migrations
This document describes breaking changes in the configuration file format between Constellation releases.
This document describes breaking changes and migrations between Constellation releases.
Use [`constellation config migrate`](./cli.md#constellation-config-migrate) to automatically update an old config file to a new format.
## Migrating from Azure's service principal authentication to managed identity authentication
- The `provider.azure.appClientID` and `provider.azure.appClientSecret` fields are no longer required and should be removed.
- To keep using an existing UAMI add the `Owner` permission with the scope of your `resourceGroup`.
- Otherwise, simply [create new Constellation IAM credentials](../workflows/config.md#creating-iam-credentials) and use the created UAMI.
- To migrate the authentication for an existing Constellation on Azure to an UAMI with the necessary permissions:
1. Remove the `aadClientId` and `aadClientSecret` from the azureconfig secret.
2. Set `useManagedIdentityExtension` to `true` and use the `userAssignedIdentity` from the Constellation config for the value of `userAssignedIdentityID`.
3. Restart the CSI driver, cloud controller manager, cluster autoscaler, and Constellation operator pods.
## Migrating from CLI versions before 2.8
- The `measurements` field for each cloud service provider was replaced with a global `attestation` field.

View File

@ -143,10 +143,10 @@ type AzureConfig struct {
UserAssignedIdentity string `yaml:"userAssignedIdentity" validate:"required"`
// description: |
// Application client ID of the Active Directory app registration.
AppClientID string `yaml:"appClientID" validate:"uuid"`
AppClientID string `yaml:"appClientID,omitempty" validate:"omitempty,uuid"`
// description: |
// Client secret value of the Active Directory app registration credentials. Alternatively leave empty and pass value via CONSTELL_AZURE_CLIENT_SECRET_VALUE environment variable.
ClientSecretValue string `yaml:"clientSecretValue" validate:"required"`
ClientSecretValue string `yaml:"clientSecretValue,omitempty" validate:"omitempty"`
// description: |
// VM instance type to use for Constellation nodes.
InstanceType string `yaml:"instanceType" validate:"azure_instance_type"`
@ -419,6 +419,14 @@ func NewWithClient(fileHandler file.Handler, name string, client fetcher.HTTPCli
c.MicroserviceVersion = Default().MicroserviceVersion
}
// TODO(3u13r): Remove this deprecation warning and enforce assigned managed identity after the v2.8.0 but before the v2.9.0 release.
if c.Provider.Azure != nil &&
(c.Provider.Azure.AppClientID != "" || c.Provider.Azure.ClientSecretValue != "") {
// Deprecation warning for old auth method
fmt.Fprintf(os.Stderr, "WARNING: Using a service principal for authentication is deprecated and will be removed in an upcoming version.")
fmt.Fprintf(os.Stderr, " Migrate to using a user assigned managed identity by following the migration guide: https://docs.edgeless.systems/constellation/reference/migration.")
}
return c, c.Validate(force)
}

View File

@ -277,8 +277,8 @@ func TestNewWithDefaultOptions(t *testing.T) {
}
func TestValidate(t *testing.T) {
const defaultErrCount = 34 // expect this number of error messages by default because user-specific values are not set and multiple providers are defined by default
const azErrCount = 9
const defaultErrCount = 32 // expect this number of error messages by default because user-specific values are not set and multiple providers are defined by default
const azErrCount = 7
const gcpErrCount = 6
// TODO(AB#3132,3u13r): refactor config validation tests