ECS中Java调用Shell脚本时AWS身份不符,如何改为使用任务角色?
完全可行,问题根源出在你使用了bash -lc启动登录shell,导致环境变量被重置,进而AWS CLI fallback到了EC2实例角色的凭证。
问题原因
ECS任务角色的凭证是通过容器内的专属环境变量(比如AWS_CONTAINER_CREDENTIALS_FULL_URI、AWS_CONTAINER_AUTHORIZATION_TOKEN)让AWS SDK/CLI识别的。当你用-lc参数启动bash时,这会触发登录shell模式——它会加载系统profile配置(如/etc/profile、~/.bash_profile),这个过程会重置大量环境变量,包括AWS的凭证相关变量。失去这些变量后,AWS CLI会自动转向EC2实例的元数据服务获取凭证,这就是你看到测试2用了EC2实例角色的原因。
而通过docker exec进入容器后直接运行脚本时,用的是非登录shell,保留了容器初始化时的完整环境变量,所以能正常调用任务角色。
解决方法
方法1:去掉-lc参数(推荐)
如果不需要加载登录shell的配置,直接调用脚本即可。子进程会继承Spring Boot进程的所有环境变量,包括AWS任务角色的凭证变量。修改Java代码:
// 移除-lc参数,直接执行脚本 Process process = Runtime.getRuntime().exec(new String[]{"/bin/bash", "/test-identity.sh"});
方法2:保留-lc但传递环境变量
如果必须用登录shell(比如需要加载特定profile配置),可以通过ProcessBuilder把当前Java进程的环境变量传递给子shell,避免变量被重置:
ProcessBuilder pb = new ProcessBuilder("/bin/bash", "-lc", "/test-identity.sh"); // 复制当前Java进程的所有环境变量到子进程 pb.environment().putAll(System.getenv()); Process process = pb.start();
也可以精准传递AWS核心变量(减少不必要的变量传递):
ProcessBuilder pb = new ProcessBuilder("/bin/bash", "-lc", "/test-identity.sh"); Map<String, String> env = pb.environment(); // 传递任务角色凭证的关键变量 env.put("AWS_CONTAINER_CREDENTIALS_FULL_URI", System.getenv("AWS_CONTAINER_CREDENTIALS_FULL_URI")); env.put("AWS_CONTAINER_AUTHORIZATION_TOKEN", System.getenv("AWS_CONTAINER_AUTHORIZATION_TOKEN")); // 可选:传递AWS区域等其他必要变量 env.put("AWS_REGION", System.getenv("AWS_REGION")); Process process = pb.start();
方法3:修改脚本强制使用任务角色凭证
如果不想改动Java代码,也可以在/test-identity.sh中手动指定任务角色的凭证来源:
#!/bin/bash # 手动指定任务角色的凭证环境变量 export AWS_CONTAINER_CREDENTIALS_FULL_URI=$(curl -s http://169.254.170.2/v2/credentials/$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI 2>/dev/null | jq -r '.Credentials.AccessKeyId') # 直接调用任务角色凭证服务执行命令 aws sts get-caller-identity --endpoint-url $AWS_CONTAINER_CREDENTIALS_FULL_URI
注:这种方法需要容器内预装jq工具,不如前两种方法直接。
验证
修改后重新部署应用,运行测试2,aws sts get-caller-identity的结果会和Java SDK使用的任务角色完全一致。
内容的提问来源于stack exchange,提问作者meletis

