Amplify未授权角色附加的AppConfig策略失效问题求助
使用Amplify Gen.2搭配自定义CDK为Swift iOS应用部署AWS后端,CDK构造中实现了AWS AppConfig服务,并为已授权和未授权IAM角色配置了相同的访问策略(未授权角色已启用)。
iOS应用通过Amplify包及aws-sdk-swift中的AWSAppConfigData模块获取配置,部署过程无报错,用户登录状态下可正常获取AppConfig配置,但未登录时出现如下权限错误:
User: arn:aws:sts::xxx:assumed-role/amplify-xxx-sandbox001-s-amplifyAuthunauthenticate-xxx/CognitoIdentityCredentials is not authorized to perform: appconfig:StartConfigurationSession on resource: arn:aws:appconfig:eu-central-1:xxx:application/xxx/environment/xxx/configuration/xxx because no session policy allows the appconfig:StartConfigurationSession action
在AWS控制台中查看,已授权和未授权角色均附加了相同的策略,且IAM策略模拟器测试两个角色的权限均正常。怀疑存在优先级更高的策略阻止未授权访问,以下是相关代码,寻求排查方向。
相关CDK代码
import { Construct } from 'constructs'; import * as iam from 'aws-cdk-lib/aws-iam'; import * as appconfig from 'aws-cdk-lib/aws-appconfig'; import * as ssm from 'aws-cdk-lib/aws-ssm'; import * as cdk from 'aws-cdk-lib'; export class AppConfigConstruct extends Construct { constructor(scope: Construct, id: string, props: AppConfigConstructProps) { super(scope, id); /* Creating AppConfig ... */ // Creating managed policy const appConfigPolicy = new iam.ManagedPolicy(this, `${id}AppConfigPolicy`, { statements: [ new iam.PolicyStatement({ sid: `StartGetConfiguration`, effect: iam.Effect.ALLOW, actions: [ 'appconfig:StartConfigurationSession', 'appconfig:GetLatestConfiguration' ], resources: [ `arn:aws:appconfig:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:application/${appConfigAppId}/environment/${appConfigEnvId}/configuration/${appConfigId}` ] }) ] }); // Attaching to unauth role const unauthenticatedUserIamRole = iam.Role.fromRoleArn(this, 'UnauthRole', props.unauthenticatedUserIamRoleArn); unauthenticatedUserIamRole.addManagedPolicy(appConfigPolicy); // Attaching to auth role const authenticatedUserIamRole = iam.Role.fromRoleArn(this, 'AuthRole', props.authenticatedUserIamRoleArn); authenticatedUserIamRole.addManagedPolicy(appConfigPolicy); } }
相关Swift代码
import Amplify import AWSPluginsCore import SmithyIdentity import AWSAppConfigData final public class Configuration { public static let shared = Configuration() private init() {} private func credentialIdentityResolver() async throws -> StaticAWSCredentialIdentityResolver { guard let awsCredentialsProvider = try await Amplify.Auth.fetchAuthSession() as? AuthAWSCredentialsProvider else { throw ConfigurationError.noAuthAWSCredentialsProvider } guard let credentials = try awsCredentialsProvider.getAWSCredentials().get() as? AWSTemporaryCredentials else { throw ConfigurationError.noAWSTemporaryCredentials } let identity = AWSCredentialIdentity( accessKey: credentials.accessKeyId, secret: credentials.secretAccessKey, expiration: .now.addingTimeInterval(.minutes(1)), sessionToken: credentials.sessionToken ) return try StaticAWSCredentialIdentityResolver(identity) } public func setup(applicationIdentifier: String) async { do { let staticAWSCredentialIdentityResolver = try await credentialIdentityResolver() let appConfigDataClientConfiguration = try await AppConfigDataClient.AppConfigDataClientConfiguration( awsCredentialIdentityResolver: staticAWSCredentialIdentityResolver, region: "eu-central-1" ) let appConfigClient = AppConfigDataClient( config: appConfigDataClientConfiguration ) let startConfigurationSessionInput = StartConfigurationSessionInput( applicationIdentifier: applicationIdentifier, configurationProfileIdentifier: "SSMConfigProfile", environmentIdentifier: "ProdEnvironment", requiredMinimumPollIntervalInSeconds: nil ) let sessionResponse = try await appConfigClient.startConfigurationSession( input: startConfigurationSessionInput ) guard let token = sessionResponse.initialConfigurationToken else { throw ConfigurationError.noInitialConfigurationToken } let configResponse = try await appConfigClient.getLatestConfiguration( input: GetLatestConfigurationInput(configurationToken: token) ) guard let configData = configResponse.configuration, !configData.isEmpty, let configString = String(data: configData, encoding: .utf8) else { return } } catch { Log.configuration.error("Error - \(error)") } } }
排查方向
- 检查Cognito身份池的未授权角色会话策略:Cognito身份池可能会给临时凭证附加会话策略,该策略优先级高于角色附加的托管/内联策略。进入Cognito控制台→目标身份池→编辑身份池,查看未授权角色的“会话策略”是否存在,若有则确认是否允许
appconfig:StartConfigurationSession和appconfig:GetLatestConfiguration动作。 - 验证CDK中角色ARN准确性:确认
props.unauthenticatedUserIamRoleArn指向的是正确的未授权角色,避免CDK附加策略时选错角色。 - 检查角色的权限边界:如果未授权角色设置了权限边界,即使附加了允许策略,权限边界也会限制实际可执行的动作。进入IAM控制台找到该角色,查看“权限边界”标签,确认边界策略允许AppConfig相关动作。
- 确认AppConfig资源ARN匹配:CDK中构造的资源ARN是否和报错中的ARN完全一致?比如应用ID、环境ID、配置ID是否正确,避免变量替换错误导致策略资源不匹配。
- 验证临时凭证关联的角色:在iOS端打印
credentials的roleArn字段,确认未登录时的临时凭证确实关联了预期的未授权角色。 - 检查Amplify Auth未授权配置:确保Amplify Auth已正确启用未授权访问,在
amplifyconfiguration.json中检查auth模块的allowGuestAccess是否设为true。
内容的提问来源于stack exchange,提问作者Marco Boerner

