20200707のAWSに関する記事は16件です。

AWSのリソースイベントを検知する(Amazon EventBridge)

今回は「iam操作を検知」してみます。
何度か「ap-northeast-1」で試してみても上手くいかなかったんですが以下を読む限りiamのイベントは「us-east-1」のEventBridgeにカスタムイベントを追加しないと取れないとのことでした。

https://aws.amazon.com/jp/premiumsupport/knowledge-center/iam-cloudwatch-sns-rule/

EventBridgeの設定
iam-bdg.png

検知後Lambdaのログ出力
cwl.png

重要度の高いイベントを検知/適宜監視していきたい。

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

CloudWatchLogsを監視してSlack複数チャンネルに通知するSA

はじめに

最近、アプリケーションをECS(Fargate)で動かすことが増えてきました。
Fargateを使う場合、(firelensを使わなければ)基本的にログはCloudWatch Logsに出力すると思います。
そのログをキーワード監視したかったのですが、コレだ!!っていう機能・やり方が見つからなかったので、
SA(Serverless Application)を自作してSAR(Serverless Application Repository)に登録してみました。
この記事は、その紹介です。

求める機能

CloudWatch Logsをキーワード監視してSlack通知する方法を調べてみると、この記事にあるような
メトリクスフィルター CloudWatch Alarm AWS Chatbotを組み合わせる方法が見つかります。
その方法でもキーワード監視はできますが、運用を想定すると以下のような機能があった方が
運用しやすいと考えました。

  • ログ毎に監視するキーワード(フィルタパターン)を指定できる
  • パターンマッチしたメッセージを含めて通知できる
  • 複数のログを監視できる
    • リソース(LambdaやCloudWatchAlarmなど)追加なしに、監視対象を追加できる
    • 視認性のために、ログごとに通知先チャンネルや通知ユーザ名/アイコンなどが変えられる

探してないなら、、ということで作りました。

cloudwatch-logs-to-slack-multi紹介

SARのURLはこちら

機能概要

CloudWatch Logsにはサブスクリプションフィルタという機能があります。 公式
この機能はメトリクスフィルタと同様にフィルタパターンを指定でき、パターン一致した結果をKinesisや
Lambdaなど他のサービスに配信することができます。また、複数ロググループに同じ配信先を指定する
ことができます。

この機能を使用して、監視したいロググループごとにサブスクリプションフィルタを作り、配信先として
Lambda関数に向けることで、設定内容に従ってSlack通知することができます。

設定は、パラメータストアにJSON形式で保存します。
ロググループ名(前方一致条件)毎に、Slack通知に使うWebhookURL、チャンネル、ユーザ名、アイコン
などを切り替えて通知することができます。

処理フロー

cwlogs-to-slack.png

通知例

例1/2で分かる通り、通知先チャンネルの他にも、ユーザ名、アイコン、カラーを変更できます。
これにより、エラー発生しているアプリ、環境、緊急度、などを判別しやすくなります。

例1

warn.png

例2

err.png

おまけ)ログ直リンク機能について

通知メッセージ中のjump to logをクリックするとマネコンのCloudWatchLogsの当該ログに直接
飛ぶことができます。
通知されたログがオレンジ色にハイライトされた状態で表示されます。
jumptolog.png

導入手順

1. パラメータストアでパラメータ作成

/lambda/CWLogsToSlack/Configuration という名前でパラメータを作成します。
(パラメータ名は任意ですが、スラッシュで始まる必要があります)
StringでもSecureStringどちらでもOKです。

/lambda/CWLogsToSlack/Configuration
{
  "default": {
    "hook_url":"https://hooks.slack.com/services/HOGEHOGEH/****",
    "channel":"{デフォルト通知先チャンネル}",
    "username":"{デフォルト通知ユーザ名}",
    "icon_emoji":"{デフォルトアイコン(例 :bow:)}",
    "color": "{デフォルトカラー(例 #D00000)}"
  },
  "rules": [
    {
      "if_prefix": "{`ロググループ:ログストリーム`の前方一致条件}",
      "hook_url":"{上書きするWebhookURL}",
      "channel":"{Specify when overwriting}",
      "username":"{Specify when overwriting}",
      "icon_emoji":"{Specify when overwriting}",
      "color": "{Specify when overwriting}"
    },
   <ここにif_prefixの条件数分追加>
  ]
}

if_prefixは 処理対象ログの{ロググループ:ログストリーム}という文字列を前方一致で判定し、
真であればdefault設定を個別設定(rules配列の要素のこと)で上書きした設定でSlack通知されます。
※個別設定ごとに、if_prefixhook_urlは必須

Webhookの注意点

SlackのIncomingWebhookは2種類あります。
Custom Integrationから作成するWebhookと、Slack Appから作成するWebhookです。
前者であればchannel/username/ucon_emojiを上書き可能ですが、後者のWebhookの場合だと
Webhook作成時に指定した値が固定となり上書きすることができません。
(JSONで指定しても無視されます。README.mdも参照)

ちなみにCustom Integrationの方は非推奨らしいのでご利用は計画的に。
(上書きできたりいろいろ便利ですが、権限強すぎな気もしますね)

2. Lambda関数作成画面でSARを検索

検索フィールドに、slack logsを入力し、カスタムIAMロールまたは〜 のチェックボックスを入れて
検索するとcloudwatch-logs-to-slack-multiという名前で表示されるのでリンクをクリックします。

sarsearch.png

3. デプロイ

このアプリがカスタム IAM ロールを作成することを承認します。のチェックを入れてデプロイ実行!
(デフォルトから変えた場合はパラメータ名を修正してください)

deploy.png

しばらく待って以下の表示になれば完了です。
deploy.png

ログ監視設定手順

1. CloudWatchLogs ロググループにサブスクリプションフィルタ設定

任意のロググループを選択し、アクションからLambdaサブスクリプションフィルターを作成をクリック

action.png

2. Lambda関数を指定

CWLogsToSlackを選択し、フィルタパターンを指定してストリーミングを開始ボタンをクリックします。
開始するとすぐにログ監視が始まり、フィルタにマッチしたログが通知されるようになります。

select.png

監視が必要なロググループ分上記を繰り返し実施します。

これで設定は完了です。

注意点

  • パラメータストアの設定を変更してもLambda側に即時反映されるわけではありません
    • コンテナ入れ替えを待つか、関数に変更加えて保存するか(関数入れ替える方法ってないのかなー)
  • Lambdaの同時実行数は1固定になります
    • そもそもログ監視で大量に通知されるのは異常時なので、予め絞っています

まとめ

今後もコンテナ化の流れは止まらないので、個人的に運用しやすいログ監視の仕組みを作れて満足。
まだまだ改善点はありそうなので、しばらく運用しつつ改善していきます。
そもそもCloudWatchLogsを活用するための機能が足りていない気がするので、AWS公式で出てくるかも
しれませんが、それが充実するまでは自分で隙間を埋めつつベストな仕組みを追求していこうと思います!

あと今回初めてSARに登録してみましたが、aws-sam-cliで簡単に登録できました。
今後さらにSARに有用な機能が増えていくと思うので要チェックですね!

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

S3サーバーサイド暗号化SSE with Python boto3

S3に書き込むデータを暗号化するのを、boto3(Python)で行いたいと思います。

参考
https://qiita.com/ot-nemoto/items/66cc783e8d8714f88bd8#%E9%9D%9E%E6%9A%97%E5%8F%B7%E5%8C%96%E3%83%90%E3%82%B1%E3%83%83%E3%83%88%E3%81%AB%E3%82%B5%E3%83%BC%E3%83%90%E3%82%B5%E3%82%A4%E3%83%89%E6%9A%97%E5%8F%B7%E5%8C%96%E3%82%92%E6%8C%87%E5%AE%9A%E3%81%97%E3%81%A6%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89

SSE サーバーサイド暗号化の種類は以下の通りです。

Default encryption (デフォルト暗号化)

  1. SSE with AES-256
  2. SSE with KMS AWS Managed Keys
  3. SSE with KMS CMK(Customer Managed Keys)

デフォルト暗号化以外

  1. SSE with AES-256
  2. SSE with KMS AWS Managed Keys
  3. SSE with KMS CMK(Customer Managed Keys)
  4. SSE with Customer Key(AES-256 etc.)

サンプル

前準備

ソースの中にある、BUCKET_NAME の値をご自身のS3バケット名に変更してください。
SSEKMSKeyIdをご自身のCustomer Managed Keys のKey ID に変更してください。
AES-256で暗号化できるように、SSECustomerKeyを作成してください。
私はUbuntu18.04を使用していますが、以下のコマンド実行結果を使いました。
SSE_CUSTOMER_KEY=$(cat /dev/urandom | base64 -i | fold -w 32 | head -n 1)

event = {
    "BUCKET_NAME" : "xxxxxxxxx",
    "encryption_mode" : default_encryption,
    "SSEKMSKeyId" : "yyyyyyyy",
    "SSECustomerKey" : "zzzzzzzzzzzz"
}

デフォルト暗号化、デフォルト暗号化以外の切り替え

デフォルト暗号化

デフォルト暗号化を行うときは、S3コンソールにて、S3バケットのProperties → Default Encryptionを押して、AES-256 または AWS-KMSを選択してください。
上記event変数の encryption_mode 値を default_encryption にしてください。

デフォルト暗号化でないパターン

デフォルト暗号化でないパターンで暗号化するときは、S3コンソールにて、S3バケットのProperties → Default Encryptionを押して、Noneを選択してください。
上記event変数の encryption_mode 値を non_default_encryption にしてください。

#-*- encoding:utf-8 -*-
from datetime import datetime,timedelta,timezone
import json
import os,os.path
import sys
#Third Party
import boto3

#kms
kms = boto3.client("kms")

#s3
s3 = boto3.client("s3")

def default_encryption(**event) -> None:
    """
    Check the "Default encryption" on the S3 bucket Properties
    Automatically encrypt objects when stored in Amazon S3
    Args:
        event
    Returns:
        None
    """
    #SSE with AES-256
    #SSE with KMS AWS Managed Keys
    #SSE with KMS CMK(Customer Managed Keys)
    response = s3.put_object(
        Bucket = event["BUCKET_NAME"],
        Key = "test",
        Body = "Encrypted".encode("UTF-8")
    )
    print(f'ServerSideEncryption'.ljust(20) + f' = {response["ServerSideEncryption"]}')

    #just only for KMS. check the KeyManager
    if response["ServerSideEncryption"] == "aws:kms":
        KeyManager = kms.describe_key(
            KeyId = response["SSEKMSKeyId"]
        )["KeyMetadata"]["KeyManager"]
        print(f"KeyManager".ljust(20) + f" = {KeyManager}")

    #Body
    Body = s3.get_object(
        Bucket = event["BUCKET_NAME"],
        Key = "test"
    )["Body"].read().decode("UTF-8")
    print(f"Body".ljust(20) + f" = {Body}")


def non_default_encryption(**event) -> None:
    """
    Encrypt the data on your behalf
    Args:
        event
    Returns:
        None
    """
    #SSE with AES-256
    #SSE with KMS AWS Managed Keys
    #SSE with KMS CMK(Customer Managed Keys)
    #SSE with Client operations key. This is not the key which S3 or KMS operates
    l = [
        {"ServerSideEncryption" : "AES256"},
        {"ServerSideEncryption" : "aws:kms" },
        {"ServerSideEncryption" : "aws:kms","SSEKMSKeyId" : event["SSEKMSKeyId"]},
        {"SSECustomerAlgorithm" : "AES256","SSECustomerKey" : event["SSECustomerKey"]}
    ]
    for item in l:
        params = {
            "Bucket" : event["BUCKET_NAME"],
            "Key" : "test",
            "Body" : "Encrypted".encode("UTF-8")
        }
        for key in item:
            params[key] = item[key]

        response = s3.put_object(**params)
        if "ServerSideEncryption" in response:
            print(f'ServerSideEncryption'.ljust(20) + f' = {response["ServerSideEncryption"]}')
            #just only for KMS. check the KeyManager
            if response["ServerSideEncryption"] == "aws:kms":
                KeyManager = kms.describe_key(
                    KeyId = response["SSEKMSKeyId"]
                )["KeyMetadata"]["KeyManager"]
                print(f"KeyManager".ljust(20) + f" = {KeyManager}")

        elif "SSECustomerAlgorithm" in response:
            print(f'SSECustomerAlgorithm'.ljust(20) + f' = {response["SSECustomerAlgorithm"]}')


        #Body
        params = {
            "Bucket" : event["BUCKET_NAME"],
            "Key" : "test"
        }
        if "SSECustomerAlgorithm" in item:
            params["SSECustomerAlgorithm"] = item["SSECustomerAlgorithm"]
            params["SSECustomerKey"] = item["SSECustomerKey"]

        Body = s3.get_object(
            **params    
        )["Body"].read().decode("UTF-8")
        print(f"Body".ljust(20) + f" = {Body}")



if __name__ == "__main__":
    event = {
        "BUCKET_NAME" : "xxxxxxxxx",
        "encryption_mode" : default_encryption,
        "SSEKMSKeyId" : "yyyyyyyy",
        "SSECustomerKey" : "zzzzzzzzzzzz"
    }

    event["encryption_mode"](**event)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSのECSでコンテナにWEBターミナル接続

ECSを使用するとデバッグ環境でコンテナに接続するのが面倒なので、コンテナにWebターミナルであるGoTTYを入れてみる方法を考えてみました。
あくまでもデバッグ目的で使用します。

自分のアプリのDockerファイルにgottyを設定

Dockerfileのgotty設定部分 (alpineを使用しています)

# GoTTY
RUN addgroup -S gotty && adduser -S -g gotty gotty \
    && mkdir -p /gotty \
    && chown -R gotty /gotty && chgrp -R gotty /gotty
RUN wget -qO- https://github.com/yudai/gotty/releases/download/v0.0.12/gotty_linux_amd64.tar.gz | tar zx -C /gotty/
RUN echo 'port = "8080"' >> /home/gotty/.gotty
RUN echo 'credential = "myacc:pass"' >> /home/gotty/.gotty
RUN chown -R gotty /gotty/gotty /home/gotty

# Supervisor
RUN echo '[supervisord]' >> /etc/supervisord.conf
RUN echo 'nodaemon=true' >> /etc/supervisord.conf
RUN echo '[program:myapp]' >> /etc/supervisord.conf
RUN echo 'command=/myworks/myapp'   >> /etc/supervisord.conf
RUN echo '[program:gotty]' >> /etc/supervisord.conf
RUN echo 'command=/gotty/gotty --config "/home/gotty/.gotty" -w /bin/ash' >> /etc/supervisord.conf
RUN echo 'autostart=true' >> /etc/supervisord.conf
# RUN echo 'user=gotty' >> /etc/supervisord.conf

EXPOSE 80 8080

CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]

portでターミナルのポート
credentialでベーシック認証のアカウントとパスワード
ただ確認したいだけであれば"user=gotty"の部分のコメントを外してください。

ECSのタスク定義

コンテナから自分のアプリの80とターミナルの8080を通したいのですが、私が使用していたecs-cliのバージョンではマルチポートの設定がうまくできなかったのでawsクライアントでタスク定義からやります。
targetGroupArnは自分のターゲットグループARNを入れてください。

  • ポート定義ファイル"multiport.json"を作成
{
"loadBalancers": [
{
"containerName": "myapp",
"containerPort": 80,
"targetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:*********:targetgroup/mycluster/*****"
},
{
"containerName": "myapp",
"containerPort": 8080,
"targetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:*********:targetgroup/mycluster/****"
}
]
}
  • タスク定義ファイル"task-definition.json"を作成
{
    "memory": "512", 
    "networkMode": "bridge", 
    "family": "mytask", 
    "placementConstraints": [], 
    "cpu": "256", 
    "executionRoleArn": "arn:aws:iam:*************", 
    "volumes": [], 
    "requiresCompatibilities": [
        "EC2"
    ], 
    "taskRoleArn": "arn:aws:iam::*************", 
    "containerDefinitions": [
        {
            "links": [], 
            "image": "********.dkr.ecr.ap-northeast-1.amazonaws.com/myapp/develop:1.0.0", 
            "hostname": "myhost", 
            "environment": [
            ], 
            "extraHosts": [], 
            "dnsServers": [], 
            "volumesFrom": [], 
            "dnsSearchDomains": [], 
            "mountPoints": [], 
            "logConfiguration": {
                "logDriver": "awslogs", 
                "options": {
                    "awslogs-region": "ap-northeast-1", 
                    "awslogs-stream-prefix": "docker", 
                    "awslogs-group": "/myapp/group"
                }
            }, 
            "dockerSecurityOptions": [], 
            "entryPoint": [], 
            "linuxParameters": {
                "devices": [], 
                "capabilities": {}
            }, 
            "essential": true, 
            "readonlyRootFilesystem": false, 
            "name": "myapp", 
            "dockerLabels": {}, 
            "privileged": false, 
            "portMappings": [
                {
                    "protocol": "tcp", 
                    "containerPort": 80, 
                    "hostPort": 0
                }, 
                {
                    "protocol": "tcp", 
                    "containerPort": 8080, 
                    "hostPort": 8080
                } 
            ], 
            "command": [], 
            "cpu": 0, 
            "ulimits": []
        }
    ] 
}

タスクの定義とECSサービス起動

revision=`aws ecs register-task-definition --cli-input-json file://task-definition.json | jq '.taskDefinition.revision'`
echo "revision:${revision}"
aws ecs create-service --cluster ${CLUSTER_NAME} --service ${SERVICE_NAME} --task-definition mytask:${revision} --deployment-configuration maximumPercent=${MAX_PARCENT},minimumHealthyPercent=${MIN_PARCENT} --desired-count 1 --launch-type EC2 --cli-input-json file://multiport.json

ブラウザでアクセス

指定した8080ポートにブラウザでアクセスするとターミナルが立ち上がります。

gotty.png

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

AWSハンズオン実践メモ 〜サーバーレスアーキテクチャで翻訳 Web API を構築する〜

はじめに

AWS公式のハンズオンシリーズの中から、翻訳 Web API の構築を通して、サーバーレスアーキテクチャの基本を学ぶハンズオンを実施しました。

本記事は自身のハンズオン学習メモとして投稿します。

目次

ハンズオンの目的

  • AWS Lambda, Amazon API Gateway, Amazon DynamoDB の基本を学ぶ
  • 上記のサービスを組み合わせて、サーバーレスな Web API を作成する

翻訳 Web API の構築を通して、サーバーレスアーキテクチャの基本を学んでいただきます。サーバーレスアーキテクチャの特徴とその中核となる AWS サービスの概要をお伝えした上で、実際に手を動かしてその理解を深めることができます。主に取り扱う AWS サービスは AWS Lambda、Amazon API Gateway、Amazon DynamoDB の3つです。

20200706.png
(https://aws.amazon.com/jp/aws-jp-introduction/aws-jp-webinar-hands-on/ より引用)

本編

AWS Lambda ハンズオン① - AWS Lambda を単体で利用する

  • Lambda Functionを作成
  • IAMロールを自動生成し、付与
  • メモリとタイムアウト値を変更
  • テストを実行
  • log取得のコードを追加

AWS Lambda ハンズオン② - AWS Lambda から他の AWS サービスを呼び出す

  • Python SDK のドキュメントを⾒ながら、Amazon Translate の呼び出しを実装
  • IAM ロールを修正
  • レスポンスとして、JSON 形式で英語訳を返すようにする
  • Lambda 関数をテスト実⾏

Amazon API Gateway ハンズオン① - Amazon API Gateway を単体で利用する

  • Mock データを返す API を作成
    • 新規に API を作成
    • sample リソースを作成し、GETメソッドを作成
    • 統合タイプとして Mock を選択(まだ Lambda 関数との連携は⾏わず、固定の JSON を返す)
    • dev ステージにデプロイする
    • API を試しに実⾏

Amazon API Gateway ハンズオン② - Amazon API Gateway と AWS Lambda を連携する

  • ⼊⼒した⽇本語を英語に翻訳するAPIを作成
    • /translate リソースを作成し、GET メソッドを作成
    • 統合タイプとして Lambda 関数を選択
    • プロキシ統合を設定し、Input / Output をパススルーする
    • メソッドリクエストでクエリパラメータの設定
    • Lambda 関数を修正
    • Lambda 関数をテスト実⾏
    • API をデプロイ

Amazon DynamoDB ハンズオン① - Amazon DynamoDB テーブルを作成する

  • DynamoDB のテーブルを作る
    • Partition Key として timestamp を指定
    • テーブル作成
    • データを⼿動で入力

Amazon DynamoDB ハンズオン② - AWS Lambda から Amazon DynamoDB に Item を Put する

  • 翻訳履歴を DynamoDB にストアするように修正
    • Python SDK のドキュメントを⾒ながらDynamoDB にストアするように Lambda 関数を修正
    • IAM ロール設定を修正
    • API を叩いて確認

作成した AWS リソースの削除

粛々と削除。特筆すべきことはなし。

おわりに

Lambda、API Gateway、DynamoDBを触った事がなかったので、いい経験になった。

今回初めてサーバレスアーキテクチャを体験したが、基盤レイヤーを考慮せずにアプリケーション部分のみに集中できるのはやはり便利だと感じた。

Lambdaで軽い処理を入れる、等は今後仕事でも使う機会が多いと思うので、実装に必要となるコーディングスキルも併せて身につけていかなければならないなと感じた。

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

railsで開発中にAWSから高額請求されたが、、

はじめに以下の記事にあるように、railsでアプリを開発中に初期設定のミスが原因で高額請求に至った。
https://qiita.com/381704/items/d5216295eadd67eb9e8c

完全に注意不足であり、しっかり利用してしまったあとなので支払うしかないと思っていたが、駄目元でAWSのサポートセンターに以下のようなメールを送ってみた。(本文を載せるのは抵抗があったが、証拠&同じ状況の人の希望になればと思い残しておく)
スクリーンショット 2020-07-07 16.17.45.png

2日後にAWSサポートより返信が来た。返信内容には、現在料金が発生しているRDSのインスタンスとスナップショットの削除をすることとあり、その後に返金が可能かの相談にはいるということだ。
内容通り、RDSの削除をして再度AWSに削除完了のメールを送信した。
その2日後AWSサポートより返答があり、今回に限り返金措置を設けてくださるとのこと!!!

請求料金は合計600$以上になったが、AWS様より返金をしてくださり、最終的な請求料金は100$ほどになった。落ち度は完全に私のほうにあるにも関わらず、今回の対応をしてくださったことは本当に感謝してもしきれないほどだ。

離職中だったので金銭的に苦しい状況だったため本当に助かりました。ありがとうございました。
反省として請求アラームを2個付け、UdemyでAWSに関する教材を購入し基礎からAWSの利用法を学び直しました。
これからもサービスを利用させていただきます。

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

水樹奈々さんの公式サイトがダウンしないのは AWS を使っているからだった。

こんにちは、 くわブロ です。

水樹奈々さん、結婚!

ネットニュースだけでなくテレビ局の報道でも、水樹奈々さんの結婚でワッショイワッショイ状態です。
当方は職業がIT系なので、水樹奈々さんのファンの方よりも、「公式サイトはダウンしていないかな?」と事務所の情シスの方へ思いを馳せてしまいます。(ファンの方、すいません。)

水樹奈々さんの 公式サイト を見に行ったところサクサク見れますし、これだけアクセス激増している状況でもダウンしていないので AWSクラウドでこんな構成を組んでいるんじゃないかな? と予想してみたらビンゴでした。(この記事書いている最中も似たことをツイートしてる人が居て、ちょっと笑いました。みんな情シス目線。)

予想していた構成図

静的コンテンツ配信の鉄板とも言える構成。

ユーザー → Route53(DNS) → CloudFront(CDN) → S3(静的Webサイト)

画像などの静的コンテンツをS3に配置する&ホスティングして、前段キャッシュでCloudFrontを使って、Route53で名前解決させる。(ちなみに、AWSの公式ハンズオンでも同じ構成です。)

さっそく確かめてみました。

Route53 で名前解決しているか確かめてみた。

ビンゴ。

➜  ~ dig www.mizukinana.jp +noall +authority

; <<>> DiG 9.10.6 <<>> www.mizukinana.jp +noall +authority
;; global options: +cmd
mizukinana.jp.      75048   IN  NS  ns-1966.awsdns-53.co.uk.
mizukinana.jp.      75048   IN  NS  ns-1265.awsdns-30.org.
mizukinana.jp.      75048   IN  NS  ns-579.awsdns-08.net.
mizukinana.jp.      75048   IN  NS  ns-290.awsdns-36.com.
➜  ~

CloudFront を挟んでいるか確かめてみた。

ビンゴ。

➜  ~ for ipaddress in `dig www.mizukinana.jp +short`
do
dig -x $ipaddress +short
done
server-13-249-171-115.nrt12.r.cloudfront.net.
server-13-249-171-96.nrt12.r.cloudfront.net.
server-13-249-171-93.nrt12.r.cloudfront.net.
server-13-249-171-102.nrt12.r.cloudfront.net.
➜  ~

S3 から配信されているか確かめてみた。

ビンゴ。
(追記)
本 dig 結果からは、リダイレクトページがS3に配置されている点しか断定は出来ません。
よって、 オリジン(CloudFrontの配信元) = S3 は推測になりますので、打ち消し線で修正しますね。
MovableTypeで作られていて、リダイレクトページはS3に置いてあること、からオリジンがS3と推測しましたが、断定するのは Too Much でした。失礼しました。
コメントいただいた @shin3250 さん、ありがとうございます!

➜  ~ for ipaddr in `dig mizukinana.jp +short`
do
dig -x $ipaddr +short
done
s3-website-ap-northeast-1.amazonaws.com.
➜  ~

まとめ

水樹奈々さんは阪神タイガースのファンらしいです。




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

EventBridge+LambdaによるAMI作成通知を実装するCloudFormationテンプレート作成

EventBridge+LambdaによるAMI作成通知を実装するCloudFormationテンプレートを作成しました。

今回参考にさせていただいた記事

AMIの作成時間が知りたかったのでSNS Topicで通知させてみた

CloudFormationテンプレートによる実装概要

CloudFormationテンプレートでスタックを作成すると下記のような構成が出来上がります。
CloudTrailについてはCloudFormationテンプレートに含んでいませんので別途有効化が必要となります。

概要.png

この構成は下記のCloudFormationテンプレートで構築しています。

  • AMI作成時のSNS通知用CloudFormationテンプレート(notify-sns-createimage.yml)
    • CloudTrail有効化を前提
    • SNSの通知先指定メールアドレス宛に届く確認メールは別途対応前提

AMI作成時のSNS通知用CloudFormationテンプレート(notify-sns-createimage.yml)

AMI作成時にCreateImageというAWS API Call が呼び出されたときにEventBridge経由にてSNS通知(Email)するものです。Lambdaでは下記の処理を行います。

  • AMI作成時にSNS通知
メールフォーマット
件名:
Start CreateImage (対象インスタンスID(対象AMI))

本文:
Start CreateImage

Region:対象リージョン
InstanceID:対象インスタンスID
AMI Name:対象AMI
AMI ID:対象AMI ID

AMI作成時のSNS通知用CloudFormationテンプレート(notify-sns-createimage.yml)の内容は下記となります。
CloudFormationスタック作成時に入力が必要となるパラメータを説明します。

  • EventName :Event名(初期値:notify-sns-createimage_event)
  • FunctionName:Lambda関数名(初期値:notify-sns-createimage)
  • RoleName : Lambda関数で利用するIAMロール名(初期値:notify-sns-createimage)
  • SNSSubscriptionEmail : SNSのエンドポイント(送信先メールアドレス) (初期値:xxxx@example.com)
    • 必ず受信可能なメールアドレスに変更してください
  • SNSTopicName : SNS Topic名(初期値:notify-sns-createimage_event)

parameter.png

notify-sns-createimage.yml
AWSTemplateFormatVersion: 2010-09-09
Parameters:
  RoleName:
    Type: String
    Default: notify-sns-createimage
  FunctionName:
    Type: String
    Default: notify-sns-createimage
  EventName:
    Type: String
    Default: notify-sns-createimage_event
  SNSTopicName:
    Type: String
    Default: notify-sns-createimage
  SNSSubscriptionEmail:
    Type: String
    Default: xxxx@example.com
Resources:
  LambdaRole:
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName: !Sub '${RoleName}'
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - 'sts:AssumeRole'
            Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
        Version: 2012-10-17
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/AWSLambdaExecute'
      Path: /
  Lambda:
    Type: 'AWS::Lambda::Function'
    DependsOn: LambdaRole
    Properties:
      Code:
        ZipFile: |
          import os
          import boto3

          def lambda_handler(event, context):
              awsRegion = event['detail']['awsRegion']
              name = event['detail']['requestParameters']['name']
              instanceId = event['detail']['requestParameters']['instanceId']
              imageId = event['detail']['responseElements']['imageId']

              client = boto3.client('sns')

              sns_response = client.publish(
                  TopicArn= os.environ['SNS_TopicName'] ,
                  Message=('Start CreateImage\n\nRegion:'
                  + awsRegion
                  + '\nInstanceID:'
                  + instanceId
                  + '\nAMI Name:'
                  + name
                  + '\nAMI ID:'
                  + imageId
                  + '\n\n'),
                            Subject= 'Start CreateImage  (' + instanceId + '(' + name + '))'
              )

              return sns_response
      Description: SNS notification when CreateImage is executed
      FunctionName: !Sub ${FunctionName}
      Handler: index.lambda_handler
      MemorySize: 128
      Role: !GetAtt LambdaRole.Arn
      Runtime: python3.6
      Timeout: 300
      Environment:
        Variables:
          'AWS_ACCOUNT': !Sub ${AWS::AccountId}
          'SNS_TopicName': !Sub arn:aws:sns:${AWS::Region}:${AWS::AccountId}:${SNSTopicName}
      Tags:
        - Key: Name
          Value: !Sub ${RoleName}
        - Key: CloudformationArn
          Value: !Ref 'AWS::StackId'
  NotifyMailSNSTopic:
    Type: AWS::SNS::Topic
    Properties:
      DisplayName: !Sub ${SNSTopicName}
      TopicName: !Sub ${SNSTopicName}
      Subscription:
        - Endpoint: !Sub ${SNSSubscriptionEmail}
          Protocol: email
  NotifyMailSNSPolicy:
    Type: AWS::SNS::TopicPolicy
    DependsOn: NotifyMailSNSTopic
    Properties:
      PolicyDocument:
        Id: MyTopicPolicy
        Version: '2012-10-17'
        Statement:
        - Sid: NotifyMailSNSPolicy
          Effect: Allow
          Principal:
           AWS: !GetAtt LambdaRole.Arn
          Action:
          - sns:Publish
          Resource: !Ref NotifyMailSNSTopic
      Topics:
        - !Ref NotifyMailSNSTopic
  Rule:
    Type: 'AWS::Events::Rule'
    Properties:
      Description: !Sub ${EventName}
      Name: !Sub ${EventName}
      EventPattern: 
        source: 
          - "aws.ec2"
        detail-type: 
          - "AWS API Call via CloudTrail"
        detail: 
          eventSource: 
            - "ec2.amazonaws.com"
          eventName: 
            - "CreateImage"
      State: "ENABLED"
      Targets:
        - Arn: !GetAtt 
            - Lambda
            - Arn
          Id: lambda
  LambdaEvent:
    Type: 'AWS::Lambda::Permission'
    Properties:
      Action: 'lambda:InvokeFunction'
      FunctionName: !Ref Lambda
      Principal: events.amazonaws.com
      SourceArn: !GetAtt 
        - Rule
        - Arn
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS RDSでマルチAZ配置できない場合の対処法

はじめに

既存のAWS RDSのマルチAZ配置に変更したところ...
以下のようなエラーが出たので、解決方法を載せておきます!

DB Subnet Group doesn't meet availability zone coverage requirement. 
Please add subnets to cover at least 2 availability zones. 

解決方法

このエラーは、以下の意味になります。
『サブネットグループに少なくとも2つのアベイラビリティーゾーンを設定してください!』
だから、解決方法としては、追加すればOKです

サブネットグループの追加方法

AWSマネージメントコンソールでRDSを開いて、サプネットグループをクリックしてください
スクリーンショット 2020-07-07 15.00.17.png

ボックスにチェックをして、編集をクリックします
スクリーンショット 2020-07-07 15.04.19.png

サブネットを追加で追加してください
スクリーンショット 2020-07-07 15.08.16.png

左のデーターベースに移動し、変更をクリックして、RDSをマルチAZ配置に変更します。
スクリーンショット 2020-07-07 15.14.46.png

RDSの設定を見て、マルチAZが以下のように反映されていれば、クリアです!
お疲れ様でした!
スクリーンショット 2020-07-07 15.31.20.png

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

httpd.confのDocumentRootを修正して再起動しても起動出来なくなった。

【はじめに】

Amazon EC2 LinuxからApacheのDocumentRootを変更時に
勝手にドツボにハマったので、対処方法を備忘録として残します。

またこれからAWS周りを勉強していく中で
作業中にハマったものをアウトプットとして残していこうと思います。

【開発環境】

Amazon EC2 Linux
Windows 10 HOME
Apache/2.4.43
Tera Term 4.1.105

httpd.confの編集

下記ファイルを編集していきます。

sudo vi /etc/httpd/conf/httpd.conf
httpd.conf
DocumentRoot "/var/www/html"
↓
DocumentRoot "/var/www/html/sample/public”

DocumentRootを変更。

:wq (で保存)

sudo service httpd restart

Apacheを再起動すると。。

Redirecting to /bin/systemctl restart httpd.service
Job for httpd.service failed because the control process exited with error code. See "systemctl status httpd.service" and "journalctl -xe                           " for details.

何やらエラーが出ています。

systemctl status httpd.service

表示されているコマンドを打ってみると・・

httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)
  Drop-In: /usr/lib/systemd/system/httpd.service.d
           mqphp-fpm.conf
   Active: failed (Result: exit-code) since Tue 2020-07-07 14:00:14 JST; 13s ago
     Docs: man:httpd.service(8)
  Process: 4107 ExecStart=/usr/sbin/httpd $OPTIONS -DFOREGROUND (code=exited, status=1/FAILURE)
 Main PID: 4107 (code=exited, status=1/FAILURE)
   Status: "Reading configuration..."

Jul 07 14:00:14 ip-172-31-11-201.ap-northeast-1.compute.internal systemd[1]: Starting The Apache HTTP Server...
Jul 07 14:00:14 ip-172-31-11-201.ap-northeast-1.compute.internal httpd[4107]: AH00526: Syntax error on line 121 of /etc/httpd/conf/...nf:
Jul 07 14:00:14 ip-172-31-11-201.ap-northeast-1.compute.internal httpd[4107]: DocumentRoot '/var/www/html/sample/public\xe2\x80\x9d...ble
Jul 07 14:00:14 ip-172-31-11-201.ap-northeast-1.compute.internal systemd[1]: httpd.service: main process exited, code=exited, statu...URE
Jul 07 14:00:14 ip-172-31-11-201.ap-northeast-1.compute.internal systemd[1]: Failed to start The Apache HTTP Server.
Jul 07 14:00:14 ip-172-31-11-201.ap-northeast-1.compute.internal systemd[1]: Unit httpd.service entered failed state.
Jul 07 14:00:14 ip-172-31-11-201.ap-northeast-1.compute.internal systemd[1]: httpd.service failed.
Hint: Some lines were ellipsized, use -l to show in full.

この中のメッセージを見ていくと、、

 DocumentRoot '/var/www/html/sample/public\xe2\x80\x9d...ble

と何やら変更したDocumentRoot のパスの後ろの部分が文字化け?しています。
まさかと思って、httpd.confをもう一回見てみると

httpd_1.jpg

半角のはずの "(ダブルクォーテーション) が全角で書かれていました。。
んでこれを半角に直して再び再起動すると、、

[ec2-user@ip-172-31-11-201 conf]$ sudo service httpd restart
Redirecting to /bin/systemctl restart httpd.service

エラーなく再起動が完了しました。

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

[AWS] S3サーバアクセスログについて

S3アクセスログ

  • バケットに対する詳細ログが記録される
  • アクセスログはセキュリティやアクセス監査に役立つ
  • S3の請求についての理解にも役立つ

ベストエフォート型のサーバーログ配信

  • ログは記録された時間から数時間以内に配信される
    • 配信されない場合もある
  • ログの目的はトラフィックの特性を理解すること
  • ログの完全性や適時性は保証されない

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/ServerLogs.html#LogDeliveryBestEffort

バケットのログ記録ステータスの変更が有効になるまでには時間がかかる

  • ログ取得有効化してから数時間以内のログは記録されたりされなかったりする

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/ServerLogs.html#BucketLoggingStatusChanges

アクセスログの取得

  • アクセスログ取得はデフォルトで 無効 になっている
  • 保存先バケットは取得元バケットと同一リージョンに作成する必要がある
  • デフォルトではアクセスログの保存期間の設定はされていない
  • ログ配信グループ と呼ばれる特別なログ配信アカウントを使用してアクセスログを書き込む
    • 通常のアクセスコントロールの制約に従われる

保存先バケットの作成

  • 取得元と同一リージョンに作成する必要がある
  • 取得元と保存先バケットは同一アカウント所有である必要がある
  • 取得元にそのままアクセスログを保存可能だが非推奨
    • アクセスログを書き込みに対して更にアクセスログが書き込みされる

バケット作成例

取得元と同一アカウントのS3にリージョン別の保存先を作成

  • s3-access-log-ap-northeast-1
  • s3-access-log-us-east-1
  • s3-access-log-us-west-2

ログ配信グループへの書き込み権限付与

  • 取得元でログ取得を有効化すると保存先ACLにログ配信グループ書き込みアクセスが許可される

取得有効化

  1. AWS マネジメントコンソールにサインインし、Amazon S3 コンソール (https://console.aws.amazon.com/s3/) を開きます。
  2. [バケット名] リストで、サーバーアクセスログ記録を有効にするバケットの名前を選択します。
  3. [プロパティ] を選択します。
  4. [Server access logging (サーバーアクセスのログ記録)] を選択します。
  5. [ログの有効化] を選択します。[Target (ターゲット)] で、ログレコードオブジェクトを受け取るバケットの名前を選択します。 ターゲットバケットは、ソースバケットと同じリージョン内になければならず、デフォルトの保存期間設定を持ってはいけません。
  6. (オプション) [ターゲットプレフィックス] に、ログオブジェクトのキー名プレフィックスを入力します。これにより、すべてのログオブジェクト名が同じ文字列で始まります。
  7. [Save] を選択します。

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/user-guide/server-access-logging.html

ログ形式

  • ログは 改行 で区切った複数のレコードが1つのファイルにまとまって出力される
  • 各ログレコードは1個のリクエストを表している
  • 各フィールドは スペース で区切られている

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/LogFormat.html

79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be awsexamplebucket1 [06/Feb/2019:00:00:38 +0000] 192.0.2.3 79a59df900b949e55d96a1e698fbacedfd6e09d98eacf8f8d5218e7cd47ef2be 3E57427F3EXAMPLE REST.GET.VERSIONING - "GET /awsexamplebucket1?versioning HTTP/1.1" 200 - 113 - 7 - "-" "S3Console/0.4" - s9lzHYrFp76ZVxRcpX9+5cjAnEH2ROuNkd2BHfIa6UkFVdtjf5mKR3/eTPFvsiP/XV/VLi31234= SigV2 ECDHE-RSA-AES128-GCM-SHA256 AuthHeader awsexamplebucket1.s3.us-west-1.amazonaws.com TLSV1.1

フィールド

ログファイルの削除

  • ライフサイクル設定で削除する

ログ検索

  • Athenaを使ってクエリでの検索
  • AWSドキュメトにクエリ例ものっている

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/using-s3-access-logs-to-identify-requests.html#querying-s3-access-logs-for-requests

https://www.youtube.com/watch?v=8mSxMK1FdSs

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

SlackチャンネルにEC2インスタンスが落ちたことを通知してくれるようにしてみた

背景

時折、開発環境のEC2インスタンスが勝手に落ちていることがあり、気が付いた人からの問い合わせでその都度手動でEC2インスタンスを再起動していました。
開発者達が開発に集中できるようにするために自動で通知してくれるような仕組みを構築しました。

実装一覧

  • SlackチャンネルにEC2インスタンスがstoppedになったことを通知できるようにする
  • stoppedになったら、自動で再起動できるようにする
  • 上記の機能を9:00~21:00の間に限定できるようにする

9:00~21:00の間に制限を設けている理由ですが、開発環境ではコスト改善のために21時になったら、自動で開発環境の全てのEC2インスタンスがshutdownされ、翌朝9時になったら自動で起動できるようにしてあります。
なので、仮に上記の機能が21時以降も有効になってしまうと、幾つものEC2インスタンスのステータス通知がSlackチャンネルを埋めるだけでなく、深夜早朝帯も開発環境が起動され続けるという恐れがあります。

使用ツール

  • CloudWatch Event
  • Lambda
  • Incoming Webhook

手順

①SlackからIncoming Webhookアプリをインストールする
何かしらのサービス情報をSlackのチャンネルに通知するためにはIncoming WebhookというSlackアプリを活用することで実現可能となります。公式サイト
インストール後、サービス情報を共有したいSlackチャンネルを選択し、WebhookURLを控えておきます。
vGx3klsIw5EqkjY1591324726_1591325055.png
②Lambda関数を作成する
新規でLambda関数を作成し、以下の関数を書きます。
image.png

lambda_function.py
from __future__ import print_function
from time import strptime, strftime

import os, json, boto3, urllib.request

#GMT表記
before = strptime('00:00:00', '%H:%M:%S')
after = strptime('11:59:00', '%H:%M:%S')
now = strptime(strftime('%H:%M:%S'), '%H:%M:%S')

print(now)

#自動起動/シャットダウンバッチに引っかからないようにJST9:00-JST21:00の間で処理を動かすようにしている
if (now >= before and now <= after):

   #変数eventの中身がCloudWatch Eventから受け取ったEC2インスタンスのstop
   def lambda_handler(event,context):

      instances = [ event ]
      region = 'ap-northeast-1'
      ec2 = boto3.client('ec2', region_name=region)

      #色付きメッセージ発行
      attachments = {
        'attachments': [{
          # titleのリンクをクリックするとtitle_linkで設定したページへ飛びます。
          'title': 'EC2が停止しました。',
          'title_link': 'https://api.slack.com/docs/message-attachments',
          'color': "warning",
        }]
      }

      req = json.dumps(attachments).encode('ascii')

      message_color = urllib.request.Request(os.environ['slackUrl'], req )

      try:
         response = urllib.request.urlopen(message_color)
         response.read()

      except Exception as e:
         print(e)

      #インスタンスの起動停止メッセージ
      message_stop = {
         'text': "<!here> EC2 Instance " + (event) + " stopped"
      }

      data = json.dumps(message_stop).encode('ascii')

      req = urllib.request.Request(os.environ['slackUrl'], data )
      try:
          response = urllib.request.urlopen(req)
          response.read()

          print("Message posted: %s" % message_stop )
      except Exception as e:
          print(e)

      #停止したインスタンスの再起動
      ec2.start_instances(InstanceIds=instances)
      message_restart = {
         'text': "started your instances: " + str(instances)
      }

      data2 = json.dumps(message_restart).encode('ascii')

      req = urllib.request.Request(os.environ['slackUrl'], data2 )
      try:
          response = urllib.request.urlopen(req)
          response.read()

          print("Message posted: %s" % message_restart )
      except Exception as e:
          print(e)

   #自動起動/シャットダウンが走る9時前、21時以降は処理をさせない
else:
   print("out time of function")

関数内で未定義の変数slackUrlはLambda側から取得したWebhookURLをセットしました。
image.png
③CloudWatch EventでLambda関数が発動するルールを作成する
EC2インスタンスがstoppedになったら先ほど作成したLambda関数を起動できるようにルールを作成します。
image.png
この時、Lambda関数へ渡す入力内容を絞ることでSlackチャンネルに表示される内容を見やすいものに加工することができます。
※全表示の場合(一部モザイク処理)
ec2.png
以上で、Slackチャンネルへ通知する手順は完了しました。最後にEC2インスタンスを停止させて、Slackチャンネルへ通知が届き、再起動ができるか確認します。
ec3.png
ちゃんと実装したかったことができるようになりました。

所感

AWSとSlackの連携方法として、CloudWatch AlarmからLambdaを飛ばしてSlackチャンネルへアラーム内容を連携する方法が一般的で、検索すると多くの記事が出てきてLambda関数にもそれ用のテンプレートがあります。
また、最近AWSとSlackが、提携を発表しそれぞれのサービスの結びつきが今後ますます強くなっていくと思われます。参考リンク
最近ですと、AWS Chatbotで簡単にAWSサービスの内容をSlackへ連携しやすくなりましたが、まだ提供できるサービスが少ないためCloudWatch Eventでも対応できるようになりましたら検証してみたいと思います。

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

MacのTouch Barにmfaコードをコピーするボタンを作る

昨今、mfaが必須と言われていますが、毎回スマホのロックを解除してコードを見るのが面倒じゃないですか?
MacのTouch Barにmfaコードをクリップボードにコピーするボタンを配置する方法をご紹介します。

OATH Toolkitの導入

まずは、CLIにてmfaコードを扱えるようにOath Toolkit (https://www.nongnu.org/oath-toolkit/) を導入します。

$ brew install oath-toolkit

mfaの設定

AWSや、googleにmfa認証の設定画面へ行く。以下AWSの場合の例です。

仮想MFAデバイスの設定画面にて、シークレットキーの表示で表示される32文字のキーを覚える。
Image 2020-07-07 08-32-07.jpg

※ 基本的に仮想MFAデバイスはユーザー毎に1つしか登録することはできません。
今スマホで登録ずみのMFAデバイスがある場合は、一度削除して、登録し直しましょう。
同じキーを使用して、PCとスマホ両方でmfaコードを生成することは可能です。
その場合は、上記の同じ32文字のキーをPCとスマホ両方に登録しましょう。

CLIでのmfaコードの生成

oathtoolを使って、以下のコマンドにてmfaコードを表示することができるようになります。

$ oathtool --totp --base32 "32文字のキーをスペース無しで入れる”

pbcopyコマンドにて、クリップボードにコピーすることも可能です。

$ oathtool --totp --base32 "32文字のキーをスペース無しで入れる” | pbcopy

上記をaliasに登録するだけでも大分手間が省けるかと思いますし、これでCLIにてmfaコードを扱えるようになりましたので、
色々ログインを自動化することもできるでしょう。

※32文字のシークレットキーが漏れると誰でもmfaコードを生成することが可能になります。暗号化して保存するのがベターでしょう。

Touch Barへの登録

さて、ここからは上記コマンドをTouch Barに登録する方法のご紹介です。

Automatorを起動します。
あまり使ったことはありませんでしたが、macの操作を色々自動化できる奴です。macOS標準で入っています。
Image 2020-07-07 08-52-37.jpg

クイックアクションを作成します。
Image 2020-07-07 08-55-05.jpg

"シェルスクリプトを実行"のアクションを右側のワークフローのエリアにドラッグ&ドロップして追加します。
Image 2020-07-07 08-59-22.jpg

シェルスクリプトの内容は以下で登録します。

/usr/local/bin/oathtool --totp --base32 "32文字のキーをスペース無しで入れる”

次に"クリップボードにコピー"のアクションを右側のワークフローのエリアにドラッグ&ドロップして追加します。
Image 2020-07-07 09-03-18.jpg

ワークフローのインプットを"入力なし"に設定します。
Image 2020-07-07 09-05-14.jpg

ファイルの保存メニューから適当な名前をつけてワークフローを保存します。

次は、作成したクイックアクションのワークフローをtouchbarから呼び出せるようにします。

システム環境設定 -> 機能拡張から、TouchBarを選び、先ほど保存したクイックアクションのチェックをつけます。
Image 2020-07-07 09-16-39.jpg

システム環境設定 > キーボードを開いてTouchBarをカスタマイズボタンをクリックします。

Image 2020-07-07 09-11-02.jpg

クイックアクションのメニューをTouchBarに追加します。
クイックアクションを展開済みにするかなど、この辺りは好みで。

以下のリンク先を参照すると分かりやすい
https://amamemo.com/memo/7894
https://mutsunic.com/mac-touch-bar/

これでTouch Barのボタンでいつでもmfaコードがクリップボードにコピーされるので、
ボタンを押したら、後は入力したい場所に貼り付けるだけでOK!!

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

AWS Client VPN が SAML 対応したので Okta で試してみた

はじめに

_s__o_ です。

2020 年 5 月 19 日付けで AWS Client VPN が SAML 認証に対応しました。

[参考] AWS Client VPN で SAML 2.0 経由のフェデレーション認証のサポートを開始

Client VPN の ID 認証は、これまでは AWS Directory Service の利用が必須でした。ID 認証のためだけに AWS Directory Service を立て、さらには ID 管理のためだけに AWS EC2 で Windows を立て……など、あまり費用対効果が良いとは言いがたいものでした (ちなみに、MFA を有効にする場合は、AWS Directory Service に加えて Radius サーバも別途必要。。。)。

そのため、この機能追加は、非常に嬉しいものです。パスワード管理が IdP 依存になるので、IdP でポリシーや更新 UI を用意しておけば、システムとは完全に独立した形でパスワードを管理することができるようになります。また、IdP が MFA に対応していれば、よりセキュアな仕組みでシステムを構築することができます。

というわけで、2020/07/06 現在、AWS Client VPN が公式で対応している「Okta」を使って、AWS Client VPN の SAML 認証を試してみたいと思います。

なお、この記事は Okta や SAML 設定の部分に重きをおいており、Client VPN 設定に関しては最低限のことしか記載していません。Client VPN も含んだ設定に関しては、後述の「参考」に記載のサイトが詳しいので、そちらを参照してください。

Okta 作業

Okta 初期登録

30 日間は無料で試用できるので、下記サイトを参考にしながら登録しました。

[参考] Okta を試してみた

特につまづいた部分はありませんでしたが、下記は留意しておく必要があります。

  • Gmail (gmail.com) など、自組織を特定できないメールアドレスでは初期登録 (管理者登録) 不可。プロバイダから付与されたメールアドレスなどで初期登録する必要あり
  • 2020/07/06 時点、初期登録 (管理者登録) の際は MFA 必須。事前に Okta の MFA 用ツール をダウンロードしておくとよい
  • 管理者画面はすべて英語。エンドユーザ画面は日本語対応

Okta エンドユーザ登録

エンドユーザを登録します。「Directory」 > 「People」 > 「Add Person」でサクッと登録できます。なお、エンドユーザに関しては、Gmail などのメールアドレスでも登録可能です。

01_Okta_ユーザ登録.png

Okta アプリ登録

アプリとして「AWS Client VPN」を登録します。「Applications」を選択して、検索欄に「AWS」と入力すれば、候補に「AWS Client VPN」が出てきます。「AWS Client VPN」を選択したら、特に設定などはいじらず、そのまま「Done」を押して登録です。

02_Okta_Client_VPN_設定画面.png

Okta アプリアサイン

「AWS Client VPN」を、先ほど作成したエンドユーザにアサインします。「Applications」 > 「AWS Client VPN」 > 「Assignments」でアサイン可能です。

03_Okta_Client_VPN_アサイン画面.png

Okta SAML 設定

AWS Client VPN 用の SAML 設定を行います。「Applications」 > 「AWS Client VPN」 > 「SSO」を選択して、「Edit」ボタンを押下します。設定画面に移行するので、下記 2 点を設定します。

  • memberOf を「Matches .*」で設定する。Okta のグループ情報を Client VPN に連携するため
  • Port を「35001」に設定する。AWS Client VPN から SAML 認証する際に使用するポート

04_Okta_SAML設定.png

設定が完了したら、AWS の IdP 登録で使用するため、メタデータの XML をダウンロードしておきます。下図赤枠部分を右クリックして、「名前を付けてリンク先を保存」で保存できます。

05_Okta_メタデータダウンロード.png

AWS 作業

IdP 登録

IAM から登録します。「IAM」 > 「ID プロバイダー」 > 「プロバイダの作成」で作成します。メタデータドキュメントでは、先ほどダウンロードしたメタデータを指定します。

06_AWS_IdP登録.png

Client VPN Endpoint 作成

ポイントのみ解説します。認証オプションで「ユーザーベースの認証を使用」を選択します。おそらく、前までは「Active Directory 認証」のみだけだったと思いますが、ここに「統合認証」の選択が増えているはずです。こちらを選択し、下部の「SAML プロバイダー ARN」で、先ほど作成した IdP の ARN を指定します。

07_ClientVPN登録.png

エンドポイント作成後、「関連付け」や「認証」で、接続先サブネットや接続許可 CIDR を指定します。詳しい手順に関しては、後述の「参考」のサイトを参照してください。

接続試行

エンドポイントの ovpn ファイル (プロファイル) をダウンロードし、AWS 公式の「AWS VPN Client」にインポートします。

ちなみに、私が試した範囲では、VPN クライアントから認証がフェデレートする (Okta の認証画面が表示される) のは、AWS 公式の「AWS VPN Client」だけでした。残念ながら、OpenVPN クライアント (使用したのは OpenVPN Connect) では、VPN クライアントから認証がフェデレートせず、認証失敗として接続不可でした。

話を戻して、プロファイルを「AWS VPN Client」にインポートしたら、「接続」を押して接続開始します。「ID 連携中...」と画面に表示された後、ブラウザで Okta の画面が立ち上がります。

08_Okta_SSO.png

ここで Okta のエンドユーザ ID/Password を入力し、「サインイン」ボタンを押下します。ID/Password が正しければしばらく待ったのち、「接続完了」と表示され、はれて AWS Client VPN に接続できるようになります。

まとめ

以上、AWS Client VPN の SAML 認証を Okta で試してみた、でした。

2020/07/06 現在、公式でサポート (公式に手順が記載) [参考] されている IdP は「Okta」ぐらいのようですが、「AWS SSO」で試して成功したような記事もあるようです。

[参考] AWS Client VPNでSAML認証がサポートされたのでAWS SSOで認証してみた

まだ出始めの機能なので、公式対応の IdP もこれからドンドン増えていくと思います。期待大です。

あと、個人的には、何とかして OpenVPN クライアントからも SAML 認証を利用したいなあと思っています。というのも、AWS 公式の「AWS VPN Client」は、2020/07/06 現在、proxy 接続に非対応のため、proxy を挟むような企業 NW 環境では、OpenVPN クライアント一択となってしまうからです。。。

いずれにせよ、SAML 認証をサポートしたことで、ちょっと煩雑だった Client VPN の ID 認証まわりがスッキリし始めているのは確かです。サポート状況をウォッチしていきつつ、その他のいろんな IdP も試していきたいと思います。

参考

[参考] 多要素認証の実装が簡単になるぞ!AWS Client VPN で SAML ベースのフェデレーション認証がサポートされました

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

SAM CLI の Docker内(--use-container) ビルドをでProxy環境下で実施する方法

SAMでは、Lambdaと近い環境でビルドできる

SAM(Serverless Application Model) CLI のビルドコマンド(sam build)には、
Lambda用の npmpip などで、アーキテクチャ(WindowsかLinux等)に依存するモジュールが有る場合に対応する
オプション (--use-container)がある。

このオプションを使用すると、通常、実行環境(windows等)で行うビルド処理を、Lambda(Amazon Linux) に近い
Docker環境内で行うようなるため、コンパイルを伴うようなモジュールでも問題なく、Lambda用のリソースを作成することが可能となる。

Proxyの憂鬱

ここにも、毎度同じみのProxyの憂鬱がある。

実行環境が、Proxy経由でインターネットに繋がっている場合、
当然、Docker内からもProxy経由でしかインターネットに繋ぐことが出来ない為、
Docker インスタンスに対して、環境変数等でProxy設定を与えてあげる必要がある。

しかし、SAM のビルドコマンド(sam build)で、実行する場合、動的にインスタンスを作成し
ビルドが終了すると動的に削除する為、Proxyを与えることが難しかった。

※Docker Desktopのproxy設定は、Docker Imageをダウンロードする際に使用するProxy設定で、Docker Image内には適用されない。

そこで、SAM では、 Docker SDK >= 3.7.0 でサポートされた、
global docker configuration file. 機能を使って、
外部からProxy設定を可能にするよう対応している。

既に、上記のPull Request は、マージされてReleaseされているが、
特にドキュメントには残されていない為、見つけにくいので、ここに書き残したいと思います。

global docker configuration file.

設定方法は、以下の記事 @2fbCvmiYKX さんの記事や Dockerのドキュメントが詳しいが、

%USERPROFILE%\.docker\config.json の編集し

変更前
{
  "auths": {
    "https://index.docker.io/v1/": {}
  },
  "credsStore": "desktop",
  "experimental": "enabled",
  "stackOrchestrator": "kubernetes"
}

を以下のように修正する

変更後
{
  "auths": {
    "https://index.docker.io/v1/": {}
  },
  "credsStore": "desktop",
  "experimental": "enabled",
  "stackOrchestrator": "kubernetes",
  "proxies": {
    "default": {
      "httpProxy": "http://proxy.example.com:8080",
      "httpsProxy": "http://proxy.example.com:8080",
      "noProxy": "no_proxy.example.com"
    }
  }
}

これで、以下の通り、Proxy環境下であっても、SAM CLI の Docker内(--use-container) ビルドを実施することが、可能となる。

PS C:\Users\komikoni\workspace\sam app> sam build --use-container

Starting Build inside a container
Building function 'AuthFunc'
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine

Fetching lambci/lambda:build-nodejs12.x Docker container image......
Mounting C:\Users\komikoni\workspace\sam app\node-function as /tmp/samcli/source:ro,delegated inside runtime container
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
Running NodejsNpmBuilder:NpmPack
Running NodejsNpmBuilder:CopyNpmrc
Running NodejsNpmBuilder:CopySource
Running NodejsNpmBuilder:NpmInstall
Running NodejsNpmBuilder:CleanUpNpmrc
Building function 'NodeFunc'

Fetching lambci/lambda:build-nodejs12.x Docker container
image..............................................................................................................................................................................................................
Mounting C:\Users\komikoni\workspace\sam app\node-function as /tmp/samcli/source:ro,delegated inside runtime container
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
Running NodejsNpmBuilder:NpmPack
Running NodejsNpmBuilder:CopyNpmrc
Running NodejsNpmBuilder:CopySource
Running NodejsNpmBuilder:NpmInstall
Running NodejsNpmBuilder:CleanUpNpmrc
Building function 'PythonFunc'
//./pipe/docker_engine
//./pipe/docker_engine

Fetching lambci/lambda:build-python3.6 Docker container image..............................................................................................................................................................................................................
Mounting C:\Users\komikoni\workspace\sam app\python-function as /tmp/samcli/source:ro,delegated inside runtime container
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine

Build Succeeded

Built Artifacts  : .aws-sam\build
Built Template   : .aws-sam\build\template.yaml

Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Deploy: sam deploy --guided

Running PythonPipBuilder:ResolveDependencies
Running PythonPipBuilder:CopySource
PS C:\Users\komikoni\workspace\sam app> 
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SAM CLI の Docker内(--use-container) ビルドをProxy環境下で実施する方法

2017年頃からサーバレスに浸っている、小西啓介です。

image.png

SAMでは、Lambdaに近い環境でビルドできる

SAM(Serverless Application Model) CLI のビルドコマンド(sam build)には、
Lambda用の npmpip などで、アーキテクチャ(WindowsかLinux等)に依存するモジュールが有る場合に対応する
オプション (--use-container)がある。

このオプションを使用すると、通常、実行環境(windows等)で行うビルド処理を、Lambda(Amazon Linux) に近い
Docker環境内で行うようなるため、コンパイルを伴うようなモジュールでも問題なく、Lambda用のリソースを作成することが可能となる。

Proxyの憂鬱

ここにも、毎度おなじみのProxyの憂鬱がある。

PS C:\Users\komikoni\workspace\sam app> sam build --use-container

Building function 'NodeFunc'
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine

Fetching lambci/lambda:build-nodejs12.x Docker container image......
Mounting C:\Users\komikoni\workspace\sam app\node-function as /tmp/samcli/source:ro,delegated inside runtime container
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine

Build Failed
Error: NodejsNpmBuilder:NpmInstall - NPM Failed: npm ERR! code ENOTFOUND
npm ERR! errno ENOTFOUND
npm ERR! network request to https://registry.npmjs.org/jsonwebtoken failed, reason: getaddrinfo ENOTFOUND registry.npmjs.org
npm ERR! network This is a problem related to network connectivity.
npm ERR! network In most cases you are behind a proxy or have bad network settings.
npm ERR! network
npm ERR! network If you are behind a proxy, please make sure that the
npm ERR! network 'proxy' config is set properly.  See: 'npm help config'

npm ERR! A complete log of this run can be found in:

実行環境が、Proxy経由でインターネットに繋がっている場合、
当然、Docker内からもProxy経由でしかインターネットに繋ぐことが出来ない為、
Docker インスタンスに対して、環境変数等でProxy設定を与えてあげる必要がある。

しかし、SAM のビルドコマンド(sam build)で、実行する場合、動的にDockerインスタンスを作成し
ビルドが終了すると自動的に削除される為、Proxyを与えることが難しかった。

※Docker Desktopのproxy設定は、Docker Imageをダウンロードする際に使用するProxy設定で、Docker Image内には適用されない。

この課題は、以下のIssueに上がっている。

そこで、SAM では、 Docker SDK >= 3.7.0 でサポートされた、global docker configuration file. 機能を使って、
外部からProxy設定を可能となるよう対応が行われた。

既に、上記のPull Request は、マージされてReleaseされているですが、
特にドキュメント上には、記載がなさそうなので、ここに書き残したいと思います。

global docker configuration file の設定

設定方法は、以下の記事 @2fbCvmiYKX さんの記事や Dockerのドキュメントが詳しいが、

%USERPROFILE%\.docker\config.json の編集し

変更前
{
  "auths": {
    "https://index.docker.io/v1/": {}
  },
  "credsStore": "desktop",
  "experimental": "enabled",
  "stackOrchestrator": "kubernetes"
}

を以下のように修正する

変更後
{
  "auths": {
    "https://index.docker.io/v1/": {}
  },
  "credsStore": "desktop",
  "experimental": "enabled",
  "stackOrchestrator": "kubernetes",
  "proxies": {
    "default": {
      "httpProxy": "http://proxy.example.com:8080",
      "httpsProxy": "http://proxy.example.com:8080",
      "noProxy": "no_proxy.example.com"
    }
  }
}

これで、以下の通り、Proxy環境下であっても、SAM CLI の Docker内(--use-container) ビルドを実施することが、可能となる。

PS C:\Users\komikoni\workspace\sam app> sam build --use-container

Starting Build inside a container
Building function 'AuthFunc'
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine

Fetching lambci/lambda:build-nodejs12.x Docker container image......
Mounting C:\Users\komikoni\workspace\sam app\node-function as /tmp/samcli/source:ro,delegated inside runtime container
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
Running NodejsNpmBuilder:NpmPack
Running NodejsNpmBuilder:CopyNpmrc
Running NodejsNpmBuilder:CopySource
Running NodejsNpmBuilder:NpmInstall
Running NodejsNpmBuilder:CleanUpNpmrc
Building function 'NodeFunc'

Fetching lambci/lambda:build-nodejs12.x Docker container
image..............................................................................................................................................................................................................
Mounting C:\Users\komikoni\workspace\sam app\node-function as /tmp/samcli/source:ro,delegated inside runtime container
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
Running NodejsNpmBuilder:NpmPack
Running NodejsNpmBuilder:CopyNpmrc
Running NodejsNpmBuilder:CopySource
Running NodejsNpmBuilder:NpmInstall
Running NodejsNpmBuilder:CleanUpNpmrc
Building function 'PythonFunc'
//./pipe/docker_engine
//./pipe/docker_engine

Fetching lambci/lambda:build-python3.6 Docker container image..............................................................................................................................................................................................................
Mounting C:\Users\komikoni\workspace\sam app\python-function as /tmp/samcli/source:ro,delegated inside runtime container
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine
//./pipe/docker_engine

Build Succeeded

Built Artifacts  : .aws-sam\build
Built Template   : .aws-sam\build\template.yaml

Commands you can use next
=========================
[*] Invoke Function: sam local invoke
[*] Deploy: sam deploy --guided

Running PythonPipBuilder:ResolveDependencies
Running PythonPipBuilder:CopySource
PS C:\Users\komikoni\workspace\sam app> 
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む