Preface
隨著組織慢慢變大,在 AWS 上面常常會遇到一個問題就是,我的 IAM entity 的 permission 是不是開的太大了,這個問題常常發生在 developer 想要快速驗證自己的 application 能不能 work,而作為 admin 的我們有時會給予太大的權限,等到該專案開展到一定程度的時候,其實需要使用到的權限應該是穩定下來了,但又難以找每個專案負責人慢慢 review 權限,這樣一來,其實違反了 least privilege 的原則,也就是只給於需要的權限就好。
IAM access advisor API
AWS 其實有推出一組用來分析 IAM 權限管理的 API,而 AWS 官方的 blog 也有幾篇介紹,完全可以符合我們的需求,把一些用不到的權限限縮。
generate-service-last-accessed-details
針對 IAM ser, role, group, or policy 產生最後存取 (last accessed data) 的資訊,呼叫這個 API 後會拿到一組JobId
,接著要等待一陣子,才能透過get-service-last-accessed-details
得到資料。get-service-last-accessed-details
透過這個 API 輸入 JobId 去得到 last accessed 的資料get-service-last-accessed-details-with-entities
其實跟上面的 API 很類似,只是可以指定 —service-namespaces 去看特定的 servicelist-policies-granting-service-access
可以看到這個權限(針對 service) 是從哪個 policy 來的
有了以上這幾組 API 我們就可以實作一個簡單的 script 去掃出是否有權限太大的 IAM entity。
Simple Example
這個範例很大一部分是參考 trek10inc 的 config-excess-access-exorcism 來的,不過有做一些簡單的修改,有了這個程式可以幫我們快速定位,那個 IAM role 開的權限太大,而這個 repo 其實想做到的事情更潮,是將其設定為 AWS config 的 rule,由此一來就可以讓 AWS 幫我們定期去掃 IAM entities。
先透過下面這個 function 拿到該 IAM entity 所有的 service 權限,這邊要注意的是要把 paginate 的資料也拿回來,因為有些權限太多需要好幾個 API call 才拿得齊。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28def get_iam_last_access_details(iam, arn):
job = iam.generate_service_last_accessed_details(Arn=arn)
job_id = job['JobId']
service_results = []
while True:
result = iam.get_service_last_accessed_details(JobId=job_id)
if result['JobStatus'] == 'IN_PROGRESS':
print("Awaiting job")
continue
elif result['JobStatus'] == 'FAILED':
raise Exception(f"Could not get access information for {arn}")
else:
service_results.extend(paginate_access_details(job_id, result))
break
time.sleep(5)
return service_results
def paginate_access_details(job_id, result):
more_data, marker = result['IsTruncated'], result.get('Marker')
if not more_data:
return result['ServicesLastAccessed']
all_service_info = result['ServicesLastAccessed'][:]
while more_data:
page = iam.get_service_last_accessed_details(JobId=job_id, Marker=marker)
more_data, marker = page['IsTruncated'], page['Marker']
all_service_info.extend(page['ServicesLastAccessed'])
return all_service_info
來個簡單的測試1
2detail = get_iam_last_access_details(iam, "arn:aws:iam::AWS_ACCOUNT:role/service-role/AmazonEC2RunCommandRoleForManagedInstances")
pprint(detail)
Output 會長得像這樣:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16[ { 'ServiceName': 'Amazon CloudWatch',
'ServiceNamespace': 'cloudwatch',
'TotalAuthenticatedEntities': 0},
{ 'ServiceName': 'AWS Directory Service',
'ServiceNamespace': 'ds',
'TotalAuthenticatedEntities': 0},
{ 'ServiceName': 'Amazon EC2',
'ServiceNamespace': 'ec2',
'TotalAuthenticatedEntities': 0},
{ 'LastAuthenticated': datetime.datetime(2019, 4, 8, 9, 41, tzinfo=tzutc()),
'LastAuthenticatedEntity': 'arn:aws:iam::774915305292:role/service-role/AmazonEC2RunCommandRoleForManagedInstances',
'ServiceName': 'Amazon Message Delivery Service',
'ServiceNamespace': 'ec2messages',
'TotalAuthenticatedEntities': 1},
...
]
有了這個 output 我們就可以來開心的來分析啦,主要就是看 LastAuthenticated
這個欄位,如果沒有這個欄位就代表根本沒使用過,這個權限就該被剷除,另外也可以檢查是否這個使用的日期是不是在 180 天前,太久沒用也代表可能不需要了。
1 | def never_accessed_services_check(iam, arn): |
1 | def no_access_in_180_days_check(iam, arn): |
在知道是哪個 service 有問題後,還可以用 aws iam list-policies-granting-service-access --arn arn:aws:iam::AWS_ACCOUNT:role/service-role/AmazonEC2RunCommandRoleForManagedInstances --service-namespaces s3
去看這個 service 的權限是從哪個 policy 來的。
1 | { |
sample code 可以用下列的程式碼
1 | def get_policies(iam, arn, service_namespace_list): |
就可以找出需要修正的 policy 像是這樣
1 | [ |
心得
管理 IAM 其實需要相當的心力,透過一些 AWS 的 cli 加上 python boto lib,可以讓我們事倍功半,很推薦大家多試試看這些 API 掃掃看,我也有蠻多意外的發現XD
Reference
- https://www.trek10.com/blog/excess-access-exorcism-with-aws-config/
- remove unnecessary permissions in your iam policies by using service last accessed data
header image creditJason Briscoe