3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Boxの監査ログをAWS ElasticsearchServiceに投げ込む

Last updated at Posted at 2019-01-30

条件

  • BoxをBusiness以上で利用している

目的

  • Boxの管理コンソールには出力されない詳細な監査ログを取りたい
    • Downloadされた
    • ファイルを更新した
    • 外部コラボレーターを作成した
    • etc .....

監査ログの置き場

実行環境とフロー

  • AWS
    • CloudWatch ルールでLambda(Node.js)を定周期起動
    • BoxApiを利用して、取得した監査ログをElasticsearchServiceに投げ込む
    • Kibanaで簡単に可視化

Box側の設定

  • BoxApiを利用するにはBoxアプリを作成する必要がある。
  • 詳しい解説記事が既にQiitaにあるのでこちら参照して作成してください(ステップ4まで実行すれば大丈夫)

AWS側の事前準備

  • ElasticsearchServiceの作成
    • indexまで作っておきましょう
  • S3バケットの用意
    • BoxAPIで監査ログを取得する際の次回読み込みポジションを保存しておく領域です。
    • 当然S3じゃなくても大丈夫ですが今回はS3で行きます!
  • credentials の用意
    • IAMユーザのcredentialsを用意
      • :arrow_up: のS3バケットへのアクセス権

ローカルで試してみよう

npmインストール

  • aws-sdk
  • box-node-sdk
    • BoxのNode.jp用SDK
  • elasticsearch
    • elasticsearchのNode.jp用SDK
  • dotenv
$ mkdir box-auditlogs-node-es
$ cd box-auditlogs-node-es
$ npm install aws-sdk box-node-sdk dotenv elasticsearch

ソース

  • 下記構成でそれぞれ作成してください

tree

.
├── index.js
├── main.js
├── .env
└── package.json

index.js

index.js
'use strict';

const aws = require('aws-sdk');
const elasticsearch = require('elasticsearch');
const BoxSDK = require('box-node-sdk');

// AWS Setting
aws.config.region = process.env.AWS_REGION;
if (process.env.LOCAL) { // This part is unnecessary when not using profile
    var credentials = new aws.SharedIniFileCredentials({profile: process.env.LOCAL_PROFILE});
    aws.config.credentials = credentials;
}

// Box Setting
const boxConfig = JSON.parse(process.env.BOX_CONFIG);
boxConfig.boxAppSettings.appAuth.keyID = boxConfig.boxAppSettings.appAuth.publicKeyID;
const sdk = new BoxSDK(boxConfig.boxAppSettings);
const client = sdk.getAppAuthClient('enterprise', boxConfig.enterpriseID);

// Elasticsearch
var ES_INDEX = process.env.ES_INDEX; // Elasticsearch index name
var ES_TYPE = process.env.ES_TYPE; // Elsticsearch index type name
var ES_CLIENT = new elasticsearch.Client({
    host: process.env.ES_ENDPOINT
});

// S3 Setting
var s3 = new aws.S3();
var bucketName = process.env.S3_NEXT_POSITION_BUCKETNAME;
var fileName = 'next_position.txt';
var contentType = 'text/plain';

exports.handler = (event, context, callback) => {
    main();
};

async function main() {
    // get initial value [next_position] from S3 and generate parameters of BoxApi
    const s3_read_params = {
        Key: fileName,
        Bucket: bucketName,
    };
    var next_position = null;
    var box_params = {
        stream_type: 'admin_logs',
        limit: 500
    }

    try {
        const res = await s3.getObject(s3_read_params).promise();
        next_position = res.Body.toString('ascii');
        box_params.stream_position = next_position;
    } catch(err) {
        console.error(err);
        box_params.created_after = process.env.BOX_START_DATE;
    }

    // Call BoxApi
    client.events.get(box_params, function(err, stream) {
        if (err) {
            console.log("error");
        }
    }).then(event => {
        var next_position = event.next_stream_position;
        // Send to Elasticsearch
        if (event.chunk_size != 0){
            sendToES(event.entries);
        }
        // Upload the next_position file to S3
        var params = {
            Key: fileName,
            Body: next_position,
            Bucket: bucketName,
            ContentType: contentType,
        };
        s3.putObject(params, function(err, data) {
            if (err) {
                console.log("Error uploading data: ", err);
            } else {
                console.log("Successfully uploaded data to " + bucketName + "/" + fileName);
            }
        });
    });
}

