20200310のAWSに関する記事は17件です。

aws ecr get-loginでポリシー不足でエラーが出力

記録としてメモしとく。

やりたかったこと

AWS-CLI使って Amazon ECRにログインしたい。

前提

私の環境
- Windows10 Pro
image.png
- AWS CLIインストール済 

> aws --version
aws-cli/1.16.313 Python/3.7.2 Windows/10 botocore/1.13.49

事象

PS C:\Users\taketakekaho> aws ecr get-login --region ap-northeast-1 --profile <profile> --no-include-email

An error occurred (AccessDeniedException) when calling the GetAuthorizationToken operation: User: arn:aws:iam::XXXXXXXXXXXX:user/<profile> is not authorized to perform: ecr:GetAuthorizationToken on resource: *

なんかアクセス権的なものがないようでエラーが出る。

対処

このAmazonEC2ContainerRegistryPowerUserというポリシーが足りなかったらしい。以下のようにアタッチした所解消しました。
image.png

というか…

情報探す中でたまたま読んだクラスメソッドさんの技術ブログ([アップデート]AWS CLI v2 で $ aws ecr get-login を使うときの注意点)で $ aws ecr get-login はv1でも非推奨 ということを初めて知った…気を付けよう。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GuardDutyの結果をEvent Bridge→SNSで通知する

はじめに

前回、AWS LambdaからEvent Bridgeに通知してみました。
今回は、GuardDutyEventBridgeSNS(Email)と連携させてみます。

最近業務でセキュリティ!セキュリティ!と叫ばれておりますが、そこまでまともにセキュリティ系のサービスを使っていなかったので、GuardDutyを触り始めたのが理由です。。

構築手順

GuardDutyの有効化

AWSコンソールからGuardDutyを有効化しておきます。(初回の場合、30日間は無料です。)

SNSの作成

Emailで通知が受け取れるように設定しておきます。
sns.PNG

EventBridgeの設定

イベントバスの作成

defaultのイベントバスを利用するので不要です。
最初カスタムイベントバスにルールを作ってしまい、全然アラートが飛ばす悩みました、、、
ここでは前回作成した、custom-application-event-busをそのまま使います。(無い方は任意の名前で作って下さい)

イベントルールの作成

ルール名は任意でOK
guard-1.PNG

カスタムパターンを選択し、イベントパターンに以下を入力します。
今回はSeverityがHighのものだけ通知するようにしています。Severityの定義はこちらを参照。

{
    "source": [
        "aws.guardduty"
    ],
    "detail-type": [
        "GuardDuty Finding"
    ],
    "detail": {
        "severity": [
            { "numeric": [ ">=", 7 ] }
        ]
    }
}

ターゲットには先ほど作成したSNS Topicを指定します。
guard-3.PNG

GuardDutyにサンプルを作成し通知

サンプル結果の作成

GuardDutyの検出を意図的に発生させるのは難しいため、サンプルで試します。
AWSコンソールのGuardDutyの画面で、「設定」→「結果サンプルの作成」を選択します。
(10数件ほどメールが来るのでご承知おきを。)

メールの確認

SNS作成時に設定したEmailに以下のようなメールが届いていれば完了です!

{
    "version": "0",
    "id": "0eef64b4-0f67-244d-400b-7677324fe2af",
    "detail-type": "GuardDuty Finding",
    "source": "aws.guardduty",
    "account": "xxxxxxxxxx",
    "time": "2020-03-10T13:50:01Z",
    "region": "us-west-2",
    "resources": [],
    "detail": {
        ・・・
        "severity": 8,
        "createdAt": "2020-03-07T13:34:05.589Z",
        "updatedAt": "2020-03-10T13:49:52.052Z",
        "title": "DGA domain name queried by EC2 instance i-99999999.",
        "description": "EC2 instance i-99999999 is querying algorithmically generated domains. Such domains are commonly used by malware and could be an indication of a compromised EC2 instance."
    }
}

さいごに

セキュリティの検知をどの範囲で、どのような方法ですれば良いのか?はとても難しいと思います。
まずは、GuardDutyのような自動で検出してくれるサービスを有効にし、EventBridgeである程度必要なものに絞り込み、自分達に有用なアラートを取捨選択していければと思っています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RDSスナップショットをS3に自動エクスポート

概要

RDSの自動スナップショットをバックアップのために、S3に自動でエクスポートするServerless functionを作ってみました。
とりあえず最新のスナップショットのみをバックアップするようにしています。

処理のイメージはcloud watch eventでn時にlambda functionを実行するというシンプルなものです。

IAM ロール

AWSRDSSnapshotExportRoleのような適当なロールを作成し、そのロールに AmazonS3FullAccessAWSKeyManagementServicePowerUser をアタッチします。

そして、 そのロールの 信頼関係 を編集します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "export.rds.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Service の部分を export.rds.amazonaws.com とすればOKです。

KMSでキーを作成

KMSコンソールにアクセスをして、 カスタマー管理型のキー でキーを作成します。
その時に管理者にするのは上で作成したロールにしましょう。
作成したキーのIDはあとで使います。

Serverless Frameworkでプロジェクト作成

$ sls create -t aws-nodejs -p hoge

$ cd hoge

$ npm init

$ npm i --save aws-sdk

serverless.yml

service: hoge
provider:
  name: aws
  runtime: nodejs12.x
  region: ap-northeast-1
  role: Lambda用のロールを設定(lambdaとrdsのfullaccessが必要)
  timeout: 100

package:
  individually: true
  exclude:
    - package.json
    - package-lock.json
    - .serverless/**
    - serverless.yml

functions:
  main:
    handler: handler.main
    name: hoge
    environment:
      IamRoleArn: 先ほど作成したロールのARNを設定
      KmsKeyId: 先ほど作成したキーのID
      S3BucketName: エクスポートしたS3バケット名
    events:
      - schedule:
          rate: cron(0 12 * * ? *)

毎日12時にエクスポートを実行する

handler.js

handler.jsを編集します。

'use strict';

const AWS = require('aws-sdk')
const RDS = new AWS.RDS({apiVersion: '2014-10-31'})

module.exports.main= async event => {
  try {
    const lastSnapshot = await getLastSnapshot('RDSのデータベース名')
    await startExportTask(lastSnapshot)
  } catch (e) {
    console.log(e)
  }
}

function getLastSnapshot(db){
  return new Promise(function(resolve, reject){
    let params = {
      DBInstanceIdentifier: db,
      SnapshotType: 'automated' // 自動で作成されたsnapshotを取得する
    }

    RDS.describeDBSnapshots(params, function(err,data){
      if(err){
        console.error(err,err.stack)
        reject(err)
      } else {
        console.log(data.DBSnapshots.pop())
        resolve(data.DBSnapshots.pop())
      }
    })
  })
}

function startExportTask(obj){
  return new Promise(function(resolve, reject){
    const params = {
      ExportTaskIdentifier: obj.DBSnapshotIdentifier, /* required */
      IamRoleArn: process.env.IamRoleArn, /* required */
      KmsKeyId: process.env.KmsKeyId, /* required */
      S3BucketName: process.env.S3BucketName, /* required */
      SourceArn: obj.DBSnapshotArn, /* required */
      S3Prefix: `${obj.DBInstanceIdentifier}/`
    }

    RDS.startExportTask(params, function(err,data){
      if(err){
        console.error(err, err.stack)
        reject(err)
      } else {
        console.log(data)
        resolve(data)
      }
    })
  })
}

lambdaのバグについて

プロジェクトの作成の際にわざわざaws-sdkをローカルでインストールしているのですが、実はこれにはわけがあって、lambdaにデフォルトで内包されているaws-sdkがなぜかstartExportTaskメソッドが入っておらず(おそらくバグだと思う)、なのでローカルでインストールし、node_modulsごとデプロイして、node_modulsの中のsdkを参照するようにして回避しています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Athenaでクエリを発行しようとすると「no viable alternative at input」となる

事象

Amazon Athenaのクエリエディタでクエリ(SHOW PARTITION <table_name>)を実行すると以下のようなエラーとなる。

  • line 1:6: no viable alternative at input 'show partition' (service: amazonathena; status code: 400; error code: invalidrequestexception; request id: b8ec4813-f216-4767-88e4-da44da2b223e)

image.png

解決

文法の正しくないクエリを実行しようとしていたからだった。

  • 誤:SHOW PARTITION <table_name>
  • 正:SHOW PARTITIONS <table_name>

正しいクエリであればエラーなく実行できた。
image.png

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

sopsでのクレデンシャル管理【Tips】

TL; DR

Terraformなどで秘匿情報を扱う際に、lifecycle { ignore_changes = ["hogehoge"] }などで管理から外していたりすることがあります。基本的には問題ないと思うのですが、DBのパスワードなど秘伝のタレ化しがちです。折角のコード化なのでこれらも一緒に管理できるとちょっとだけ幸せになれると思います。
そこでsopsというmozilla社の開発している暗号化/復号化ツールがあります。これを用いてクレデンシャル管理も一緒にやり、少しだけ幸せになろうというTipsです。

Install

Macユーザーならおなじみbrewでインストール可能です。
goがインストール済みであればgo getでもインストール可能です。
わたしはbrewでインストールしました。

