Lambdaのポイントメモ

概要

Lambdaで開発する時に、「あれどうだっけな」ってなった時のメモです。

Lambda関数の種類

  • 同期呼び出し

    • ストリームベース

      • Kinesis Streams
      • DynamoDB Streams
    • それ以外
      • Cognito
      • Echo
      • Lex
      • API Gateway
  • 非同期呼び出し
    • S3
    • SNS
    • CloudFormation
    • CloudWatchLogs
    • CloudWatchEvents
    • SES
    • CodeCommit
    • Config

イベントソース

ストリームベースのイベントソース

  • 通常のイベントソースはイベントソースがLambda関数を呼び出す。
  • ストリームベースのイベントソースは、Lambdaがストリームに対してポーリングを行ない、変更や新しいイベントの存在を検出したらレコードを取得し、Lambda関数を実行する。

アクセス権限

アイデンティティベースのポリシー

  • IAMユーザー、IAMグループ、IAMロールに対してアタッチするポリシー

リソースベースのポリシー

  • Lambdaに対してアタッチされるポリシー
  • Lambdaにイベントソースをセットアップして、サービスまたはイベントソースに対して、Lambda関数を呼び出すアクセス権限を付与する。
  • ポリシーの付与には、AWS CLIかAWS SDKを用いて指定する。

クロスアカウントアクセス

  • Lambda関数を所有するアカウントとそれを呼び出すアプリケーションやイベントソースのアカウントが別でも呼び出せる。

テスト

  • ユニットテストはローカルで実行、これ以外はクラウド上で実行する。

    • ユニットテストは比較的手元で実行したい事が多いテストだから。
  • Lambda関数をローカルで実行するた為に必要なもの
    • ローカルで呼び出す為のエントリポイント
    • イベント
    • contextオブジェクト

エントリポイント

  • ローカル環境で実行する際の呼び出しをどの様に行うか。
  • 実行関数を作って、if文で制御して普通に実行する。

イベントデータ

contextオブジェクト

  • contextオブジェクトには実行中のLambda関数のランタイム情報が格納されている。
  • ハンドラ内部からcontextオブジェクトの各プロパティならびにいくつかのメソッドにアクセスが可能。
  • 極論、ランタイム情報へのアクセスが不要であれば、ローカル実行時は特にエミュレートしなくとも良い。
  • 言語毎に用意されているプロパティ名やメソッド名は異なる

VPC

  • Lambda関数からVPC内にあるパブリックなIPを持っていないAWSリソースにアクセスする事が可能。

    • VPC内のみの接続にしてあるRDSの接続も可能
    • インターネットからアクセスがサポートされていないElastiCacheの様なサービスでも呼び出し可能

アクセス制御

  • EC2やRDSのLambda制御を行いたい場合は、セキュリティグループで行う事ができる。

レイテンシ

  • VPC内のリソースへのアクセスはレイテンシ増の原因となりやすい為、アクセスは最低限にするべき。

    • ENIの作成に時間がかかる。

ENIのレイテンシ

  • ENI は新規のリクエストや久しぶりのリクエストが発生した場合自動的にLambdaによって作成される。
  • 初期化処理に時間がかかる。(10~30秒)
  • 2回目以降は普通のレイテンシ

RDSとの接続の場合

  1. RDSにLambdaから直接レコードを書き込むのではなく、一旦DynamoDBにレコードを書き込み、その時点でレスポンスしてします。
  2. その後、DynamoDB ストリームとLambdaを利用して、非同期にRDSへとデータを反映する。

環境変数

  • 関数からアクセス可能な環境変数を定義する事が可能。

    • コードの変更なく動的に設定した値を渡せる。
    • 無駄なハードコーディングが不要になる。
  • 環境変数へのアクセスは言語毎に変わる。

環境変数の利用制限

  • 合計サイズが4KB
  • 文字で始める
  • 英数字とアンダースコアのみ
  • AWS Lambdaが予約する特定のキーセットは利用不可

Lambda関数自体が利用する環境変数

  • 予約語のうち、更新可能なものと更新不可のものがある。

環境変数の暗号化

  • キーと値はAWS KMS で自動的に暗号化されて保存される。

    • 独自のサービスキーを利用することも可能。

      • 別途IAMロールに対してポリシーが必要。

エラーハンドリング

  • 処理が異常終了した場合の振る舞いは、イベントソースのタイプと呼び出しタイプによって異なる。

    • 関数の実行がタイムアウト
    • 関数の呼び出しがスロットリング
    • 関数の処理でエラーor例外が発生

ストリームベースのイベントソースの場合

  • Amazon Kinesis StreamsもしくはDynamoDB Streamsがイベントソースの場合
  • レコードの有効期限が切れるまで再試行され続ける。
    • その間、エラーの発生したシャードからの読み取りはブロックされ、そのレコードのバッチの有効期限が切れるか処理が成功するまで、新しいレコードの読み込みは行われない。
    • レコードの処理順序が保証される。

ストリームベース以外のイベントソースの場合

同期呼び出しの場合

  • 呼び出し元にエラーが返される。
  • 呼び出し元はそのエラーを受け取り、必要に応じてリトライの処理を実装する。

非同期呼び出しの場合

  • 2回自動的にリトライされる。
  • 3回処理が失敗した場合、デッドレターキューが構成されていれば、そのイベントはデッドレターキューとして指定されているSQSのキューもしくはSNSのトピックに送信される。
    • これにより、障害発生時のイベントを取りこぼす事がなくなる
  • デッドレターキューが構成されていなければイベントは破棄される。

デッドレターキュー

  • デッドレターキューは、関数を非同期で呼び出す場合において、2回のリトライ、つまり3回の試行にも関わらず処理が正常終了しなかった場合にそのイベントを指定されたSQSもしくはSNSへと送る仕組み。

    • コードのロジックエラー
    • スロットリング
  • Lambdaでデッドレターキューを設定する前に、キューもしくはトピックを作成しておく必要がある。

終わり

今後も仕様が変わる度にしっかりキャッチアップしていきたいですね。

続きを読む

[EC2のスケジュール 停止] 会社から帰る時にスケジュール実行したlambdaからEC2(staging)をとめる

stgingで使っているEC2を停止するのを忘れてしまうということがよくありました。(主な犯人は僕です)手動で毎回止めるのもだるいし、人間は忘れっぽい生き物ですのでEC2のスケジュール停止をlambdaで実現しました。

設定の流れ

  1. EC2を停止するlambda関数を作成

    • EC2をlambdaから停止・起動するためのIAMのロールを作成
    • node.js で関数を実装
    • テスト
  2. CloudWatch Eventsでスケジュール設定

EC2を停止するlambda関数を作成

「一から作成」で新規関数を作成します。

