P
Cloud Blog ProAWS Blog · Cộng đồng VN
Mình đã giải thích cho BrSE và dev cách chuyển từ hardcoded Access Key đến IAM Role

Mình đã giải thích cho BrSE và dev cách chuyển từ hardcoded Access Key đến IAM Role

Phong NguyễnPhong Nguyễn··10 phút đọc·183 lượt xem

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 KeyIAM Role (Khuyến nghị)
Lưu trữ credentialsTrong source code / fileKhông cần lưu trữ
Thời hạnVĩnh viễn đến khi bị thu hồiTạm thời, tự động renew
RotateThủ công, cần deploy lạiTự động
AuditKhó truy vếtCloudTrail ghi đầy đủ
Rủi ro bị lộRất caoRấ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 ProviderMô tả
AWS Access KeysAccess 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 providerCredentials từ phiên đăng nhập console hiện tại
IAM Identity CenterCredentials từ AWS IAM Identity Center (SSO tập trung cho tổ chức)
Assume RoleGiả định quyền của một IAM role → nhận credentials tạm thời
Container credentialsDành cho ECS/EKS — tự lấy credentials từ container metadata endpoint
Process credential providerLấ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:

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ồnClass xử lý
1Java System PropertiesSystemPropertyCredentialsProvider — đọc aws.accessKeyId, aws.secretAccessKey, aws.sessionToken
2Environment VariablesEnvironmentVariableCredentialsProvider — đọc AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN
3Web Identity TokenWebIdentityTokenFileCredentialsProvider — dùng token file, gọi STS lấy credentials tạm thời. EKS tự inject token này
4Shared credentials/config filesProfileCredentialsProvider — đọc profile [default] trong ~/.aws/credentials hoặc ~/.aws/config
5ECS Container credentialsContainerCredentialsProvider — đọc từ ECS metadata endpoint
6EC2 Instance IAM RoleInstanceProfileCredentialsProvider

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.


Tài liệu tham khảo

Quay lại trang chủ

Bình luận