Implement credential validation in AWS provider#61
Conversation
| // Check permissions using IAM Policy Simulator | ||
| // Process in batches of 100 (API limit) | ||
| const batchSize = 100 | ||
| var missingPerms []string | ||
|
|
||
| for i := 0; i < len(requiredPerms); i += batchSize { | ||
| end := i + batchSize | ||
| if end > len(requiredPerms) { | ||
| end = len(requiredPerms) | ||
| } | ||
| batch := requiredPerms[i:end] | ||
|
|
||
| simResult, err := iamClient.SimulatePrincipalPolicy(ctx, &iam.SimulatePrincipalPolicyInput{ | ||
| PolicySourceArn: identity.Arn, | ||
| ActionNames: batch, | ||
| ResourceArns: []string{"*"}, | ||
| }) | ||
| if err != nil { | ||
| span.RecordError(err) | ||
| return nil, fmt.Errorf("failed to simulate IAM policy: %w", err) | ||
| } | ||
|
|
||
| for _, evalResult := range simResult.EvaluationResults { | ||
| if evalResult.EvalDecision != iamtypes.PolicyEvaluationDecisionTypeAllowed { | ||
| missingPerms = append(missingPerms, aws.ToString(evalResult.EvalActionName)) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
I think this batch loop should be moved to utilities, since might be used elsewhere in the code. And IMO I don't like this being here.
There was a problem hiding this comment.
Replaced the hand-rolled batching loop with slices.Chunk from the Go 1.23+ stdlib (we're on Go 1.25). That removes the need for a custom utility while keeping the 100-action API limit explicit via the iamSimulateBatchSize constant.
| func getRequiredPermissions(cfg *Config) []string { | ||
| perms := []string{ | ||
| // STS - always required | ||
| "sts:GetCallerIdentity", | ||
|
|
||
| // S3 - state bucket management (always required) | ||
| "s3:HeadBucket", | ||
| "s3:CreateBucket", | ||
| "s3:PutBucketVersioning", | ||
| "s3:PutPublicAccessBlock", | ||
| "s3:ListObjectVersions", | ||
| "s3:DeleteObject", | ||
| "s3:DeleteBucket", | ||
|
|
||
| // EC2 - core (always required) | ||
| "ec2:DescribeAvailabilityZones", | ||
| "ec2:CreateTags", | ||
| "ec2:DeleteTags", | ||
|
|
||
| // EKS - always required | ||
| "eks:CreateCluster", | ||
| "eks:DeleteCluster", | ||
| "eks:DescribeCluster", | ||
| "eks:UpdateClusterVersion", | ||
| "eks:UpdateClusterConfig", | ||
| "eks:CreateNodegroup", | ||
| "eks:DeleteNodegroup", | ||
| "eks:DescribeNodegroup", | ||
| "eks:ListNodegroups", | ||
| "eks:UpdateNodegroupConfig", | ||
| "eks:TagResource", | ||
| "eks:UntagResource", | ||
| } |
There was a problem hiding this comment.
This may be unnecessary, but would it make sense to keep the list of permissions in a separate file and reference it from the get method here?
I think that would make the permissions easier to locate in the codebase and would also be helpful from a documentation perspective.
There was a problem hiding this comment.
Good call. Moved the permission list to pkg/provider/aws/permissions.go and split it into awsBasePermissions, awsVPCPermissions, awsIAMPermissions, and awsEFSPermissions so each set is easy to find and audit. getRequiredPermissions now just composes them conditionally based on the cluster config.
b377fb0 to
5686e10
Compare
Adds an optional CredentialValidator interface to the provider package and an AWS implementation that verifies the caller has all permissions required for a Nebari deployment. Default validation now uses sts:GetCallerIdentity instead of Credentials.Retrieve, which only checks the local credential chain without confirming AWS will accept them. The new --validate-creds flag on the validate command runs the full permission check via iam:SimulatePrincipalPolicy, batched in groups of 100 (the API limit) using slices.Chunk. Providers that don't implement CredentialValidator print a note explaining the flag isn't supported. The permission list lives in pkg/provider/aws/permissions.go to make it easy to find and audit. Permissions are composed conditionally based on cluster config: - VPC permissions skipped when ExistingVPCID is set - IAM permissions skipped when ExistingClusterRoleArn is set - EFS permissions only included when EFS.Enabled is true Closes #6
5686e10 to
91089c0
Compare
There was a problem hiding this comment.
I validated the deploy/destroy permission set against a real AWS account (scoped IAM user, full nic deploy + nic destroy cycle). Two issues with the current list:
1. Three SDK operation names that aren't valid IAM actions (they match nothing, so grant no access):
| current | correct IAM action |
|---|---|
s3:HeadBucket |
s3:ListBucket |
s3:PutPublicAccessBlock |
s3:PutBucketPublicAccessBlock |
s3:ListObjectVersions |
s3:ListBucketVersions |
2. ~65 actions are missing. A from-scratch deploy + destroy needs 133 actions; this list has 71. Biggest gaps, each confirmed by an AccessDenied during testing:
elasticloadbalancing:*(5):nic destroyaborts at LB cleanup without theses3:GetObject/PutObject/DeleteObjectVersion: state locking + versioned-bucket teardownssm:GetParameter: EKS AMI lookup (deploy fails at node-group planning)logs:*(6), EKS add-on CRUD, access-entry + pod-identity APIs, IAM policy/OIDC-provider CRUD, launch templates +ec2:RunInstances
The full validated 133-action policy is here: https://github.com/nebari-dev/nebari-docs/blob/docs/aws-provider-howto/docs/static/policies/aws/nic-deploy.json
|
closed as stale |
Summary
Implements AWS credential validation as described in #6. This adds:
sts:GetCallerIdentitycheck on everynic validatecommand--validate-credsflag runs IAM Policy Simulator to verify all required permissionsChanges
New Interface
CredentialValidatorinterface topkg/provider/provider.goCLI Flag
--validate-credsflag tonic validatecommandAWS Implementation
pkg/provider/aws/credentials.go: Core validation logicgetRequiredPermissions(): Config-aware permission list buildervalidateCredentialsWithClients(): IAM Policy Simulator integrationValidateCredentials(): Provider method implementationValidate()to useGetCallerIdentityfor basic credential checkPermissions Checked
existing_vpc_id), IAM roles (skip ifexisting_cluster_role_arn), EFS (only if enabled)Test Plan
getRequiredPermissions()with various config combinationsvalidateCredentialsWithClients()with mocked AWS clientsgolangci-lintpasses with 0 issues--validate-credsflagUsage
Related Issues
--gen-permsflag)