
Mình đã giải thích cho BrSE và dev cách chuyển từ hardcoded Access Key đến IAM Role
Bối cảnh:
Không ít hơn 3 lần mình gặp tình huống phải đi giải thích cho Khách hàng / BrSE / Team dev cách xác thực khi application chạy trên AWS access đến các resource như S3, DynamoDB v.v
Lần 1: Khi mình build lại infra cho 1 dự án java thì phát hiện vendor bên thứ 3 (trước đó đã build infra và code app) sử dụng access key trong source code. Cảm thấy khó khá hiểu nhưng không biết được nguyên nhân là gì? họ không hiểu AWS ư? cũng không đúng họ đã build hạ tầng chạy EC2 trên AWS mà, dự án cả 10k$/tháng chứ chả ít.
Dù không biết nguyên nhân là gì nhưng thời điểm mình migration EC2 sang ECS thì gặp vấn đề này và đương nhiên là dùng ECS Task role, có điều là cần đội dev của khách update lại code java và cũng cần giải thích cho khách hiểu vì sao phải làm như thế (Government Cloud không cho phép hardcoded như vậy)
Khách và đội dev của họ không hiểu AWS nhiều nên mình đã giải thích khá kỹ càng.
Lần 2: lần này là 1 bạn dev trong công ty cũng là học viên của mình hỏi, giải thích tý là hiểu ngay
Lần 3: chính là hôm nay
Một bạn BrSE đã bối rối khi khách hàng yêu cầu update từ sử dụng Access key sang sử dụng role. Mình đã call giải thích và bạn truyền đạt lại cho dev offshore, tuy nhiên dev cũng không hiểu luôn và kết quả là hỏi lại khách khiến khách khá bực mình.
Chuyện bình thường hàng ngày thôi, đây cũng chỉ là một case thường gặp khi BrSE làm việc với khách hàng. Để giúp các bạn ấy hiểu 1 chút về vấn đề này mình đã viết blog này.
AWS Access Key và Secret Key được hardcode trực tiếp trong source code.
// ❌ Những gì mình tìm thấy trong source code
AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(
new BasicAWSCredentials("AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY")
))
.build();
Đây là một trong những lỗi bảo mật phổ biến nhất nhưng cũng nguy hiểm nhất khi làm việc với AWS. Access key bị lộ — dù chỉ một khoảnh khắc trên Git history — có thể dẫn đến thiệt hại tài chính lớn và vi phạm dữ liệu nghiêm trọng.
| Tiêu chí | Hardcoded Access Key | IAM Role (Khuyến nghị) |
|---|---|---|
| Lưu trữ credentials | Trong source code / file | Không cần lưu trữ |
| Thời hạn | Vĩnh viễn đến khi bị thu hồi | Tạm thời, tự động renew |
| Rotate | Thủ công, cần deploy lại | Tự động |
| Audit | Khó truy vết | CloudTrail ghi đầy đủ |
| Rủi ro bị lộ | Rất cao | Rất thấp |
Understand the Credential Provider Chain
Trước khi đi vào giải pháp cụ thể, cần hiểu một khái niệm nền tảng: Credential Provider Chain.
Tất cả AWS SDK đều có một chuỗi các nguồn (sources) mà chúng lần lượt kiểm tra để tìm credentials hợp lệ. Quá trình tìm kiếm có hệ thống này gọi là credential provider chain. Ngay khi tìm thấy credentials hợp lệ ở một nguồn, SDK dừng tìm kiếm và dùng luôn nguồn đó.
Điểm quan trọng nhất: khi dùng bất kỳ standardized credential provider nào trong chain, AWS SDK tự động renew credentials khi hết hạn mà không cần thêm bất kỳ dòng code nào. Đây chính là lý do cốt lõi tại sao nên dùng chain thay vì tự quản lý key thủ công.
Các Standardized Credential Provider
Mặc dù mỗi SDK có chain riêng, hầu hết đều bao gồm các nguồn sau:
| Credential Provider | Mô tả |
|---|---|
| AWS Access Keys | Access Key ID + Secret Access Key của IAM user (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) |
| Web Identity / OpenID Connect | Đăng nhập qua IdP bên ngoài (Google, Facebook, Amazon...) → dùng JWT token đổi lấy IAM role tạm thời qua AWS STS |
| Login credentials provider | Credentials từ phiên đăng nhập console hiện tại |
| IAM Identity Center | Credentials từ AWS IAM Identity Center (SSO tập trung cho tổ chức) |
| Assume Role | Giả định quyền của một IAM role → nhận credentials tạm thời |
| Container credentials | Dành cho ECS/EKS — tự lấy credentials từ container metadata endpoint |
| Process credential provider | Lấy từ tiến trình/script bên ngoài, bao gồm IAM Roles Anywhere |
| IMDS credential provider | ✅ Dành cho EC2 — lấy từ Instance Metadata Service, gắn với IAM role của instance |
Thứ tự ưu tiên khi cấu hình
Ngoài việc chọn nguồn credentials, mỗi setting còn có thể được cấu hình theo nhiều cách:
Trong code (cao nhất) > Environment Variables > Shared config/credentials file (thấp nhất)
Hiểu thứ tự này rất quan trọng: nếu vô tình để lại một environment variable cũ trên EC2, nó sẽ được ưu tiên hơn IAM Role và có thể gây lỗi khó debug.
SDK-specific Credential Provider Chains
Mỗi SDK và tool của AWS có credential provider chain riêng với thứ tự tìm kiếm khác nhau. AWS cung cấp tài liệu chi tiết cho từng SDK:
- AWS CLI
- SDK for C++
- SDK for Go
- SDK for Java
- SDK for JavaScript
- SDK for Kotlin
- SDK for .NET
- SDK for PHP
- SDK for Python (Boto3)
- SDK for Ruby
- SDK for Rust
- SDK for Swift
- Tools for PowerShell
Vì application của khách hàng viết bằng Java, phần tiếp theo sẽ tập trung vào SDK for Java 2.x.
Default Credentials Provider Chain trong AWS SDK for Java 2.x
DefaultCredentialsProvider là class triển khai credential provider chain cho Java. Khi bạn tạo service client mà không chỉ định credentials provider, SDK tự động dùng DefaultCredentialsProvider.
// SDK dùng DefaultCredentialsProvider vì không chỉ định credentials
DynamoDbClient ddb = DynamoDbClient.builder()
.region(Region.AP_NORTHEAST_1)
.build();
Thứ tự tìm kiếm trong Java
DefaultCredentialsProvider duyệt qua các nguồn theo thứ tự sau:
| Thứ tự | Nguồn | Class xử lý |
|---|---|---|
| 1 | Java System Properties | SystemPropertyCredentialsProvider — đọc aws.accessKeyId, aws.secretAccessKey, aws.sessionToken |
| 2 | Environment Variables | EnvironmentVariableCredentialsProvider — đọc AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN |
| 3 | Web Identity Token | WebIdentityTokenFileCredentialsProvider — dùng token file, gọi STS lấy credentials tạm thời. EKS tự inject token này |
| 4 | Shared credentials/config files | ProfileCredentialsProvider — đọc profile [default] trong ~/.aws/credentials hoặc ~/.aws/config |
| 5 | ECS Container credentials | ContainerCredentialsProvider — đọc từ ECS metadata endpoint |
| 6 | EC2 Instance IAM Role | InstanceProfileCredentialsProvider |
Nếu cả 6 bước đều thất bại, SDK ném exception:
SdkClientException: Unable to load credentials from any of the providers in the chain
AwsCredentialsProviderChain(credentialsProviders=[SystemPropertyCredentialsProvider(),
EnvironmentVariableCredentialsProvider(), WebIdentityTokenCredentialsProvider(),
ProfileCredentialsProvider(), ContainerCredentialsProvider(), InstanceProfileCredentialsProvider()])
Dùng DefaultCredentialsProvider tường minh trong code
Bạn có thể khai báo tường minh để code dễ đọc hơn — kết quả hoàn toàn giống nhau:
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
DefaultCredentialsProvider provider = DefaultCredentialsProvider.builder().build();
S3Client s3 = S3Client.builder()
.region(Region.AP_NORTHEAST_1)
.credentialsProvider(provider)
.build();
Hoặc tùy chỉnh thêm khi build:
DefaultCredentialsProvider customProvider = DefaultCredentialsProvider.builder()
.profileName("custom-profile") // Dùng profile cụ thể thay vì [default]
.asyncCredentialUpdateEnabled(true) // Tự động renew credentials bất đồng bộ
.build();
Read IAM Role Credentials trên Amazon EC2 với SDK for Java 2.x
Cơ chế hoạt động
Để gắn IAM Role vào EC2, bạn tạo một Instance Profile chứa role đó và attach vào instance. Tất cả application chạy trên instance sẽ tự động nhận được temporary credentials từ IMDS (Instance Metadata Service) — đây chính là bước số 6 trong chain Java ở trên.
EC2 Instance (gắn Instance Profile chứa IAM Role)
└── App gọi AWS API
└── DefaultCredentialsProvider duyệt qua chain
└── Bước 6: InstanceProfileCredentialsProvider gọi IMDS
└── Nhận Temporary Credentials
└── Tự động renew trước khi hết hạn
Lấy credentials tự động (khuyến nghị)
Chỉ cần không chỉ định credentials provider — SDK tự dùng InstanceProfileCredentialsProvider ở bước cuối chain:
// ✅ Không chỉ định credentials → chain tự tìm đến IMDS ở bước cuối
S3Client s3 = S3Client.builder()
.region(Region.AP_NORTHEAST_1)
.build();
Lấy credentials tường minh (bỏ qua các bước trên chain)
Nếu muốn chỉ định thẳng, bỏ qua toàn bộ chain:
// ✅ Chỉ định thẳng InstanceProfileCredentialsProvider
S3Client s3 = S3Client.builder()
.region(Region.AP_NORTHEAST_1)
.credentialsProvider(InstanceProfileCredentialsProvider.create())
.build();
Tăng cường bảo mật: Chỉ dùng IMDSv2, tắt IMDSv1
EC2 hỗ trợ hai phiên bản IMDS. Mặc định, Java SDK thử IMDSv2 trước, nếu thất bại sẽ fallback sang IMDSv1. Vì IMDSv1 kém an toàn hơn (dễ bị tấn công SSRF), AWS khuyến nghị tắt hoàn toàn IMDSv1 bằng một trong ba cách:
# Cách 1: Environment variable trên EC2 instance
export AWS_EC2_METADATA_V1_DISABLED=true
# Cách 2: JVM system property
-Daws.disableEc2MetadataV1=true
# Cách 3: Shared config file (~/.aws/config)
[default]
ec2_metadata_v1_disabled = true
Với cài đặt này, nếu IMDSv2 thất bại thì SDK sẽ báo lỗi ngay thay vì silently fallback về IMDSv1 kém an toàn hơn.
Hướng dẫn thực hiện cho khách hàng
Bước 1: Tạo IAM Role và gắn vào EC2
Tạo IAM Role với policy tối thiểu cần thiết (Principle of Least Privilege), tạo Instance Profile chứa role đó, và attach vào EC2 instance qua AWS Console, CLI, hoặc Infrastructure as Code.
Bước 2: Cập nhật source code
// ❌ Trước — hardcoded credentials
AmazonS3 s3 = AmazonS3ClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(
new BasicAWSCredentials("AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/...")
))
.build();
// ✅ Sau — để SDK tự dùng DefaultCredentialsProvider chain
S3Client s3 = S3Client.builder()
.region(Region.AP_NORTHEAST_1)
.build();
Bước 3: Tắt IMDSv1
Thêm AWS_EC2_METADATA_V1_DISABLED=true vào environment của EC2 instance để đảm bảo chỉ dùng IMDSv2.
Bước 4: Test done và thu hồi Access Key ngay lập tức
Key đã bị lộ trong source code cần được thu hồi.
Kết quả
Sau khi triển khai IAM Role cho khách hàng Nhật Bản:
- Không còn credentials trong source code — vượt qua security audit
- Tự động rotate credentials — không cần can thiệp thủ công
- CloudTrail audit log đầy đủ — biết chính xác API nào được gọi từ instance nào
- Tuân thủ AWS Well-Architected Framework — trụ cột Security
Nguyên tắc cốt lõi: Không có credentials nào trong code là credentials an toàn nhất.