https://gyazo.com/3a46795ca6ed2019674451fa7c40c265

lambdaからEC2の起動・停止をするにあたり、IAMのロールを作成します。

https://gyazo.com/890e460ddafeeb2aa003495293c8fe17

ポリシーを編集します。

https://gyazo.com/554b539050c1208cf8192ac161091941

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents",
                "ec2:StartInstances",
                "ec2:StopInstances"
            ],
            "Resource": [
                "arn:aws:logs:*:*:*",
                "arn:aws:ec2:*"
            ]
        }
    ]
}

EC2を停止するための関数コードを入力します。

const INSTANCE_ID = '*****';

var AWS = require('aws-sdk'); 
AWS.config.region = '******'; // 東京リージョン: ap-northeast-1

function ec2Stop(cb){
  var ec2 = new AWS.EC2();
  var params = {
    InstanceIds: [
      INSTANCE_ID
    ]
  };

  ec2.stopInstances(params, function(err, data) {
    if (!!err) {
      console.log(err, err.stack);
    } else {
      console.log(data);
      cb();
    }
  });
}

exports.handler = function(event, context) {
  console.log('start');
  ec2Stop(function() {
    context.done(null, 'Stoped Instance');
  });
};

この段階で「テスト」を実行して以下のようになれば成功です。

https://gyazo.com/88457ba6a983ca0785092d8ab60056c9

CloudWatch Eventsでスケジュール設定

トリガータブで新規のトリガーを追加します。

https://gyazo.com/dc62eb7532e618d4a67dd29558b73cbd

こんな感じで18時に自動で止まるようにします。

https://gyazo.com/ab5e6d877d9775a50b6af2cba6e68bb1

cronの設定はUTCで時間を設定する必要があるので cron(0 9 * * ? *) です。
平日だけなどのパターンは以下に記載されているので参考にしてください。

http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/tutorial-scheduled-events-schedule-expressions.html

まとめ

これでヒューマンエラー・めんどくさい作業なしでEC2が止まるようになりました。

参考にしたLambdaのScheduleイベントでEC2を自動起動&自動停止してみたのパクリになってしまいましたが、AWSのUIが変わっていたりしていたので、新しいUIのキャプチャでも貼っておくかということで手順をまとめてみました。

参考

LambdaのScheduleイベントでEC2を自動起動&自動停止してみた

続きを読む

Angular+Cognitoのユーザー認証付きSPAのサンプル

はじめに

Angularで作るSPAにCognito認証を組み込むサンプルを作りました。
ログイン・ログアウトだけではつまらないので、ログイン時に使えるS3ファイルアップロード機能も追加しています。
awslabsから公開されているaws-cognito-angular2-quickstartというサンプルが大変参考になりました。

作っていく

AWS側の準備

S3バケットの作成

Cognito Identity Pool に設定するロールの設定に、アクセス許可をするS3の情報を記載する必要があるので、事前にS3バケットを作成しておきます。
今回は、 angular-cognito-s3-file-uploader という名前のバケットを作成しました。

バケットポリシーと、CORSの設定は以下のとおりです。

{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "1",
            "Effect": "Allow",
            "Principal": {
                "Federated": "cognito-identity.amazonaws.com"
            },
            "Action": [
                "s3:ListBucket",
                "s3:DeleteObject",
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::angular-cognito-s3-file-uploader/*",
                "arn:aws:s3:::angular-cognito-s3-file-uploader"
            ]
        }
    ]
}
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>DELETE</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
    <AllowedHeader>Authorization</AllowedHeader>
</CORSRule>
</CORSConfiguration>

Cognito User Pool作成

AWS Consoleにログインし、Cognito -> 「ユーザープールの管理」-> 「ユーザープールを作成する」を選択します。

1.png

今回は作成するアプリケーション名と同様の名前でユーザープールを作成しました。「angular-cognito-s3-file-uploader」
「ユーザープールをどのように作成しますか?」では、「デフォルトを確認する」を選択しました。

内容を確認して、「プールの作成」をクリックします。

2.png

「アプリクライアント」-> 「アプリクライアントの追加」からアプリクライアントを追加します。今回は「angular-cognito-s3-file-uploader-client」という名前にしました。
次のように内容を入力して、「アプリクライアントの作成」をクリックします。

3.png

4.png

Cognito Identity Pool の作成

Cognito -> 「フェデレーテッドアイデンティティの管理」を選択します。
選択すると、「新しい ID プールの作成」の画面が表示されるので、次のように設定して、「プールの作成」をクリックします。
認証プロバイダーにはCognitoを選択して、先ほど作成したユーザープールIDとアプリクライアントIDを入力します。

5.png

割り当てるIAMロールを設定する画面が表示され、新規で作成するか既存のロールを割り当てるかを選択することが出来ます。今回は、次のようなIAMロールを新規で作成しました。
s3:ListBucket、s3DeleteObject、GetObject、PutObjectを新たに追加しています。Resourceには対象のS3バケットを指定します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "mobileanalytics:PutEvents",
                "cognito-sync:*",
                "cognito-identity:*",
                "s3:ListBucket"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:DeleteObject",
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::angular-cognito-s3-file-uploader/",
                "arn:aws:s3:::angular-cognito-s3-file-uploader/*"
            ]
        }
    ]
}

ここまでの手順で作成したリソースの次の情報を今後の手順で使用します。

  • S3バケット名
  • Pool ID
  • アプリクライアント ID
  • IdentityPoolId

Angular側の実装

雛形作成

angular-cliを使って、アプリの雛形を作成していきます。

ng new angular-cognito-s3-file-uploader

必要なパッケージをインストールします。

$ npm i --save amazon-cognito-identity-js
$ npm i --save aws-sdk

必要なサービスを作成

  • cognito.service (cognito関連のサービス)
  • s3.service (s3操作関連のサービス)
$ ng g service service/cognito
$ ng g service service/s3

必要なコンポーネントを作成

  • signup.component (サインアップ画面)
  • login.component (ログイン画面)
  • upload.component (ファイルアップロード画面)
  • filelist.component (ファイルリスト表示画面)
$ ng g component signup
$ ng g component login
$ ng g component upload
$ ng g component files

プロジェクトの設定

type定義を追加
(aws-sdkを使うためにnodeを定義)

tsconfig.app.json
{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/app",
    "baseUrl": "./",
    "module": "es2015",
    "types": [
      "node"
    ]
  },
  "exclude": [
    "test.ts",
    "**/*.spec.ts"
  ]
}

AWSのリソース情報を設定ファイルに記述します。

  • IdentityPoolId
  • Pool ID
  • アプリクライアント ID
  • S3バケット名
