用Python抓取亞馬遜雲(AWS)的日誌(CloudTrail)數據


如今是雲的時代,許多公司都把自己的IT架構部署在基礎架構雲(IaaS)上。著名的IaaS提供商有亞馬遜,微軟(Azure),IBM等,國內也有諸如阿里雲等。這裏亞馬遜毫無疑問是該市場的領軍者。

AWS提供了非常多的服務,領先了競爭對手一大截。並且AWS提供非常豐富的API,其API基於Rest,所以很容易被不同的語言的平臺來調用。

在如今的大數據時代,利用數據在做決策是大數據的核心價值,AWS提供了許多服務來獲取其運行數據cloudtrail和cloudwatch是經常被用到的兩個。CloudTrail是對AWS的所有API調用的日誌,CloudWatch是監控AWS服務的性能數據。(新出的Config服務可用於監控AWS的資源變化)

今天我們來看看如何使用Python(Boto AWS的開源Python SDK)來自動配置ClouTrail的服務並獲取日誌內容。

我們先來看看CloudTrail的概念和相關的配置。

  • S3 Bucket

    在打開CloudTrail的服務時,需要指定一個相關的S3的Bucket,S3是亞馬遜提供的存儲服務,你可以把它當作一個基於雲的文件系統。CloudTrail的API調用日誌,會以壓縮文件的形式,存儲在你指定的Bucket裏。

  • SNS

    SNS是亞馬遜提供的通知服務,該服務使用的是訂閱/發佈(Subsrcibe/Publish)的模式。在創建CloudTrail的時候,可以關聯一個SNS的Topic(可選),這樣做的好處是當有API調用時,可以第一時間得到通知。可以使用不同的客戶端來訂閱SNS的通知,例如Email,Mobile的Notification Service,SQS等

  • SQS

    SQS是亞馬遜提供的隊列服務,在本文中,我們使用SQS訂閱SNS的的內容,這樣我們的Python程序就可以從SQS的隊列中獲取相應的通知。


配置CloudTrail

首先我們需要創建SNS,並指定相應的策略。代碼如下:

import boto.sns
import json

key_id='yourawskeyid'
secret_key='yourawssecretkey'

region_name="eu-central-1"
trail_topic_name="topicABC"
sns_policy_sid="snspolicy0001"

sns_conn = boto.sns.connect_to_region(region_name,
                                         aws_access_key_id=key_id,
                                         aws_secret_access_key=secret_key)

sns_topic = sns_conn.create_topic(trail_topic_name)

# Get ARN of SNS topic
sns_arn = sns_topic['CreateTopicResponse']['CreateTopicResult']['TopicArn']

# Add related policy
attrs = sns_conn.get_topic_attributes(sns_arn)
policy = attrs['GetTopicAttributesResponse']['GetTopicAttributesResult']['Attributes']['Policy']
policy_obj = json.loads(policy)
statements = policy_obj['Statement']

default_statement = statements[0]
new_statement = default_statement.copy()
new_statement["Sid"] = sns_policy_sid
new_statement["Action"] = "SNS:Publish"
new_statement["Principal"] = {
        "AWS": [
          "arn:aws:iam::903692715234:root",
          "arn:aws:iam::035351147821:root", 
          "arn:aws:iam::859597730677:root",
          "arn:aws:iam::814480443879:root",
          "arn:aws:iam::216624486486:root",
          "arn:aws:iam::086441151436:root",
          "arn:aws:iam::388731089494:root",
          "arn:aws:iam::284668455005:root",
          "arn:aws:iam::113285607260:root"
        ]
      }
new_statement.pop("Condition", None)
statements.append(new_statement)
new_policy = json.dumps(policy_obj)
sns_conn.set_topic_attributes(sns_arn,"Policy",new_policy)


CloudTrail是和區域(Region)相關的,不同的Region有不同的CloudTrail服務,所以,在創建對應的SNS時,需要保證使用同一個Region。

這裏要注意的是我們創建了新的policy來使得CloudTrail擁有向我們創建的SNS發佈消息(Action=“SNS:Publish”)的權限。我們的做法是從缺省的策略中拷貝了一份,修改了相應的Action和Sid(隨便取一個不重複的名字),Principal部分是一個缺省的account的列表,這裏是硬編碼,AWS有可能會修改該列表的值,但在當前環境下,該值是固定的。最後移除Condition的值。把新創建的Policy片段添加到原來的Policy中就好了。

然後我們需要創建一個SQS的隊列,並訂閱我們創建的SNS的Topic。這一步相對比較簡單。

import boto.sqs

sqs_queue_name="sqs_queue"
sqs_conn = boto.sqs.connect_to_region(region_name,
                                         aws_access_key_id=key_id,
                                         aws_secret_access_key=secret_key)
sqs_queue = sqs_conn.create_queue(sqs_queue_name)
sns_conn.subscribe_sqs_queue(sns_arn, sqs_queue)


然後,我們需要創建一個S3的Bucket用來存儲CloudTrail產生的日誌文件。同樣的,需要指定響應的策略以保證CloudTrail能夠有權限寫入對應的日誌文件。

import boto

