AWS Lambdaで自動化

AWSクラウドでコマンダーを実行

概説

コマンダーは、さまざまな問題を解決し、Keeper Security環境に関する貴重な情報が得られる強力なツールです。ローカルのデスクトップやサーバーで使用するだけでなく、AWSのようなクラウド環境でコマンダーを実行して、スケジュールされたまたはオンデマンドの要件を実行することも可能です。

この例では、コマンダーをAWS Lambdaと連携させ、ユーザーおよび使用状況のレポートを定期的に実行する方法を解説します。

要件

  • コマンダー

  • マスターパスワードログイン方式を使用するKeeperユーザーアカウント (SSOログインとMFAはユーザー操作なしでは利用できません)。

  • AWSでLambda関数および関連するレイヤーを作成する権限、ならびにAWS CloudShellへのアクセス権限が必要です。

手順

Cloud 9を使用してLambdaレイヤーを作成

セットアップ

コマンダーは、AWS Lambdaで実行する環境に一致するマシンでパッケージ化する必要があります。そのためにCloudShellでコマンダーパッケージを作成することができます。

AWS CloudShellにはPython 3.7があらかじめインストールされていますが、Lambdaレイヤーを構築する際にサポートされているPythonのバージョン (3.6~3.11) を使用していることを確認するために、CloudShellコンソールでインストールされているPythonインタープリタのバージョンを確認してください。

$ python3 --version
Python 3.9.16

使用中のPythonのバージョンがCommanderでサポートされていない場合は、上記のサポートされているバージョンのいずれかをインストールしてから作業をお進めください。

レイヤーコンテンツの構築

次の段階では、CloudShell環境内で実行できる便利なシェルスクリプトをご用意しました。このスクリプトは、Lambdaレイヤー用に必要なkeepercommanderパッケージを含むZIPファイルを作成します。

このスクリプトは、さまざまなコマンド呼び出しをまとめるだけでなく、keepercommanderパッケージとその依存関係に特有のビルドプロセスの細かな問題を抽象化することで、レイヤーコンテンツのパッケージングを簡略化し、効率化することを目的としています。エラーが発生しにくいため、この方法を使用することを強くお勧めします。

スクリプトを見る
package_layer_content.sh
#!/usr/bin/env bash

# AWS Lambda 関数用の `keepercommander` 依存関係レイヤーを作成する
# 
# 1. このスクリプトを CloudShell 環境内の任意のフォルダにアップロードします。
# 2. (オプション) プロジェクトの `requirements.txt` ファイルを同じフォルダにアップロードします。
# 3. そのフォルダ内で次のコマンドを実行します:
#    source ./package_layer_content.sh
# 4. 実行後、同じフォルダ内に `commander-layer.zip` という名前のファイルが作成されます。
#    この ZIP ファイルを S3 バケットにアップロードし、Lambda レイヤーの作成に使用します。

MAX_LIB_SIZE=262144000
LAYER_FILENAME='commander-layer.zip'
LAYER_PATH=$(pwd)/$LAYER_FILENAME
LIB_DIR='python'
VENV='commander-venv'
OTHER_DEPS='requirements.txt'

# 過去のアーティファクトをクリーンアップ
test -f $LAYER_FILENAME && rm $LAYER_FILENAME
test -d $LIB_DIR && rm -rf $LIB_DIR
test -d $VENV && rm -rf $VENV

# ZIPファイル用のパッケージフォルダを作成
mkdir $LIB_DIR

# 仮想環境を作成して実行
python -m venv $VENV
source ./$VENV/bin/activate

# 依存関係をインストールしてパッケージ化
pip install cryptography --platform manylinux2014_x86_64 --only-binary=:all: -t $LIB_DIR
pip install keepercommander -t $LIB_DIR

if test -f $OTHER_DEPS; then
  pip install -r $OTHER_DEPS -t $LIB_DIR
fi

deactivate

# 解凍後のライブラリサイズを確認
LIB_SIZE=$(du -sb $LIB_DIR | cut -f 1)
LIB_SIZE_MB=$(du -sm $LIB_DIR | cut -f 1)

