Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MI/WI - Generate secrets for platform identities #3802

Merged
9 changes: 9 additions & 0 deletions pkg/api/admin/platformworkloadidentityroleset.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,13 @@ type PlatformWorkloadIdentityRole struct {

// ServiceAccounts represents the set of service accounts associated with the given operator, since each service account needs its own federated credential.
ServiceAccounts []string `json:"serviceAccounts,omitempty" mutable:"true" validate:"required"`

// SecretLocation represents the location of the in-cluster secret containing credentials for the platform workload identity.
SecretLocation SecretLocation `json:"secretLocation,omitempty" mutable:"true" validate:"required"`
}

// SecretLocation represents the location of the in-cluster secret containing credentials for the platform workload identity.
type SecretLocation struct {
Namespace string `json:"namespace,omitempty" mutable:"true" validate:"required"`
Name string `json:"name,omitempty" mutable:"true" validate:"required"`
}
4 changes: 4 additions & 0 deletions pkg/api/admin/platformworkloadidentityroleset_convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ func (c platformWorkloadIdentityRoleSetConverter) ToExternal(s *api.PlatformWork
RoleDefinitionName: r.RoleDefinitionName,
RoleDefinitionID: r.RoleDefinitionID,
ServiceAccounts: make([]string, 0, len(r.ServiceAccounts)),
SecretLocation: SecretLocation{
Namespace: r.SecretLocation.Namespace,
Name: r.SecretLocation.Name,
},
}

role.ServiceAccounts = append(role.ServiceAccounts, r.ServiceAccounts...)
Expand Down
15 changes: 11 additions & 4 deletions pkg/api/platformworkloadidentityroleset.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,15 @@ type PlatformWorkloadIdentityRoleSetProperties struct {

// PlatformWorkloadIdentityRole represents a mapping from a particular OCP operator to the built-in role that should be assigned to that operator's corresponding managed identity.
type PlatformWorkloadIdentityRole struct {
OperatorName string `json:"operatorName,omitempty"`
RoleDefinitionName string `json:"roleDefinitionName,omitempty"`
RoleDefinitionID string `json:"roleDefinitionId,omitempty"`
ServiceAccounts []string `json:"serviceAccounts,omitempty"`
OperatorName string `json:"operatorName,omitempty"`
RoleDefinitionName string `json:"roleDefinitionName,omitempty"`
RoleDefinitionID string `json:"roleDefinitionId,omitempty"`
ServiceAccounts []string `json:"serviceAccounts,omitempty"`
SecretLocation SecretLocation `json:"secretLocation,omitempty"`
}

// SecretLocation represents the location of the in-cluster secret containing credentials for the platform workload identity.
type SecretLocation struct {
Namespace string `json:"namespace,omitempty"`
Name string `json:"name,omitempty"`
}
126 changes: 126 additions & 0 deletions pkg/cluster/workloadidentityresources.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package cluster

// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.

import (
"fmt"

configv1 "github.com/openshift/api/config/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kruntime "k8s.io/apimachinery/pkg/runtime"
)

const (
azureFederatedTokenFileLocation = "/var/run/secrets/openshift/serviceaccount/token"

ccoSecretNamespace = "openshift-cloud-credential-operator"
ccoSecretName = "azure-credentials"
ccoSecretFilename = "azure-ad-pod-identity-webhook-config.yaml"
authenticationConfigName = "cluster"
authenticationConfigFilename = "cluster-authentication-02-config.yaml"
)

func (m *manager) generateWorkloadIdentityResources() (map[string]kruntime.Object, error) {
if !m.doc.OpenShiftCluster.UsesWorkloadIdentity() {
return nil, fmt.Errorf("generateWorkloadIdentityResources called for a CSP cluster")
}

resources := map[string]kruntime.Object{}
if platformWorkloadIdentitySecrets, err := m.generatePlatformWorkloadIdentitySecrets(); err != nil {
return nil, err
} else {
for _, secret := range platformWorkloadIdentitySecrets {
key := fmt.Sprintf("%s-%s-credentials.yaml", secret.ObjectMeta.Namespace, secret.ObjectMeta.Name)
resources[key] = secret
}
}

if cloudCredentialOperatorSecret, err := m.generateCloudCredentialOperatorSecret(); err != nil {
return nil, err
} else {
resources[ccoSecretFilename] = cloudCredentialOperatorSecret
}

if authenticationConfig, err := m.generateAuthenticationConfig(); err != nil {
return nil, err
} else {
resources[authenticationConfigFilename] = authenticationConfig
}

return resources, nil
}

func (m *manager) generatePlatformWorkloadIdentitySecrets() ([]*corev1.Secret, error) {
subscriptionId := m.subscriptionDoc.ID
tenantId := m.subscriptionDoc.Subscription.Properties.TenantID
region := m.doc.OpenShiftCluster.Location

roles := m.platformWorkloadIdentityRolesByVersion.GetPlatformWorkloadIdentityRolesByRoleName()

secrets := []*corev1.Secret{}
for _, identity := range m.doc.OpenShiftCluster.Properties.PlatformWorkloadIdentityProfile.PlatformWorkloadIdentities {
if role, ok := roles[identity.OperatorName]; ok {
secrets = append(secrets, &corev1.Secret{
tsatam marked this conversation as resolved.
Show resolved Hide resolved
TypeMeta: metav1.TypeMeta{
APIVersion: corev1.SchemeGroupVersion.Identifier(),
Kind: "Secret",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: role.SecretLocation.Namespace,
Name: role.SecretLocation.Name,
},
Type: corev1.SecretTypeOpaque,
StringData: map[string]string{
"azure_client_id": identity.ClientID,
"azure_subscription_id": subscriptionId,
"azure_tenant_id": tenantId,
"azure_region": region,
"azure_federated_token_file": azureFederatedTokenFileLocation,
},
})
}
}

return secrets, nil
}

func (m *manager) generateCloudCredentialOperatorSecret() (*corev1.Secret, error) {
tsatam marked this conversation as resolved.
Show resolved Hide resolved
tenantId := m.subscriptionDoc.Subscription.Properties.TenantID

return &corev1.Secret{
tsatam marked this conversation as resolved.
Show resolved Hide resolved
TypeMeta: metav1.TypeMeta{
APIVersion: corev1.SchemeGroupVersion.Identifier(),
Kind: "Secret",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: ccoSecretNamespace,
Name: ccoSecretName,
},
Type: corev1.SecretTypeOpaque,
StringData: map[string]string{
"azure_tenant_id": tenantId,
},
}, nil
}

func (m *manager) generateAuthenticationConfig() (*configv1.Authentication, error) {
tsatam marked this conversation as resolved.
Show resolved Hide resolved
oidcIssuer := m.doc.OpenShiftCluster.Properties.ClusterProfile.OIDCIssuer
if oidcIssuer == nil {
return nil, fmt.Errorf("oidcIssuer not present in clusterdoc")
}

return &configv1.Authentication{
tsatam marked this conversation as resolved.
Show resolved Hide resolved
TypeMeta: metav1.TypeMeta{
APIVersion: configv1.SchemeGroupVersion.Identifier(),
Kind: "Authentication",
},
ObjectMeta: metav1.ObjectMeta{
Name: authenticationConfigName,
},
Spec: configv1.AuthenticationSpec{
ServiceAccountIssuer: (string)(*oidcIssuer),
},
}, nil
}
Loading
Loading