bucket_name="bucket000"
policy_sid="testpolicy000"
s3_conn = boto.connect_s3(aws_access_key_id=key_id,aws_secret_access_key=secret_key)
bucket = s3_conn.create_bucket(bucket_name)
bucket_policy = '''{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "%Sid%GetPolicy",
			"Effect": "Allow",
			"Principal": {
				"AWS": [
					"arn:aws:iam::903692715234:root",
					"arn:aws:iam::035351147821:root",
					"arn:aws:iam::859597730677:root",
					"arn:aws:iam::814480443879:root",
					"arn:aws:iam::216624486486:root",
					"arn:aws:iam::086441151436:root",
					"arn:aws:iam::388731089494:root",
					"arn:aws:iam::284668455005:root",
					"arn:aws:iam::113285607260:root"
				]
			},
			"Action": "s3:GetBucketAcl",
			"Resource": "arn:aws:s3:::%bucket_name%"
		},
		{
			"Sid": "%Sid%PutPolicy",
			"Effect": "Allow",
			"Principal": {
				"AWS": [
					"arn:aws:iam::903692715234:root",
					"arn:aws:iam::035351147821:root",
					"arn:aws:iam::859597730677:root",
					"arn:aws:iam::814480443879:root",
					"arn:aws:iam::216624486486:root",
					"arn:aws:iam::086441151436:root",
					"arn:aws:iam::388731089494:root",
					"arn:aws:iam::284668455005:root",
					"arn:aws:iam::113285607260:root"
				]
			},
			"Action": "s3:PutObject",
			"Resource": "arn:aws:s3:::%bucket_name%/*",
			"Condition": {
				"StringEquals": {
					"s3:x-amz-acl": "bucket-owner-full-control"
				}
			}
		}
	]
}'''
bucket_policy = bucket_policy.replace("%bucket_name%",bucket_name)
bucket_policy = bucket_policy.replace("%Sid%",policy_sid)
bucket.set_policy(bucket_policy)


這裏我們使用一個缺省的Policy文件,替換掉響應的字段就好了。

最後,我們創建CloudTrail的服務:

import boto.cloudtrail

trail_name="Trailabc"
log_prefix="log"

cloudtrail_conn=boto.cloudtrail.connect_to_region(region_name,
                                         aws_access_key_id=key_id,
                                         aws_secret_access_key=secret_key)

##cloudtrail_conn.describe_trails()
cloudtrail_conn.create_trail(trail_name,bucket_name, s3_key_prefix=log_prefix,sns_topic_name=trail_topic_name)
cloudtrail_conn.start_logging(trail_name)


好了,現在CloudTrail已經配置好了,並且關聯的SNS也被我們創建的SQS隊列訂閱,下面我們就可以抓取日誌了

獲取日誌數據

每當有一個API調用,CloudTrail都會把響應的日誌文件寫入到S3我們創建的Bucket中,同時在我們在創建的SNS的topic中發佈一條消息,因爲我們使用SQS的隊列訂閱了該消息,所以我們可以通過讀取SQS消息的方式來獲得日誌數據。

首先連接到SQS的隊列,並從中讀取消息

import boto.sqs

sqs_queue_name="sqs_queue"
sqs_conn = boto.sqs.connect_to_region(region_name,
                                         aws_access_key_id=key_id,
                                         aws_secret_access_key=secret_key)
                                         
sqs_queue = sqs_conn.get_queue(sqs_queue_name)
notifications = sqs_queue.get_messages()


然後我們從消息中獲得響應的日誌文件在S3中的地址,並利用該地址從S3中獲得對應的日誌文件

for notification in notifications:
    envelope = json.loads(notification.get_body())
    message = json.loads(envelope['Message'])
    bucket_name = message['s3Bucket']
    s3_bucket = s3_conn.get_bucket(bucket_name)
    for key in message['s3ObjectKey']:
        s3_file = s3_bucket.get_key(key)
        with io.BytesIO(s3_file.read()) as bfile:
            with gzip.GzipFile(fileobj=bfile) as gz:
                logjson = json.loads(gz.read())


logjson就是對應的日記內容的JSON格式。這裏有一個例子

{
    "Records": [{
        "eventVersion": "1.0",
        "userIdentity": {
            "type": "IAMUser",
            "principalId": "EX_PRINCIPAL_ID",
            "arn": "arn:aws:iam::123456789012:user/Alice",
            "accessKeyId": "EXAMPLE_KEY_ID",
            "accountId": "123456789012",
            "userName": "Alice"
        },
        "eventTime": "2014-03-06T21:22:54Z",
        "eventSource": "ec2.amazonaws.com",
        "eventName": "StartInstances",
        "awsRegion": "us-west-2",
        "sourceIPAddress": "205.251.233.176",
        "userAgent": "ec2-api-tools 1.6.12.2",
        "requestParameters": {
            "instancesSet": {
                 "items": [{
                      "instanceId": "i-ebeaf9e2"
                }]
            }
        },
        "responseElements": {
            "instancesSet": {
                "items": [{
                      "instanceId": "i-ebeaf9e2",
                      "currentState": {
                          "code": 0,
                          "name": "pending"
                      },
                      "previousState": {
                          "code": 80,
                          "name": "stopped"
                      }
                    }]
            }
        }
    },
    ... additional entries ...
	]
}


你可以使用以上代碼來監控所有的cloudtrail的日誌,拿到的JSON格式的日誌可以放在你的數據庫(Mongo不錯)中,然後利用你的BI工具做分析。

注意你也可以不創建SNS和SQS,直接掃描bucket的內容,這樣做的好處是配置更簡單,缺點是實時性比較差,掃面Bucket需要額外的計算,並且需要在本地保存文件掃描的狀態,code會更加複雜。

利用CloudTrail的日誌,你可以做很多事情,比如看看有沒有非法的登陸,各個服務的使用頻率,總之,當你有了足夠多的數據,你就可以從中發現足夠的價值。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章