-
-
Save kempei/b18292229bb287d93dbcd82954590a18 to your computer and use it in GitHub Desktop.
AWS Config Rules カスタムルールを作成してみよう ref: https://qiita.com/kempe/items/52ff78b65fc04209f734
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"recordVersion": "1.3", | |
"configurationItem": { | |
"relationships": [], | |
"configurationItemCaptureTime": "2018-07-04T01:54:02.810Z", | |
"availabilityZone": null, | |
"configurationStateMd5Hash": "", | |
"tags": {}, | |
"resourceType": "AWS::EC2::Instance", | |
"configurationItemVersion": "1.3", | |
"configurationStateId": 1530669242810, | |
"relatedEvents": [], | |
"awsRegion": "ap-northeast-1", | |
"ARN": "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxxxx:instance/i-05888074e384774ef", | |
"supplementaryConfiguration": {}, | |
"resourceName": null, | |
"configuration": null, | |
"resourceId": "i-05888074e384774ef", | |
"resourceCreationTime": null, | |
"configurationItemStatus": "ResourceDeleted", | |
"awsAccountId": "xxxxxxxxxxxxxxx" | |
}, | |
"notificationCreationTime": "2018-07-05T14:40:13.752Z", | |
"messageType": "ConfigurationItemChangeNotification", | |
"configurationItemDiff": null | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"ConfigRuleName": "EC2-Exposed-Instance", | |
"Description": "Evaluates EC2 instances which expose port(s).", | |
"Scope": { | |
"ComplianceResourceTypes": [ | |
"AWS::EC2::Instance" | |
] | |
}, | |
"Source": { | |
"Owner": "CUSTOM_LAMBDA", | |
"SourceIdentifier": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxx:function:awsconfig-ec2-exposed-instance", | |
"SourceDetails": [{ | |
"EventSource": "aws.config", | |
"MessageType": "ConfigurationItemChangeNotification" | |
}] | |
}, | |
"InputParameters": "{\"examplePort1\":\"8080\", \"exampleRange1\":\"1-1024\", \"examplePort2\":\"2375\"}" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode) | |
# | |
# Ensure that no EC2 instances allow public access to the specified ports. | |
# Description: Checks that all instances block access to the specified ports. | |
# | |
# Trigger Type: Change Triggered | |
# Scope of Changes: EC2:Instance | |
# Accepted Parameters: examplePort1, exampleRange1, examplePort2, ... | |
# Example Values: 8080, 1-1024, 2375, ... | |
import json | |
import boto3 | |
# ルールを適用する対象となるリソースの配列です。evaluate_compliance()で使用します。 | |
APPLICABLE_RESOURCES = ["AWS::EC2::Instance"] | |
# 隠されているべきポート番号定義(forbidden ports)をrangeに変換します。1つの場合はそのポートのみです。 | |
def expand_range(ports): | |
if "-" in ports: | |
return range(int(ports.split("-")[0]), int(ports.split("-")[1])+1) | |
else: | |
return [int(ports)] | |
# 0.0.0.0/0が含まれるIpRanges設定を探し、収集します。 | |
def find_exposed_ports(ip_permissions): | |
exposed_ports = [] | |
for permission in ip_permissions: | |
if next((r for r in permission["IpRanges"] | |
if "0.0.0.0/0" in r["CidrIp"]), None): | |
exposed_ports.extend(range(permission["FromPort"], | |
permission["ToPort"]+1)) | |
return exposed_ports | |
# exporsed_portsに、forbidden_portsが含まれていた場合にルール違反を返します。最初の1つが見つかった時点で探索は終了します。 | |
# exporsed_ports * forbidden_ports の計算量となります。 | |
# forbidden_portsの例: {"examplePort1":"8080", "exampleRange1":"1-1024", "examplePort2":"2375"} | |
def find_violation(ip_permissions, forbidden_ports): | |
exposed_ports = find_exposed_ports(ip_permissions) | |
for forbidden in forbidden_ports: | |
ports = expand_range(forbidden_ports[forbidden]) | |
for port in ports: | |
if port in exposed_ports: | |
return "A forbidden port is exposed to the internet." | |
return None | |
# 評価の主ファンクションです。 | |
def evaluate_compliance(configuration_item, rule_parameters): | |
# 対象のリソースタイプではない場合は NOT_APPLICABLE として返します。 | |
if configuration_item["resourceType"] not in APPLICABLE_RESOURCES: | |
return { | |
"compliance_type": "NOT_APPLICABLE", | |
"annotation": "The rule doesn't apply to resources of type " + | |
configuration_item["resourceType"] + "." | |
} | |
# リソースが既に削除されている場合は NOT_APPLICABLE として返します。 | |
if configuration_item['configurationItemStatus'] == "ResourceDeleted": | |
return { | |
"compliance_type": "NOT_APPLICABLE", | |
"annotation": "The configurationItem was deleted and therefore cannot be validated" | |
} | |
security_groups = configuration_item["configuration"].get("securityGroups") | |
# そもそもセキュリティグループがアタッチされていない場合は NON_COMPLIANT として返します。 | |
if security_groups is None: | |
return { | |
"compliance_type": "NON_COMPLIANT", | |
"annotation": "The instance doesn't pertain to any security groups." | |
} | |
ec2 = boto3.resource("ec2") | |
# すべてのセキュリティグループについて検査します。 | |
for security_group in security_groups: | |
# IPパーミッションを取り出します。 | |
ip_permissions = ec2.SecurityGroup( | |
security_group["groupId"] | |
).ip_permissions | |
# 違反がないかを検査します。 | |
violation = find_violation( | |
ip_permissions, | |
rule_parameters | |
) | |
# None以外が返却された場合は違反として NON_COMPLIANT とその内容を返します。 | |
if violation: | |
return { | |
"compliance_type": "NON_COMPLIANT", | |
"annotation": violation | |
} | |
# 検査に合格したので COMPLIANT を返します。 | |
return { | |
"compliance_type": "COMPLIANT", | |
"annotation": "This resource is compliant with the rule." | |
} | |
# Lambdaの主関数です。検査を行う対象のリソース毎に呼び出されます。 | |
# eventの以下3つを使用します。 | |
# invokingEvent/configurationItem: リソースの設定内容 | |
# ruleParameters: AWS Config Rules におけるルールのパラメータ -> (key, value)の配列 | |
# resultToken: 結果をAWS Configにputする時に使用するトークン | |
def lambda_handler(event, context): | |
# 各データ/パラメータ/トークンの取り出し | |
invoking_event = json.loads(event["invokingEvent"]) | |
configuration_item = invoking_event["configurationItem"] | |
rule_parameters = json.loads(event["ruleParameters"]) | |
result_token = "No token found." | |
if "resultToken" in event: | |
result_token = event["resultToken"] | |
# 評価 | |
evaluation = evaluate_compliance(configuration_item, rule_parameters) | |
# boto3を使用して AWS Config に結果を put | |
config = boto3.client("config") | |
config.put_evaluations( | |
Evaluations=[ | |
{ | |
# 通常はinputのリソースタイプ/リソースIDを使用する | |
"ComplianceResourceType": | |
configuration_item["resourceType"], | |
"ComplianceResourceId": | |
configuration_item["resourceId"], | |
# 以下2つが評価結果 | |
# COMPLIANT, NON_COMPLIANT, NOT_APPLICABLE | |
"ComplianceType": | |
evaluation["compliance_type"], | |
# 評価結果の文字列 | |
"Annotation": | |
evaluation["annotation"], | |
# 並べ替えに使うタイムスタンプ | |
# キャプチャ時のタイムスタンプを用いれば良い | |
"OrderingTimestamp": | |
configuration_item["configurationItemCaptureTime"] | |
}, | |
], | |
ResultToken=result_token | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ mkdir Config | |
$ cd Config/ | |
$ git clone https://github.com/awslabs/aws-config-rules.git | |
Cloning into 'aws-config-rules'... | |
remote: Counting objects: 575, done. | |
remote: Compressing objects: 100% (10/10), done. | |
remote: Total 575 (delta 2), reused 3 (delta 0), pack-reused 565 | |
Receiving objects: 100% (575/575), 157.63 KiB | 140.00 KiB/s, done. | |
Resolving deltas: 100% (331/331), done. | |
$ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ aws lambda add-permission \ | |
> --function-name awsconfig-ec2-exposed-instance \ | |
> --statement-id 1 \ | |
> --principal config.amazonaws.com \ | |
> --action lambda:InvokeFunction \ | |
> --source-account xxxxxxxxxxxxxx | |
{ | |
"Statement": "{\"Sid\":\"1\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"config.amazonaws.com\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxxx:function:awsconfig-ec2-exposed-instance\",\"Condition\":{\"StringEquals\":{\"AWS:SourceAccount\":\"xxxxxxxxxxxxx\"}}}" | |
} | |
$ aws lambda add-permission \ | |
> --function-name awsconfig-iam-inactive-user \ | |
> --statement-id 1 \ | |
> --principal config.amazonaws.com \ | |
> --action lambda:InvokeFunction \ | |
> --source-account xxxxxxxxxxxxx | |
{ | |
"Statement": "{\"Sid\":\"1\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"config.amazonaws.com\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxxxxx:function:awsconfig-iam-inactive-user\",\"Condition\":{\"StringEquals\":{\"AWS:SourceAccount\":\"xxxxxxxxxxxxx\"}}}" | |
} | |
$ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ aws configservice put-config-rule --config-rule file://ec2-exposed-instance-rule.json | |
$ aws configservice put-config-rule --config-rule file://iam-inactive-user-rule.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ ls | |
aws-config-rules lambda-exec-role-policy.json | |
※git cloneを実行したディレクトリ | |
$ aws iam create-role --role-name lambda-config-rules-role --assume-role-policy-document file://lambda-exec-role-policy.json | |
{ | |
"Role": { | |
"AssumeRolePolicyDocument": { | |
"Version": "2012-10-17", | |
"Statement": [ | |
{ | |
"Action": "sts:AssumeRole", | |
"Sid": "", | |
"Effect": "Allow", | |
"Principal": { | |
"Service": "lambda.amazonaws.com" | |
} | |
} | |
] | |
}, | |
"RoleId": "AROAJSIB5RAT7Y2JOAEUA", | |
"CreateDate": "2018-07-05T12:38:22.114Z", | |
"RoleName": "lambda-config-rules-role", | |
"Path": "/", | |
"Arn": "arn:aws:iam::xxxxxxxxxxxxxx:role/lambda-config-rules-role" | |
} | |
} | |
$ aws iam get-role --role-name lambda-config-rules-role | |
※create-role時と同じ結果が返される |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ aws iam attach-role-policy --role-name lambda-config-rules-role --policy-arn "arn:aws:iam::aws:policy/service-role/AWSConfigRulesExecutionRole" | |
$ aws iam attach-role-policy --role-name lambda-config-rules-role --policy-arn "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess" | |
$ aws iam attach-role-policy --role-name lambda-config-rules-role --policy-arn "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess" | |
$ aws iam attach-role-policy --role-name lambda-config-rules-role --policy-arn "arn:aws:iam::aws:policy/IAMReadOnlyAccess" | |
$ aws iam list-attached-role-policies --role-name lambda-config-rules-role | |
{ | |
"AttachedPolicies": [ | |
{ | |
"PolicyName": "AmazonEC2ReadOnlyAccess", | |
"PolicyArn": "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess" | |
}, | |
{ | |
"PolicyName": "CloudWatchLogsFullAccess", | |
"PolicyArn": "arn:aws:iam::aws:policy/CloudWatchLogsFullAccess" | |
}, | |
{ | |
"PolicyName": "IAMReadOnlyAccess", | |
"PolicyArn": "arn:aws:iam::aws:policy/IAMReadOnlyAccess" | |
}, | |
{ | |
"PolicyName": "AWSConfigRulesExecutionRole", | |
"PolicyArn": "arn:aws:iam::aws:policy/service-role/AWSConfigRulesExecutionRole" | |
} | |
] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ zip -j ec2-exposed-instance.zip aws-config-rules/python/ec2-exposed-instance.py | |
adding: ec2-exposed-instance.py (deflated 68%) | |
$ zip -j iam-inactive-user.zip aws-config-rules/python/iam-inactive-user.py | |
adding: iam-inactive-user.py (deflated 61%) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ aws lambda create-function \ | |
--function-name awsconfig-ec2-exposed-instance \ | |
--runtime python2.7 \ | |
--role arn:aws:iam::xxxxxxxxxx:role/lambda-config-rules-role \ | |
--handler ec2-exposed-instance.lambda_handler \ | |
--timeout 300 \ | |
--zip-file fileb://ec2-exposed-instance.zip | |
{ | |
"TracingConfig": { | |
"Mode": "PassThrough" | |
}, | |
"CodeSha256": "f+ZThR3wLwsK9AHcupb7MNFLtKl3H5tnAxA77vg2ILo=", | |
"FunctionName": "awsconfig-ec2-exposed-instance", | |
"CodeSize": 1473, | |
"RevisionId": "ba1b0512-2646-4571-92e3-728fe627d8a3", | |
"MemorySize": 128, | |
"FunctionArn": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxxx:function:awsconfig-ec2-exposed-instance", | |
"Version": "$LATEST", | |
"Role": "arn:aws:iam::xxxxxxxxxxxxxxx:role/lambda-config-rules-role", | |
"Timeout": 300, | |
"LastModified": "2018-07-05T13:18:28.119+0000", | |
"Handler": "lambda_handler", | |
"Runtime": "python2.7", | |
"Description": "" | |
} | |
$ aws lambda create-function \ | |
--function-name awsconfig-iam-inactive-user \ | |
--runtime python2.7 \ | |
--role arn:aws:iam::xxxxxxxxxx:role/lambda-config-rules-role \ | |
--handler iam-inactive-user.lambda_handler \ | |
--timeout 300 \ | |
--zip-file fileb://iam-inactive-user.zip | |
{ | |
"TracingConfig": { | |
"Mode": "PassThrough" | |
}, | |
"CodeSha256": "yRuXariCEPmjSqZ0GKOHcVhzk2/1B14+C+9BpMQzkXs=", | |
"FunctionName": "awsconfig-iam-inactive-user", | |
"CodeSize": 1115, | |
"RevisionId": "ba7be6ef-5148-4790-80c2-73f232e5536d", | |
"MemorySize": 128, | |
"FunctionArn": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:awsconfig-iam-inactive-user", | |
"Version": "$LATEST", | |
"Role": "arn:aws:iam::xxxxxxxxxxxx:role/lambda-config-rules-role", | |
"Timeout": 300, | |
"LastModified": "2018-07-05T13:19:29.453+0000", | |
"Handler": "lambda_handler", | |
"Runtime": "python2.7", | |
"Description": "" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"ConfigRuleName": "IAM-Inactive-Users", | |
"Description": "Evaluates inactive IAM users.", | |
"Scope": { | |
"ComplianceResourceTypes": [ | |
"AWS::IAM::User" | |
] | |
}, | |
"Source": { | |
"Owner": "CUSTOM_LAMBDA", | |
"SourceIdentifier": "arn:aws:lambda:ap-northeast-1:xxxxxxxxxxx:function:awsconfig-iam-inactive-user", | |
"SourceDetails": [{ | |
"EventSource": "aws.config", | |
"MessageType": "ConfigurationItemChangeNotification" | |
}] | |
}, | |
"InputParameters": "{\"maxInactiveDays\":\"90\"}" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# This file made available under CC0 1.0 Universal (https://creativecommons.org/publicdomain/zero/1.0/legalcode) | |
# | |
# Ensure that no users have been inactive for a period longer than specified. | |
# Description: Checks that all users have been active for earlier than specified. | |
# | |
# Trigger Type: Change Triggered | |
# Scope of Changes: IAM:User | |
# Required Parameters: maxInactiveDays | |
# Example Value: 90 | |
import json | |
import boto3 | |
import datetime | |
APPLICABLE_RESOURCES = ["AWS::IAM::User"] | |
# 与えられた日付と現在の差分の日数を計算します | |
def calculate_age(date): | |
now = datetime.datetime.utcnow().date() | |
then = date.date() | |
age = now - then | |
return age.days | |
def evaluate_compliance(configuration_item, rule_parameters): | |
if configuration_item["resourceType"] not in APPLICABLE_RESOURCES: | |
return "NOT_APPLICABLE" | |
config = boto3.client("config") | |
# 与えられているイベントから変更があるかもしれないので、念のためAWS Configの最新情報からリソースIDをキーとしてユーザ名を取り出します | |
resource_information = config.get_resource_config_history( | |
resourceType=configuration_item["resourceType"], | |
resourceId=configuration_item["resourceId"] | |
) | |
user_name = resource_information["configurationItems"][0]["resourceName"] | |
# IAM の API を用いて当該IAMユーザの PasswordLastUsed を取り出します | |
iam = boto3.client("iam") | |
user = iam.get_user(UserName=user_name) | |
last_used = user["User"].get("PasswordLastUsed") | |
# パラメータから未使用期間の閾値を取り出します | |
max_inactive_days = int(rule_parameters["maxInactiveDays"]) | |
# 未使用期間がパラメータで与えられた閾値を超えていた場合は NON_COMPIANT として返します | |
if last_used is not None and calculate_age(last_used) > max_inactive_days: | |
return "NON_COMPLIANT" | |
return "COMPLIANT" | |
def lambda_handler(event, context): | |
invoking_event = json.loads(event["invokingEvent"]) | |
configuration_item = invoking_event["configurationItem"] | |
rule_parameters = json.loads(event["ruleParameters"]) | |
result_token = "No token found." | |
if "resultToken" in event: | |
result_token = event["resultToken"] | |
config = boto3.client("config") | |
config.put_evaluations( | |
Evaluations=[ | |
{ | |
"ComplianceResourceType": | |
configuration_item["resourceType"], | |
"ComplianceResourceId": | |
configuration_item["resourceId"], | |
"ComplianceType": | |
evaluate_compliance(configuration_item, rule_parameters), | |
"Annotation": | |
"The user has never logged in.", # COMPIANTの場合も同じ Annotation となっています | |
"OrderingTimestamp": | |
configuration_item["configurationItemCaptureTime"] | |
}, | |
], | |
ResultToken=result_token | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"recordVersion": "1.3", | |
"configurationItem": { | |
"relationships": [ | |
{ | |
"resourceType": "AWS::IAM::Policy", | |
"resourceId": "ANPAJUAZCQRMVSYFQNJPI", | |
"name": "Is attached to CustomerManagedPolicy", | |
"resourceName": "trainexpenser-codecommit-readonly" | |
} | |
], | |
"configurationItemCaptureTime": "2018-07-05T15:22:21.203Z", | |
"availabilityZone": "Not Applicable", | |
"configurationStateMd5Hash": "", | |
"tags": {}, | |
"resourceType": "AWS::IAM::User", | |
"configurationItemVersion": "1.3", | |
"configurationStateId": 1530804141203, | |
"relatedEvents": [], | |
"awsRegion": "global", | |
"ARN": "arn:aws:iam::xxxxxxxxxxxx:user/trainexpenser-codecommit-readonly", | |
"supplementaryConfiguration": {}, | |
"resourceName": "trainexpenser-codecommit-readonly", | |
"configuration": { | |
"userName": "trainexpenser-codecommit-readonly", | |
"groupList": [], | |
"createDate": "2017-12-01T05:01:21.000Z", | |
"userId": "AIDAIKBNZFI2LJRYEOI3G", | |
"userPolicyList": null, | |
"path": "/", | |
"attachedManagedPolicies": [ | |
{ | |
"policyName": "trainexpenser-codecommit-readonly", | |
"policyArn": "arn:aws:iam::xxxxxxxxxxxxx:policy/trainexpenser-codecommit-readonly" | |
} | |
], | |
"arn": "arn:aws:iam::xxxxxxxxxxxxx:user/trainexpenser-codecommit-readonly" | |
}, | |
"resourceId": "AIDAIKBNZFI2LJRYEOI3G", | |
"resourceCreationTime": "2017-12-01T05:01:21.000Z", | |
"configurationItemStatus": "ResourceDiscovered", | |
"awsAccountId": "xxxxxxxxxxxx" | |
}, | |
"notificationCreationTime": "2018-07-05T15:22:49.724Z", | |
"messageType": "ConfigurationItemChangeNotification", | |
"configurationItemDiff": null | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"Version": "2012-10-17", | |
"Statement": [ | |
{ | |
"Action": "sts:AssumeRole", | |
"Principal": { | |
"Service": "lambda.amazonaws.com" | |
}, | |
"Effect": "Allow", | |
"Sid": "" | |
} | |
] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment