jcode Phone-Server AWS IAM 最小权限设计:从 AdministratorAccess 到三平面角色与防锁死迁移
2026/9/13 2:21:33 网站建设 项目流程

jcode Phone-Server AWS IAM 最小权限设计:从 AdministratorAccess 到三平面角色与防锁死迁移

【免费下载链接】jcodeThe most RAM efficient harness项目地址: https://gitcode.com/GitHub_Trending/jcod/jcode

本文基于 jcode 仓库中 Phone-Server IAM 最小权限评估文档 展开,讲解如何把一条长期持有AdministratorAccess的部署凭据拆解为「日常操作 / 重建供应 / 紧急管理」三个互不越权的权限平面,并完整覆盖各运行时角色的 IAM 策略、只读盘点命令、防锁死迁移步骤与验收标准。读完后你将掌握:如何依据真实 API 调用证据为 EC2、Lambda、API Gateway、Bedrock 编写最小权限策略,以及如何在不锁死账号的前提下安全移除管理员权限并轮换凭据。

背景:一个部署凭据持有 AdministratorAccess 意味着什么

jcode 的 phone-server 是一个自管理的 AWS 云主机:一台 EC2 实例运行jcode serveWebSocket 网关,手机(iOS 应用或 SSH 客户端)无需笔记本中转即可驱动 jcode 会话。部署位于 AWS 账号302154194530、区域us-east-1,实例i-08214cf66cd3f80c7,弹性 IP54.196.207.97(见 部署说明)。

该部署的 IAM 评估起始于一次设计评审,评估日期为 2026-07-12。评估文档的核心结论是:部署用户jade-deploy不应保留AdministratorAccess——一条被泄露的长期部署凭据,当前可以修改身份、关闭成本护栏、外泄数据、创建任意基础设施,并在账号任何位置建立持久化。

文档同时说明了一个务实的现状:实际加固(详见 README 的 "Live deployment" 章节)已经落地并验证,唯一有意保留的缺口是jade-deploy上的AdministratorAccess,它在一条独立的 root/MFA 恢复登录路径被验证之前不会被移除。

目标架构是用「只能 assume-role 的主体 + 三个独立权限平面」替换现状:

  1. JcodePhoneOperator:日常部署与现有栈的维护;
  2. JcodePhoneProvisioner:低频的重建操作,受 MFA 门禁、限时(最长会话 1 小时)、限定us-east-1jcode-phone-*资源、并受运行时边界约束;
  3. 独立的紧急管理员路径:受 MFA 保护、绝不被自动化使用,用于在移除现有管理员挂载时避免锁死。

另外,实例角色上的AmazonBedrockFullAccess也应替换为仅推理权限——从源码看,Bedrock provider 只做模型目录发现和流式对话(ConverseStream),并不需要 Bedrock 管理权限。

证据链:策略必须匹配真实的 API 调用

最小权限策略的前提是精确知道每个组件实际调用了哪些 AWS API。评估文档给出的仓库证据如下,均可在当前仓库中逐一验证:

组件实际调用的 AWS API源码依据
Wake Lambdaec2:DescribeInstancesec2:StartInstanceswake-lambda.py 中ec2.describe_instances(...)ec2.start_instances(InstanceIds=[INSTANCE_ID])
Wake Lambda(SSM 配对路径)ssm:DescribeInstanceInformationssm:SendCommandAWS-RunShellScript文档)、ssm:GetCommandInvocationwake-lambda.py 的ssm_online()fetch_pair_code()
Breaker Lambdaec2:DescribeInstancesec2:StopInstancessns:Publisharn:aws:sns:us-east-1:302154194530:jcode-guard-warnbreaker-lambda.py
EC2 实例上的 Bedrock providerListFoundationModelsListInferenceProfiles、流式 Converse(由bedrock:InvokeModelWithResponseStream授权)jcode-provider-bedrock/src/lib.rs 的目录刷新逻辑与 流式调用

