IAM role được một chủ thể nhận để sử dụng trong phiên có thời hạn
Đọc riêng chính sách tin cậy, quyền nhận role và quyền phiên; chạy mô hình cục bộ để phân biệt giới hạn quyền với thời điểm hết hạn.

IAM role là một danh tính có thể được sử dụng nhiều lần, với tập quyền xác định. Phiên role là một lần sử dụng tạm thời danh tính đó. Nhầm hai khái niệm này dễ dẫn đến hai lỗi: coi chính sách tin cậy là quyền đọc dữ liệu, hoặc cho rằng nhận role thành công đồng nghĩa với việc mọi yêu cầu tiếp theo đều được phép.
Bài này dành cho lập trình viên đang học cách truy cập AWS giữa hai tài khoản. Bạn cần đọc được JSON và phân biệt hành động với mã định danh tài nguyên. Chúng ta sẽ đọc bốn tài liệu chính sách nhỏ, rồi chạy mô hình Python cục bộ để tách bước nhận role, giới hạn quyền và thời điểm hết hạn. Chương trình không tạo thông tin xác thực AWS, không gọi điểm cuối AWS và không thao tác với bucket dùng làm ví dụ.
Phân biệt bên gọi, role và phiên
Tài liệu IAM role giải thích rằng role không có mật khẩu hay access key dài hạn thông thường gắn với chính nó. Một chủ thể được phép nhận role sẽ nhận thông tin xác thực tạm thời cho một phiên. Role vẫn tồn tại để phục vụ các phiên sau khi phiên hiện tại hết hạn.
Bên gọi được khai báo ở đây là role ci-reader trong tài khoản 111122223333. Công việc của nó cần role report-reader trong tài khoản 444455556666. Các số tài khoản và tên tài nguyên minh họa quan hệ này; chúng không phải cấu hình triển khai cho một tài khoản đang có. Trong yêu cầu thật, bên gọi phải dùng thông tin xác thực hợp lệ. Biết hoặc ghi được ARN không có nghĩa là đã xác thực thành chủ thể mang ARN đó.
ARN của role đích xác định role đang được yêu cầu. ARN dạng assumed-role xác định chủ thể của phiên sau khi nhận role. ARN thứ hai chứa tên phiên. Theo tài liệu API AssumeRole, tên đó có thể xuất hiện trong nhật ký của bên sở hữu tài nguyên. Nên đặt tên có ý nghĩa vận hành, nhưng một tên do bên gọi tự cung cấp mà không bị ràng buộc không phải bằng chứng độc lập về danh tính của một người.
Nhận role giữa hai tài khoản cần xét cả hai phía
Chính sách tin cậy, hay trust policy, của role đích xác định ai được nhận role. Trong bài, nó chỉ rõ role bên gọi và cho phép hành động nhận role. Nó không chứa quyền đọc object S3. Chính sách danh tính của bên gọi cho phép bên đó yêu cầu role đích. Tài liệu đánh giá quyền giữa hai tài khoản yêu cầu kết quả đánh giá ở cả hai tài khoản đều cho phép; các giới hạn khác đang áp dụng vẫn có thể từ chối yêu cầu.
Cách giải thích hai phía này chủ ý nói về hai tài khoản khác nhau. Không nên biến nó thành quy tắc chung rằng mọi cách cấu hình trust trong cùng một tài khoản đều cần cùng một quyền bổ sung. Quyền cấp qua chính sách tài nguyên trong cùng tài khoản có những quy tắc đánh giá khác. Xác định ranh giới tài khoản trước sẽ tránh dùng một ví dụ có vẻ mâu thuẫn để suy ra quy tắc chẩn đoán sai.
Bài về trust policy ngày 3 tháng 11 năm 2022 của Jonathan Jenkyn và Liam Wadman, được làm rõ thêm vào tháng 6 năm 2023, phân biệt trust policy, chính sách danh tính và ranh giới quyền. Ví dụ chọn một role cụ thể trong bài đó cũng giải thích vì sao tin cậy ARN tài khoản kết thúc bằng :root rộng hơn việc chọn đúng một role. Chủ thể tài khoản ấy không có nghĩa là chỉ người dùng root của tài khoản. Ví dụ của chúng ta chọn một role để quan hệ được phép dễ nhìn thấy.
Sau bước nhận role, yêu cầu dùng thông tin xác thực trả về sẽ sử dụng quyền của phiên role. Nó không tự cộng quyền dịch vụ của bên gọi ban đầu với quyền dịch vụ của role đích. Nguồn thông tin xác thực ban đầu cũng không tự biến mất; ứng dụng phải thực sự chọn bộ thông tin xác thực mong muốn cho yêu cầu tiếp theo của client.
Bốn tài liệu có bốn nhiệm vụ khác nhau
Khối Python dưới đây nhóm bốn tài liệu JSON bằng các nhãn cục bộ. Object bên ngoài là cách tổ chức tệp bài tập, không phải một chính sách IAM duy nhất để gửi lên AWS. trust thuộc role đích. caller biểu diễn quyền nhận role của bên gọi. role cho phép đọc object A và B. session yêu cầu giới hạn riêng phiên này ở A và C.
Kết quả quan trọng là chỉ còn A. Session policy, tức chính sách giới hạn phiên, không thể thêm C khi chính sách danh tính của role chưa cấp C. Tương tự, việc role cho phép B không vượt qua được giới hạn phiên đang bỏ B ra ngoài. Tài liệu API mô tả phép lấy giao của hai tập quyền. So sánh này giả định không có quyền cấp riêng từ chính sách tài nguyên, lệnh từ chối tường minh, chính sách tổ chức, ranh giới quyền hoặc điều kiện khác làm thay đổi kết quả; nó không phải toàn bộ thuật toán đánh giá IAM.
Lưu mã thành role_session_lab.py rồi chạy python3 -I role_session_lab.py. Ví dụ đã được kiểm tra với Python 3.12. Bộ đọc chính sách nhỏ chỉ chấp nhận đúng cấu trúc cho phép dùng trong bài. Nó từ chối ký tự đại diện trong hành động và tài nguyên, cùng các trường bổ sung. Principal trong trust policy được so sánh đúng theo giá trị với caller được truyền vào; mã không triển khai cách IAM khớp ký tự đại diện cho principal. Đây không phải trình mô phỏng IAM, cơ chế xác thực hay công cụ xác nhận chính sách hợp lệ với AWS.
Mô hình dùng một bản chụp quyền cố định và số giây nguyên trên đồng hồ giả định. Phiên bắt đầu ở giây 100, kéo dài 900 giây và hết hạn ở giây 1000. Chuỗi ARN của nó chỉ là nhãn; chương trình không sinh access key, secret key hay session token. Các tên lý do từ chối được in ra giải thích bước kiểm tra trong chương trình này, không phải mã lỗi AWS hoặc cam kết về thứ tự chẩn đoán của AWS.
from dataclasses import dataclass
import json
# Four separate policy documents, grouped only for this local exercise.
policies = json.loads('''{
"trust": {
"Version": "2012-10-17",
"Statement": [{"Effect": "Allow", "Action": "sts:AssumeRole",
"Principal": {"AWS": "arn:aws:iam::111122223333:role/ci-reader"}}]
},
"caller": {
"Version": "2012-10-17",
"Statement": [{"Effect": "Allow", "Action": "sts:AssumeRole",
"Resource": ["arn:aws:iam::444455556666:role/report-reader"]}]
},
"role": {
"Version": "2012-10-17",
"Statement": [{"Effect": "Allow", "Action": "s3:GetObject",
"Resource": ["arn:aws:s3:::example-reports/a.csv",
"arn:aws:s3:::example-reports/b.csv"]}]
},
"session": {
"Version": "2012-10-17",
"Statement": [{"Effect": "Allow", "Action": "s3:GetObject",
"Resource": ["arn:aws:s3:::example-reports/a.csv",
"arn:aws:s3:::example-reports/c.csv"]}]
}
}''')
CALLER = 'arn:aws:iam::111122223333:role/ci-reader'
TARGET = 'arn:aws:iam::444455556666:role/report-reader'
PREFIX = 'arn:aws:s3:::example-reports/'
READ = 's3:GetObject'
def one_allow(document, fields):
if (set(document) != {'Version', 'Statement'} or
document['Version'] != '2012-10-17' or
not isinstance(document['Statement'], list) or
len(document['Statement']) != 1):
raise ValueError('outside the teaching subset')
statement = document['Statement'][0]
if set(statement) != fields or statement['Effect'] != 'Allow':
raise ValueError('outside the teaching subset')
return statement
def grants(document):
s = one_allow(document, {'Effect', 'Action', 'Resource'})
action, resources = s['Action'], s['Resource']
if (not isinstance(action, str) or not isinstance(resources, list)
or not resources or any(not isinstance(r, str) for r in resources)
or any('*' in x or '?' in x for x in [action, *resources])):
raise ValueError('only exact action/resource pairs are supported')
return frozenset((action, r) for r in resources)
@dataclass(frozen=True)
class SessionModel:
arn: str
issued: int
expires: int
role_grants: frozenset
session_grants: frozenset
def assume(caller, caller_policy, duration=900, issued=100):
trust = one_allow(policies['trust'], {'Effect', 'Action', 'Principal'})
if trust['Action'] != 'sts:AssumeRole' or trust['Principal'] != {'AWS': caller}:
raise PermissionError('trust gate')
if caller_policy is None or ('sts:AssumeRole', TARGET) not in grants(caller_policy):
raise PermissionError('caller permission gate')
# The declared role maximum is 3600; this role-to-role example is also chained.
if type(duration) is not int or not 900 <= duration <= 3600:
raise ValueError('duration outside this example')
return SessionModel(
'arn:aws:sts::444455556666:assumed-role/report-reader/lab-01',
issued, issued + duration, grants(policies['role']), grants(policies['session']))
def request(session, action, resource, now):
if now < session.issued:
return 'not-yet-issued'
if now >= session.expires:
return 'expired'
if (action, resource) not in session.role_grants:
return 'outside-role'
if (action, resource) not in session.session_grants:
return 'outside-session'
return 'allowed-in-model'
s = assume(CALLER, policies['caller'])
assert s.expires == 1000
print('local session label:', s.arn)
cases = [
('a.csv', READ, 100, 'allowed-in-model'),
('b.csv', READ, 100, 'outside-session'),
('c.csv', READ, 100, 'outside-role'),
('a.csv', 's3:PutObject', 100, 'outside-role'),
('a.csv', READ, 999, 'allowed-in-model'),
('a.csv', READ, 1000, 'expired'),
]
for name, action, now, expected in cases:
actual = request(s, action, PREFIX + name, now)
assert actual == expected
print(name, action, now, actual)
for caller, policy, expected in [
('arn:aws:iam::111122223333:role/other', policies['caller'], 'trust gate'),
(CALLER, None, 'caller permission gate'),
]:
try:
assume(caller, policy)
except PermissionError as error:
assert str(error) == expected
print('assumption refused:', error)
else:
raise AssertionError('expected refusal')
fresh = assume(CALLER, policies['caller'], issued=1000)
assert request(fresh, READ, PREFIX + 'a.csv', 1000) == 'allowed-in-model'
assert request(s, READ, PREFIX + 'a.csv', 1000) == 'expired'
print('new local session expires:', fresh.expires, '; old one stays expired')
Phiên còn hạn vẫn có thể thiếu quyền
Sáu dòng đầu tách ba câu hỏi: phiên cục bộ còn trong thời hạn không, role có cho phép hành động trên object đó không, và giới hạn phiên có giữ lại quyền ấy không? Đọc A thành công. Đọc B bị giới hạn phiên chặn. Đọc C nằm ngoài quyền của role, dù C có trong tài liệu phiên. Ghi A cũng thất bại vì quyền đọc không bao gồm hành động ghi.
Hai lỗi nhận role xảy ra ở bước trước đó. Một bên gọi khác không khớp quan hệ tin cậy này. Bên gọi được tin cậy nhưng thiếu quyền nhận role giữa hai tài khoản cũng thất bại. Thêm quyền object S3 vào role đích không sửa được hai lỗi ấy, vì quyền đó liên quan đến yêu cầu tài nguyên ở bước sau.
So sánh cuối dùng cùng hành động và object ngay trước và đúng lúc hết hạn. Giây 999 được phép; giây 1000 bị từ chối. Một phiên cục bộ mới được cấp có thể hoạt động ở giây 1000, trong khi phiên cũ vẫn hết hạn. Với AWS thật, lấy bộ thông tin xác thực thay thế là một thao tác mới cần được cho phép, không phải kéo dài tuổi thọ của bộ đã hết hạn.
Thông tin xác thực tạm thời vẫn cần được quản lý
Thông tin xác thực tạm thời AWS gồm access key ID, secret access key và session token. Tài liệu sử dụng thông tin xác thực tạm thời yêu cầu token khi sử dụng bộ thông tin này và giải thích rằng bộ đã hết hạn không thể được kéo dài. Chỉ chép hai trường khóa sang tiến trình khác không chuyển được một bộ thông tin xác thực tạm thời đầy đủ.
Một credential provider phù hợp trong SDK có thể lấy bộ thay thế trước khi bộ cũ hết hạn. Điều đó khác với chép kết quả của một lần gọi vào cấu hình cố định của ứng dụng rồi cho rằng nó tự gia hạn. Hành vi provider phụ thuộc vào SDK và nguồn cấp thông tin xác thực; bài này không chạy provider hoặc hứa rằng mọi client được tạo thủ công đều tự làm mới.
Với AssumeRole, thời lượng yêu cầu được tài liệu quy định bắt đầu từ 900 giây và bị giới hạn bởi thời lượng tối đa đã cấu hình cho role. Nhận role nối tiếp, hay role chaining, còn có trần một giờ. Bên gọi trong bài đã là một role và thời lượng tối đa được khai báo là một giờ, nên mô hình chấp nhận từ 900 đến 3600 giây. Tăng giới hạn của role cũng không xóa được trần dành cho role chaining. Phiên ngắn làm giảm thời gian sử dụng một bộ thông tin xác thực bị lộ, nhưng công việc chạy lâu cần đường lấy bộ thay thế đáng tin cậy.
Hết hạn cũng khác với thu hồi quyền. Tài liệu thu hồi phiên role mô tả chính sách từ chối có điều kiện theo thời điểm cấp phiên. Đó là thay đổi quyền, không phải cách sửa thời điểm hết hạn ban đầu của thông tin xác thực. Mô hình bản chụp cố định không mô phỏng việc lan truyền chính sách, thay đổi quyền của phiên đang tồn tại hoặc thu hồi quyền; không được dùng riêng phép kiểm tra thời hạn này để dự đoán mọi hành vi sau khi sửa chính sách IAM.
Đổi một điều kiện và dự đoán kết quả
Trước tiên, thêm B vào session policy. A và B sẽ đọc được trong thời hạn phiên, còn C vẫn nằm ngoài quyền của role. Sau đó bỏ C khỏi session policy. Tập quyền thực sự có hiệu lực không đổi, cho thấy quyền được yêu cầu thêm chưa chắc trở thành quyền có hiệu lực.
Tiếp theo, thử thời lượng 899, 900, 3600 và 3601 giây. Hai giá trị ở giữa hợp lệ theo quy tắc đã khai báo; hai giá trị ngoài cùng gây lỗi. Dịch thời điểm cấp phiên cục bộ sẽ dịch thời điểm hết hạn đúng bằng khoảng đó. Nó không sửa quyền nhận role còn thiếu hoặc thêm quyền trên tài nguyên.
Cuối cùng, thêm trường Condition hoặc tài nguyên có ký tự đại diện vào một bản sao tài liệu. Bộ đọc phục vụ bài học phải từ chối cấu trúc không hỗ trợ. Hạn chế của mô hình là không thay thế được việc chẩn đoán đầy đủ: cần đọc các điều kiện, mọi chính sách áp dụng, nguồn thông tin xác thực thật và cặp hành động/tài nguyên của yêu cầu. Mô hình giúp tách những câu hỏi đó; nó không chứng nhận rằng một yêu cầu trong môi trường thật sẽ thành công.