environment.ts
export const environment = {
  production: false,

  region: 'ap-northeast-1',

  identityPoolId: 'ap-northeast-1:XXXXXXXX-YYYY-XXXX-YYYY-XXXXXXXXXXXX',
  userPoolId: 'ap-northeast-1_XXXXXXXXX',
  clientId: 'YYYYYYYYYYYYYYYYYYYYYYYYYY',

  bucketName: 'angular-cognito-s3-file-uploader'
};

cognito.service

Cognito認証関連を行うサービスを実装します。
amazon-cognito-identity-jsと、aws-sdkからそれぞれ必要なモジュールをインポートします。
コンストラクタでは、AWS SDKのconfigにCognito関連の情報を設定します。

cognito.service.ts
import { Injectable } from '@angular/core';
import { environment } from './../../environments/environment';
import {
  CognitoUserPool,
  CognitoUserAttribute,
  CognitoUser,
  AuthenticationDetails,
  CognitoIdentityServiceProvider } from 'amazon-cognito-identity-js';
import * as AWS from 'aws-sdk';

@Injectable()
export class CognitoService {
  private userPool: CognitoUserPool;
  private poolData: any;
  public cognitoCreds: AWS.CognitoIdentityCredentials;

  constructor() {
    AWS.config.region = environment.region;
    AWS.config.credentials = new AWS.CognitoIdentityCredentials({
      IdentityPoolId: environment.identityPoolId
    });
    this.poolData = {
      UserPoolId: environment.userPoolId,
      ClientId: environment.clientId
    };
    this.userPool = new CognitoUserPool(this.poolData);
  }
}
...

続きは、以後順を追って実装していきます。

s3.service

s3操作を行うサービスを実装します。
コンストラクタでは、AWS SDKのconfigにCognito関連の情報を設定します。
また、cognitoサービスをDIして、getCredentials() というメソッドから認証情報を受け取るようにしました。

s3.service.ts
import { Injectable } from '@angular/core';
import { environment } from './../../environments/environment';
import { CognitoUserPool, CognitoUserAttribute, CognitoUser, AuthenticationDetails } from 'amazon-cognito-identity-js';
import * as AWS from 'aws-sdk';
import { CognitoService } from './cognito.service';

@Injectable()
export class S3Service {
  private s3: AWS.S3;

  constructor(
    private cognito: CognitoService
  ) {
    this.cognito.getCredentials().then((data) => {
      AWS.config.credentials = data;
    }).catch((err) => {
      console.log(err);
    });
    const clientParams: any = {
      region: environment.region,
      apiVersion: '2006-03-01',
      params: { Bucket: environment.bucketName }
    };
    this.s3 = new AWS.S3(clientParams);
  }
}
...

こちらも続きは、以後順を追って実装していきます。

signup.compnent

次にサインアップページを作成します。
まずは、cognito.service認証関係のメソッドを実装していきます。

signUpメソッドは、formから受け取ったメールアドレスとパスワードでをCognitoUserPoolに登録します。
戻り値として実行結果をPromiseで返します。
正常に実行された場合、入力したメールアドレスに次のような確認コードが送信されます。

cognito.service.ts
  signUp(username: string, password: string): Promise<any> {
    const dataEmail = { Name: 'email', Value: username };
    const attributeList = [];
    attributeList.push(new CognitoUserAttribute(dataEmail));
    return new Promise((resolve, reject) => {
      this.userPool.signUp(username, password, attributeList, null, (err, result) => {
        if (err) {
          reject(err);
        } else {
          resolve(result);
        }
      });
    });
  }

  confirmation(username: string, confirmation_code: string): Promise<any> {
    const userData = { Username: username, Pool: this.userPool };
    const cognitoUser = new CognitoUser(userData);
    return  new Promise((resolve, reject) => {
      cognitoUser.confirmRegistration(confirmation_code, true, (err, result) => {
        if (err) {
          reject(err);
        } else {
          resolve(result);
        }
      });
    });
  }

属性を持つ配列attributeListには、必須属性となっているemailを設定しています。
参考にしたaws-cognito-angular2-quickstartでは、Signup画面と確認コード入力画面をroutringで別画面にしていますが、今回は同一の画面で画面遷移無しで確認コードの入力を求めるように作りました。
「あとで確認コードの入力する」といったことは考えていないので、必要であれば別途実装する必要があります。

6.png

confirmationメソッドは、前記の手順で受信した確認コードformから受取、アカウントの確認を行います。

ビュー側は、次のように至ってシンプルです。

siginup.component.html
<div class="signup" *ngIf="!successfullySignup">
  <form [formGroup]="signupForm" (ngSubmit)="onSubmitSignup(signupForm.value)">
    <label>Email: </label>
    <input type="email" formControlName="email">
    <label>Password: </label>
    <input type="password" formControlName="password">
    <button type="submit">Submit</button>
  </form>
</div>

<div class="confirmation" *ngIf="successfullySignup">
  <form [formGroup]="confirmationForm" (ngSubmit)="onSubmitConfirmation(confirmationForm.value)">
      <label>Email: </label>
      <input type="email" formControlName="email">
    <label>Confirmation Code: </label>
    <input type="text" formControlName="confirmationCode">
    <button type="submit">Confirm</button>
  </form>
</div>

先ほど実装した、signUpconfirmationメソッドを呼び出すビューモデル部分です。

siginup.component.ts
  onSubmitSignup(value: any) {
    const email = value.email, password = value.password;
    this.cognito.signUp(email, password)
    .then((result) => {
      console.log(result);
      this.successfullySignup = true;
    }).catch((err) => {
      console.log(err);
    });
  }

  onSubmitConfirmation(value: any) {
    const email = value.email, confirmationCode = value.confirmationCode;
    console.log(email);
    this.cognito.confirmation(email, confirmationCode)
    .then((result) => {
      return console.log(result) || this.router.navigate(['/login']);
    }).catch((err) => {
      console.log(err);
    });
  }

login.component

まずは、cognito.service認証関係のメソッドを実装していきます。

cognito.service.ts
  signUp(username: string, password: string): Promise<any> {
    const dataEmail = { Name: 'email', Value: username };
    const attributeList = [];
    attributeList.push(new CognitoUserAttribute(dataEmail));
    return new Promise((resolve, reject) => {
      this.userPool.signUp(username, password, attributeList, null, (err, result) => {
        if (err) {
          reject(err);
        } else {
          resolve(result);
        }
      });
    });
  }

  confirmation(username: string, confirmation_code: string): Promise<any> {
    const userData = { Username: username, Pool: this.userPool };
    const cognitoUser = new CognitoUser(userData);
    return  new Promise((resolve, reject) => {
      cognitoUser.confirmRegistration(confirmation_code, true, (err, result) => {
        if (err) {
          reject(err);
        } else {
          resolve(result);
        }
      });
    });
  }

upload.component