Wake Lambda 的 SSM 配对流程值得细看,因为它决定了权限要放大到什么程度:fetch_pair_code()通过ssm.send_command(InstanceIds=[INSTANCE_ID], DocumentName="AWS-RunShellScript", ...)在目标实例上以ec2-user身份执行jcode pair,随后轮询ssm.get_command_invocation直到命令进入终态,再从输出中用正则提取 6 位配对码并生成jcode://pair?...深链。评估文档因此明确提示:ssm:SendCommand应被视为「特权远程代码执行」,必须严格限定到该唯一实例和 AWS 托管文档。

Bedrock 侧还有一个细节:provider 中由环境变量JCODE_BEDROCK_VALIDATE_STS控制的 STS 身份校验(validate_credentials_if_requested)默认关闭,正常路径不需要额外服务权限——这正是评估文档中「provider 的可选 STS 身份校验不需要额外服务权限」这一说法的源码依据。同时,当 AWS 拒绝请求时,provider 会直接提示需要bedrock:InvokeModelbedrock:InvokeModelWithResponseStreambedrock:ListFoundationModelsbedrock:ListInferenceProfiles四类权限(错误提示),与本文给出的策略语句一一对应。

声明的命名资源清单

类型资源
EC2 实例arn:aws:ec2:us-east-1:302154194530:instance/i-08214cf66cd3f80c7
弹性 IP54.196.207.97(分配 ID 需另行盘点)
Lambdajcode-phone-wake
Lambdajcode-guard-breaker
API Gateway v28c3wp4cbag
SNSjcode-guard-stopjcode-guard-warn
CloudWatch 告警jcode-bedrock-tokens-warnjcode-bedrock-tokens-stop(无效的账单告警已被 Budget/SNS 熔断路径替代)
Budgetjcode-dev-monthly-cost
Bedrock 模型路由us.anthropic.claude-opus-4-6-v1

实态核查结果

评估完成后,账号已通过jcode-bedrockprofile 做了全面盘点:运行时角色名、Lambda 角色、EC2 实例与安全组、弹性 IP、Budget 订阅者、CloudWatch 告警、日志保留、API Gateway stage、S3/DynamoDB 资源、访问密钥元数据均直接验证过。当前实态为:Wake Lambda 已改用 SSM 配对,所有公网入站 EC2 端口已关闭,根 EBS 卷已加密,CloudTrail 与 Access Analyzer 已启用,旧的部署密钥处于停用状态。这与 README 安全说明 中「实例角色为推理权限 + SSM 受管实例访问」「7644 端口不再公开、legacy pair 服务已停用」的描述一致。

目标身份设计:三平面与 assume-only 主体

人类访问

首选终态:

  • 人类操作者使用 IAM Identity Center 或其他联合身份;
  • operator 与 provisioner 两个角色的 assume 都要求 MFA;
  • 不为人类访问创建长期密钥;
  • jade-deploy仅在迁移期间保留,观察期结束后删除。

如果过渡期必须保留 IAM 用户,则:

  • jade-deploy无控制台密码、无任何服务权限;
  • 唯一权限是对两个部署角色的sts:AssumeRole
  • 它不能修改自己、策略、访问密钥、MFA 设备或角色信任策略。

jade-deploy的 assume-only 策略:

{ "Version": "2012-10-17", "Statement": [ { "Sid": "AssumePhoneServerRolesOnly", "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": [ "arn:aws:iam::302154194530:role/jcode-phone/JcodePhoneOperator", "arn:aws:iam::302154194530:role/jcode-phone/JcodePhoneProvisioner" ] } ] }

两个角色的信任策略都应要求 MFA(对人类 IAM 主体):

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::302154194530:user/jade-deploy" }, "Action": "sts:AssumeRole", "Condition": { "Bool": { "aws:MultiFactorAuthPresent": "true" }, "NumericLessThan": { "aws:MultiFactorAuthAge": "3600" } } } ] }

JcodePhoneProvisioner的最大会话时长设为 1 小时;两个角色都不允许修改自己的信任策略或权限。

对无人值守 CI,使用独立的 OIDC 联合角色,限定到具体仓库、分支/环境与 workflow;不要为了 CI 而放宽人类角色的 MFA 信任。