install sops
$ brew install sops
==> Downloading https://homebrew.bintray.com/bottles/sops-3.5.0_1.catalina.bottle.tar.gz
Already downloaded: /Users/Library/Caches/Homebrew/downloads/fa1e1ceaec6d966efe2c08d9d4eae6b27d01f616d9d7043f2545f8075268c7ac--sops-3.5.0_1.catalina.bottle.tar.gz
==> Pouring sops-3.5.0_1.catalina.bottle.tar.gz
?  /usr/local/Cellar/sops/3.5.0_1: 7 files, 27.9MB

$ sops --version
sops 3.5.0 (latest)
go install
$ go get -u go.mozilla.org/sops/v3/cmd/sops
$ cd $GOPATH/src/go.mozilla.org/sops/
$ git checkout develop
$ make install

# Makefileのinstall
$ cat Makefile | grep "install:" -A 3
install:
    $(GO) install go.mozilla.org/sops/v3/cmd/sops

tag: all

暗号鍵

sopsを利用するに当たって暗号化に利用する鍵が必要になります。
PGPを利用するか、以下のクラウドサービスの鍵を利用することが可能です。
対象の鍵はオプションもしくは変数として呼び出すことができるため適宜利用してください。

対象 サービス名 オプション 変数名
PGP - --pgp SOPS_PGP_FP
GCP Cloud Key Management Service --gcp-kms SOPS_GCP_KMS_IDS
Azure Key Vault --azure-kv SOPS_AZURE_KEYVAULT_URLS
AWS Key Management Service --kms SOPS_KMS_ARN

必要な環境に応じて用意してください。

使い方

ざっとした使い方ですので参考までに見てもらえるといいかと思います。
筆者はAWS KMSを用いて実施しました。
※ あらかじめexport SOPS_KMS_ARN="arn:aws:kms:ap-northeast-1:123456789012:key/hogehoge"を入力して環境変数に入れています。

新規作成

ファイルが無い状態でsops ファイル名と入力するとエディタが開かれ下記のようなサンプルが表示されます。

sops new file
$ sops sample_secret.yml

hello: Welcome to SOPS! Edit this file as you please!
example_key: example_value
# Example comment
example_array:
- example_value1
- example_value2
example_number: 1234.5679
example_booleans:
- true
- false

試しに下記のように編集してみました。

edited file
example_key: example_value
example_number: 1234.5679

これを保存して抜けるともう暗号化されています。