s3.serviceにファイルアップロードを行うメソッドを作成します。

s3.service.ts
  uploadFile(file: any): Promise<any> {
    const params = {
      Bucket: environment.bucketName,
      Key: file.name,
      ContentType: file.type,
      Body: file,
      StorageClass: 'STANDARD',
      ACL: 'private' };
    return this.s3.upload(params).promise();
  }

ファイルインプットが変更されたときに実行されるonInputChange、アップロードボタンが押されたときに実行されるonClickUpload、ログアウトを行うonClickLogoutを作成します。

upload.component.ts
  onInputChange(event: any) {
    const files = event.target.files;
    this.uploadFile = files[0];
  }

  onClickUpload() {
    if (this.uploadFile) {
    this.s3.uploadFile(this.uploadFile).then((data) => {
      if (data) {
        this.uploadResult = 'アップロードが完了しました。';
      }
    }).catch((err) => {
      console.log(err);
    });
    } else {
      this.uploadResult = 'ファイルが選択されていません。';
    }
  }

  onClickLogout() {
    this.cognito.logout();
    this.router.navigate(['/login']);
  }

ビュー側は次のような感じです。

upload.component.html
<div class="usermenu">
  <h2>ユーザーメニュー</h2>
  <button [routerLink]="['/files']">ファイル一覧</button>
  <button (click)="onClickLogout()">ログアウト</button>
</div>

<div class="fileupload">
  <h2>アップロード</h2>
  <input type="file" accept="*/*" (change)="onInputChange($event)">
  <button (click)="onClickUpload()">アップロード</button>
  <p *ngIf="uploadResult !== ''">アップロード結果: {{uploadResult}}</p>
</div>

filelist.component

s3.serviceにファイル一覧を取得するgetFileListとファイルをダウンロードするgetFileメソッドを作成します。ココらへんは、AWS SDKにPromiseを返すAPIが用意されているので、パラメータを渡して呼ぶだけです。

s3.service.ts
  getFileList(): Promise<AWS.S3.ListObjectsOutput> {
    const params = { Bucket: environment.bucketName };
    return this.s3.listObjects(params).promise();
  }

  getFile(key: string): Promise<AWS.S3.GetObjectOutput> {
    const params = { Bucket: environment.bucketName, Key: key };
    return this.s3.getObject(params).promise();
  }

ngOnInitでページが表示されるタイミングで、アップロード済みのファイル一覧を取得します。
ファイル一覧のファイル名をクリックした際に呼ばれるonClickFileメソッドでは、s3から取得したデータをhtml5リンクのdownload属性を使ってダウンロードさせます。

files.component.ts
  ngOnInit() {
    this.s3.getFileList().then((data) => {
      if (data) {
        this.remoteFiles = data.Contents;
      }
    }).catch((err) => {
      console.log(err);
    });
  }

  onClickFile(item: any) {
    this.s3.getFile(item.Key).then((data) => {
      const blob = new Blob([data.Body], { type: data.ContentType });
      const url = window.URL.createObjectURL(blob);
      const linkElement = document.createElement('a');
      linkElement.download = item.Key;
      linkElement.href = url;
      linkElement.click();
    }).catch((err) => {
      console.log(err);
    });
  }

ビュー側は次のような感じです。

files.component.html
<div class="usermenu">
  <h2>ユーザーメニュー</h2>
  <button [routerLink]="['/upload']">アップロード</button>
  <button (click)="onClickLogout()">ログアウト</button>
</div>

<div class="filelist">
  <h2>ファイル一覧</h2>
  <table class="filelist-table">
    <thead>
      <tr>
        <th>ファイル名</th>
        <th>更新日</th>
        <th>サイズ</th>
      </tr> 
    </thead>
    <tbody>
      <tr *ngFor="let item of remoteFiles">
        <td><a (click)="onClickFile(item)">{{item.Key}}</a></td>
        <td>{{item.LastModified}}</td>
        <td>{{item.Size}}</td>
      </tr>
    </tbody>
  </table>
</div>

ここまでで、必要な実装が完了しました✨

動作確認

起動

$ npm start

ブラウザから http:localhost:4200にアクセス

デモ

動画を見ても分かるように、ログイン後にページ遷移してもログイン状態が保たれていることが分かるかと思います。
また、ログアウト後に直URLでファイル一覧にアクセスした場合も、内容が表示されないことが分かるかと思います。

所感

今回はフロントエンドのフレームワークにAngularを使いましたが、AWS SDK自体はjavascript実装なので、他のフロントエンドのフレームワークとの組み合わせも簡単にできるかと思います。ただ、AngularCLIで作るDI可能なサービスはこの規模のでもアプリを作る際にもかなり重宝するので、Cognito+Angularで認証付きSPAを作るのはかなりオススメです🐣

参考

冒頭でも書いたaws-cognito-angular2-quickstartというサンプル
https://github.com/awslabs/aws-cognito-angular2-quickstart

amazon-cognito-identity-jsの詳細解説
http://tarepan.hatenablog.com/entry/cognito_UserPools_session_management

シンプルな実装でわかりやすかったです
https://qiita.com/jobbin/items/d2fc0f714eb1f1cfc965

続きを読む

EC2上にS3をマウントする方法

s3fs-fuseインストール

sudo apt-get update

sudo apt-get install build-essential git libfuse-dev libcurl4-openssl-dev libxml2-dev mime-support automake libtool
sudo apt-get install pkg-config libssl-dev

git clone https://github.com/s3fs-fuse/s3fs-fuse

cd s3fs-fuse/
./autogen.sh
./configure --prefix=/usr --with-openssl

make
sudo make install

参照先s3バケットの設定

sudo touch /etc/passwd-s3fs

sudo vi /etc/passwd-s3fs
# 以下を設定
---
s3パケット名:aws_access_key:aws_secret_key
---

sudo chmod 640 /etc/passwd-s3fs

マウント

sudo mkdir /mnt/s3fs

sudo s3fs s3バケット名:/参照ディレクトリ名 /mnt/s3fs -o allow_other -o use_cache=/tmp

【参考】
https://github.com/s3fs-fuse/s3fs-fuse/wiki/Installation%20Notes#tested-on-ubuntu-1404-lts
http://www.mori-soft.com/2008-08-15-01-36-37/os/215-s3-s3

続きを読む

[JAWS-UG CLI] Alexa Skills Kit Command Line Interface (ASK CLI)のセットアップ

Alexa Skills Kit Command Line Interface (ASK CLI)をセットアップします。

オリジナル手順:https://developer.amazon.com/ja/docs/smapi/quick-start-alexa-skills-kit-command-line-interface.html#step-2-install-and-initialize-ask-cli

前提条件

1. nodeのインストール (nodebrewを使う場合)

