Skip to content

Commit

Permalink
Merge pull request openshift#750 from tbrisker/sts4
Browse files Browse the repository at this point in the history
Addon STS flow
  • Loading branch information
openshift-ci[bot] authored Jun 30, 2022
2 parents d935a59 + 4826806 commit d8e69bb
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 19 deletions.
4 changes: 2 additions & 2 deletions cmd/create/operatorroles/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ func createRoles(r *rosa.Runtime,
}
policyDetails = policies["operator_iam_role_policy"]

policy, err := aws.GenerateRolePolicyDoc(cluster, accountID, operator, policyDetails)
policy, err := aws.GenerateOperatorRolePolicyDoc(cluster, accountID, operator, policyDetails)
if err != nil {
return err
}
Expand Down Expand Up @@ -360,7 +360,7 @@ func buildCommands(r *rosa.Runtime,
}

policyDetail := policies["operator_iam_role_policy"]
policy, err := aws.GenerateRolePolicyDoc(cluster, accountID, operator, policyDetail)
policy, err := aws.GenerateOperatorRolePolicyDoc(cluster, accountID, operator, policyDetail)
if err != nil {
return "", err
}
Expand Down
116 changes: 110 additions & 6 deletions cmd/install/addon/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@ import (
"strconv"
"strings"

"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/iam"
cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1"
"github.com/spf13/cobra"
errors "github.com/zgalor/weberr"

"github.com/openshift/rosa/pkg/arguments"
"github.com/openshift/rosa/pkg/aws"
"github.com/openshift/rosa/pkg/aws/tags"
"github.com/openshift/rosa/pkg/interactive"
"github.com/openshift/rosa/pkg/interactive/confirm"
"github.com/openshift/rosa/pkg/ocm"
Expand Down Expand Up @@ -80,20 +84,65 @@ func run(cmd *cobra.Command, argv []string) {
os.Exit(1)
}

addOn, err := r.OCMClient.GetAddOnInstallation(cluster.ID(), addOnID)
if err != nil && errors.GetType(err) != errors.NotFound {
r.Reporter.Errorf("An error occurred while trying to get addon installation : %v", err)
ensureAddonNotInstalled(r, cluster.ID(), addOnID)

addOn, err := r.OCMClient.GetAddOn(addOnID)
if err != nil {
r.Reporter.Warnf("Failed to get add-on '%s'", addOnID)
os.Exit(1)
}
if addOn != nil {
r.Reporter.Warnf("Addon '%s' is already installed on cluster '%s'", addOnID, clusterKey)
os.Exit(0)

// Verify if addon requires STS authentication
isSTS := cluster.AWS().STS().RoleARN() != "" && len(addOn.CredentialsRequests()) > 0
if isSTS {
r.Reporter.Warnf("Addon '%s' needs access to resources in account '%s'", addOnID, r.Creator.AccountID)
}

if !confirm.Confirm("install add-on '%s' on cluster '%s'", addOnID, clusterKey) {
os.Exit(0)
}

if isSTS {
prefix := aws.GetPrefixFromOperatorRole(cluster)

for _, cr := range addOn.CredentialsRequests() {
roleName := generateRoleName(cr, prefix)
roleArn := aws.GetRoleARN(r.Creator.AccountID, roleName)
_, err = r.AWSClient.GetRoleByARN(roleArn)
if err != nil {
aerr, ok := err.(awserr.Error)
if ok && aerr.Code() == iam.ErrCodeNoSuchEntityException {
err = createAddonRole(r, roleName, cr, cmd, cluster)
if err != nil {
r.Reporter.Errorf("%s", err)
os.Exit(1)
}
} else {
r.Reporter.Errorf("%s", err)
os.Exit(1)
}
}
// TODO : verify the role has the right permissions

operatorRole, err := cmv1.NewOperatorIAMRole().
Name(cr.Name()).
Namespace(cr.Namespace()).
RoleARN(roleArn).
ServiceAccount(cr.ServiceAccount()).
Build()
if err != nil {
r.Reporter.Errorf("Failed to build operator role '%s': %s", roleName, err)
os.Exit(1)
}

err = r.OCMClient.AddClusterOperatorRole(cluster, operatorRole)
if err != nil {
r.Reporter.Errorf("Failed to add operator role to cluster '%s': %s", clusterKey, err)
os.Exit(1)
}
}
}

parameters, err := r.OCMClient.GetAddOnParameters(cluster.ID(), addOnID)
if err != nil {
r.Reporter.Errorf("Failed to get add-on '%s' parameters: %v", addOnID, err)
Expand Down Expand Up @@ -207,6 +256,53 @@ func run(cmd *cobra.Command, argv []string) {
}
}

func ensureAddonNotInstalled(r *rosa.Runtime, clusterID, addOnID string) {
installation, err := r.OCMClient.GetAddOnInstallation(clusterID, addOnID)
if err != nil && errors.GetType(err) != errors.NotFound {
r.Reporter.Errorf("An error occurred while trying to get addon installation : %v", err)
os.Exit(1)
}
if installation != nil {
r.Reporter.Warnf("Addon '%s' is already installed on cluster '%s'", addOnID, clusterID)
os.Exit(0)
}
}

func createAddonRole(r *rosa.Runtime, roleName string, cr *cmv1.CredentialRequest, cmd *cobra.Command,
cluster *cmv1.Cluster) error {
policy := aws.NewPolicyDocument()
policy.AllowActions(cr.PolicyPermissions()...)

policies, err := r.OCMClient.GetPolicies("OperatorRole")
if err != nil {
return err
}
policyDetails := policies["operator_iam_role_policy"]
assumePolicy, err := aws.GenerateAddonPolicyDoc(cluster, r.Creator.AccountID, cr, policyDetails)
if err != nil {
return err
}

r.Reporter.Debugf("Creating role '%s'", roleName)

roleARN, err := r.AWSClient.EnsureRole(roleName, assumePolicy, "", "",
map[string]string{
tags.ClusterID: cluster.ID(),
"addon_namespace": cr.Namespace(),
"addon_name": cr.Name(),
})
if err != nil {
return err
}
r.Reporter.Infof("Created role '%s' with ARN '%s'", roleName, roleARN)

err = r.AWSClient.PutRolePolicy(roleName, roleName, policy.String())
if err != nil {
return err
}
return nil
}

func buildCommand(clusterName string, addonName string, params []ocm.AddOnParam) string {
command := fmt.Sprintf("rosa install addon --cluster %s %s -y", clusterName, addonName)

Expand All @@ -218,3 +314,11 @@ func buildCommand(clusterName string, addonName string, params []ocm.AddOnParam)

return command
}

func generateRoleName(cr *cmv1.CredentialRequest, prefix string) string {
roleName := fmt.Sprintf("%s-%s-%s", prefix, cr.Namespace(), cr.Name())
if len(roleName) > 64 {
roleName = roleName[0:64]
}
return roleName
}
4 changes: 2 additions & 2 deletions cmd/upgrade/operatorroles/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ func buildMissingOperatorRoleCommand(missingRoles map[string]*cmv1.STSOperator,
roleName := getRoleName(cluster, operator)
policyARN := aws.GetOperatorPolicyARN(accountID, prefix, operator.Namespace(), operator.Name())
policyDetails := policies["operator_iam_role_policy"]
policy, err := aws.GenerateRolePolicyDoc(cluster, accountID, operator, policyDetails)
policy, err := aws.GenerateOperatorRolePolicyDoc(cluster, accountID, operator, policyDetails)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -415,7 +415,7 @@ func upgradeMissingOperatorRole(missingRoles map[string]*cmv1.STSOperator, clust
policyDetails := policies["operator_iam_role_policy"]

policyARN := aws.GetOperatorPolicyARN(accountID, prefix, operator.Namespace(), operator.Name())
policy, err := aws.GenerateRolePolicyDoc(cluster, accountID, operator, policyDetails)
policy, err := aws.GenerateOperatorRolePolicyDoc(cluster, accountID, operator, policyDetails)
if err != nil {
return err
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/aws/policies.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ func (c *awsClient) createRole(name string, policy string, permissionsBoundary s
}

func (c *awsClient) isRoleCompatible(name string, version string) (bool, error) {
// Ignore if there is no version
if version == "" {
return true, nil
}
output, err := c.iamClient.ListRoleTags(&iam.ListRoleTagsInput{
RoleName: aws.String(name),
})
Expand Down
28 changes: 19 additions & 9 deletions pkg/aws/policy_document.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,7 @@ func getPolicyDocument(policyDocument *string) (*PolicyDocument, error) {
return &data, nil
}

func GenerateRolePolicyDoc(cluster *cmv1.Cluster, accountID string, operator *cmv1.STSOperator,
policyDetails string) (string, error) {
func GenerateRolePolicyDoc(cluster *cmv1.Cluster, accountID, serviceAccounts, policyDetails string) (string, error) {
oidcEndpointURL, err := url.ParseRequestURI(cluster.AWS().STS().OIDCEndpointURL())
if err != nil {
return "", err
Expand All @@ -280,17 +279,28 @@ func GenerateRolePolicyDoc(cluster *cmv1.Cluster, accountID string, operator *cm

oidcProviderARN := fmt.Sprintf("arn:aws:iam::%s:oidc-provider/%s", accountID, issuerURL)

serviceAccounts := []string{}
for _, sa := range operator.ServiceAccounts() {
serviceAccounts = append(serviceAccounts,
fmt.Sprintf("system:serviceaccount:%s:%s", operator.Namespace(), sa))
}

policy := InterpolatePolicyDocument(policyDetails, map[string]string{
"oidc_provider_arn": oidcProviderARN,
"issuer_url": issuerURL,
"service_accounts": strings.Join(serviceAccounts, `" , "`),
"service_accounts": serviceAccounts,
})

return policy, nil
}

func GenerateOperatorRolePolicyDoc(cluster *cmv1.Cluster, accountID string, operator *cmv1.STSOperator,
policyDetails string) (string, error) {
serviceAccounts := make([]string, len(operator.ServiceAccounts()))
for i, sa := range operator.ServiceAccounts() {
serviceAccounts[i] = fmt.Sprintf("system:serviceaccount:%s:%s", operator.Namespace(), sa)
}
service_accounts := strings.Join(serviceAccounts, `" , "`)

return GenerateRolePolicyDoc(cluster, accountID, service_accounts, policyDetails)
}

func GenerateAddonPolicyDoc(cluster *cmv1.Cluster, accountID string, cr *cmv1.CredentialRequest,
policyDetails string) (string, error) {
service_accounts := fmt.Sprintf("system:serviceaccount:%s:%s", cr.Namespace(), cr.ServiceAccount())
return GenerateRolePolicyDoc(cluster, accountID, service_accounts, policyDetails)
}

0 comments on commit d8e69bb

Please sign in to comment.