update attached role policies

This commit is contained in:
kkb0318
2024-07-05 19:36:34 +09:00
parent fff9016a06
commit 4612d40ea9
4 changed files with 69 additions and 39 deletions
+53 -29
View File
@@ -40,12 +40,14 @@ func (r *RoleManager) PolicyArn(policy string) *string {
} }
// ExtractNewPolicies returns the names of the policies that are in the current settings (r.Policies) but are not yet attached to the role. // ExtractNewPolicies returns the names of the policies that are in the current settings (r.Policies) but are not yet attached to the role.
func (r *RoleManager) ExtractNewPolicies(l *iam.ListAttachedRolePoliciesOutput) []string { func (r *RoleManager) ExtractNewPolicies(l *iam.ListAttachedRolePoliciesOutput) []string {
result := []string{} result := []string{}
if l == nil {
return r.Policies // return all policies
}
for _, p := range r.Policies { for _, p := range r.Policies {
if slices.ContainsFunc(l.AttachedPolicies, func(a types.AttachedPolicy) bool { if !slices.ContainsFunc(l.AttachedPolicies, func(ap types.AttachedPolicy) bool {
return *r.PolicyArn(p) != *a.PolicyArn return *r.PolicyArn(p) == *ap.PolicyArn
}) { }) {
result = append(result, p) result = append(result, p)
} }
@@ -56,9 +58,12 @@ func (r *RoleManager) ExtractNewPolicies(l *iam.ListAttachedRolePoliciesOutput)
// ExtractStalePolicies returns the ARNs of the policies that are attached to the role but are not in the current settings (r.Policies). // ExtractStalePolicies returns the ARNs of the policies that are attached to the role but are not in the current settings (r.Policies).
func (r *RoleManager) ExtractStalePolicies(l *iam.ListAttachedRolePoliciesOutput) []string { func (r *RoleManager) ExtractStalePolicies(l *iam.ListAttachedRolePoliciesOutput) []string {
result := []string{} result := []string{}
if l == nil {
return result
}
for _, ap := range l.AttachedPolicies { for _, ap := range l.AttachedPolicies {
if slices.ContainsFunc(r.Policies, func(p string) bool { if !slices.ContainsFunc(r.Policies, func(p string) bool {
return *r.PolicyArn(p) != *ap.PolicyArn return *r.PolicyArn(p) == *ap.PolicyArn
}) { }) {
result = append(result, *ap.PolicyArn) result = append(result, *ap.PolicyArn)
} }
@@ -69,17 +74,11 @@ func (r *RoleManager) ExtractStalePolicies(l *iam.ListAttachedRolePoliciesOutput
// DeleteIRSARole detaches specified policies from the IAM role and deletes the IAM role // DeleteIRSARole detaches specified policies from the IAM role and deletes the IAM role
func (a *AwsIamClient) DeleteIRSARole(ctx context.Context, r RoleManager) error { func (a *AwsIamClient) DeleteIRSARole(ctx context.Context, r RoleManager) error {
for _, policy := range r.Policies { for _, policy := range r.Policies {
detachRolePolicyInput := &iam.DetachRolePolicyInput{ err := a.DetachRolePolicy(ctx, aws.String(r.RoleName), r.PolicyArn(policy))
RoleName: aws.String(r.RoleName), if err != nil {
PolicyArn: r.PolicyArn(policy),
}
_, err := a.Client.DetachRolePolicy(ctx, detachRolePolicyInput)
// Ignore error if the policy is already detached or the role does not exist
if errorHandler(err, []string{"NoSuchEntity"}) != nil {
return err return err
} }
log.Printf("Policy %s detached from role %s successfully", policy, r.RoleName) log.Printf("Policy %s detached from role %s successfully", policy, r.RoleName)
} }
input := &iam.DeleteRoleInput{RoleName: aws.String(r.RoleName)} input := &iam.DeleteRoleInput{RoleName: aws.String(r.RoleName)}
_, err := a.Client.DeleteRole(ctx, input) _, err := a.Client.DeleteRole(ctx, input)
@@ -91,8 +90,32 @@ func (a *AwsIamClient) DeleteIRSARole(ctx context.Context, r RoleManager) error
return nil return nil
} }
// CreateIRSARole creates an IAM role with the specified trust policy and attaches specified policies to it // DetachRolePolicy detaches specified policies from the IAM role
func (a *AwsIamClient) CreateIRSARole(ctx context.Context, issuerMeta issuer.OIDCIssuerMeta, r RoleManager) error { func (a *AwsIamClient) DetachRolePolicy(ctx context.Context, roleName, policyArn *string) error {
detachRolePolicyInput := &iam.DetachRolePolicyInput{
RoleName: roleName,
PolicyArn: policyArn,
}
_, err := a.Client.DetachRolePolicy(ctx, detachRolePolicyInput)
// Ignore error if the policy is already detached or the role does not exist
if errorHandler(err, []string{"NoSuchEntity"}) != nil {
return err
}
return nil
}
// AttachRolePolicy attaches specidied policy
func (a *AwsIamClient) AttachRolePolicy(ctx context.Context, roleName, policyArn *string) error {
attachRolePolicyInput := &iam.AttachRolePolicyInput{
RoleName: roleName,
PolicyArn: policyArn,
}
_, err := a.Client.AttachRolePolicy(ctx, attachRolePolicyInput)
return err
}
// UpdateIRSARole creates an IAM role with the specified trust policy and attaches specified policies to it
func (a *AwsIamClient) UpdateIRSARole(ctx context.Context, issuerMeta issuer.OIDCIssuerMeta, r RoleManager) error {
providerArn := fmt.Sprintf("arn:aws:iam::%s:oidc-provider/%s", r.AccountId, issuerMeta.IssuerHostPath()) providerArn := fmt.Sprintf("arn:aws:iam::%s:oidc-provider/%s", r.AccountId, issuerMeta.IssuerHostPath())
statement := make([]map[string]interface{}, len(r.ServiceAccount.Namespaces)) statement := make([]map[string]interface{}, len(r.ServiceAccount.Namespaces))
for i, ns := range r.ServiceAccount.Namespaces { for i, ns := range r.ServiceAccount.Namespaces {
@@ -137,26 +160,27 @@ func (a *AwsIamClient) CreateIRSARole(ctx context.Context, issuerMeta issuer.OID
if err != nil { if err != nil {
return fmt.Errorf("failed to update assume role policy for role %s: %w", r.RoleName, err) return fmt.Errorf("failed to update assume role policy for role %s: %w", r.RoleName, err)
} }
log.Printf("Assume role policy for %s updated successfully", r.RoleName)
// TODO:
// listPoliciesOutput, err := a.Client.ListAttachedRolePolicies(ctx, &iam.ListAttachedRolePoliciesInput{RoleName: aws.String(r.RoleName)})
// if err != nil {
// return fmt.Errorf("failed to list attached role policies with %s: %w", r.RoleName, err)
// }
for _, policy := range r.Policies { listPoliciesOutput, err := a.Client.ListAttachedRolePolicies(ctx, &iam.ListAttachedRolePoliciesInput{RoleName: aws.String(r.RoleName)})
attachRolePolicyInput := &iam.AttachRolePolicyInput{ if err != nil {
RoleName: aws.String(r.RoleName), return fmt.Errorf("failed to list attached role policies with %s: %w", r.RoleName, err)
PolicyArn: r.PolicyArn(policy), }
}
_, err = a.Client.AttachRolePolicy(ctx, attachRolePolicyInput) for _, policy := range r.ExtractNewPolicies(listPoliciesOutput) {
err := a.AttachRolePolicy(ctx, aws.String(r.RoleName), r.PolicyArn(policy))
if err != nil { if err != nil {
return err return err
} }
log.Printf("Policy %s attached to role %s successfully", policy, r.RoleName) log.Printf("Policy %s attached to role %s successfully", policy, r.RoleName)
} }
for _, policy := range r.ExtractStalePolicies(listPoliciesOutput) {
err := a.DetachRolePolicy(ctx, aws.String(r.RoleName), r.PolicyArn(policy))
if err != nil {
return err
}
log.Printf("Policy %s detached to role %s successfully", policy, r.RoleName)
}
log.Printf("Assume role policy for %s updated successfully", r.RoleName)
return nil return nil
} }
@@ -165,7 +189,7 @@ func errorHandler(err error, errorCodes []string) error {
if err != nil { if err != nil {
var ae smithy.APIError var ae smithy.APIError
if errors.As(err, &ae) && slices.Contains(errorCodes, ae.ErrorCode()) { if errors.As(err, &ae) && slices.Contains(errorCodes, ae.ErrorCode()) {
fmt.Printf("Skipped error: %s \n", err.Error()) // fmt.Printf("Skipped error: %s \n", err.Error())
return nil return nil
} }
} }
+3 -2
View File
@@ -66,14 +66,15 @@ func TestExtractStalePolicies(t *testing.T) {
}{ }{
{ {
"StalePolicyExists", "StalePolicyExists",
[]string{"ReadOnlyAccess"}, []string{"ReadOnlyAccess", "AdministratorAccess"},
&iam.ListAttachedRolePoliciesOutput{ &iam.ListAttachedRolePoliciesOutput{
AttachedPolicies: []types.AttachedPolicy{ AttachedPolicies: []types.AttachedPolicy{
{PolicyArn: aws.String("arn:aws:iam::aws:policy/ReadOnlyAccess")}, {PolicyArn: aws.String("arn:aws:iam::aws:policy/ReadOnlyAccess")},
{PolicyArn: aws.String("arn:aws:iam::aws:policy/AdministratorAccess")}, {PolicyArn: aws.String("arn:aws:iam::aws:policy/AdministratorAccess")},
{PolicyArn: aws.String("arn:aws:iam::aws:policy/PowerUserAccess")},
}, },
}, },
[]string{"arn:aws:iam::aws:policy/AdministratorAccess"}, []string{"arn:aws:iam::aws:policy/PowerUserAccess"},
}, },
{ {
"MultipleStalePoliciesExist", "MultipleStalePoliciesExist",
+1 -1
View File
@@ -184,7 +184,7 @@ func (r *IRSAReconciler) reconcile(ctx context.Context, obj *irsav1alpha1.IRSA,
Policies: obj.Spec.IamPolicies, Policies: obj.Spec.IamPolicies,
AccountId: accountId, AccountId: accountId,
} }
err = r.AwsClient.IamClient().CreateIRSARole( err = r.AwsClient.IamClient().UpdateIRSARole(
ctx, ctx,
issuerMeta, issuerMeta,
roleManager, roleManager,
@@ -347,13 +347,14 @@ func (m *mockAwsClient) StsClient() *awsclient.AwsStsClient {
type ( type (
mockAwsIamAPI struct { mockAwsIamAPI struct {
createOidcErr error createOidcErr error
deleteOidcErr error deleteOidcErr error
createRoleErr error createRoleErr error
deleteRoleErr error deleteRoleErr error
updateAssumeRolePolicyError error updateAssumeRolePolicyError error
attachRolePolicyError error listAttachedRolePoliciesError error
detachRolePolicyError error attachRolePolicyError error
detachRolePolicyError error
} }
mockAwsS3API struct { mockAwsS3API struct {
createBucketErr bool createBucketErr bool
@@ -374,6 +375,10 @@ func (m *mockAwsIamAPI) CreateRole(ctx context.Context, params *iam.CreateRoleIn
return nil, m.createRoleErr return nil, m.createRoleErr
} }
func (m *mockAwsIamAPI) ListAttachedRolePolicies(ctx context.Context, params *iam.ListAttachedRolePoliciesInput, optFns ...func(*iam.Options)) (*iam.ListAttachedRolePoliciesOutput, error) {
return nil, m.listAttachedRolePoliciesError
}
func (m *mockAwsIamAPI) UpdateAssumeRolePolicy(ctx context.Context, params *iam.UpdateAssumeRolePolicyInput, optFns ...func(*iam.Options)) (*iam.UpdateAssumeRolePolicyOutput, error) { func (m *mockAwsIamAPI) UpdateAssumeRolePolicy(ctx context.Context, params *iam.UpdateAssumeRolePolicyInput, optFns ...func(*iam.Options)) (*iam.UpdateAssumeRolePolicyOutput, error) {
return nil, m.updateAssumeRolePolicyError return nil, m.updateAssumeRolePolicyError
} }