TL;DR Geben Sie mir einfach den Code
Bei der Bewertung einiger bestehender IAM-Richtlinien in einer Codebasis habe ich immer wieder dieselben Schritte wiederholt: Navigieren Sie in Google und suchen Sie unter iam actions servicename nach Informationen zu den verwendeten Aktionen.
Profi-Tipp: Es ist viel einfacher, diese Seite einfach als Lesezeichen zu speichern: Aktionen, Ressourcen und Zustandsschlüssel für AWS-Services
Die Informationen, die ich für meine Arbeit zur Richtlinienvalidierung benötigte, sind recht einfach:
- Was sind die Dienstleistungen?
- Was sind alle Aktionen, die ein Dienst hat?
- Welche Aktionen passen zu einem Muster wie "Desc*" für einen Dienst?
- Was sind obligatorische und optionale Ressourcen für jede Aktion?
- Welche Bedingungsschlüssel können verwendet werden?
- Ist die Aktion schreibgeschützt, schreibgeschützt oder etwas anderes?
Die erste Anlaufstelle, um herauszufinden, ob diese Informationen irgendwie offengelegt werden, wären die AWS SDKs. Ich habe die boto3-Dokumentation für den Service iam durchgesehen und bin leer ausgegangen.
Nachdem ich viele Richtlinien manuell erstellt hatte, erinnerte ich mich daran, dass AWS über ein Tool zur Richtlinienbearbeitung in der Konsole verfügt, das die Informationen zu verwenden scheint, die ich manuell nachgeschlagen hatte. Also begann ich mein Abenteuer, indem ich versuchte, meine Kämpfe zu automatisieren.
Ich beschloss, etwas Zeit in den Richtlinieneditor zu investieren, der eine Art API verwendet, mit der ich einige Dinge automatisieren kann. Mit dem Inspektionsbereich von Chrome, der die Registerkarte Netzwerk verwendet, sah ich eine Menge http-Anfragen an:
us-east-1.console.aws.amazon.com/iamv2/api/iamv2
Nach einigen Experimenten und Spielereien mit Cookies und CSRF-Tokens fand ich heraus, wie diese undokumentierte API funktioniert. Also habe ich mir ein kleines Python-Programm ausgedacht, um das zu automatisieren. Da es nur 80 Zeilen Code sind, werde ich ihn hier veröffentlichen. Ich werde wahrscheinlich bald ein installierbares Python-Paket daraus machen. Das Repository mit dem Code und einigen Beispielen ist GitHub - binxio/aws-iamv2.
import requests
import json
import boto3
from bs4 import BeautifulSoup
# composePolicy, decomposePolicy, checkMultiMFAStatus, createX509, cuid, generateKeyPairs
methods = { "services" : lambda p: None,
"actions" : lambda p: { "serviceName": p, "RegionName": "eu-central-1" },
"resources" : lambda p: None,
"contextKeys" : lambda p: None,
"globalConditionKeys" : lambda p: None,
"getServiceLinkedRoleTemplate": lambda p: { "serviceName": p },
"policySummary" : lambda p: { "policyDocument": p },
"validate" : lambda p: { "policy": json.dumps(p), "type": "" } }
class ConsoleSession:
def __init__(self, boto3_session):
self._credentials = boto3_session.get_credentials()
self._signed_in = False
self._csrf_token = None
self._cache = {method: {} for method in methods}
self._rsession = requests.Session()
def __getattribute__(self, name):
if name in methods:
def make_lambda(method, converter):
return lambda param=None: self.get_api_result(method, converter(param))
return make_lambda(name, methods[name])
else:
return object.__getattribute__(self, name)
def signin(self):
token = json.loads(self._rsession.get(
"https://signin.aws.amazon.com/federation",
params={
"Action": "getSigninToken",
"Session": json.dumps({
"sessionId": self._credentials.access_key,
"sessionKey": self._credentials.secret_key,
"sessionToken": self._credentials.token
})
}
).text)["SigninToken"]
self._rsession.get(
"https://signin.aws.amazon.com/federation",
params={
"Action": "login",
"Issuer": None,
"Destination": "https://console.aws.amazon.com/",
"SigninToken": token
}
)
for m in BeautifulSoup(self._rsession.get(
"https://us-east-1.console.aws.amazon.com/iamv2/home#",
params={ "region": "eu-central-1", "state": "hashArgs" }
).text, "html.parser").find_all("meta"):
if m.get("name") == "awsc-csrf-token":
self._csrf_token = m["content"]
self._signed_in = True
def get_api_result(self, path, param=None):
not self._signed_in and self.signin()
params = json.dumps(param)
if self._cache[path].get(params, None):
return self._cache[path][params]
self._cache[path][params] = json.loads(self._rsession.post(
"https://us-east-1.console.aws.amazon.com/iamv2/api/iamv2",
headers={
"Content-Type": "application/json",
"X-CSRF-Token": self._csrf_token,
},
data=json.dumps({
"headers": { "Content-Type": "application/json" },
"path": f"/prod/{path}",
"method": "POST",
"region": "us-east-1",
"params": {},
**({ "contentString": params } if params else {})
})
).text)
return self._cache[path][params]
Ein paar Dinge zu diesem Code:
- Die Klasse
ConsoleSessionnimmt eineboto3.Sessionals Eingabe entgegen. Diese Sitzung benötigt keine aktuellen Rechte für AWS. - Der Code sieht vielleicht etwas seltsam aus, weil ich ihn als dynamische Klasse erstellen wollte. Daher musste ich nur eine Zeile hinzufügen, um einen zusätzlichen API-Endpunkt zu implementieren. Dieser verwendet die Überschreibung
__getattribute__und das Objektmethods. - Ich verwende das Modul
requestsund starte einerequests.Session(), die einen Großteil der Arbeit erledigt, die für die erfolgreiche Bearbeitung von Cookies für http-Anfragen erforderlich ist. - Um die
awsc-csrf-tokenvon der Seite zu holen, verwende ichBeautifulSoup - Die Methoden in
methodswurden nicht alle von mir entdeckt. Ich habe eine Suche aufiamv2auf github durchgeführt und einige json-Dateien gefunden, in denen diese API bereits detailliert beschrieben ist. - Da sich die Informationen, die für die von mir benötigten API-Aufrufe abgerufen werden, nicht pro Anfrage ändern, habe ich eine einfache Zwischenspeicherfunktion implementiert. Für einige Methoden wie
policySummaryundvalidateist dies möglicherweise nicht optimal.
Die eigentliche Anmeldung erfolgt über drei HTTP-Anfragen:
- Sie erhalten eine
SigninTokenvonsignin.aws.amazon.com/federation - Verwenden Sie den Token auf
login, umhttps://console.aws.amazon.com/ - Rufen Sie
https://us-east-1.console.aws.amazon.com/iamv2/home#ab, um dieawsc-csrf-tokenvon der Seite zu erhalten.
Beispiel Verwendung
Der folgende Code zeigt ein Beispiel für die Verwendung:
import boto3
from iamv2 import ConsoleSession
import re
awssvcs = {}
console_session = None
def get_iam_info():
global console_session
boto_session = boto3.Session(region_name='us-east-1')
console_session = ConsoleSession(boto_session)
services = console_session.services()
for service in services:
name = service["serviceName"]
if name not in awssvcs:
awssvcs[name] = { "parts": [] }
awssvcs[name]["parts"].append(service)
def get_statement_actions(statement):
result = []
actions = statement.get("Action") or statement.get("NotAction")
reverse = "NotAction" in statement
reverse = not reverse if statement["Effect"] == "Deny" else reverse
actions = [actions] if isinstance(actions, str) else actions
for action in actions:
service, act = action.split(':')
if "Actions" not in awssvcs[service]:
awssvcs[service]["Actions"] = console_session.actions(awssvcs[service]["parts"][0]["serviceKeyName"])
actrgx = act.replace('*', '[A-Za-z]+')
for svc_action in awssvcs[service]["Actions"]:
if bool(re.match(actrgx, svc_action["actionName"], flags=re.IGNORECASE)) ^ reverse:
result.append(svc_action)
return result
def get_policy_actions(policy):
for statement in policy["Statement"]:
yield get_statement_actions(statement)
get_policy_actions listet einfach alle Aktionen auf, die durch die Anweisungen in der Richtlinie erlaubt sind. Hier ist es in Aktion:
policy = {
"Version": "2012-10-17",
"Statement": [{
"Sid": "ReadOnlyCloudTrail",
"Effect": "Deny",
"NotAction": "cloudtrail:De*",
"Resource": "*"
}]
}
get_iam_info()
for statement_actions in get_policy_actions(policy):
statement_actions = sorted(statement_actions, key=lambda x: x["actionName"])
for action in statement_actions:
print(f'{action["actionName"]:40} {", ".join(action["actionGroups"])}')
Sie erhalten dann die folgende Liste:
DeleteChannel ReadWrite
DeleteEventDataStore ReadWrite
DeleteResourcePolicy ReadWrite
DeleteServiceLinkedChannel ReadWrite
DeleteTrail ReadWrite
DeregisterOrganizationDelegatedAdmin ReadWrite
DescribeQuery ReadOnly, ReadWrite
DescribeTrails ReadOnly, ReadWrite
Sie können sehen, dass diese Richtlinie einige Aktionen zulässt, die nicht schreibgeschützt sind. Sie könnten diesen Code in Tests verwenden, die in Ihrer Pipeline ausgewertet werden, um sicherzustellen, dass Sie in dieser Richtlinie nicht versehentlich nicht-lesbare Aktionen zulassen.
Zukunft
Ich werde ein installierbares Python-Paket aus dem API-Teil erstellen. Außerdem habe ich einige Ideen für ein paar nette Policy-Tools:
- Prüfen Sie auf häufige Fehler in Policen.
- Generieren Sie schreibgeschützte Dienstanweisungen für in einer Berechtigungsgrenze
Verfasst von

Jacco Kulman
Jacco is a Cloud Consultant at Binx.io. As an experienced development team lead he coded for the banking- and hospitality- and media-industries. He is a big fan of serverless architectures. In his free time he reads science fiction, contributes to open source projects and enjoys being a life-long-learner.
Unsere Ideen
Weitere Blogs
Contact