if [ "$LIB_SIZE" -ge $MAX_LIB_SIZE ]; then
  echo "*****************************************************************************************************************"
  echo 'Operation was aborted'
  echo "The resulting layer has too many dependencies and its size ($LIB_SIZE_MB MB) exceeds the maximum allowed (~262 MB)."
  echo 'Try breaking up your dependencies into smaller groups and package them as separate layers.'
  echo "*****************************************************************************************************************"
else
  zip -r $LAYER_FILENAME $LIB_DIR
  echo "***************************************************************************"
  echo "***************************************************************************"
  echo 'Lambda layer file has been created'
  printf "To download, copy the following file path: %s\n%s\n$LAYER_PATH%s\n%s\n"
  echo 'and click on "Actions" in the upper-right corner of your CloudShell console'
  echo "***************************************************************************"
fi

# クリーンアップ
rm -rf $LIB_DIR
rm -rf $VENV

上記のスクリプトを使用するには、以下を実行します。

  1. スクリプトをCloudShell環境内の任意のフォルダ(できれば空のフォルダ)にアップロードします。

    • (オプション) プロジェクトに依存関係のリストが含まれているrequirements.txtファイルがある場合、そのファイルを同じフォルダにアップロードし、keepercommanderパッケージに加えて依存関係も結果のレイヤーに含めることができます。

  2. 同じフォルダ内で、ターミナルで以下のコマンドを実行します。

source ./package_layer_content.sh
  1. 現在のフォルダにcommander-layer.zipというZIPファイルが作成されているはずです。このファイルが、Lambdaレイヤーのコンテンツを表しています。

Lambdaレイヤーのコンテンツにはサイズ制限があります (S3に保存されている場合でも)。上記のスクリプトでrequirements.txtファイルで追加の依存関係を含めようとしてその結果コンテンツの総サイズが制限を超えると、作成されたZIPファイルが使用できなくなります。この状況が検出された場合、スクリプトはレイヤーコンテンツを出力せず、代わりにメッセージが表示されます。

比較的簡単な解決策としては、依存関係を小さなグループに分割し、それぞれを個別のレイヤーにパッケージ化します。requirements.txtからいくつか (またはすべて) の依存関係を削除し、再度スクリプトを実行することができます。その結果、パッケージに含まれなかった依存関係は、標準のパッケージングプロセスを使用して別のレイヤーとしてパッケージ化できます。

コンテンツZipファイルからのレイヤーの作成と更新

結果として生成されるZIPファイルは50MB以上 (Lambdaレイヤーに直接アップロードできる最大サイズ) になるため、最初にAWS S3バケットにアップロードし、その後、生成されたS3アイテムをLambdaレイヤーにリンクする必要があります。

残りの手順を完了する方法はいくつかありますが、GUIベースの方法を好まれる場合はAWSコンソールをご使用ください。ただし、すでにCloudShell環境内にいるため、組み込みのAWS CLIコマンドラインツールを使用するのが最も簡単ですので、ここではその方法をご紹介します。

  1. まず、ZIPファイルをAWS S3にアップロードする必要があります。このタスク用にS3バケットをまだ作成していない場合は、CloudShellで以下のコマンドを実行して作成できます。

$ aws s3 mb <bucket-name>

ここで、 <bucket-name>はグローバルで固有の名前である必要があります。

  1. 新しくパッケージしたZipファイルをCloudShellからS3バケットへアップロードします。

$ aws s3 cp ./commander-layer.zip 's3://<bucket-name>'
  1. アップロードしたコンテンツでLambdaレイヤーを公開します。

$ aws lambda publish-layer-version --layer-name <layer-name> \
--description <layer-description> \
--content "S3Bucket=<bucket-name>,S3Key=commander-layer.zip" \
--compatible-runtimes python<your-version>

Lambdaの作成

AWS Lambdaで、Lambdaエディタを使用してPython関数を作成します。

lambda_handler関数は、処理される際にLambdaによって呼び出されます。

以下は、コマンダーLambda関数の完全な例となります。

#  _  __
# | |/ /___ ___ _ __  ___ _ _ ®
# | ' </ -_) -_) '_ \/ -_) '_|
# |_|\_\___\___| .__/\___|_|
#              |_|
#
# Keeper Commander
# Copyright 2023 Keeper Security Inc.
# Contact: ops@keepersecurity.com
#
#
import json
import os
import datetime
from typing import Optional

from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

import boto3