运行时策略:实例与每个 Lambda 各用独立执行角色

总原则:实例与每个 Lambda 使用各自的执行角色,运行时绝不复用部署角色。

EC2 实例:仅推理权限

用以下客户管理策略替换AmazonBedrockFullAccess。注意两点:List*操作必须Resource: "*";推理资源刻意只覆盖已配置的 Claude Opus 4.6 跨区 profile 及其底层 foundation model——因为跨区推理可能路由到us-east-1之外,只写单区域的 foundation-model ARN 会失效。

{ "Version": "2012-10-17", "Statement": [ { "Sid": "DiscoverBedrockModels", "Effect": "Allow", "Action": [ "bedrock:ListFoundationModels", "bedrock:ListInferenceProfiles" ], "Resource": "*" }, { "Sid": "InvokeConfiguredClaudeProfile", "Effect": "Allow", "Action": [ "bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream" ], "Resource": [ "arn:aws:bedrock:us-east-1:302154194530:inference-profile/us.anthropic.claude-opus-4-6-v1", "arn:aws:bedrock:*::foundation-model/anthropic.claude-opus-4-6-v1:0" ] } ] }

应用前用bedrock list-inference-profiles核实确切的 inference-profile ARN 与底层模型 ID。如果部署的是 AWS 所有的 profile、或底层模型版本后缀不同,应替换为返回的 ARN,而不是放宽模型名通配。区域通配符是刻意为之,因为 US 跨区推理 profile 可路由到多个 US 区域。测试完成后,若所有生产路径都只用ConverseStream,可移除bedrock:InvokeModel——保留它只是很小的兼容性余地,不是管理权限。provider 的可选 STS 身份校验在正常操作中不需要额外服务权限(见上文源码分析)。

这与源码行为吻合:refresh_catalog() 先调list_foundation_models()收集模型 ID、推断 ON_DEMAND / INFERENCE_PROFILE 支持类型、标记 LEGACY 模型,再调list_inference_profiles()收集 profile 与优选路由;流式推理 走converse_stream()。目录刷新即/refresh-model-list的底层实现,缺少bedrock:ListInferenceProfiles时源码会明确报错要求授权(错误提示)。

Wake Lambda

{ "Version": "2012-10-17", "Statement": [ { "Sid": "ReadTargetInstanceState", "Effect": "Allow", "Action": "ec2:DescribeInstances", "Resource": "*" }, { "Sid": "StartTargetInstanceOnly", "Effect": "Allow", "Action": "ec2:StartInstances", "Resource": "arn:aws:ec2:us-east-1:302154194530:instance/i-08214cf66cd3f80c7" }, { "Sid": "WriteOwnLogs", "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:us-east-1:302154194530:log-group:/aws/lambda/jcode-phone-wake:*" } ] }

日志组应在开通期创建并配置保留策略,而不是在运行时授予*上的logs:CreateLogGroup

当前部署已采用 SSM 唤醒/配对实现(legacy 的公网:7644路径已禁用),因此还要追加 SSM 语句:

{ "Version": "2012-10-17", "Statement": [ { "Sid": "ReadManagedInstanceStatus", "Effect": "Allow", "Action": "ssm:DescribeInstanceInformation", "Resource": "*" }, { "Sid": "RunPairCommandOnTargetOnly", "Effect": "Allow", "Action": "ssm:SendCommand", "Resource": [ "arn:aws:ec2:us-east-1:302154194530:instance/i-08214cf66cd3f80c7", "arn:aws:ssm:us-east-1::document/AWS-RunShellScript" ] }, { "Sid": "ReadPairCommandResult", "Effect": "Allow", "Action": "ssm:GetCommandInvocation", "Resource": "*" } ] }

该变体同时要求 EC2 角色上有 SSM 受管实例策略。建议它与 Bedrock 策略分开维护,在 CloudTrail 中核对确切的 Systems Manager 调用,并在操作可行时从AmazonSSMManagedInstanceCore收窄。始终记住:ssm:SendCommand是特权远程代码执行,范围必须锁定到那一台实例和 AWS 托管文档。wake-lambda.py 中ssm_online()fetch_pair_code()的调用正是这三条语句对应的最小集合。