コマンド
curl -L git.io/nodebrew | perl - setup
結果
Fetching nodebrew...
Installed nodebrew in $HOME/.nodebrew

========================================
Export a path to nodebrew:

export PATH=$HOME/.nodebrew/current/bin:$PATH
========================================

~/.bashrcにPATHを追記します。

.bashrcに追記
echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.bashrc
コマンド
. ~/.bashrc 
コマンド
which nodebrew
結果(例)
/Users/taro/.nodebrew/current/bin/nodebrew

nodeをバイナリでインストールします。(v6.10.0の例)

コマンド
nodebrew install-binary v6.10.0
コマンド
nodebrew use v6.10.0
node -v
結果(例)
v6.10.0

自動的にnpmもインストールされているはずです。

コマンド
which npm
結果(例)
/Users/taro/.nodebrew/current/bin/npm
コマンド
npm -v
結果(例)
3.10.10

2. ASK CLIのインストール

コマンド
npm install -g ask-cli
コマンド
which ask
結果(例)
<HOMEディレクトリ>/.nodebrew/current/bin/ask

3. ASKの初期化

コマンド
ask init --no-browser
出力
There is no AWS credentials setup yet, do you want to continue the initialization? (Default: False)
入力
Y
出力
Paste the following url to your browser:
<URL>

? Please enter your Authorization Code:
  • をコピーしてブラウザのURL欄に貼り付けます。
  • Authorization Codeが表示されるので、黒い枠の中のテキストをコピーします。
  • 上記でターミナルに表示されている”Please enter your Authorization Code:”の後ろにペーストし、エンターキーを押します。
出力
Tokens fetched and recorded in ask-cli config.
Vendor ID set as XXXXXXXXXXXX

Profile [default] initialized successfully.

4. 事後確認

コマンド
ask init -l
結果(例)
Profile              Associated AWS Profile
  [default]                 ** NULL **

~/.ask/cli_configに認証情報が保存されています。

完了

続きを読む

Serverless FrameworkでAWS Lamda関数を作成する

概要

Serverless Frameworkとは、Lambda、API Gateway、DynamoDBなどを作成、管理、デプロイできるツールです。
Frameworkと付いていますが、ツールです。
この記事では、python3でLambda関数を作成します。

環境

  • CentOS7.2
  • serverless 1.23.0
  • node v6.11.3
  • npm 3.10.10
  • OpenSSL 1.0.2k-fips 26 Jan 2017

npmのインストール

以下の記事を参照
npmのインストール手順

Serverless Frameworkのインストール

slsというディレクトリを作成し、そこで作業を行います。

$ mkdir sls
$ cd sls
$ npm init
$ npm install --save serverless

serverlessコマンドのパスを通します

$ npm bin serverless
$ echo 'export PATH="$HOME/sls/node_modules/.bin/:$PATH"' >> ~/.bash_profile
$ source ~/.bash_profile

インストール確認

$ serverless -v
1.23.0

aws credential登録

以下のコマンドで、AWSのキーを登録します。

$ serverless config credentials --provider aws --key XXXXXXXXXXXXEXAMPLE --secret XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXEXAMPLEKEY
Serverless: Setting up AWS...
Serverless: Saving your AWS profile in "~/.aws/credentials"...
Serverless: Success! Your AWS access keys were stored under the "default" profile.

Lambda関数の作成

以下のコマンドで、Lambda関数を作成します。

$ serverless create -t aws-python3 -p sample-app

Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/home/vagrant/sls/sample-app"
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  ___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v1.23.0
 -------'

Serverless: Successfully generated boilerplate for template: "aws-python3"

オプションの説明ですが、
-pは、Lambda関数名のprefixとなります。

また、-tで実装する言語を選びます。
以下のいずれかを選択します。

  • -t

    • aws-nodejs
    • aws-python
    • aws-python3
    • aws-java-maven
    • aws-java-gradle
    • aws-scala-sbt
    • aws-csharp
    • openwhisk-nodejs

すると、以下のファイルが生成されます。
handler.pyは、Lambda関数のテンプレート、serverless.ymlは設定ファイルになります。

$ ll sample-app/
total 8
-rw-rw-r--. 1 vagrant vagrant  497 Oct 10 04:42 handler.py
-rw-rw-r--. 1 vagrant vagrant 2758 Oct 10 04:42 serverless.yml

関数の情報を設定

serverless.ymlに関数の設定情報が書かれているので、環境合わせて編集します。

serverless.yml
provider:
  name: aws
  runtime: python3.6

# you can overwrite defaults here
- #  stage: dev
-#  region: us-east-1
+  stage: production
+  region: ap-northeast-1

# *snip*

# 関数名などの定義
functions:
-  hello:
-    handler: handler.hello
+  sample-func:
+    handler: handler.main

serverless.ymlで、関数のメソッド名を変更したので、handler.pyも以下のように編集します。

handler.py
-def hello(event, context):
+def main(event, context):

deploy

以下のコマンドでデプロイをします。

$ cd sample-app
$ serverless deploy

Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
.....
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (389 B)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
...............
Serverless: Stack update finished...
Service Information
service: sample-app
stage: production
region: ap-northeast-1
stack: sample-app-production
api keys:
  None
endpoints:
  None
functions:
  sample-func: sample-app-production-sample-func

すると、sample-app-production-sample-funcという名前のLambda関数が作成されます。

Lambda関数の実行

deployが出来たら、以下のコマンドで、Lambda関数を実行することができます。

$ serverless invoke -f sample-func

{
    "statusCode": 200,
    "body": "{"message": "Go Serverless v1.0! Your function executed successfully!", "input": {}}"
}

パラメータを付ける場合は、-dオプションで指定します。
戻り値にinputの項目が増えているのが確認できます。

$ serverless invoke -f sample-func -d '{"key":"value"}'

{
    "statusCode": 200,
    "body": "{"message": "Go Serverless v1.0! Your function executed successfully!", "input": {"key": "value"}}"
}

以下のようなjsonファイルを作成して、パラメータを渡すこともできます。

event.json
{
  "key" : "value"
}

-pオプションでjsonファイルを指定して実行

$ serverless invoke -f sample-func -p event.json
{
    "statusCode": 200,
    "body": "{"message": "Go Serverless v1.0! Your function executed successfully!", "input": {"key": "value"}}"
}

Lambda関数の削除

関数の削除は以下のコマンドで行います。
関連するS3のファイルもすべて消してくれます。
AWS Console上で手動でLambda関数を削除すると、S3のファイルなどが残ってしまいます。

関数のあるディレクトリに移動でして実行します。

$ cd sample-function
$ serverless remove -v

-sオプションで、特定のstageのみを削除することもできます。

$ serverless remove -v -s dev

その他