from keepercommander import api
from keepercommander.commands.enterprise import UserReportCommand, SecurityAuditReportCommand
from keepercommander.params import KeeperParams

# 既存の2つのCommanderレポートのデータを組み合わせて、レポートを作成 (形式:JSON)
def create_user_report(params):  # タイプ:(KeeperParams) -> オプション[str]
    user_report = UserReportCommand()
    user_report_data = user_report.execute(params, format='json')
    data = json.loads(user_report_data)
    users = {x['email']: x for x in data}
    security_audit_report = SecurityAuditReportCommand()
    security_audit_report_data = security_audit_report.execute(params, format='json')
    if security_audit_report_data:
        data = json.loads(security_audit_report_data)
        for x in data:
            if 'email' in x:
                email = x['email']
                if email in users:
                    user = users[email]
                    for key in x:
                        if key not in user:
                            if key not in ('node_path', 'username'):
                                user[key] = x[key]
                else:
                    users[email] = x

    return json.dumps(list(users.values()), indent=2)


# このLambdaのエントリポイント
def lambda_handler(event, context):
    params = get_params()
    api.login(params)
    
    # AWSの組み込みロギングモジュールを使用して、Commander関連の問題 (無効なクレデンシャルや不適切なアカウント権限など) をログに記録して異常終了
    if not params.session_token:
        print('Not connected') 
        return 'Error:See Lambda log for details'
    if not params.enterprise:
        print('Not enterprise administrator')
        return 'Error:See Lambda log for details'
    
    # レポートの生成と送信 
    report = create_user_report(params)
    response = email_result(report)
    return response


# このLambdaの環境変数で指定された受信者にレポートデータを (JSON添付ファイルとして) メールで送信
def email_result(report):
    sender = os.environ.get('KEEPER_SENDER')
    sendto = os.environ.get('KEEPER_SENDTO')
    region = 'us-east-1'
    ses_client = boto3.client('ses', region_name=region)

    message = MIMEMultipart('mixed')
    message['Subject'] = 'Keeper Commander User Security Report With  CSV (attached)'
    message['From'] = sender
    message['To'] = sendto
    now = datetime.datetime.now()

    body = MIMEText(f'User Report Output created and sent at {now}', 'plain')
    message.attach(body)

    attachment = MIMEApplication(report)
    attachment.add_header(
        'Content-Disposition',
        'attachment',
        filename='user-report.json'
    )
    message.attach(attachment)

    response = ses_client.send_raw_email(
        Source=message['From'],
        Destinations=[sendto],
        RawMessage={'Data': message.as_string()}
    )
    
    return response


# 環境変数から必要なCommanderパラメータを取得
def get_params():
    user = os.environ.get('KEEPER_USER')
    pw = os.environ.get('KEEPER_PASSWORD')
    server = os.environ.get('KEEPER_SERVER')
    private_key = os.environ.get('KEEPER_PRIVATE_KEY')
    token = os.environ.get('KEEPER_DEVICE_TOKEN')
    my_params = KeeperParams()
    
    # # パスワードログインを強制する(SSO + マスターパスワードアカウントに必要)
    my_params.config = {'sso_master_password': True}
    
    my_params.user = user
    my_params.password = pw
    my_params.server = server
    my_params.device_private_key = private_key
    my_params.device_token = token
    return my_params

この関数は以下の要素で構成されています。

lambda_handler関数

この関数は、lambdaが実行されたときに呼び出されます。他のすべての関数は、この関数から呼び出す必要があります。

コマンダーパラメータの収集

コマンダーは、ユーザーアカウントを認証するために複数のパラメータを使用します。 これらを環境変数として添付し、Lambdaに注入します。 以下の説明をご参照ください。

コマンダー関数

上記の手順は全て、Lambdaでコマンダーを実行するために必要となります。これらの手順が完了すると、コマンダーSDKコードを実行できるようになります。

この例では、ユーザーステータスレポートを実行し、結果をメールアドレスに送信します。

コマンダーパラメータの設定

コマンダーは、複数のパラメータを使用してユーザーアカウントを認証し、アクセスするKeeperリージョンを識別します。 これらのパラメータをLambdaに渡すために、環境変数として設定します。

パラメータの収集

CLIにログインすると、コマンダーが必要なパラメータを自動的に作成してくれます。 必要なパラメータを生成するには、マシンでコマンダーのCLIにログインするのが最も簡単です。