sample_secret.yml
example_key: ENC[AES256_GCM,data:D9pPc7dRd6PuoLUPPw==,iv:zWZxW42YhJsETnWCfgDnIQL55iTSxNrthVmB5gC4bJI=,tag:E/6EBAQ/MnLbCpGCOSnFHg==,type:str]
example_number: ENC[AES256_GCM,data:93sOpzQJvu4N,iv:aigNbYXg+2uSB7cB+JEOnCguBea49DjU2eSGyaBJM5I=,tag:Kf//or7XpUmPChrdhx4Idg==,type:float]
sops:
    kms:
    -   arn: arn:aws:kms:ap-northeast-1:123456789012:key/890hoge1-67ho-34ga-01fu-1234hoge567
        created_at: '2020-03-10T08:12:15Z'
        enc: AQICAHjzJdn8BrQ9cSoqu5NVOLqeyLNJxw4Gm2+8UkJCyqr6ngHoCN1jRMUoKeYuz1ybQhRDAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM10R9JpvAo2TpWegYAgEQgDte4OJEdbpEmI0pyuZ6Wmz3qvNtw4TJv7TfEujHIpkf6g9Kapq3wEoETiqjy2z+CYt2VptB9+fQZ3h0hQ==
        aws_profile: ""
    gcp_kms: []
    azure_kv: []
    lastmodified: '2020-03-10T08:33:14Z'
    mac: ENC[AES256_GCM,data:JDiJlMegTqzPRbG2hlcAziRToXhwoz9q8g5d2e0X/8B/sP8T8a+/Z9EhOprsBO8T/S7LaC2pAgOevYXonn906VouY+Kn/8kDGyYFU9qAr50gUJ1DZApCUneM58uKUYIhdo0+erKO7594+hSbbf0Ny9Br1ldPmlHf/TkJ1UtN87o=,iv:qpZQ3fxYXijrKd5u9uXDg+2eOx6hxZv909VZCislKbk=,tag:T+77DkwadQp0YhYf69PXbQ==,type:str]
    pgp: []
    unencrypted_suffix: _unencrypted
    version: 3.5.0

復号化

上記で作成したファイルを復号化してみます。
-dオプションをつけるだけなので非常に簡単です。

decryption
$ sops -d sample_secret.yml
example_key: example_value
example_number: 1234.5679

シークレットの追加

新規同様sops ファイル名で開いて追加するだけです。

add secret
$ sops sample_secret.yml

example_key: example_value
example_number: 1234.5679
additional: value_add

これで保存して確認すると、

sample_secret.yml
example_key: ENC[AES256_GCM,data:D9pPc7dRd6PuoLUPPw==,iv:zWZxW42YhJsETnWCfgDnIQL55iTSxNrthVmB5gC4bJI=,tag:E/6EBAQ/MnLbCpGCOSnFHg==,type:str]
example_number: ENC[AES256_GCM,data:93sOpzQJvu4N,iv:aigNbYXg+2uSB7cB+JEOnCguBea49DjU2eSGyaBJM5I=,tag:Kf//or7XpUmPChrdhx4Idg==,type:float]
additional: ENC[AES256_GCM,data:KCkWJ7gd9GHV,iv:RaAhQzuRckK6jaXBTl9lgk0YEs3RHS7xxozOgfTtXPo=,tag:sfi7QStVN5BMy/SGX5RO6Q==,type:str]
sops:
    kms:
    -   arn: arn:aws:kms:ap-northeast-1:123456789012:key/890hoge1-67ho-34ga-01fu-1234hoge567
        created_at: '2020-03-10T08:12:15Z'
        enc: AQICAHjzJdn8BrQ9cSoqu5NVOLqeyLNJxw4Gm2+8UkJCyqr6ngHoCN1jRMUoKeYuz1ybQhRDAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQM10R9JpvAo2TpWegYAgEQgDte4OJEdbpEmI0pyuZ6Wmz3qvNtw4TJv7TfEujHIpkf6g9Kapq3wEoETiqjy2z+CYt2VptB9+fQZ3h0hQ==
        aws_profile: ""
    gcp_kms: []
    azure_kv: []
    lastmodified: '2020-03-10T08:42:48Z'  # 更新されていることがわかる
    mac: ENC[AES256_GCM,data:JDiJlMegTqzPRbG2hlcAziRToXhwoz9q8g5d2e0X/8B/sP8T8a+/Z9EhOprsBO8T/S7LaC2pAgOevYXonn906VouY+Kn/8kDGyYFU9qAr50gUJ1DZApCUneM58uKUYIhdo0+erKO7594+hSbbf0Ny9Br1ldPmlHf/TkJ1UtN87o=,iv:qpZQ3fxYXijrKd5u9uXDg+2eOx6hxZv909VZCislKbk=,tag:T+77DkwadQp0YhYf69PXbQ==,type:str]
    pgp: []
    unencrypted_suffix: _unencrypted
    version: 3.5.0

これで簡単にクレデンシャルを追加できます。

ファイルからリダイレクト

複数あってめんどくさい場合はリダイレクトする形で吐き出してあげましょう。

$ cat new_file.yml
simple: yaml_file
add: fromfile
test: encryption
$ 
$ sops -e new_file.yml > enc_new_file.yml
$ 
$ ll enc_new_file.yml
-rw-r--r--  1 staff  staff  1252  3 10 17:49 enc_new_file.yml

確認すると、

enc_new_file.yml
simple: ENC[AES256_GCM,data:jdgFlc05BALm,iv:n4jexuOttomE2Rr/rfUfgdtO79b/AkMg1+id/P6q5Oc=,tag:0t4rwKW/emWymw8TYQjTeg==,type:str]
add: ENC[AES256_GCM,data:qjYDDU4x4yU=,iv:KBlpm+WFZcHIlHtdMLJljRWxoTIdjR8aEuQ/PQzTLBs=,tag:LaE8tXuQskvknUUdftH/MA==,type:str]
test: ENC[AES256_GCM,data:eA5R4hRAYK/erQ==,iv:qkrpdBxWUNI6lJEuQcxfDsIL3mCWEIHlVUhZ6P4Fdsg=,tag:KJI97tTgntFSupfhgVLTBg==,type:str]
sops:
    kms:
    -   arn: arn:aws:kms:ap-northeast-1:123456789012:key/890hoge1-67ho-34ga-01fu-1234hoge567
        created_at: '2020-03-10T08:49:23Z'
        enc: AQICAHjzJdn8BrQ9cSoqu5NVOLqeyLNJxw4Gm2+8UkJCyqr6ngHhoC2HA0cFLSpkhEEpdOdEAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMkqxdUJrTZdJglnV2AgEQgDs8jfVf1giaaMUGp0czzmgUZlAI49D3ekFiEEr9RNcWwoj55KWgNr/q+Umg3Fu+RpPd6wc32eXmw3i2Iw==
        aws_profile: ""
    gcp_kms: []
    azure_kv: []
    lastmodified: '2020-03-10T08:49:24Z'
    mac: ENC[AES256_GCM,data:Af2KHbsAqo35DyS2m4XufoqFc7x4XfNetGC1BIYcVZhy8strbhFEAn0EYn7dHXgpZJI/ZwPzmA2adhioA1+qhJIL8U/mrHJTufPJWEw/8VTkl3JoCBMWO9gB6+Ip525AwNFi6sJEj6GLbwMaI4alZsSSFGs82HiBLe0Q2OpJNQ4=,iv:VlCaQipPx9p96QB4VWl1X7UxTcDQLO1d2SVP6LMz1+g=,tag:Hkt/zqjoN60HMKR6tro60A==,type:str]
    pgp: []
    unencrypted_suffix: _unencrypted
    version: 3.5.0
decryption
$ sops -d enc_new_file.yml
simple: yaml_file
add: fromfile
test: encryption

使い方はざっとこんな感じです。

Terraformで使ってみる

Terraformで使う場合は公式のproviderではないので自分でpluginを持ってくる必要があります。
https://github.com/carlpett/terraform-provider-sops

README.mdにも記載あるとおりreleaseから自分の環境に合わせて持ってきます。
下記に配置してね、と記載ありますがこれだとダメな場合があるのでその場合は.terraform/plugins/配下に置きましょう。

・Windows %APPDATA%\terraform.d\plugins
・All other systems ~/.terraform.d/plugins

書き方

非常にシンプルです。

sops.tf
provider "sops" {}

data "credentials" "new_file" {
  filename = "enc_new_file.yml"
}

resource "aws_ssm_parameter" "test" {
  name   = "sops-test"
  type   = "String"
  key_id = "alias/aws/ssm"
  value  = data.credentials.new_file.data["simple"]
}

プロバイダのバージョン指定は不要です。 ※ コミュニティ提供のため指定できない。
data構文にてクレデンシャルのファイルを指定します。あとはdataリソースとして呼び出してあげるだけです。
これでインフラコードとクレデンシャルが一緒に管理できて幸せですね!

所感

KMSやKey Vault側で権限を管理しているため、権限管理も簡単ですしなにより一緒に管理できるのが非常に楽でいいです!たとえYAMLファイルをお漏らししても復号できないので安心ですね。(そもそもお漏らししないのが一番ですが。。)
ぜひこれで少しでも幸せになる方が増えればと思います。

参考

ファイルをAWS KMSで暗号化して安全にgit commitできるようにするmozilla/sopsの使い方

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSでの権限設定がわかりません!→IAMについてわかりやすくまとめてみました。

IAMとは

IAM = Identify and Access Managementの略です。これもAWSで提供されているサービスの一つになります。AWSで提供されている各種リソースへの認証および認可について管理するサービスです。

まずリソースへアクセスする際に該当ユーザーについて認証をして、アクセスできるか確認します。さらにアクセスできる度合いも制御します。例えばフルアクセス、読み込みのみ等です。

"誰"が"何に"対して"どのように"アクセスできるかを決め、実現するサービスです。

IAMの概要

IAM Overview.jpg

  1. ユーザーはAWS CLI, AWS SDK, AWSマネージメントコンソールを利用してAWSにログインする。
  2. AWSにログインする際には、IAMユーザーあるいはIAMグループを用いて、パスワードなどを利用して認証する
  3. 認証されたIAMユーザーおよびIAMグループはIAMポリシーに従って各AWSリソースに対するアクセス権限が許可される(フルアクセス、読み込み・書き込み、読み込みのみ等)

IAMはなぜ重要か

AWS権限設定はIAMに統合
AWSは利用できるサービスが多岐にわたります。それに対して権限設定はIAMに統合されています。

例えば開発・QA・本番環境の構築を考えます。

  • 開発メンバーは開発環境のすべてのサービス、リソースにアクセスできる。
  • プロビジョニングアカウントはそれぞれQAと本番環境にアクセスできるがLambdaの変更はできない。
  • データベース運用メンバーは本番環境のS3へのすべての権限を持っている

これらの権限設定はすべてのIAMのユーザー、グループ、ポリシー、ロールにより設定します。

IAMで設定すべき必要なリソースがわかりにくい

またIAMを正しく理解すべきもう一つの理由は、対象となるサービスおよびリソースの関連性が複雑ためです。

例えばLambdaを考えてみます。Lambda関数を利用するには該当の関数に対する許可があればよいのは明らかです。Lambdaを作成した際に自動的にIAM実行ロールが作成されます。

しかし例えばLambdaでDyanamoDBを利用していると、DynamoDBの該当のテーブルに対しても許可が必要となります。コードを書いているだけでは許可が必要かどうかわかりません。実際に動かしてみてから、実は必要だったことが明らかになり、適宜ポリシーで許可を与えることになります。このような場合Lambda作成者とIAMの管理者が同じであればトライアルアンドエラーで済ませることができますが、Lambda作成者が日本、IAM管理者が海外にいる場合時間ばかりかかります。

IAMに関係する用語

サービス

AWSで提供されるサービス。例えばEC2, S3, Lambdaなどです。

AWSリソース

AWSで提供されているサービスの中で、ユーザーが実際に操作できる対象です。

  • 例えばAmazon EC2サービスならEC2インスタンス、Amazon S3ならばS3バゲットがあります。

ルートユーザー

ルートユーザーはAWSアカウントを作成した際に作るユーザーになります。

  • アカウントに紐づいているすべてのAWSサービスとリソースについてアクセス権を持ちます。
  • IAMアクセスポリシーによるアクセス許可はできない→常にすべてのサービス・リソースにアクセスできます。
  • 基本ルートユーザーは使いません。
  • 管理者用に別のIAMユーザーを作成して、管理者権限を割り振りましょう。
  • アカウント乗っ取りを防ぐためにMFAを設定するのがよいでしょう。

IAMユーザー

IAMユーザーはAWSを操作する人に個別に割り当てるユーザーになります。

  • ユーザーはログインする際に認証ができます。
  • ユーザーに紐づけられたIAMポリシーにより各種リソースに対する認可を設定できます。
  • ただし基本はIAMグループに所属させて、グループごとにポリシーを設定するのが望ましいでしょう。
  • 一つのIAMユーザーは複数のグループに所属することができます。

IAMグループ

ユーザーをアサインするグループです。

  • グループに紐づけられたIAMポリシーにより各種リソースに対する認可を設定できます。
  • グループに設定された認可はグループに所属しているIAMユーザーに引き継がれます。

IAMポリシー

AWSリソースに対して許可されるあるいは許可されない操作、つまり権限を設定します。

  • IAMポリシーはJSONで記述します。一見取り付きにくいですが、フォーマットは決まっています。慣れればそれほど難しくはありません。
  • ポリシー作成時にはAWS Policy GeneratorもしくはIAM Policy Simulatorが利用できます。

IAMロール

IAMを構成する要素の中でもっとも理解が難しいのがIAMロールになります。
IAMロールを用いるメリットは2つあります。

  • IAMポリシーを個別にオブジェクトに割り振る必要がなくなります。
  • IAMポリシーの割り当てについて柔軟な条件を付けることができる

IAMロールはAWSサービスもしくは、個別のアプリケーションに割り当てられます。

  • IAMロールはIAMユーザーもしくはグループには付与されません。
  • IAMロールにはIAMポリシーを紐づけます。

詳細および使用例については後程説明します。ここでは権限をユーザーだけではなく、サービスおよびAWSリソースにも割り振ることができる、ということだけ理解してください。

IAMユーザーの認証方法

ユーザーがログインする際に認証する方法は複数あります。アカウントの重要度あるいは環境に合わせて認証方法は変えるべきです。主な認証を方法を記載しておきます。

パスワード認証

  • 主にマネージメントコンソールへログインする際に利用されます。
  • ユーザー名とパスワードの組み合わせ。

アクセスキー認証

  • API経由でAWSリソースへアクセスする際に利用される認証方法です
  • 長期的な認証を提供するために用いられます。
  • アクセスキーIDとシークレットアクセスキーの組み合わせになります。認証の際には両方が必要です。
  • IAMユーザーもしくはルートユーザーに紐づけることができます。最大2つまで作成できます。
  • ただしルートユーザーに紐づくアクセスキーは避けるべきでしょう。

MFA

  • 認証を強化したい場合に用います。
  • 第一要素はパスワード認証もしくはアクセスキー認証になります。
  • 第二要素は認証されたデバイスもしくはSMSに送信される認証コードにより認証します。

IAMロールの使い方

IAMロールは説明だけ読んでいてもなかなか理解できません。しかしユースケースでIAMロールを利用しないときーつまりパスワード認証もしくはアクセスキー認証の時ーと、IAMロールを利用するときを比べてみるとすぐ理解できます。

ここではもっともよくつかわれるパターンであるEC2からのS3バゲット利用シーンを使って比べてみます。

まず下記はEC2インスタンスのアプリケーションからS3バゲットを利用する際に、アクセスキーを利用して認証をする方法です。
IAM Role-1.png

この例ではアクセスキーがEC2インスタンス内のアプリに埋め込まれています。

  • ACCESSキーはアプリケーションとポリシーの組み合わせによりユニークになります。
  • アクセスキーを変更するためにはアプリを修正する必要があります。
  • EC2インスタンスに不正アクセスがあった場合には、アクセスキーが漏洩する可能性があります。

ではそれに対してIAMロールを利用してみます。
IAM Role-2.png

  • アクセスキーはアプリケーション内部には埋め込まれていません。
  • 権限のつけ外しは、アプリケーションをいじることなく可能です。

IAMの使い方

2020-03-10_16h26_03.png

IAMポリシー全体のデザイン

テスト開発ならともかくとして本番環境ならば、かならずIAMの全体像をデザインする。はじめてであればなかなかうまくいかない。しかし最初にデザインをしておき、問題が起きたらデザインの手直し→実装とすればセキュリティについて漏れがなくなりやすい。デザインは以下のポイントで整理する。

  • 管理するオブジェクトもしくはサービスの一覧を作成する
  • 上記で明らかになった対象について必要なポリシーを明らかにする
  • ポリシーにアタッチするグループを作成する
  • 各グループに所属するユーザーの一覧を作成する

IAMオブジェクトの作成

デザインが仕上がったら順番にIAMオブジェクトを作成していきます。

  • IAMユーザーを作成します。
  • IAMグループを作成します。
  • IAMポリシーを作成します。
  • IAMポリシーをIAMグループに割り当てます。
  • IAMユーザーをIAMグループに割り当てます。

IAMポリシーの内容

IAMポリシーでは、どのAWSサービスの、どのリソースに対して、どのようなアクションを許可するかをJSON形式で記述する。ただし、AWSコンソールではGUIが提供されているために、一般的なポリシーを作成するならばJSONを気にする必要はない。

** GUI形式でポリシーを作成 **
2020-03-10_16h42_15.png

** 同じポリシーをJSON形式で作成 **
2020-03-10_16h42_21.png

参考

Introduction to AWS Identity and Access Management
英語ですが10分ちょっとでIAMの概念とIAMロールのユースケースを理解できます。IAM初めての人はまずここから!

【図解/AWS】IAM入門~ロールとグループとポリシーの違い,設計・設定手順について~
概要を理解したら、次にIAMユーザー、グループ、ポリシー、ロールについて勉強する必要があります。このページはそれぞれの概念がわかりやすく説明されています。

AWS IAM入門
IAMロールについての基本およびユースケースがわかりやすく記載されています。上記の資料と合わせて読めば、ほぼIAMロールについて理解できます。

【[AWS Black Belt Online Seminar] AWS Identity and Access Management (AWS IAM) Part1 資料公開

IAMロールについてさらに理解を深めたければAWSブラックベルトです。スライド68枚、動画50分近くとかなり内容が濃くなっていますが、これを理解すれば間違いなくIAMブラックベルトでしょう。その反面IAM初心者にはおお勧めできませんので、まずは上記3つをこなし、自分でもいくつかIAMサービスをいじってからチャレンジしましょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[作ってみた]fondeskからSlackへの受電通知にメンションをつけるbot

はじめに

皆さんリモートワークしていますか?弊社では2月中旬から全社的なリモートワークが始まり、この記事を書いている現在(2020/03/10)も引き続きリモートワークをしています。
本記事はリモートワーク時の電話対応をスムーズにストレスなくするべく、AWS Lambda・API Gatewayを使用してbotを開発しましたよというお話です。(先人としてGASを使ったbotを開発された方がいらっしゃいました。)

関連ツール&技術

今回紹介するbotの関連項目です。

  • Slack
  • AWS
    • API Gateway
    • Lambda
    • S3
  • Serverless Framework
  • Python3.7

リモート時の電話対応について

リモートワークを実施する場合に、電話対応をどうしよう…オフィスに誰もいない?ということで弊社ではfondeskを導入しました。

fondeskとはオフィスへの電話対応を代行して、各種ツール(メールとかSlackとかとか)へ通知してくれるサービスです。
(導入方法・設定等は割愛します)

Slackへの通知

弊社では社内コミュニケーションツールとしてSlackを使用しているので、通知先としてSlackを選択しました。このように入電があると指定したチャンネルに通知が飛んできます。
6WtBQ1J1EmmqHW61583806265_1583806377.png

Slack通知の問題点

便利なfondeskですが、チャンネルへの投稿はしてくれますが残念ながら個人宛のメンションは飛びません。公式を見に行くとこんな質問が…電話の宛先によって通知先を変えたり、メンションを付けたりすることはできますか?

宛先に応じた通知機能は多くのご要望をいただいていますが、期待される精度での通知が難しいだけでなく、ユーザーのみなさまにも多くのご負担がかかってしまうため、慎重に検討を進めています。

現時点ではメンションはつかないということで、メンション機能は自前で作ることとします。

先達としてGASを使ったbotを開発された方がいらっしゃいました。今回は自分が慣れている、Slack bot + API Gateway + Lambdaの構成で作成してみようと思います。

botの要件

  1. fondeskアプリの投稿に反応する
  2. 投稿内容から宛先を割り出しメンション付きで投稿する
  3. 宛先を特定できなかった場合@hereで投稿する

フロー

  1. fondeskからSlackに通知(メンションなし)が飛ぶ
  2. チャンネルに招待されたSlackAppがAPI Gatewayにリクエストを飛ばす
  3. API GatewayがLambdaを起動し、宛先を特定する
  4. LambdaからSlackに投稿(メンション付き)をする

設定&実装

設定

Slack側

まずは、fondeskの投稿を検知し、APIGatewayにリクエストを飛ばすためのbotを作成します。
お好きな名前でAppsを作成し、以下のような権限を与えておきます。(commandsは別件でslash commandを使用するために付与しています。本記事の内容のみであれば不要です。)

スクリーンショット 2020-03-10 13.59.30.png

後述するLambda+APIGatewayをserverlessでデプロイした後に、APIGatewayのエンドポイントを貼り付けておきます。
Subscribeするeventsはmessage.channelsです。

ROImKStcKbqbqu41583816769_1583816895.png

設定が終わった後、fondeskが通知するチャンネルにbotユーザーを招待しておきます。

AWS側

基本的にはServerlessでズドンとやります。
LambdaにはS3とファイルのやり取りをしてもらうので、Get/Put Object権限を与えておきます。またLambda起動のトリガーとしてAPI Gatewayを定義しておきます。(x-www-form-urlencodedをjsonにマッピングするテンプレートを置いていますが、本当に必要かは謎です。slash commandと混同している?)

service: fondesk-bot
provider:
  name: aws
  runtime: python3.7
  stage: ${opt:stage, self:custom.defaultStage}
  region: ap-northeast-1
  memorySize: 512
  logRetentionInDays: 30
  iamRoleStatements:
  - Effect: Allow
    Action:
      - "s3:GetObject"
      - "s3:PutObject"
    Resource: 
      - ${self:custom.S3Bucket.arn}
      - ${self:custom.S3Bucket.arn_obj}

functions:
  receiver:
    handler: receiver.handler
    name: fondesker-receiver-${self:provider.stage}
    timeout: 10
    events:
      - http:
          path: fondesker/receiver
          method: post
          integration: lambda
          request:
            passThrough: WHEN_NO_TEMPLATES
            template:
              application/x-www-form-urlencoded:
                '{"body": $input.json(''$'')}'
    environment:
      FONDESK_BOT_ID: ${env:FONDESK_BOT_ID}
      SLACK_TOKEN: ${self:custom.token.${self:provider.stage}}
      S3BUCKET: ${self:custom.S3Bucket.name}

実装

API GatewayがLambdaを起動する。

起動したLambdaに受け渡されるeventの中身はこんな感じになっています。着目するポイントは次の4点です。

  1. event.type
  2. event.subtype
  3. event.bot_id
  4. event.attachments.fields

1.~3.でfondeskアプリからの投稿であることを特定し、4.の中身で宛先を特定するという流れです。
event.attachements.fields内に宛先項目があれば、良かったのですがありません…どうやら「内容」に入っているようです。

{
    "body": {
        "token": "<token>",
        "team_id": "<team_id>",
        "api_app_id": "<app_id>",
        "event": {
            "type": "message",
            "subtype": "bot_message",
            "text": "",
            "ts": "1583124518.035200",
            "bot_id": "<bot_id>",
            "attachments": [{
                "fallback": "test",
                "id": 1,
                "ts": 1583124518,
                "color": "36a64f",
                "fields": [
                    {
                        "title": "発信者",
                        "value": "test",
                        "short": false
                    },
                    {
                        "title": "折り返し先の電話番号",
                        "value": "xx-xxxx-xxxx",
                        "short": true
                    },
                    {
                        "title": "折り返しの連絡",
                        "value": "必要",
                        "short": true
                    },
                    {
                        "title": "内容",
                        "value": "◯◯様宛に入電。先程連絡頂きました件についてです。",
                        "short": false
                    }
                ]
            }],
            "channel": "<channel_id>",
            "event_ts": "1583124518.035200",
            "channel_type": "channel"
        },
        "type": "event_callback",
        "event_id": "<event_id>",
        "event_time": 1583124518,
        "authed_users": ["<user_id>"]
    },
    ...
}

宛先を特定する

「内容」欄から宛先らしい所を抽出することを試みます。正規表現沼や自然言語処理沼には入って行きたくないので、今回は単純に辞書をS3に持つことにして、Lambdaはその辞書を参照し宛先を特定します。

辞書の構造


{
    "<SLACKUSER_ID>": ["テスト", "てすと", "test"]
}

内容欄の中から"宛"を検索し、存在した場合"宛"以前を、存在しない場合は内容欄全体を宛先検索に使用します。

if subtype == "bot_message" and slack_event["bot_id"] == FONDESK_BOT_ID:
    value = [field["value"] for field in attachments[0]["fields"] if field["title"] == "内容"][0]
    if "宛" in value:
        value = value.split("宛")[0]
    user_ids = lookup_user_id(value)


def lookup_user_id(value: str) -> List[Optional[str]]:
    """
    (前提)"SlackユーザーID": [名前表記候補のリスト]という構成のJSONファイルを準備しておく。
    params:
        value: 宛名が入っている(と期待する)文字列
    return:
        user_ids: SlackユーザーIDのリスト。該当者がいない場合は空のリスト。
    """
    BUCKET_NAME = os.environ["S3BUCKET"]
    s3_bucket = S3Bucket("ap-northeast-1", BUCKET_NAME)    
    name_list = json.load(s3_bucket.get_object_body("synonyms.json"))
    user_ids = []
    for user_id, candidates in name_list.items():
        if any([candidate in value for candidate in candidates]):
            user_ids.append(user_id)

    return user_ids

LambdaからSlackに投稿(メンション付き)をする

Slackではhere, channelなどは<!here>, <!channel>でメンションできますが、個人にメンションしたいときは<@user_id>としなければいけません。
以前までは<@username>でもメンションできたようですが、変更されたようです。

message = ""

if user_ids:
    for user_id in user_ids:
        message += f"<@{user_id}> "
    message += "さん、"
else:
    message += "<!here> "
message += "入電ですよ!"
post_slack(channel, message)
def post_slack(channel: str, message: str) -> None:
    """
    Slackにメッセージを投げます
    """
    params = {"token": SLACK_TOKEN, "channel": channel, "text": message}
    requests.post(SLACK_MESSAGE_URL,
                  headers={"Content-Type": "application/json"},
                  params=params)

↓のようにメンションが飛べばOKです。

メンション有り版

DV6wvOg1MoZ7Xbb1583824832_1583824879.png

メンション無し版

HndBfOtJdZzzR2s1583824762_1583824807.png

運用してみて気付いたポイント

  • 辞書の更新が結構手間
  • Slackタイムアウト結構厳しい

辞書の更新に関しては、最初は手作業でやっていたのですが、結構手間なので最終的には辞書の更新機能を別途slash command化して、ユーザーに委ねる形にしました。slash commandに関しては、この記事内に書くと長くなるので別記事にまとめようと思います。

S3にあるJSONファイルをダウンロードするようにした所、botからの投稿が3連打されるという事象が有りました。Slackはレスポンスを3000ms以内に受け取らないとタイムアウトし、3度ほどリトライされるようです。これについては、Lambdaへの割当メモリを増やすという対策でなんとかなっています。どうにもならなくなった場合は、LambdaからLambdaを非同期実行で呼び出すという対策が必要になりそうです。
Ktey8N2TSneON4S1583825299_1583825343.png

まとめ

今回はfondeskからの投稿にメンションをつけるbotの紹介をしました。fondeskによってリモートワークしていても電話対応できるし、メンションがつくことで見落としへの対策が取れました。

あとは、普段一緒に仕事をしない別部署の同僚とマージリクエストのやり取りができて、全社的な取り組みっぽくなった点も個人的には良かったです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS NAT Gatewayの費用を抑えようと思ったら高くなった話

概要

NAT Gatewayの費用を抑えようと思ってVPCエンドポイントを使ったら、逆に高くなった話です

結論

  • 前提条件
    • 外部APIへのリクエストなどすでにNAT Gatewayを使っている
    • セキュリティ(インターネットに出てはいけない)のことは考慮不要
    • インターネットに出てアクセスするレイテンシは考慮不要

NAT Gatewayを普段使っているのであれば、VPCエンドポイントはS3とDynamoDB(ゲートウェイエンドポイント)だけでいい
と思ってます(個人的見解です)

考察

現状

  • NAT Gatewayの費用が高くなっていった
  • NAT Gatewayを利用しているリクエストは以下
    • S3とかECRとかDynamoDBとかCloudWatchLogsとかAWSのサービスにアクセスしているもの
    • 外部APIにアクセスしているもの

AWSのサービスにはVPCエンドポイントを利用することでNAT Gatewayを通さずにアクセスできる

VPCエンドポイント

インターフェイスエンドポイントは各AZごとにENIが作成され、そこ経由でアクセスする
ゲートウェイエンドポイントはルートテーブルのみ

インターフェイスエンドポイントはENIを作成する関係で使っていなくても時間課金される(AZ単位で)

インターフェイスエンドポイントを作れば作るほど、起動時間で課金されてしまう
https://aws.amazon.com/jp/privatelink/pricing/

損益分岐点

  • 東京リージョン、3AZ利用
  • VPCエンドポイント費用:($0.014/h*24h*31day*3AZ){VPCエンドポイント数}+$0.01/GB通信量
  • NAT Gateway費用:$0.062/GB*通信量
    • 現状NAT Gatewayを使っているという前提なので起動時間課金については考慮しない
通信量\VPCエンドポイント数 1 2 3 4 5 NATGW増加費用
100GB 32.248 63.496 94.744 125.992 157.24 6.2
200GB 33.248 64.496 95.744 126.992 158.24 12.4
300GB 34.248 65.496 96.744 127.992 159.24 18.6
400GB 35.248 66.496 97.744 128.992 160.24 24.8
500GB 36.248 67.496 98.744 129.992 161.24 31
600GB 37.248 68.496 99.744 130.992 162.24 37.2
1000GB 41.248 72.496 103.744 134.992 166.24 62

結果、VPCエンドポイント1つでかつ600GB以上の通信をその1つのVPCエンドポイントでする場合に限り、NAT Gatewayを通すよりも安くなる

600GBの通信、CloudWatchLogsとかKinesisとかであれば可能性ありそうでしょうか・・・?
VPCエンドポイントの起動時間課金が安くなるorなくなること、複数AWSサービスに対して1つのVPCエンドポイントでまかなえるようになることを期待しています。

ちなみに、VPCエンドポイントを9個作ったので、費用跳ね上がりました・・・とさ。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

プライベートサブネットのEC2からCloudwatchへログを飛ばす

やりたいこと

プライベートサブネットにおいたEC2から、Cloudwatchへログを飛ばしたい
※Cloudwatch agentをEC2にインストールしている前提で。

普通にできそうじゃない…?

Cloudwatch agentからCloudwatchに飛ばすとなると、1回外へ出ていこうとする。
プライベートサブネットは外へ出る足がないので、サブネットの中で糞詰まってしまう。

やりかた

  • 前提:Cloudwatchに、飛ばしたいログを受けとるためのロググループを作成済みであること

  • セキュリティグループを作成する

  • EC2をsourceとした443のinbound通信を受けられるルールを作成する

  • VPCエンドポイントを作成する

VPC → エンドポイントを選択

image.png

エンドポイントの作成を選択

image.png

どのサービス用のエンドポイントであるかを指定する

Cloudwach Logs用なので、com.amazonaws.リージョン名.logsを選択する
logsで検索すれば出てくる

image.png

検索して出てきたエンドポイントを選択する

image.png

すると、VPC、サブネット、セキュリティグループ等々が選択できるようになる

VPCとサブネットの選択
ログを飛ばしたいEC2が存在するVPCを選択する。サブネットも同様。
image.png

セキュリティグループの選択

1番で作成したセキュリティグループを選択する

image.png

file_config.jsonに"endpoint_override"を追記する

AWSのリファレンスによるとこんな設定が必要とのこと。

CloudWatch エージェント設定ファイルを手動で作成または編集する

image.png

ちょっと何言ってるがよくわからないが、この赤枠の値をfile_config.jsonに書けばOK

image.png

file_config.jsonはコチラ↓
/etc/amazon/amazon-cloudwatch-agent/amazon-cloudwatch-agent.d

最後に、Cloudwatchで飛ばしたかったログが飛んできてるか確認する。
普通にCloudwatch見るだけなので省略。

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Cloudformation/CDKで最新のSolution Stackを指定する方法

https://forums.aws.amazon.com/thread.jspa?threadID=252614

This API returns platform's with their ARN's. One ARN might look like this: "arn:aws:elasticbeanstalk:us-west-2::platform/Node.js running on 64bit Amazon Linux/4.0.0". This ARN can be passed in via the "SolutionStackName" parameter. Additionally, you can truncate the version and just specify "arn:aws:elasticbeanstalk:us-west-2::platform/Node.js running on 64bit Amazon Linux", and this should resolve to the latest 64bit Node JS platform available to you when creating or updating an environment.

SolutionStackNameパラメタとして
arn:aws:elasticbeanstalk:us-west-2::platform/Node.js running on 64bit Amazon Linux/4.0.0
のようにバージョンを明示せずに、
arn:aws:elasticbeanstalk:us-west-2::platform/Node.js running on 64bit Amazon Linux
と書けば、最新版が使われるとのことです。

知らなかった。。。。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ありの~ままの~DBスキーマを、go言語ソースコードに自動変換【xorm/reverse】

はい:clap:
今回ご紹介するのは、こちらのxorm/reverseです。
https://gitea.com/xorm/reverse

なんと!
DBのテーブル構造を直接見に行って、go言語の構造体ソースコードに自動変換してくれます。
対象DB種類も、MySQL以外に色々対応しています。

ライセンスは、MITかな?
LICENSEファイルには明示的には書いてないですが、xorm公式ページではMITとなっているので。
https://xorm.io/

こちら、実は2年以上前から開発されているようです。
以前はリポジトリはgithubだったんですが、親プロジェクトxormと共にgiteaに移籍したみたいですね。

また当時のバージョンではC言語や他の言語ソースへもリバースできたんですが、リニューアル以降はまだlanguage/golang.go以外の出力プラグインは作られてないみたいですね。

パブリックリポジトリなので、gitea のアカウントを持ってなくてもマイPCからgit cloneできました。
ただ、EC2インスタンスからのgit cloneは失敗しました。AWSはブロックしてるのかしら?
ままエアロ。

使い方

・・・をご紹介しようと思ったのですが、早速失敗しました。

READMEどおり

$ go get xorm.io/reverse

したら

# xorm.io/reverse/vendor/github.com/mattn/go-sqlite3
exec: "gcc": executable file not found in %PATH%

gccがインストールされてないと動かない、と。

gcc入れるのダルいなー。

それとちょっと改造する用事もあったので、git cloneしてソースをいじってxorm/reverseのmainコードを直接実行するようにしちゃいましょう。

(下準備)リバース対象のお試しMySQLを用意

https://github.com/yagrush/simple-docker-mysql
↑こちらに、docker-composeですぐ起動できるお試しMySQLをご用意しました。
これをgit cloneして起動します。

docker-compose自体の設定は割愛します。
あるいはAWS EC2用でよろしければ、こちら https://qiita.com/yagrush/items/e85d2da1b0ef9997fa07 をご参照ください。

$ cd ~
$ git clone https://github.com/yagrush/simple-docker-mysql.git
$ cd simple-docker-mysql
$ docker-compose up --build -d

xorm/reverse実行を試みる

xorm/reversegit cloneしてくる

https://gitea.com/xorm/reverse にgit用URLが(githubと同じように)載っているので、ありがたくgit cloneしましょう。:pray:

$ cd ~
$ git clone https://gitea.com/xorm/reverse.git
$ cd reverse

sqlite3 を対応DBから外す

sqlite3は今回使わないし、
gcc見つからないエラーをちゃっちゃと回避したいので、↓をコメントアウトしちゃいます。

  • go.mod
    require宣言の github.com/mattn/go-sqlite3

  • main.go
    import宣言の _ "github.com/mattn/go-sqlite3"

reverse設定ファイルを作る

reverse/examples/goxorm.ymlmy-mysql.yml に改名して、プロジェクトルート reverse/ にコピーします。

そして、中身を適宜編集します。
例えばこんな感じ。

kind: reverse
name: my-mysql
source:
  database: mysql
  conn_str: 'root:hogerootpassword@tcp(127.0.0.1:3306)/hogedb'
targets:
- type: codes
  include_tables:
    - hogetable
  exclude_tables:
    - foo
  language: golang
  template_path: example/template/goxorm.tmpl
  output_dir: models

実行

早速実行してみます。

$ cd reverse
$ go run main.go -f my-mysql.yml
$

何も表示されないですが、エラーもないし、どうやら無事実行されましたかね。

出力を見てみましょう。
設定ファイルにoutput_dir: modelsと書いていたので、これかな?

$ cat models/models.go
package models

type Hogetable struct {
        Hogename string `xorm:"not null VARCHAR(32)"`
        Id       int    `xorm:"not null pk autoincr INT"`
}

素晴らしい…:heart_eyes_cat:

ちなみに「package modelsはいったいどこから来てん?」というと、
↓設定ファイルから参照しているテンプレートファイルgoxorm.tmplにあります。

  template_path: example/template/goxorm.tmpl

これをいじれば、さらにカスタマイズできます。

あと、構造体のフィールドに xorm: タグがおまけで付いてますね。
これはxorm/reverseの親プロジェクトでもある、オープンソースORMxorm用みたいですね。

ちなみにリバース対象にしたDBスキーマは↓こんな感じです。
(上の方でご紹介したお試しセットまんまです)

CREATE DATABASE IF NOT EXISTS hogedb;
use hogedb;
CREATE TABLE hogetable (
  id INT NOT NULL AUTO_INCREMENT,
    hogename VARCHAR(32) NOT NULL,
    PRIMARY KEY (id)
);

改造

と、ここまでは良かったのですが、実際使ってみるとつまづきが・・・
この続きは、また!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Cloud9環境作成時にInstalling Nakで止まってしまった時の対処法

状況

Cloud9の「Create environment」を選択すると、作成されるEC2のAMIは自動的にAmazon Linux AMIになってしまう。今回は最新のAmazon Linux 2を使ってみたいのでEC2を作成→Cloud9からSSH接続設定をしてみた。

その際、Cloud9の初回初回起動時にAWS Cloud9 Installerが途中で止まってしまうケースが何度かあった。
特にInstalling Nakという箇所で30分程度止まってしまうことがあった。

対処法

Cancellボタンを押す。その後ページを再読み込みするとインストールされていないもののみが表示されているので、もう一度インストールを開始すると上手くいった。

原因はわからないのであくまで暫定の対処ですが、ハマっている人の助けになれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsで作ったアプリをAWSでデプロイ① 〜アカウント作成編〜

こんにちは
AWSをつかってRailsアプリをデプロイしたいと思います

まず、AWSアカウントを作成します
スクリーンショット 2020-03-09 14.16.07.png

注意点として一度使ったメールアドレスは、削除すると二度と使えなくなるそうです。

次の画面、僕はパーソナルです。

スクリーンショット 2020-03-09 14.20.47.png

この後、クレジットカード→本人認証画面なので割愛します
プランはベーシックです。

晴れてAWSアカウント作成完了!
早速EC2インスタンスを作りたかったのですが、どうやらアカウント作成直後はできないみたいです。
少し待ちます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSのRDSで接続エラーにハマった話

記事概要

  • 趣味で使っているRedmineをコンテナ環境に移行したいなと思って、Redmine内のMySQLをマネージドサービスのRDSに移行しようとした
  • セキュリティグループなど適切に設定した(つもりだった)
  • バックアップファイルをEC2(redmineが稼働中)からRDSにエクスポートしようとしたところでエラー
  • 最初は接続出来たような気がするのに、しばらく日にちが経った後に接続しようとしたらエラー…?

遭遇したエラー

普通につながるかどうか具体的にはこんなエラーが出る

ubuntu@ip-10-0-0-167:~$  mysql -h xxxxxx-database-1.xxxxxx.ap-northeast-1.rds.amazonaws.com -u <username> -p

ERROR 2003 (HY000): Can't connect to MySQL server on 'xxxxxx-database-1.cluster-xxxxxx.ap-northeast-1.rds.amazonaws.com' (60)

普通にググると「セキュリティグループの設定見直せ!」というアドバイスが出てくるが、そんなことはわかっている。

原因調査

RDSの基本がわかっていなかったが、

  • 複数のAZをまたがったサブネットグループを登録する
  • セキュリティグループも複数登録可能

という条件でサブネットを登録する必要がある。

ということでVPCを選択した時に全てのサブネットをグループに含めてしまった。

これがいけない。

本来ならプライベートサブネットA,Bでグループを作るつもりが、全然関係ないプライベートサブネットCが紛れ込んでいた。

プライベートサブネットCのネットワークACLはガチガチに制限されているため、アクセス元のコンテナがあるサブネットから通信は出来ない。

最初は接続出来たという謎についても何となく原因がわかった。

RDSではプライマリインスタンスとスタンバイインスタンスが複数のサブネットに配置される。

そうなるとプライマリインスタンスが…

①サブネットA/Bに配置された場合 → 接続可能
②サブネットCに配置された場合 → 接続不可

という現象が起きる。

今回は最初①で接続出来たのに、RDSのインスタンスの設定をもう一回やり直した時に②になってしまったようだ。

対処方法

サブネットグループに登録するサブネットのネットワークACLを確認する。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS ECSのビルド〜デプロイをCodePipelineで自動化した

Docker + ECS + RailsのプロジェクトでCodePipelineを使用してデプロイまでを自動化したので、その知見をまとめたい。(ブルーグリーンデプロイではなく、通常のデプロイ時の方法)

RailsのプロジェクトだけれどCodePipeline基本的な使い方は、他の言語でもそれほど変わらないと思う

デプロイの流れ

githubにpushすれば自動でデプロイが開始される。デプロイは以下の流れで行うように作った。

  1. GitHubの特定のブランチ(masterなど)にpushする
  2. pushされたことがCodepipelineに通知されビルドが開始
    1. docker-composeを利用して、Dockerをbuildする
    2. Dockerイメージタグにコミット番号を付与して一意にする
    3. ビルドが完了したらECRにpushする
  3. ビルド完了後にECSにデプロイ通知がいく

CodePipelineの設定

CodePipelineはソース管理、ビルド、デプロイをパーツのようにつなげてCD/CIを管理することができるAWSのサービス。以下のサービスをつなぎ合わせて連携することができる

  • CodeCommit
  • CodeBuild
  • CodeDeploy

スクリーンショット_2020-03-09_14_55_31-2.png

CodeCommit

スクリーンショット_2020-03-09_15_24_30.png

まずはGithubで特定のリポジトリにpushされたときに検知できるようにする。ここではmasterがpushsされたときにビルドされる設定した。

ちなみにCodeCommitはGithub以外にも、ECRやS3などと連携することもできる。CodeCommit自体にコードを管理させることも可能。

CodeBuild

スクリーンショット_2020-03-09_15_29_41.png

CodeBuildではビルドプロジェクトというものを作成する。このビルドプロジェクトはOS環境や、ビルドコマンドを記載するbuildspec.ymlのパスを設定していく。ようはビルドの設定を管理している感じだ。

スクリーンショット 2020-03-09 15.38.01.png

Ubuntuでイメージが最新バージョンのものを使っておけば特に問題はないかと思う。buildspec.ymlはGithubにあげたプロジェクトに入れておく。そのパスをビルドプロジェクトで設定すればOK

buildspec.yml

ビルドするコマンドをyamlに書いていく。ビルドは以下のような流れで行う。

  1. ECRからDockerのイメージを取得
  2. コミットハッシュを取得する(コミットハッシュはDockerイメージタグとして使用する)
  3. .envに環境変数を追加していく
  4. docker-composeを利用してビルドする
  5. dbのmigrateを行う
  6. デプロイを通知する
version: 0.2

phases:
  install:
    runtime-versions:
      docker: 18
  pre_build:
    commands:
      - echo -------- Logging in to Amazon ECR... --------
      - aws --version
      - $(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email)
      - REPOSITORY_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/hogehoge
      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
      - IMAGE_TAG=${COMMIT_HASH:=latest}
  build:
    commands:
      - echo -------- Build started on `date` --------
      - echo -------- Building the Docker image... --------
      - echo AWS_ACCESS_KEY=$AWS_ACCESS_KEY >> .env
      - echo AWS_SECRET_KEY=$AWS_SECRET_KEY >> .env
      - echo ECS_ENV_NAME=$ECS_ENV_NAME >> .env
      - docker-compose -f docker-compose-$ECS_ENV_NAME.yml build
      - docker-compose -f docker-compose-$ECS_ENV_NAME.yml run --name hogehoge-image web sh -c "bundle exec rake db:create && bundle exec rake db:migrate"
      - docker commit hogehoge-image $REPOSITORY_URI:latest
      - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
  post_build:
    commands:
      - echo -------- Build completed on `date` --------
      - echo -------- Pushing the Docker images... --------
      - docker push $REPOSITORY_URI:latest
      - docker push $REPOSITORY_URI:$IMAGE_TAG
      - echo [\{\"name\":\"hogehoge\",\"imageUri\":\"$REPOSITORY_URI:$IMAGE_TAG\"\}] > imagedefinitions.json
artifacts:
  files: imagedefinitions.json

buildspec.ymlはpre_build、build、post_buildという3段階で処理を行う。一個ずつ分解して説明していく。

pre_build

ビルドする前の下準備。 GithubのURLとか、コミットハッシュとかをあとで使うので変数に入れている。ちなみにコミットハッシュはDockerイメージタグとして後で使う。

build
  - echo AWS_ACCESS_KEY=$AWS_ACCESS_KEY >> .env
  - echo AWS_SECRET_KEY=$AWS_SECRET_KEY >> .env
  - echo S3_BUCKET=$S3_BUCKET >> .env

環境変数を.envに書き込むようにしている。僕のRailsプロジェクトでは.envで環境変数を管理しており、CodeBuildでもdocker-composeが使用したいという理由でこの形にしている。このやり方がベストプラクティスではないような気がするので、もっと良い方法を見つけたい。

ちなみに環境変数はSystem Managerで管理している。環境変数についてはのちほどもう少し詳しく記載する。

  - docker-compose -f docker-compose-$ECS_ENV_NAME.yml build
  - docker-compose -f docker-compose-$ECS_ENV_NAME.yml run --name hogehoge-image web sh -c "bundle exec rake db:create && bundle exec rake db:migrate"
  - docker commit hogehoge-image $REPOSITORY_URI:latest
  - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:

あとはdocker-composeでビルドして、migrateして、Dockerイメージをcommitしているだけ。

ちなみに僕はDockerfile内にassets:precompileを行っているため、buildspec.ymlにはコマンドが書いていない。

post_build

ECSにデプロイするためには最終的にimagedefinitions.jsonというファイルを作成する必要がある。

  - docker push $REPOSITORY_URI:latest
  - docker push $REPOSITORY_URI:$IMAGE_TAG
  - echo [\{\"name\":\"hogehoge\",\"imageUri\":\"$REPOSITORY_URI:$IMAGE_TAG\"\}] > imagedefinitions.json
artifacts:
  files: imagedefinitions.json

imagedefinitions.jsonはnameとimageUirを関連付けたjsonを書いていく。複数環境あるときは当たり前だけど複数書いていく。

  • name
    • ECSのコンテナ名
  • imageUri
    • ECRのURL

ECSのタスク定義との関連は以下のようになる。

スクリーンショット_2020-03-09_16_34_33-2.png

補足

環境変数について

環境変数は秘匿化する必要があるためSystem Managerでパラメータを管理するようにした。

68017af5-fa1b-4fb2-8307-aadc174d004a-960x690r.png

安全な文字列を選択してパラメータを設定する。「名前」欄で設定した値をCodeBuildで使用する。

d9424b69-0d44-4230-afa8-c769f4a2e55a-960x573r-2.png

System Managerで設定した値をbuildspec.ymlで使用するために、CodeBuildに環境変数として設定する必要がある。これを設定しておくとbuildspec.ymlの中で$HOGEHOGEという値で使用できるようになる。

  • 名前
    • buildspec.ymlで使用する環境変数名
    • System Managerで設定した名前
  • 入力
    • 『パラメータ』を選択する
- echo AWS_ACCOUNT_ID=$AWS_ACCOUNT_ID >> .env
- echo AWS_ACCESS_KEY=$AWS_ACCESS_KEY >> .env
- echo AWS_SECRET_KEY=$AWS_SECRET_KEY >> .env

これでbuildspec.ymlにファイルの中でCodeBuildで設定した環境変数が利用できるようになる

デプロイしたときにタスク定義のバージョンは更新されていく

0ad3ec1b-5711-4351-a838-654d76ef1f81-960x596r.png

デプロイされるとimagedefinitions.jsonで設定したコンテナ名とイメージのURLでタスク定義のイメージが変更され、リビジョンが新しく更新されていく。タスクをリビジョンで管理するメリットとして「切り戻しが簡単になる」という点がある。

もしも本番で障害が発生したとき場合にもリビジョンを戻すだけで動作する。ただしDBのカラム変更などしているときは、DBをロールバックする必要もあるので注意が必要。

CodePipelineからデプロイを実行する

なんらかの理由でソースコードをpushせずにデプロイしたい場合は、Codepipelineの画面から直接行うことができる

c7177b94-b6e1-408f-84c4-de5ea8b817ad-960x598r.png

終わり

ECSは少人数の開発にこそ向いていると思う。ECSでスケールアップから障害復旧までまかせ、CodePipelineでデプロイを自動化しておけばインフラの運用をそれほど考慮しなくて済むようになる。アプリケーション層に集中して開発ができるようになる。

まだまだECSやCodePipelineに対しての知見が足りないので、また気づきがあったら書いていきたい

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails + Docker + ECSのビルド〜デプロイをCodePipelineで自動化した

Docker + ECS + RailsのプロジェクトでCodePipelineを使用してデプロイまでを自動化したので、その知見をまとめたい。(ブルーグリーンデプロイではなく、通常のデプロイ時の方法)

RailsのプロジェクトだけれどCodePipeline基本的な使い方は、他の言語でもそれほど変わらないと思う

デプロイの流れ

githubにpushすれば自動でデプロイが開始される。デプロイは以下の流れで行うように作った。

  1. GitHubの特定のブランチ(masterなど)にpushする
  2. pushされたことがCodepipelineに通知されビルドが開始
    1. docker-composeを利用して、Dockerをbuildする
    2. Dockerイメージタグにコミット番号を付与して一意にする
    3. ビルドが完了したらECRにpushする
  3. ビルド完了後にECSにデプロイ通知がいく

CodePipelineの設定

CodePipelineはソース管理、ビルド、デプロイをパーツのようにつなげてCD/CIを管理することができるAWSのサービス。以下のサービスをつなぎ合わせて連携することができる

  • CodeCommit
  • CodeBuild
  • CodeDeploy

スクリーンショット_2020-03-09_14_55_31-2.png

CodeCommit

スクリーンショット_2020-03-09_15_24_30.png

まずはGithubで特定のリポジトリにpushされたときに検知できるようにする。ここではmasterがpushsされたときにビルドされる設定した。

ちなみにCodeCommitはGithub以外にも、ECRやS3などと連携することもできる。CodeCommit自体にコードを管理させることも可能。

CodeBuild

スクリーンショット_2020-03-09_15_29_41.png

CodeBuildではビルドプロジェクトというものを作成する。このビルドプロジェクトはOS環境や、ビルドコマンドを記載するbuildspec.ymlのパスを設定していく。ようはビルドの設定を管理している感じだ。

スクリーンショット 2020-03-09 15.38.01.png

Ubuntuでイメージが最新バージョンのものを使っておけば特に問題はないかと思う。buildspec.ymlはGithubにあげたプロジェクトに入れておく。そのパスをビルドプロジェクトで設定すればOK

buildspec.yml

ビルドするコマンドをyamlに書いていく。ビルドは以下のような流れで行う。

  1. ECRからDockerのイメージを取得
  2. コミットハッシュを取得する(コミットハッシュはDockerイメージタグとして使用する)
  3. .envに環境変数を追加していく
  4. docker-composeを利用してビルドする
  5. dbのmigrateを行う
  6. デプロイを通知する
version: 0.2

phases:
  install:
    runtime-versions:
      docker: 18
  pre_build:
    commands:
      - echo -------- Logging in to Amazon ECR... --------
      - aws --version
      - $(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email)
      - REPOSITORY_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/hogehoge
      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
      - IMAGE_TAG=${COMMIT_HASH:=latest}
  build:
    commands:
      - echo -------- Build started on `date` --------
      - echo -------- Building the Docker image... --------
      - echo AWS_ACCESS_KEY=$AWS_ACCESS_KEY >> .env
      - echo AWS_SECRET_KEY=$AWS_SECRET_KEY >> .env
      - echo ECS_ENV_NAME=$ECS_ENV_NAME >> .env
      - docker-compose -f docker-compose-$ECS_ENV_NAME.yml build
      - docker-compose -f docker-compose-$ECS_ENV_NAME.yml run --name hogehoge-image web sh -c "bundle exec rake db:create && bundle exec rake db:migrate"
      - docker commit hogehoge-image $REPOSITORY_URI:latest
      - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG
  post_build:
    commands:
      - echo -------- Build completed on `date` --------
      - echo -------- Pushing the Docker images... --------
      - docker push $REPOSITORY_URI:latest
      - docker push $REPOSITORY_URI:$IMAGE_TAG
      - echo [\{\"name\":\"hogehoge\",\"imageUri\":\"$REPOSITORY_URI:$IMAGE_TAG\"\}] > imagedefinitions.json
artifacts:
  files: imagedefinitions.json

buildspec.ymlはpre_build、build、post_buildという3段階で処理を行う。一個ずつ分解して説明していく。

pre_build

ビルドする前の下準備。 GithubのURLとか、コミットハッシュとかをあとで使うので変数に入れている。ちなみにコミットハッシュはDockerイメージタグとして後で使う。

build
  - echo AWS_ACCESS_KEY=$AWS_ACCESS_KEY >> .env
  - echo AWS_SECRET_KEY=$AWS_SECRET_KEY >> .env
  - echo S3_BUCKET=$S3_BUCKET >> .env

環境変数を.envに書き込むようにしている。僕のRailsプロジェクトでは.envで環境変数を管理しており、CodeBuildでもdocker-composeが使用したいという理由でこの形にしている。このやり方がベストプラクティスではないような気がするので、もっと良い方法を見つけたい。

ちなみに環境変数はSystem Managerで管理している。環境変数についてはのちほどもう少し詳しく記載する。

  - docker-compose -f docker-compose-$ECS_ENV_NAME.yml build
  - docker-compose -f docker-compose-$ECS_ENV_NAME.yml run --name hogehoge-image web sh -c "bundle exec rake db:create && bundle exec rake db:migrate"
  - docker commit hogehoge-image $REPOSITORY_URI:latest
  - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:

あとはdocker-composeでビルドして、migrateして、Dockerイメージをcommitしているだけ。

ちなみに僕はDockerfile内にassets:precompileを行っているため、buildspec.ymlにはコマンドが書いていない。

post_build

ECSにデプロイするためには最終的にimagedefinitions.jsonというファイルを作成する必要がある。

  - docker push $REPOSITORY_URI:latest
  - docker push $REPOSITORY_URI:$IMAGE_TAG
  - echo [\{\"name\":\"hogehoge\",\"imageUri\":\"$REPOSITORY_URI:$IMAGE_TAG\"\}] > imagedefinitions.json
artifacts:
  files: imagedefinitions.json

imagedefinitions.jsonはnameとimageUirを関連付けたjsonを書いていく。複数環境あるときは当たり前だけど複数書いていく。

  • name
    • ECSのコンテナ名
  • imageUri
    • ECRのURL

ECSのタスク定義との関連は以下のようになる。

スクリーンショット_2020-03-09_16_34_33-2.png

補足

環境変数について

環境変数は秘匿化する必要があるためSystem Managerでパラメータを管理するようにした。

68017af5-fa1b-4fb2-8307-aadc174d004a-960x690r.png

安全な文字列を選択してパラメータを設定する。「名前」欄で設定した値をCodeBuildで使用する。

d9424b69-0d44-4230-afa8-c769f4a2e55a-960x573r-2.png

System Managerで設定した値をbuildspec.ymlで使用するために、CodeBuildに環境変数として設定する必要がある。これを設定しておくとbuildspec.ymlの中で$HOGEHOGEという値で使用できるようになる。

  • 名前
    • buildspec.ymlで使用する環境変数名
    • System Managerで設定した名前
  • 入力
    • 『パラメータ』を選択する
- echo AWS_ACCOUNT_ID=$AWS_ACCOUNT_ID >> .env
- echo AWS_ACCESS_KEY=$AWS_ACCESS_KEY >> .env
- echo AWS_SECRET_KEY=$AWS_SECRET_KEY >> .env

これでbuildspec.ymlにファイルの中でCodeBuildで設定した環境変数が利用できるようになる

デプロイしたときにタスク定義のバージョンは更新されていく

0ad3ec1b-5711-4351-a838-654d76ef1f81-960x596r.png

デプロイされるとimagedefinitions.jsonで設定したコンテナ名とイメージのURLでタスク定義のイメージが変更され、リビジョンが新しく更新されていく。タスクをリビジョンで管理するメリットとして「切り戻しが簡単になる」という点がある。

もしも本番で障害が発生したとき場合にもリビジョンを戻すだけで動作する。ただしDBのカラム変更などしているときは、DBをロールバックする必要もあるので注意が必要。

CodePipelineからデプロイを実行する

なんらかの理由でソースコードをpushせずにデプロイしたい場合は、Codepipelineの画面から直接行うことができる

c7177b94-b6e1-408f-84c4-de5ea8b817ad-960x598r.png

終わり

ECSは少人数の開発にこそ向いていると思う。ECSでスケールアップから障害復旧までまかせ、CodePipelineでデプロイを自動化しておけばインフラの運用をそれほど考慮しなくて済むようになる。アプリケーション層に集中して開発ができるようになる。

まだまだECSやCodePipelineに対しての知見が足りないので、また気づきがあったら書いていきたい

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Azure ADをIdPに設定してALBでユーザ認証してみた

Azure ADをIdPに設定してALBでユーザ認証してみました。

https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/listener-authenticate-users.html

手順

  1. HTMLを出力するLambdaを作成し、ALBのターゲットに設定する
  2. Azure ADにアプリケーションを登録する
  3. ALBを設定する
  4. テストする

1. HTMLを出力するLambdaを作成し、ALBのターゲットに設定する

以下記事を参考に、Lambdaを作成してALBのターゲットに設定します。
 https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/lambda-functions.html
ALBのリスナーはHTTPSに設定し、Route53にALBのエイリアスレコードを登録します。

lambda_function.py
def lambda_handler(event, context):

    print(event)

    res = {
        "isBase64Encoded": False,
        "statusCode": 200,
        "statusDescription": "200 OK",
        "headers": {
            "Set-cookie": "cookies",
            "Content-Type": "text/html"
        }
    }

    res['body'] = """<html>
    <head>
    <title>hello world</title>
    </head>
    <body>
    <h1>Hello World</h1>
    </body>
    </html>"""

    return res

2. Azure ADにアプリケーションを登録する

Azureにログインして、Azure Active DirectoryーApp registrationsからアプリケーションを登録します。
Redirect URIは以下を指定します。
 https://Route53に登録したALBのエイリアスレコード/oauth2/idpresponse

スクリーンショット 2020-03-10 4.25.56.png

次にアプリケーションのクライアントシークレットを作成します。

2_sec.png

最後に各種エンドポイントを確認します。

3_ep.png

下記コマンドでも確認できます。

curl https://login.microsoftonline.com/********-****-****-****-************/v2.0/.well-known/openid-configuration

3. ALBを設定する

ALBのリスナーを編集して、Authenticateアクションを追加します。

4_alb.png

ALBの設定値 対応するAzure側の値
Authenticate OIDC
Issure Authorization endpointの"/oauth2/v2.0/authorize"の部分を"/v2.0"で置換したURL
Authorization endpoint OAuth 2.0 authorization endpoint (v2)
Token endpoint OAuth 2.0 token endpoint (v2)
User info endpoint https://graph.microsoft.com/oidc/userinfo
Client ID Application (client) ID
Client secret 作成したシークレット

4. テストする

ALBのエイリアスレコードをURLに指定するとサインイン画面が表示されます。

5_login.png

サインイン後、Lambdaが出力するHTMLが表示されれば成功です。

6_lambda.png

CloudwatchLogsを確認すると、アクセストークン等が渡されているのが分かります。

雑感

  • 慣れてしまえば設定はとても簡単です。(多分)
  • OpenID Connectで認証するがブラウザ・ALB間はアクセストークンでなくセッションクッキーでセキュリティを担保する方式は、アクセストークン漏洩の問題がなくてなんかいい感じ。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む