以下のモジュールで、擬似的にローカルでApi Gateway、DynamoDBを使うことができます。

$ npm install aws-sdk
# 擬似的Api Gateway
$ npm install --save-dev serverless-offline
# 擬似的DynamoDB
$ npm install --save-dev serverless-dynamodb-local

参考

続きを読む

【HashiCorp Vault】Dynamic Secrets (Secret Backend) の Lease 機能を試す

はじめに

Dynamic Secrets (Secret Backend) の Lease 機能について動作を確認しましたので備忘録として記録しておきます。

やったこと

  1. Vaultが作成したAWSユーザの有効期限(TTL)を確認する
  2. Lease機能を利用して有効期限(TTL)を設定する
  3. Lease機能を利用してAWSユーザの有効期限(TTL)を延長する

HashiCorp Vaultとは?

HashiCorpが提供している機密情報の管理ツールです。

Dynamic Secrets (Secret Backend) とは

VaultにAWSやDatabaseなどのアカウントを管理させる機能です。

この機能を利用することでVaultを通じてAWSなどのユーザ作成、削除を実施できるようになります。
有効期間が経過したユーザについてはVaultが自動で削除を行ってくれます。

参考

公式サイト

Vault by HashiCorp

ドキュメント

AWS Secret Backend – HTTP API – Vault by HashiCorp
/sys/leases – HTTP API – Vault by HashiCorp

過去の投稿

HashiCorp Vault の Dynamic Secrets (Secret Backend) を試す – Qiita


事前準備

VaultをDevモードで起動

$ vault server -dev

...
    export VAULT_ADDR='http://127.0.0.1:8200'
...
Root Token: dae8c23c-3749-0286-abc5-fbdf57b634f1

環境変数の設定

$ export VAULT_ADDR='http://127.0.0.1:8200'
$ export VAULT_TOKEN='dae8c23c-3749-0286-abc5-fbdf57b634f1'

AWS Backendをマウント

$ vault mount aws
Successfully mounted 'aws' at 'aws'!

AWSのTokenを設定

$ vault write aws/config/root 
    access_key=AKIAIFOCRMPWZMVED74Q 
    secret_key=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
Success! Data written to: aws/config/root

ポリシーを設定

policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1426528957000",
      "Effect": "Allow",
      "Action": [
        "ec2:*"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}
$ vault write aws/roles/deploy policy=@policy.json
Success! Data written to: aws/roles/deploy

1. Vaultが作成したAWSユーザの有効期限(TTL)を確認する

初期状態でLease設定や有効期限(TTL)を確認します。

Lease設定を確認する

Lease設定を確認しても、未設定のため取得できません。

$ curl 
    --header "X-Vault-Token: $VAULT_TOKEN" 
    $VAULT_ADDR/v1/aws/config/lease

{"errors":[]}

VaultにAWSユーザを作成させる

この状態(Lease未設定)でAWSユーザを作成せた場合、lease_duration": 2764800となっています。

$ curl 
    --header "X-Vault-Token: $VAULT_TOKEN" 
    $VAULT_ADDR/v1/aws/creds/deploy