コマンダーパラメータを取得するには、生成されたconfig.jsonファイルを開きます。 デフォルトでは、このファイルはマシンのUsers/[your username]/.keeper/フォルダにあります。 詳細は、設定ファイルのページをご参照ください。

以下のようなファイルが表示されます。

{
    "clone_code":"36[...]A0g",
    "user": "user@example.com",
    "server": "keepersecurity.com",
    "private_key": "sxv[...]oz3p=fzw",
    "device_token": "xko[...]r2IxdiQ"
}

AWS Lambdaのパラメータ設定

必要なコマンダーパラメータを環境変数として設定するには、まず、lambda設定に移動し、[Environment Variables] (環境変数) を選択します。

コマンダーの各パラメータをLambdaで使用する環境変数として設定します。

また、コマンダーへのログインに使用するKeeperのマスターパスワードも追加する必要があります。

環境変数名

KEEPER_USER

Keeperユーザーアカウントのメールアドレス (設定のuserフィールド)

user@example.com

KEEPER_SERVER

Keeperサーバーのドメイン (設定のserverフィールド)

keepersecurity.com

KEEPER_CLONE_CODE

設定のclone_codeフィールド

36df3[...]A0dsa4g

KEEPER_PRIVATE_KEY

設定のprivate_keyフィールド

sxv[...]oz3p=fzw

KEEPER_DEV_TOKEN

設定のdevice_tokenフィールド

xko[...]r2IxdiQ

KEEPER_PASSWORD

Keeperアカウントのパスワード

*****

KEEPER_SENDER

メールの送信元のアドレス

user@example.com

KEEPER_SENDTO

メールの送信先のアドレス

receiver@example.com

このコード例では、環境変数を使用して、送信元と送信先のメールアドレスを設定しています。 スクリプトでメールを送信しない場合、これらは必要ありません。

Lambdaの構成

タイムアウトの設定

Lambda構成の一般設定セクションで、タイムアウト値を変更することをお勧めします。一部のコマンダー関数は実行に時間がかかるため、スクリプトの完了にこの数値よりも長い時間がかかる場合、Lambdaは完了せずに自動的に終了してしまいます。

途中で終了する心配のない数値に設定してください。300秒 (5分) に設定すると十分でしょう。

レイヤーの選択

Lambdaエディタで、上記で作成したレイヤーを選択して、Lambdaビルドにコマンダーパッケージを追加します。

実行スケジュールの作成

Lambdaを呼び出すEventCloudトリガーを作成し、適切な頻度 (たとえば、1日に1回や30日に1回) で動作するように設定します。

AWSでは、メールやSMSによるトリガーなど、他のいくつかのソースからLambdaを呼び出すように設定することもできます。その他のオプションについては、Lambdaの呼び出しに関するAmazonのドキュメントをご参照ください。

メール送信の設定

この例では、レポート結果がメールで送信されます。メールを有効にするには、LambdaがSESのSendEmail/SendRawEmailサービスにアクセスできるようにする必要があります。

さらに、メール送信を有効にするには、IAM IDを作成する必要があります。

AWSでのメール送信の設定の詳細については、Amazonのドキュメントをご参照ください。

この例では、レポート結果をメールで送信します。メールを有効にするには、LambdaがSESのSendEmail/SendRawEmailサービスにアクセスできるようにする必要があります。

次の手順

この例では、2つのコマンダーレポート (security-reportsecurity-audit-report) の結果を組み合わせたレポートを実行し、JSON添付ファイルとしてメールで送信します。これにより、企業全体のセキュリティ監視データ (社内の各ユーザーの最終ログイン日や総合セキュリティスコアなど) を定期的かつ自動的に取得できます。 ただし、この設定では、その他の一連のコマンダー関数はLambdaで実行できます。

他のコマンダー機能、Lambda呼び出しメソッド、その他のAWSサービス (プッシュ通知の様々な方法を利用できるSNSなど、たとえば、SMSメッセージ) を試すことで、Keeperプロセスに自動化の恩恵がもたらされます。

コマンダーSDKコードの使用例については、コマンダーGitHubリポジトリの以下のサンプルスクリプトをご参照ください。

コマンダーのさまざまなメソッドの詳細については、コマンドリファレンスのページをご参照ください。

最終更新