// bulk send to Elasticsearch
function sendToES(records){
    var searchRecords = [];
    for(var i = 0; i < records.length; i++){
        var record = records[i];
        var header = {
            "index":{
                "_index": ES_INDEX,
                "_type": ES_TYPE,
            }
        };
        var searchRecord = {};
        searchRecords.push(header);
        searchRecords.push(record);
    };
    ES_CLIENT.bulk({
        "body": searchRecords
    }, function(err, resp){
        if(err){
            console.log(err);
        }else{
            console.log(resp);
        };
    });
};

main.js

  • ローカルでlambdaっぽく呼ぶやつ
main.js
var event = {
    version: '0',
    id: 'XXXXXXXX-XXXXX-XXXX-XXXXX-XXXXXXXX',
    'detail-type': 'Scheduled Event',
    source: 'aws.events',
    account: 'XXXXXXXXXX',
    time: '2019-01-30T06:27:04Z',
    region: 'ap-northeast-1',
    resources:
        [ 'arn:aws:events:ap-northeast-1:XXXXXXXXXX:rule/box-auditlog-exec' ],
    detail: {}
};

var context = {
    invokeid: 'invokeid',
    done: function(err,message){
        return;
    }
};

var lambda = require("./index");
lambda.handler(event,context);

.env

  • ローカル環境変数を記述するので、こちらに自分の環境を記載してください
.env
BOX_CONFIG={   "boxAppSettings": {     "clientID":  "   } }
BOX_START_DATE=2019-01-01T00:00:00+09:00
AWS_REGION=ap-northeast-1
S3_NEXT_POSITION_BUCKETNAME=mybucket
LOCAL=true
LOCAL_PROFILE=AWS_credentials_profile_name
ES_INDEX=Elasticsearch_Index_name
ES_TYPE=Elasticsearch_Type_name
ES_ENDPOINT=https://xxxxxxxxxxxxxxxx.ap-northeast-1.es.amazonaws.com/
  • BOX_CONFIG:これ
  • BOX_START_DATE:Boxを利用し始めた日時
  • S3_NEXT_POSITION_BUCKETNAME:AWS側の事前準備で作成したバケット名
  • credentialを利用する場合 LOCAL:trueのままで大丈夫
  • credentialを利用する場合 LOCAL_PROFILE:credentialのprofile名
  • ES_INDEX:Elasticsearchのインデックス名
  • ES_TYPE:ElasticsearchのType名
  • ES_ENDPOINT:Elasticsearchのエンドポイント

実行

$ node -r dotenv/config main.js

うまくいけばKibanaでこんな感じで見ることができる

  • グラフとかは適当に自分で作ってください
qiita1.png

AWSにアップして動かしてみよう

事前準備

  • lambda関数作成

    • ランタイム:Node.js V8.10
    • 環境変数:内容はローカルと同じ(LOCAL、LOCAL_PROFILEは必要無い)
    qiita2.png * タイムアウト:3分 qiita3.png

AWSへデプロイ

  • 今回はS3を経由しているが、S3を経由する必要は特に無い
$ zip -r box-auditlogs-node-es.zip index.js node_modules
$ aws s3 cp ./box-auditlogs-node-es.zip s3://[mybucket]/box-auditlogs-node-es.zip --profile [myprofile]
$ aws lambda update-function-code --function-name box-auditlogs-node-es --s3-bucket [mybucket] --s3-key box-auditlogs-node-es.zip --publish --profile [myprofile]

実行

  • CloudWatch ルールに、lambdaを指定して引数無しで起動する。
    • 周期はお好きなように
    スクリーンショット 2019-01-30 16.38.31.png

ソース置き場

こちらにソース置いておきます。(Node.jpを書くの初めてなので、ソース汚かったらPullReqください)

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?