{
  "request_id": "333374cd-1d0e-6015-adc9-85656d78a1c9",
  "lease_id": "aws/creds/deploy/6ceb69a0-249a-e65e-ff9a-9996a85bed45",
  "renewable": true,
  "lease_duration": 2764800,
  "data": {
    "access_key": "AKIAJIFBBLEAXHS4NQNQ",
    "secret_key": "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",
    "security_token": null
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

Lease設定を確認

AWSユーザのLease設定を確認すると、

  • “issue_time” : “2017-10-08T17:30:30.873476458+09:00”
  • “expire_time”: “2017-11-09T17:30:30.873476739+09:00”

となっており、有効期限(TTL)が 2764800秒 = 32日間 であることがわかりました。

$ curl 
    --header "X-Vault-Token: $VAULT_TOKEN" 
    --request PUT 
    --data '{ "lease_id": "aws/creds/deploy/6ceb69a0-249a-e65e-ff9a-9996a85bed45" }' 
    $VAULT_ADDR/v1/sys/leases/lookup

{
  "request_id": "21234bd6-e722-8993-22f7-175f2c4d8dc6",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": {
    "expire_time": "2017-11-09T17:30:30.873476739+09:00",
    "id": "aws/creds/deploy/6ceb69a0-249a-e65e-ff9a-9996a85bed45",
    "issue_time": "2017-10-08T17:30:30.873476458+09:00",
    "last_renewal": null,
    "renewable": true,
    "ttl": 2764126
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

2. Lease機能を利用して有効期限(TTL)を設定する

初期設定では有効期限(TTL)が長いので、短い有効期限(TTL)を設定して、AWSユーザが実際に削除されるのか確認したいと思います。

Lease設定

lease30mlease_max12hに設定します。

$ curl 
    --header "X-Vault-Token: $VAULT_TOKEN" 
    --request POST 
    --data '{ "lease": "30m",  "lease_max": "12h" }' 
    $VAULT_ADDR/v1/aws/config/lease

Lease設定を確認すると、lease, lease_maxが指定した値になっています。

$ curl 
    --header "X-Vault-Token: $VAULT_TOKEN" 
    $VAULT_ADDR/v1/aws/config/lease 

{
  "request_id": "3b09ce08-b6b6-848f-3622-f3711d1a7d26",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": {
    "lease": "30m0s",
    "lease_max": "12h0m0s"
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

AWSユーザ作成

Lease設定後にAWSユーザを作成させると、lease_duration": 1800となっています。

$ curl 
    --header "X-Vault-Token: $VAULT_TOKEN" 
    $VAULT_ADDR/v1/aws/creds/deploy

{
  "request_id": "5413657e-78e4-4128-e7c0-ed61c6158bc8",
  "lease_id": "aws/creds/deploy/5d7f8036-762b-8fcb-fcea-7bb7f1842f13",
  "renewable": true,
  "lease_duration": 1800,
  "data": {
    "access_key": "AKIAJCVNQTVWYDKAEIMA",
    "secret_key": "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",
    "security_token": null
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

AWSユーザのLease設定を確認すると、

  • “issue_time” : “2017-10-08T17:52:06.741853365+09:00”
  • “expire_time”: “2017-10-08T18:22:06.741853771+09:00”

となっており、有効期限(TTL)が1800秒 = 30分となってることがわかります。

$ curl 
    --header "X-Vault-Token: $VAULT_TOKEN" 
    --request PUT 
    --data '{ "lease_id": "aws/creds/deploy/5d7f8036-762b-8fcb-fcea-7bb7f1842f13" }' 
    $VAULT_ADDR/v1/sys/leases/lookup

{
  "request_id": "e528ba92-53f0-cee0-973d-069612a24207",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": {
    "expire_time": "2017-10-08T18:22:06.741853771+09:00",
    "id": "aws/creds/deploy/5d7f8036-762b-8fcb-fcea-7bb7f1842f13",
    "issue_time": "2017-10-08T17:52:06.741853365+09:00",
    "last_renewal": null,
    "renewable": true,
    "ttl": 1646
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

AWSユーザの削除確認

AWSの管理コンソールを確認したところ、expire_timeに到達したタイミングでVaultが作成したAWSユーザ(vault-root-deploy-1507452723-604)が削除されました。

expire_time到達前
vault_lease01.png

expire_time到達後
vault_lease02.png

3. Lease機能を利用してAWSユーザの有効期限(TTL)を延長する

Lease機能には有効期限(TTL)を延長(renew)させる機能があるので、こちらの動作も確認してみます。

AWSユーザ作成

前回と同様にVaultにAWSユーザを作成させます。

$ curl 
    --header "X-Vault-Token: $VAULT_TOKEN" 
    $VAULT_ADDR/v1/aws/creds/deploy

{
  "request_id": "15e22c37-ff2f-2277-6bc5-0834f4dd4eba",
  "lease_id": "aws/creds/deploy/f822cb89-98d6-0cd5-05e7-69020bda05b9",
  "renewable": true,
  "lease_duration": 1800,
  "data": {
    "access_key": "AKIAIGFBKDIZ6VU7UOUA",
    "secret_key": "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",
    "security_token": null
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

前回と同様にAWSユーザの有効期限(TTL)は30分間です。

  • “issue_time” : “2017-10-08T18:34:39.368521987+09:00”
  • “expire_time”: “2017-10-08T19:04:39.368522185+09:00”
$ curl 
    --header "X-Vault-Token: $VAULT_TOKEN" 
    --request PUT 
    --data '{ "lease_id": "aws/creds/deploy/f822cb89-98d6-0cd5-05e7-69020bda05b9" }' 
    $VAULT_ADDR/v1/sys/leases/lookup

{
  "request_id": "f1848a4a-7f3f-8e0c-90cf-7024dd242c35",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": {
    "expire_time": "2017-10-08T19:04:39.368522185+09:00",
    "id": "aws/creds/deploy/f822cb89-98d6-0cd5-05e7-69020bda05b9",
    "issue_time": "2017-10-08T18:34:39.368521987+09:00",
    "last_renewal": null,
    "renewable": true,
    "ttl": 1753
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

有効期限(TTL)の延長

Lease機能を利用してAWSユーザの有効期限(TTL)を3600秒(30分)延長させます。

$ curl 
    --header "X-Vault-Token: $VAULT_TOKEN" 
    --request PUT 
    --data '{ "lease_id": "aws/creds/deploy/28def48b-c990-d1d4-1691-610a8f2be0e9", "increment": 3600 }' 
    $VAULT_ADDR/v1/sys/leases/renew

{
  "request_id": "86f059ec-1680-c886-2ade-3e15635ed38e",
  "lease_id": "aws/creds/deploy/28def48b-c990-d1d4-1691-610a8f2be0e9",
  "renewable": true,
  "lease_duration": 3600,
  "data": null,
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

Lease設定を確認するとexpire_timeが更新されてます。

issue_timeから+3600秒(30分)されるわけではなく、延長(renew)コマンドを実行した時点(last_renewal)から3600秒(30分)延長されるようです。

延長前

  • “issue_time” : “2017-10-08T18:34:39.368521987+09:00”
  • “expire_time”: “2017-10-08T19:04:39.368522185+09:00”

延長後

  • “issue_time” : “2017-10-08T18:27:57.65077363+09:00”
  • “last_renewal”: “2017-10-08T18:35:52.918241764+09:00”
  • “expire_time” : “2017-10-08T19:35:52.918241648+09:00”
$ curl 
    --header "X-Vault-Token: $VAULT_TOKEN" 
    --request PUT 
    --data '{ "lease_id": "aws/creds/deploy/28def48b-c990-d1d4-1691-610a8f2be0e9" }' 
    $VAULT_ADDR/v1/sys/leases/lookup
{
  "request_id": "f8e035a6-a72b-395a-aca0-641ed7c747ce",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": {
    "expire_time": "2017-10-08T19:35:52.918241648+09:00",
    "id": "aws/creds/deploy/28def48b-c990-d1d4-1691-610a8f2be0e9",
    "issue_time": "2017-10-08T18:27:57.65077363+09:00",
    "last_renewal": "2017-10-08T18:35:52.918241764+09:00",
    "renewable": true,
    "ttl": 3556
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

有効期限(TTL)の最大値を確認

lease_max12hに設定していたので12時間を超える有効期限(TTL)は設定できなくなっています。

有効期限(TTL)を 43200秒(1日)延長(renew)させても、expire_timeissue_timeの12時間後となっており、lease_maxを超えた有効期限(TTL)が設定できないことがわかります。

  • “issue_time” : “2017-10-08T18:27:57.65077363+09:00”
  • “last_renewal”: “2017-10-08T18:38:06.850822545+09:00”
  • “expire_time” : “2017-10-09T06:27:57.650782301+09:00”
$ curl 
    --header "X-Vault-Token: $VAULT_TOKEN" 
    --request PUT 
    --data '{ "lease_id": "aws/creds/deploy/28def48b-c990-d1d4-1691-610a8f2be0e9", "increment": 43200 }' 
    $VAULT_ADDR/v1/sys/leases/renew

{
  "request_id": "9ed0ca65-18ee-2186-689a-1569f4f9e058",
  "lease_id": "aws/creds/deploy/28def48b-c990-d1d4-1691-610a8f2be0e9",
  "renewable": true,
  "lease_duration": 42590,
  "data": null,
  "wrap_info": null,
  "warnings": null,
  "auth": null
}
$ curl 
    --header "X-Vault-Token: $VAULT_TOKEN" 
    --request PUT 
    --data '{ "lease_id": "aws/creds/deploy/28def48b-c990-d1d4-1691-610a8f2be0e9" }' 
    $VAULT_ADDR/v1/sys/leases/lookup

{
  "request_id": "00d1c93f-431d-61f0-a116-b5d8a57af815",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": {
    "expire_time": "2017-10-09T06:27:57.650782301+09:00",
    "id": "aws/creds/deploy/28def48b-c990-d1d4-1691-610a8f2be0e9",
    "issue_time": "2017-10-08T18:27:57.65077363+09:00",
    "last_renewal": "2017-10-08T18:38:06.850822545+09:00",
    "renewable": true,
    "ttl": 42560
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null
}

続きを読む

開発用サーバーを作る on AWS(Ruby on Rails 5)

はじめに

まぁそのまんま。
こちらのRubyonRails編。

Ruby 2.4.2
Ruby on Rails 5.1.4
なり。

Amazon Linux AMI+Nginx+Unicorn
やで。

インストールなど

sudo yum update
sudo yum -y install git
sudo yum install nodejs --enablerepo=epel
git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
source ~/.bash_profile
sudo yum install gcc openssl-devel readline-devel sqlite-devel
rbenv install 2.4.2
rbenv global 2.4.2
gem update --system
gem install --no-ri --no-rdoc rails
gem install bundler
gem install sqlite3
gem install unicorn
rbenv rehash
rails -v
rails new /var/www/html/sample
sudo mkdir /var/run/unicorn
sudo chmod 777 /var/run/unicorn
sudo chown -R nginx:ec2-user /var/www/html
sudo chmod 2777 /var/www -R

まぁ、だいぶ時間がかかりまっせ。

sample/config/unicorn.rbを作成

application = 'sample'

worker_processes 2
timeout 15

pid "/var/run/unicorn/unicorn_#{application}.pid"
listen "/var/run/unicorn/unicorn_#{application}.sock"

preload_app true

before_fork do |server, worker|
  Signal.trap 'TERM' do
  puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
  Process.kill 'QUIT', Process.pid
  end

  defined?(ActiveRecord::Base) and
  ActiveRecord::Base.connection.disconnect!end

after_fork do |server, worker|
  Signal.trap 'TERM' do
  puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
  end

  defined?(ActiveRecord::Base) and
  ActiveRecord::Base.establish_connection
end

stderr_path File.expand_path('log/unicorn.log', ENV['RAILS_ROOT'])
stdout_path File.expand_path('log/unicorn.log', ENV['RAILS_ROOT'])

起動

bundle exec unicorn_rails -c config/unicorn.rb -E development -D

OSの再起動対応

~/.bashrcに追記

sudo mkdir -p /var/run/unicorn && sudo chmod 777 /var/run/unicorn

/etc/nginx/conf.d/sample.conf作成

upstream unicorn {
  server unix:/var/run/unicorn/unicorn_sample.sock;
}
server {
    listen 8080;
    server_name localhost;
    root /var/www/html/sample;

    access_log /var/log/nginx/myapp_access.log;
    error_log /var/log/nginx/myapp_error.log;

    try_files $uri/index.html $uri @unicorn;
    location @unicorn {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_pass http://unicorn;
    }
}

PHPを80で動かしてる都合上8080でごわす。

Nginx再起動

sudo service nginx restart

いじょー
Security Groupで8080はあけておきましょうね。

by 株式会社Arrvis

続きを読む

HashiCorp Vault の Dynamic Secrets (Secret Backend) を試す

はじめに

HashiCorpのVaultが提供しているDynamic Secrets (Secret Backend) 機能について公式ドキュメントを参考に動作を確認しましたので備忘録として残しておきます。

やったこと

Vaultの Dynamic Secrets (Secret Backend) 機能を利用してAWSユーザの作成・削除を行う。

HashiCorp Vaultとは?

HashiCorpが提供している機密情報の管理ツールです。

Dynamic Secrets (Secret Backend) とは

VaultにAWSやDatabaseなどのユーザを管理させる機能です。

この機能を利用することでVaultを通じてAWSなどのユーザ作成、削除を実施できるようになります。
有効期間が経過したユーザについてはVaultが自動で削除を行ってくれます。

参考

公式サイト

Vault by HashiCorp

ドキュメント

Dynamic Secrets – Getting Started – Vault by HashiCorp
AWS Secret Backend – Vault by HashiCorp


事前準備

VaultをDevモードで起動

DevモードではVaultを停止させるとデータが消えてしまいますが、今回は動作確認のためDevモードで起動します。

$ vault server -dev

...
    export VAULT_ADDR='http://127.0.0.1:8200'

環境変数の設定

$ export VAULT_ADDR='http://127.0.0.1:8200'

AWS Backendをマウント

$ vault mount aws
Successfully mounted 'aws' at 'aws'!

AWSユーザの作成

vaultがAWSを操作するためのユーザ(vault)を作成します。

本来であれば目的に応じたポリシーを付与するべきですが、今回は動作確認のため「AdministratorAccess」ポリシーを適用しました。

作成したユーザの「アクセスキー ID」と「シークレットアクセスキー」はvaultで利用するためメモしておきます。

vault01.png

vault02.png

vault03.png

vault04.png

vault05.png


AWS Backendの設定

Vault用に準備したAWSユーザの「アクセスキー ID」と「シークレットアクセスキー」をaws/config/rootに登録します。

$ vault write aws/config/root 
    access_key=AKIAJDCIHPTOLCKLM6WA 
    secret_key=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

Success! Data written to: aws/config/root

aws/config/rootに書き込んだ情報は読み取れなくなります。

$ vault read aws/config/root

Error reading aws/config/root: Error making API request.

URL: GET http://127.0.0.1:8200/v1/aws/config/root
Code: 405. Errors:

* 1 error occurred:

* unsupported operation

ロールの作成

Getting Started を参考にjsonファイルを作成します。
Actionにec2:*という記載があるので、EC2を操作するアカウントが作成されそうです。

policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1426528957000",
      "Effect": "Allow",
      "Action": [
        "ec2:*"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}

作成したjsonファイルを指定してロールを作成します。

$ vault write aws/roles/deploy policy=@policy.json

Success! Data written to: aws/roles/deploy

AWSユーザの作成

ロールを指定してAWSユーザを作成させます。
レスポンスとして作成したユーザの「アクセスキー ID」と「シークレットアクセスキー」が返却されます。

$ vault read aws/creds/deploy

Key             Value
---             -----
lease_id        aws/creds/deploy/2cfb960b-7f2e-74c4-d351-917401d27f3b
lease_duration  768h0m0s
lease_renewable true
access_key      AKIAIYMLSSQ434JIRMUQ
secret_key      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
security_token  <nil>

AWSの管理画面を確認すると新しいユーザ(vault-root-deploy-1507372260-9128)が作成されて、EC2を操作するアクセス権限をもってることがわかります。

vault06.png

vault07.png

vault08.png


AWSユーザの削除

lease_idを指定してユーザを削除します。

$ vault revoke aws/creds/deploy/2cfb960b-7f2e-74c4-d351-917401d27f3b

Success! Revoked the secret with ID 'aws/creds/deploy/2cfb960b-7f2e-74c4-d351-917401d27f3b', if it existed.

AWSの管理画面でもVaultが作成したユーザ(vault-root-deploy-1507372260-9128)が削除されたことがわかります。

vault09.png

補足

【HashiCorp Vault】Dynamic Secrets (Secret Backend) の Lease 機能を試す – Qiita

続きを読む