Breaker Lambda

{ "Version": "2012-10-17", "Statement": [ { "Sid": "ReadTargetInstanceState", "Effect": "Allow", "Action": "ec2:DescribeInstances", "Resource": "*" }, { "Sid": "StopTargetInstanceOnly", "Effect": "Allow", "Action": "ec2:StopInstances", "Resource": "arn:aws:ec2:us-east-1:302154194530:instance/i-08214cf66cd3f80c7" }, { "Sid": "PublishGuardNotification", "Effect": "Allow", "Action": "sns:Publish", "Resource": "arn:aws:sns:us-east-1:302154194530:jcode-guard-warn" }, { "Sid": "WriteOwnLogs", "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:us-east-1:302154194530:log-group:/aws/lambda/jcode-guard-breaker:*" } ] }

对应 breaker-lambda.py 的逻辑:检查实例状态 → 若在运行则停止 → 向jcode-guard-warn主题发布「jcode circuit breaker fired」消息。该 Lambda 由 SNSjcode-guard-stop订阅触发(预算超支 100% 时由 AWS Budget 通知)。

日常操作角色:JcodePhoneOperator

这个角色能更新和诊断现有资源,但不能创建身份、创建任意计算、终止实例、释放 EIP、删除护栏或修改角色信任。以下策略中的<...>占位值应在只读盘点后替换。注意 API Gateway 资源形式刻意使用不含账号 ID 的管理 ARN。

{ "Version": "2012-10-17", "Statement": [ { "Sid": "DescribePhoneServerInfrastructure", "Effect": "Allow", "Action": [ "ec2:DescribeInstances", "ec2:DescribeInstanceStatus", "ec2:DescribeAddresses", "ec2:DescribeSecurityGroups", "ec2:DescribeVolumes", "cloudwatch:DescribeAlarms", "cloudwatch:GetMetricData", "cloudwatch:GetMetricStatistics", "cloudwatch:ListMetrics" ], "Resource": "*" }, { "Sid": "ReadExistingFunctions", "Effect": "Allow", "Action": [ "lambda:GetFunction", "lambda:GetFunctionConfiguration", "lambda:GetPolicy" ], "Resource": [ "arn:aws:lambda:us-east-1:302154194530:function:jcode-phone-wake", "arn:aws:lambda:us-east-1:302154194530:function:jcode-phone-wake:*", "arn:aws:lambda:us-east-1:302154194530:function:jcode-guard-breaker", "arn:aws:lambda:us-east-1:302154194530:function:jcode-guard-breaker:*" ] }, { "Sid": "OperateExistingInstance", "Effect": "Allow", "Action": [ "ec2:StartInstances", "ec2:StopInstances", "ec2:RebootInstances", "ec2:ModifyInstanceAttribute" ], "Resource": "arn:aws:ec2:us-east-1:302154194530:instance/i-08214cf66cd3f80c7" }, { "Sid": "DeployExistingFunctions", "Effect": "Allow", "Action": [ "lambda:UpdateFunctionCode", "lambda:UpdateFunctionConfiguration", "lambda:PublishVersion", "lambda:CreateAlias", "lambda:UpdateAlias", "lambda:DeleteAlias", "lambda:InvokeFunction" ], "Resource": [ "arn:aws:lambda:us-east-1:302154194530:function:jcode-phone-wake", "arn:aws:lambda:us-east-1:302154194530:function:jcode-phone-wake:*", "arn:aws:lambda:us-east-1:302154194530:function:jcode-guard-breaker", "arn:aws:lambda:us-east-1:302154194530:function:jcode-guard-breaker:*" ] }, { "Sid": "ReadAndPatchExistingHttpApiRoot", "Effect": "Allow", "Action": [ "apigateway:GET", "apigateway:PATCH" ], "Resource": "arn:aws:apigateway:us-east-1::/apis/8c3wp4cbag" }, { "Sid": "MaintainExistingHttpApiChildren", "Effect": "Allow", "Action": [ "apigateway:GET", "apigateway:POST", "apigateway:PUT", "apigateway:PATCH", "apigateway:DELETE" ], "Resource": "arn:aws:apigateway:us-east-1::/apis/8c3wp4cbag/*" }, { "Sid": "PassInstanceRoleToEc2Only", "Effect": "Allow", "Action": "iam:PassRole", "Resource": "arn:aws:iam::302154194530:role/jcode-phone/runtime/JcodePhoneInstance", "Condition": { "StringEquals": { "iam:PassedToService": "ec2.amazonaws.com" } } }, { "Sid": "PassLambdaRolesToLambdaOnly", "Effect": "Allow", "Action": "iam:PassRole", "Resource": [ "arn:aws:iam::302154194530:role/jcode-phone/runtime/JcodePhoneWakeLambda", "arn:aws:iam::302154194530:role/jcode-phone/runtime/JcodePhoneBreakerLambda" ], "Condition": { "StringEquals": { "iam:PassedToService": "lambda.amazonaws.com" } } }, { "Sid": "ReadPhoneLogs", "Effect": "Allow", "Action": [ "logs:DescribeLogStreams", "logs:GetLogEvents", "logs:FilterLogEvents" ], "Resource": [ "arn:aws:logs:us-east-1:302154194530:log-group:/aws/lambda/jcode-phone-wake:*", "arn:aws:logs:us-east-1:302154194530:log-group:/aws/lambda/jcode-guard-breaker:*" ] }, { "Sid": "MaintainGuardTopics", "Effect": "Allow", "Action": [ "sns:GetTopicAttributes", "sns:ListSubscriptionsByTopic", "sns:Publish" ], "Resource": [ "arn:aws:sns:us-east-1:302154194530:jcode-guard-stop", "arn:aws:sns:us-east-1:302154194530:jcode-guard-warn" ] }, { "Sid": "ReadNamedBudget", "Effect": "Allow", "Action": "budgets:ViewBudget", "Resource": "arn:aws:budgets::302154194530:budget/jcode-dev-monthly-cost" } ] }

推荐的进一步收紧:

  • 若日常维护从不改动关机行为、实例类型、source/destination check 或附加的 profile,移除ec2:ModifyInstanceAttribute
  • lambda:AddPermissionlambda:RemovePermissionsns:Subscribe、告警变更、预算变更留在 MFA 门禁的 provisioner 路径——这些操作可能导致调用暴露、通知外泄或成本控制被禁用;
  • API Gateway 的DELETE只允许在/apis/8c3wp4cbag/*之下,日常角色不能删除 API 根;
  • 绝不授予cloudwatch:DeleteAlarmsbudgets:DeleteBudgetsns:DeleteTopicec2:TerminateInstancesec2:ReleaseAddress或任何 IAM 写操作。

重建/供应角色:用 CloudFormation 执行角色收敛建删权限

重建天然需要多个服务的创建/删除权限,这些权限必须远离日常角色。最易维护的做法是把栈放进 CloudFormation、CDK 或 Terraform,让人类只调用一个命名的栈部署角色。

推荐模型:

  • 人类JcodePhoneProvisioner:仅jcode-phone-server*栈的 CloudFormation 操作、只读诊断,以及仅对JcodePhoneCloudFormationExecution、条件iam:PassedToService = cloudformation.amazonaws.comiam:PassRole
  • JcodePhoneCloudFormationExecution:承载下述服务权限,只能被 CloudFormation 使用;
  • 所有创建的资源打标签Project=jcode-phone-serverManagedBy=cloudformation
  • 所有创建的运行时角色位于路径/jcode-phone/runtime/之下,且必须携带JcodePhoneRuntimeBoundary权限边界。

人类 provisioner 策略:

{ "Version": "2012-10-17", "Statement": [ { "Sid": "ManageNamedPhoneStack", "Effect": "Allow", "Action": [ "cloudformation:CreateStack", "cloudformation:UpdateStack", "cloudformation:DeleteStack", "cloudformation:DescribeStacks", "cloudformation:DescribeStackEvents", "cloudformation:DescribeStackResources", "cloudformation:GetTemplate", "cloudformation:GetTemplateSummary", "cloudformation:ListStackResources", "cloudformation:CreateChangeSet", "cloudformation:DescribeChangeSet", "cloudformation:ExecuteChangeSet", "cloudformation:DeleteChangeSet", "cloudformation:ValidateTemplate" ], "Resource": [ "arn:aws:cloudformation:us-east-1:302154194530:stack/jcode-phone-server*/*", "arn:aws:cloudformation:us-east-1:302154194530:changeSet/jcode-phone-server*/*" ] }, { "Sid": "ValidateNewTemplate", "Effect": "Allow", "Action": "cloudformation:ValidateTemplate", "Resource": "*" }, { "Sid": "PassPhoneCloudFormationExecutionRole", "Effect": "Allow", "Action": "iam:PassRole", "Resource": "arn:aws:iam::302154194530:role/jcode-phone/JcodePhoneCloudFormationExecution", "Condition": { "StringEquals": { "iam:PassedToService": "cloudformation.amazonaws.com" } } } ] }

CloudFormation 执行角色应只允许如下服务/操作包络:

服务必需的重建操作范围/护栏
EC2启动/终止那台服务器;创建/打标签卷与 ENI;创建/管理一个安全组;分配/关联/释放一个 EIP;修改关机行为;描述 AMI/子网/VPCus-east-1;请求/资源标签Project=jcode-phone-server;仅批准的实例类型;强制 IMDSv2;批准的 VPC/子网
IAM创建/更新/删除三个运行时角色与一个实例 profile;put/delete 内联策略;pass role仅限路径/jcode-phone/runtime/CreateRole要求边界 ARN;绝不允许修改 deploy/provisioner/emergency 角色
Lambda创建/更新/删除两个命名函数;版本/别名;权限函数 ARN 前缀jcode-phone-*jcode-guard-breaker*;项目标签
API Gateway v2创建/更新/删除一个 HTTP API、integration、route、stage项目标签与栈所有权
SNS创建/管理/删除jcode-guard-stopjcode-guard-warn;订阅精确的主题名 ARN
CloudWatch创建/更新/删除四个命名告警精确的告警名 ARN
Logs创建/配置/删除两个 Lambda 日志组精确的/aws/lambda/...ARN;必须设置保留
Budgets创建/更新/删除jcode-dev-monthly-cost精确的预算 ARN

lambda:AddPermission/RemovePermissionsns:Subscribe/Unsubscribe、CloudWatch 告警变更与预算变更由执行角色(而非日常操作角色)持有,从而保住集成与护栏维护能力,同时不把这些高影响操作放进日常凭据。

CloudFormation 执行角色不应持有通用的iam:*organizations:*account:*sts:AssumeRolekms:*、不受限的s3:*、不受限的secretsmanager:*,也不能修改 provisioner、operator、emergency 角色、自身角色或自身边界。

权限边界不是授权,而是上限。用它作为运行时角色的最大值,并配合各自的窄内联策略。边界只应允许:

  • 实例的 Bedrock 目录发现与已配置模型推理权限;
  • Wake Lambda 的启动/描述与自身日志权限;
  • Breaker Lambda 的停止/描述、告警发布与自身日志权限。

由于各角色权限不同,边界可以取它们的并集;每个角色的内联策略必须保持为更窄的子集。边界中还应显式拒绝 IAM、STS assume-role、Organizations、账号管理和访问密钥操作,作为纵深防御。

迁移前必须完成的只读盘点

以下命令在现有管理员会话重新认证后运行,全部只读、不做任何变更:

aws sts get-caller-identity aws iam list-attached-user-policies --user-name jade-deploy aws iam list-user-policies --user-name jade-deploy aws iam list-access-keys --user-name jade-deploy aws iam get-account-authorization-details > phone-server-iam-before.json aws ec2 describe-instances --instance-ids i-08214cf66cd3f80c7 --region us-east-1 aws ec2 describe-addresses --public-ips 54.196.207.97 --region us-east-1 aws lambda get-function --function-name jcode-phone-wake --region us-east-1 aws lambda get-function --function-name jcode-guard-breaker --region us-east-1 aws apigatewayv2 get-api --api-id 8c3wp4cbag --region us-east-1 aws sns list-topics --region us-east-1 aws cloudwatch describe-alarms --alarm-name-prefix jcode- --region us-east-1 aws budgets describe-budget --account-id 302154194530 --budget-name jcode-dev-monthly-cost aws bedrock list-inference-profiles --type-equals SYSTEM_DEFINED --region us-east-1

另需盘点至少 30 天的 CloudTrail 使用情况;只有当某个 action 对应已知的部署/维护操作时才加入策略。Access Analyzer 基于 CloudTrail 的策略生成可以作为第二意见,但不能替代人工审查,因为很少用到的恢复操作可能根本不出现在日志里。

防锁死迁移与凭据轮换:15 步操作序列

  1. 先备好恢复路径。验证账号 root 邮箱、root MFA 与恢复联系人。建立或验证一条独立的、MFA 保护的紧急管理员路径(首选 IAM Identity Center)。它必须独立于jade-deploy、无访问密钥、且在触碰AdministratorAccess之前完成测试。
  2. 捕获状态。导出 IAM 授权明细、用户策略挂载、角色信任策略、访问密钥元数据、资源 ARN 与标签;记录正被替换的AdministratorAccess挂载的精确位置。
  3. 创建运行时策略与角色。创建窄化的实例、wake、breaker 角色。此时不切换工作负载;用 IAM Access Analyzer 或aws accessanalyzer validate-policy校验策略文档。
  4. 创建 operator 与 provisioner 角色。加上 MFA 门禁信任;确保两个角色都不能修改自己、紧急路径、权限边界或任意身份。
  5. 管理员挂载仍在时授予 assume-only 访问。把小的sts:AssumeRole策略挂到jade-deploy,但暂时保留AdministratorAccess
  6. 测试角色入口。用独立 profile/会话逐个 assume 各角色,确认aws sts get-caller-identity报告的是角色 ARN;确认 operator 对 EC2、Lambda、API Gateway、日志、SNS、告警与预算的读权限。
  7. 模拟每一个必需的 API 调用。iam:SimulatePrincipalPolicy或策略模拟器跑本文的动作/资源矩阵;明确测试应被拒绝的控制项——如创建用户、挂载AdministratorAccess、终止任意实例、删除护栏。
  8. 用金丝雀变更路径测试,不碰生产。通过 provisioner 部署再删除一个带标签的小型金丝雀栈(尽量使用相同的资源类别)。对 operator,更新一个可丢弃的 Lambda 别名或金丝雀函数,而不是调用jcode-guard-breaker——它会停掉生产实例,绝不能把它当 IAM 测试。
  9. 逐个切换运行时角色。先换 Lambda 执行角色,验证日志与无副作用的 wake 状态检查;再替换 EC2 实例 profile,跑一次真实的 Bedrock 流式请求。旧角色保持完整但先不挂载,直到验证完成。
  10. 轮换凭据载体。首选:工作站切到 IAM Identity Center 与角色 profile。过渡 IAM 用户方案:创建第二把jade-deploy访问密钥,在新 profile 下配置并验证角色 assume。绝不先覆盖唯一可用的 profile——IAM 用户最多两把密钥。
  11. 仅在独立恢复路径成功后才解绑AdministratorAccess解绑前立刻在独立浏览器/profile 中确认紧急管理员路径;然后从jade-deploy解绑AdministratorAccess。同一变更窗口内不删除用户、密钥、旧运行时角色或旧策略。
  12. 跑解绑后检查。重新 assume operator 与 provisioner,重跑所有只读检查与安全部署检查,验证 phone-server 健康端点,在约定的维护窗口内验证 wake 行为、配对流程与一次完整的 Bedrock 流式请求。
  13. 停用旧密钥但先不删除。新访问路径稳定工作至少 24–72 小时后把旧密钥标记为 inactive;监控 CloudTrail 中使用该 access-key ID 的尝试与预期工作流上的AccessDenied
  14. 观察后再删除。再观察 7–14 天、确认无需回滚后,删除 inactive 密钥、清理废弃策略/角色,并在联合身份稳定后彻底删除jade-deploy
  15. 每季度复审。使用访问密钥最近使用数据、CloudTrail、Access Analyzer、凭据报告、告警/预算测试;移除未使用的 action;演练紧急路径但不把它用于日常工作。

回滚规则

如果移除管理员后某个必需操作失败,停下来,用独立的紧急管理员路径修正那个窄化角色——不要把AdministratorAccess重新挂回日常用户当默认修复手段。也绝不把已有的 STS 会话当作唯一回滚机制:策略变更与会话过期都可能使该假设失效。

与 IAM 相邻的其他安全发现

这些不是 IAM 替换所必需的,但对部署有实质影响:

  • Wake 密钥原本内嵌在 Lambda 源码里并出现在查询字符串中。查询串 token 可能出现在浏览器历史、日志、分析工具、截图与 referrer 里。应存入 Secrets Manager 或 SSM Parameter Store,改用 header 或短时签名请求比对,并只授予 wake Lambda 对那一个密钥的读权限。仓库中的 wake-lambda.py 已体现改进方向:token 走 URL fragment(浏览器不会把它发给 API Gateway),JavaScript 把它换成Authorization: Bearerheader 存于 session storage,hmac.compare_digest恒定时间比对,且 legacy?t=链接一次性 302 重定向到 fragment 形式。
  • 端口7644曾公开暴露且 Lambda 以明文 HTTP 调用。应优先使用私有 VPC 路径、SSM 中介配对或仅 tailnet 访问;若必须保留公网,应限制安全组源并加 TLS。当前部署已通过 SSM Run Command 生成配对码,该端口不再公开(legacy 的 jcode-pair.service 已停用)。
  • 配对服务曾以 root 运行并调用sudo -u ec2-user;systemd unit 应改为专用用户,加文件系统保护、NoNewPrivileges与窄范围的 sudo 规则。
  • 即使修好了jade-deploy,实例角色当前的AmazonBedrockFullAccess也偏宽。
  • AttachUserPolicyPutUserPolicyCreateAccessKeyUpdateAssumeRolePolicyPassRole以及对 emergency/部署角色的任何变更添加 CloudTrail 告警。

验收标准

迁移完成的判据是:

  • jade-deploy没有AdministratorAccess,也没有除角色 assume 之外的直接 AWS 服务权限;
  • 日常部署与维护通过JcodePhoneOperator成功完成;
  • 带标签的完整重建可以通过 MFA 门禁的 provisioner/CloudFormation 路径执行;
  • 运行时角色只包含上文记录的 API 调用;
  • 独立的紧急管理员路径经过测试;
  • 旧访问密钥经历「停用 → 观察 → 删除」;
  • CloudTrail 中没有必需操作的意外AccessDenied,观察期内也没有旧密钥的使用记录。

小结

这份评估文档的价值在于把「最小权限」落成了可执行的工程流程:从源码级 API 证据出发写策略(ec2:StartInstances只给一台实例、ssm:SendCommand只给一台实例加一个 AWS 文档、Bedrock 推理只给一个 profile 加通配区域的 foundation model ARN),用三平面角色把日常、重建、应急三种风险隔离开,再用 15 步防锁死序列完成AdministratorAccess的移除与凭据轮换。整个方案的所有资源 ARN、Lambda 代码与 Bedrock provider 行为都能在 phone-server 目录 与 jcode-provider-bedrock 源码 中逐一对证,适合作为「长期部署凭据降权」的完整参考实现。

【免费下载链接】jcodeThe most RAM efficient harness项目地址: https://gitcode.com/GitHub_Trending/jcod/jcode

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询