20210720のAWSに関する記事は14件です。

【AWS】ECS構築の基礎 −機密情報の参照−

はじめに Secrets Manager、SSMパラメータストア、S3に機密情報を環境変数として保存して、実行タスクから参照できるかどうか確かめてみました。 ECS初心者向けの記事を何本か書いていますので、どれか参考になれば幸いです。 AWS Secrets Manager AWS Secrets Managerは、AWS内リソース、オンプレミス環境、またはサードパーティアプリケーションにアクセスするための各種機密情報の管理を、簡単にするAWSのサービスです。 Secrets Managerを利用することで、AWS CLI、API、SDKから機密情報へアクセスすることができます。 アプリケーション内に機密情報をハードコードする必要がなくなり、情報漏えいのリスクを減らすことができるようになります。 また、スケジュールに基づいた機密情報のローテーションに対応しており、古くなった機密情報の更新の手間を軽減することができます。 RDSやAuroraでは、機密情報のローテーションとデータベース接続情報のローテーションが統合されています。 SSMパラメータストア SSMパラメータストアはSecrets Manager登場前からある機密情報管理サービスです。 Secrets Managerとの違いを比較すると以下のようになります。 Secrets Manager SSMパラメータストア 値段 高価 安価 ローテーション 可能 不可 KMS暗号化 必須 任意 CloudFormationとの統合 あり あり 実装 ECSで機密情報を取り扱うにあたり、以下の3つの方法あります。 下2つの方法について試しに実装してみます。 タスク定義にハードコーディング SSM/Secrets Managerで管理 S3で管理 準備 ecsTaskExecutionRoleのパーミッションの修正、および新しくタスク定義とサービスの作成を行います。 まずはecsTaskExecutionRoleにSecrets ManagerやS3に保管したパラメータの取得に関するインラインポリシーを追加します。 <account-id>にはアカウントIDを記述します。 { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "secretsmanager:GetSecretValue", "ssm:GetParameters", "kms:Decrypt" ], "Resource": [ "arn:aws:secretsmanager:ap-northeast-1:<account-id>:secret:*", "arn:aws:ssm:ap-northeast-1:<account-id>:parameter/*", "arn:aws:kms:ap-northeast-1:<account-id>:*" ] }, { "Effect": "Allow", "Action": [ "s3:GetObject" ], "Resource": [ "arn:aws:s3:::ecs-course-<account-id>/environment-demo.env" ] }, { "Effect": "Allow", "Action": [ "s3:GetBucketLocation" ], "Resource": [ "arn:aws:s3:::ecs-course-<account-id>" ] } ] } 名前を記述してポリシーをアタッチします。 次に、タスク定義を新しく作成します。 EC2起動タイプを選択して、タスク定義名の記述とタスク実行ロールの選択を行います。 コンテナの追加を行います。 今回はubuntu:latestのイメージを取得します。 エントリポイントを追加しタスク定義を作成します。 最後に、作成したタスク定義を元にサービスを新しく作成します。 SSMパラメータストア 下準備が整ったので、SSMパラメータストアから実装を行っていきます。 Systems Managerのパラメーターストアを開いて、"パラメータの作成"を押します。 パラメータストアでは機密データの暗号化が任意です。 暗号化の可否でそれぞれ検証してみます。 暗号化を行わないケースでは、タイプに"文字列"を選択します。 暗号化を行うケースでは、タイプに"安全な文字列"を選択します。 作成したタスク定義を開いて、"新しいリビジョンの作成"を押します。 コンテナ名をクリックして、環境変数を追加します。 "値を追加してください"には、以下のコマンドで取得したARNの中身を記述します。 $ aws ssm get-parameter --name plain-from-ssm { "Parameter": { "Name": "plain-from-ssm", "Type": "String", "Value": "plain variable from SSM parameter store", "Version": 1, "LastModifiedDate": "2021-07-13T07:04:13.100000+09:00", "ARN": "arn:aws:ssm:<<region>>:<<accountid>>:parameter/<<param-name>>", "DataType": "text" } } plain-ubuntu-serviceに最新リビジョンのタスク定義を反映させます(2(latest)を選択します)。 EC2を開いて、パブリック IPv4 DNSをコピーします。 EC2にSSHログインします。 $ ssh -i ~/.ssh/udemysample.pem ec2-user@ec2-18-183-76-54.ap-northeast-1.compute.amazonaws.com ubuntuの文字列を含むコンテナの中身を表示します。 [ec2-user@ip-172-16-0-156 ~]$ docker container ls | grep ubuntu b08bf03cc6c8 ubuntu:latest "sleep infinity" 17 minutes ago Up 17 minutes ecs-td-plain-ubuntu-2-plain-ubuntu-ac97cdd3abf9dfc1db01 以下のコマンドで指定したコンテナの環境変数envを参照します。 [ec2-user@ip-172-16-0-156 ~]$ docker exec -it b08bf03cc6c8 "env" plain-from-ssmとencrypted-from-ssmが確かに含まれています。 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=b08bf03cc6c8 TERM=xterm ECS_CONTAINER_METADATA_URI=http://169.254.170.2/v3/f352209c-4de8-4214-9003-c66f42856952 ECS_CONTAINER_METADATA_URI_V4=http://169.254.170.2/v4/f352209c-4de8-4214-9003-c66f42856952 plain-from-ssm=plain variable from SSM parameter store encrypted-from-ssm=encrypted variable from SSM Parameter store AWS_EXECUTION_ENV=AWS_ECS_EC2 HOME=/root Secrets Manager 次に、Secrets Managerで実装を行います。 Secrets Managerを開いて、"新しいシークレットを保存する"を押すと以下の画面になります。 シークレットの種類を選択し、シークレットに格納するキーと値のペアを指定します。 シークレットの名前を記述します。 とりあえず自動ローテーションは無効にします。 最後のステップでシークレットを取得するためのサンプルコードが表示されます。 "保存"を押します。 作成したシークレットを開いて、シークレットのARNをコピーします。 作成したタスク定義を開いて、"新しいリビジョンの作成"を押します。 コンテナの編集から環境変数を追加します。 値にはコピーしたARNをペーストします。 サービスの設定で新しいリビジョン3(latest)を選択し、新しいデプロイの強制にチェックをつけて更新します。 SSMのときと同様に、SSHログインしてからdocker container ls | grep ubuntuでコンテナIDを調べ、docker exec -it [container-id] "env"を実行します。 すると、以下のように追加した環境変数secret-from-secrets-managerが表示されます。 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=04cf089cf8e0 TERM=xterm AWS_EXECUTION_ENV=AWS_ECS_EC2 ECS_CONTAINER_METADATA_URI=http://169.254.170.2/v3/541d9a24-900e-4c23-bc22-2e055c039ee6 ECS_CONTAINER_METADATA_URI_V4=http://169.254.170.2/v4/541d9a24-900e-4c23-bc22-2e055c039ee6 plain-from-ssm=plain variable from SSM parameter store secret-from-secrets-manager={"key-from-secrets-manager":"This is the value from secrets manager"} encrypted-from-ssm=encrypted variable from SSM Parameter store HOME=/root Amazon S3 最後に、S3への環境変数の保存を行います。 以下のコマンドで、UserId、Account、Arnを確認します。 $ aws sts get-caller-identity もし別のIAMユーザが設定されていたら、以下のコマンドを実行して、アクセスキーIDやシークレットアクセスキーを設定します。 $ aws configure s3 mbコマンドで新しくバケットを作成します。 バケット名はグローバルで一意(世界に1つだけ)である必要があります。 今回はバケット名をecs-course-[Account]としました。 $ aws s3 mb s3://[バケット名] envファイルを新しく作成します。 $ vi environment-demo.env envファイルに環境変数を記述して保存します。 variable1-from-s3=value1 variable2-from-s3=value2 作成したenvファイルをS3バケットにアップロードします。 $ aws s3 cp ./environment-demo.env s3://ecs-course-s3://[バケット名]/ 以下のコマンドでバケット内のファイルを確認することができます。 $ aws s3 ls s3://[バケット名] 2021-07-18 22:17:24 50 environment-demo.env ここでECSのタスク定義から新しいリビジョンを作成します。 ”コンテナの定義”で環境ファイルを追加します。 ロケーションにはarn:aws:s3:::[バケット名]/environment-demo.envを記述します。 サービスの更新を行います。 リビジョンの変更と新しいデプロイの強制にチェックします。 docker container lsでコンテナIDを控え、docker exec -it [コンテナID] "env"を実行します。 以下のように、envファイルの中身を確認することができます。 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=3a39f9eef85b TERM=xterm ECS_CONTAINER_METADATA_URI_V4=http://169.254.170.2/v4/46e249e6-1a76-4259-a97d-7e6f1e46e5c7 encrypted-from-ssm=encrypted variable from SSM Parameter store plain-from-ssm=plain variable from SSM parameter store secret-from-secrets-manager={"key-from-secrets-manager":"This is the value from secrets manager"} variable1-from-s3=value1 variable2-from-s3=value2 AWS_EXECUTION_ENV=AWS_ECS_EC2 ECS_CONTAINER_METADATA_URI=http://169.254.170.2/v3/46e249e6-1a76-4259-a97d-7e6f1e46e5c7 HOME=/root Secrets Manager活用例 -プライベートレジストリのDockerイメージの使用- Secrets Managerにクレデンシャルを保管して、プライベートレジストリのDockerイメージからタスクを起動することができます。 Secrets Manager開き、"その他のシークレット"を選択して、 usernameとpasswordを記述します。 シークレットの名前を記述します。 シークレット作成後、ARNをコピーします。 新しくタスク定義を作成します。 コンテナの追加でコンテナ名とイメージを記述します。 また、"プライベートレジストリの認証"にチェックをつけて、Secrets ManagerのARNをペーストします。 クラスターから新しいタスクを実行します。 無事にタスクが実行されれば成功です。 参考資料
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon InspectorのレポートをCognitoで認証したユーザーに表示する仕組みを検証

目次 はじめに 構成図 リソース作成 InspectorとSNSの設定 Cognito User PoolとHosted UIを利用 API Gatewayの作成とCognito Authorizerの設定 作成リソースの動作確認 まとめ はじめに 今回はAmazon Inspectorで評価したレポートをCognitoで認証したユーザーに表示する方法を考えてみました。レポートは元々はコンソールで見れますが、共有機能がないのでその代わりに今回の仕組みを利用できます。 管理コンソールでレポートを発行してそのURLを共有する方法もありますが、URLの有効期限が15分しかないので必要なたびに管理コンソールにアクセスして発行する手間がかかります。 Cognitoを利用すると必要なタイミングでログインしてレポートが見れるのでログインID/Passwordさえ共有すればいつでもレポートを見ることができます。 構成図  補足 CloudWatch Eventを利用してタイムベースでInspectorを週1で回します。(Inspectorは実行後に通知ができるので、今回はメールで結果のURLを受信してみます) メールで受信したCognitoリンクにアクセスして、事前に共有があったID/Passwordでログインします。 Cognitoで認証が成功するとSTSでトークンが発行され、API GatewayのCognito認証を利用してレポートのダウンロードを実行します。 レポートの作成が終わったら、Lambda関数はレポートのURLにユーザーを遷移させます。 リソース作成 1. InspectorとSNSの設定 この記事ではInspector自体の説明やエージェントの設置などはスキップさせていただきます。詳しくはAWSのドキュメントをご参考ください。 Amazon Inspectorエージェント Amazon Inspectorのチュートリアル 予め作成したSNSにInspectorの「実行完了」ステータスになったら通知するように設定を行います。その他の設定は今回のために作ったサンプルEC2を対象として設定しました。 InspectorからSNSへメッセージを発行するにはSNSからアクセスポリシーの追加が必要です。 SNSアクセスポリシー { "Version": "2008-10-17", "Id": "__default_policy_ID", "Statement": [ { "Sid": "__default_statement_ID", "Effect": "Allow", "Principal": { "AWS": "*" }, "Action": [ "SNS:GetTopicAttributes", "SNS:SetTopicAttributes", "SNS:AddPermission", "SNS:RemovePermission", "SNS:DeleteTopic", "SNS:Subscribe", "SNS:ListSubscriptionsByTopic", "SNS:Publish", "SNS:Receive" ], "Resource": "arn:aws:sns:${AWS_REGION}:${AWS_ACCOUNT_ID}:inspector-sns-sample", "Condition": { "StringEquals": { "AWS:SourceOwner": "${AWS_ACCOUNT_ID}" } } }, { "Sid": "InspectorSNSPolicy", "Effect": "Allow", "Principal": { "Service": "inspector.amazonaws.com" }, "Action": "SNS:Publish", "Resource": "arn:aws:sns:${AWS_ACCOUNT_ID}:${AWS_ACCOUNT_ID}:inspector-sns-sample" } ] } 上記のInspectorSNSPolicyを参考にしましょう。InspectorにSNSの発行権限を与えています。 次は通知用のLambda関数を作成しました。 診断完了通知 const axios = require('axios'); const { DynamoDBClient, PutItemCommand } = require('@aws-sdk/client-dynamodb'); const dynamoDbClient = new DynamoDBClient(); exports.handler = async (event, context) => { const { run, time, event: inspectorEvent } = await JSON.parse(event.Records[0].Sns.Message); // Inspectorの実行完了時点で通知 if (inspectorEvent == 'ASSESSMENT_RUN_COMPLETED') { const { awsRequestId } = context; // DynamoDBにLambda関数の実行IDをKeyにしてInspectorのRunArnを保存 await dynamoDbClient.send( new PutItemCommand({ TableName: process.env.DYNAMO_DB_TABLE, Item: { request_id: { S: `${awsRequestId}` }, run_arn: { S: `${run}` }, }, }), ); // こちらに通知用のwebhookを記載してください。 // SlackならIncoming Webhookでメッセージを送ります。 // ${run} : InspectorのRunARN(後ほどのレポート作成時に使われます) // cognito login URL : ${cognito-domain}?client_id=${cognito-client-id}&response_type=code&scope=openid&redirect_uri=${認証用lambdaのapi-gatewayURL} } const response = { statusCode: 200, body: null, }; return response; }; 2. Cognito User PoolとHosted UIを利用 Cognito User Poolの作成方法やHostedUIを予め用意する必要があります。こちらもAWSのドキュメントで説明がありますのでリンクを紹介します。 Amazon Cognito チュートリアル サインアップおよびサインインでの Amazon Cognito でホストされる UI の使用 作成したCognito User PoolはログインしてSTSトークンを発行し、API GatewayでCognito Authorizerで認証するために使われます。 3. API Gatewayの作成とCognito Authorizerの設定 今回の仕組みではAPI Gatewayのリソースを2つ利用しました。 /validate : cognitoのコールバック先で、ユーザー検証とブラウザー画面遷移を担当します。 /download-report : cognito authorizerを利用して、cognitoからの正しいユーザーだったら評価レポートのURLを返します。 /validate const axios = require('axios'); const qs = require('qs'); const { DynamoDBClient, GetItemCommand } = require('@aws-sdk/client-dynamodb'); const dynamoDbClient = new DynamoDBClient(); exports.handler = async (event) => { let code, state; try { ({ queryStringParameters: { code, state }, } = event); } catch (err) {} if (code == null || state == null) { return { statusCode: 400, body: JSON.stringify('Bad request: Mandatory parameter is not defined'), }; } // CognitoのClient-IDとAPP-Secretを利用して認証コードを作成します。(Base64エンコード) const authCode = `${process.env.APP_CLIENT_ID}:${process.env.APP_CLIENT_SECRET}`; const requestData = { grant_type: 'authorization_code', code, redirect_uri: `${process.env.REDIRECT_URI}`, }; let id_token; try { ({ data: { id_token }, } = await axios({ method: 'post', url: process.env.TOKEN_URL, headers: { 'Content-Type': 'application/x-www-form-urlencoded', Authorization: `Basic ${Buffer.from(authCode).toString('base64')}`, }, data: qs.stringify(requestData), })); } catch (err) {} //console.log(`id_token: ${id_token}`); if (id_token == null) { return { statusCode: 400, body: JSON.stringify('Bad request: No result from cognito'), }; } let run_arn; try { ({ Item: { run_arn: { S: run_arn }, }, } = await dynamoDbClient.send( new GetItemCommand({ TableName: process.env.DYNAMO_DB_TABLE, Key: { request_id: { S: `${state}` }, }, }), )); } catch (err) {} //console.log(run_arn); if (run_arn == null) { return { statusCode: 400, body: JSON.stringify('Bad request: No result from DB'), }; } let status, url; try { ({ data: { status, url }, } = await axios({ method: 'get', // /download-reportでInspectorの評価レポートのURLを持ってきます。 url: process.env.DOWNLOAD_REPORT_URL, headers: { Authorization: `${id_token}`, }, data: { runArn: run_arn, }, })); } catch (err) {} //console.log(status); //console.log(url); if (status == 'COMPLETED') { return { statusCode: 302, headers: { Location: url, }, }; } return { statusCode: 400, body: JSON.stringify('Something goes wrong..'), }; }; /download-report const { InspectorClient, GetAssessmentReportCommand } = require('@aws-sdk/client-inspector'); const REGION = 'ap-northeast-1'; const inspectorClient = new InspectorClient({ region: REGION, }); // nodejsでsleepを実装 const sleep = async (t) => { return await new Promise((r) => { setTimeout(() => { r(); }, t); }); }; exports.handler = async (event) => { const { body } = event; const { runArn } = await JSON.parse(body); let data; while (true) { data = await inspectorClient.send( new GetAssessmentReportCommand({ assessmentRunArn: `${runArn}`, reportFileFormat: 'HTML', reportType: 'FINDING', }), ); const { status } = data; if (status == 'WORK_IN_PROGRESS') { // Inspectorの評価レポートはrequestを入れてからすぐ作成されるものではないのでその間は待ちます。 await sleep(5000); } else { break; } } const response = { statusCode: 200, body: JSON.stringify(data), }; return response; }; これでリソースの作成は完了です。そして実際Inspectorを回してみました。 作成リソースの動作確認 Inspectorの実行が完了したら次のようなメッセージがきます。 リンクを押してブラウザーに表示されるCognitoにログインします。 そうすると診断結果の画面に遷移されます。こちらのリンクはCognitoにログインが成功するたびに作成され、その有効期限は15分です。 まとめ 今回はCognitoを利用してAmazon Inspectorの評価レポートを共有する機能を実装してみました。評価レポートはシステムの脆弱性と関係があるので、正しい権限を持っているユーザーにだけ閲覧権限を与える必要があるます。 評価レポートをPDF化して管理するのもありですが、脆弱性が書かれているファイルの管理もしないといけないのでCognitoでサーバレス環境で認証されたユーザーにだけアクセスできるようにしたら管理も楽になるでしょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MIERUNEのAmazon Location Serviceサンプルの裏側をざっくりと!

TL;DR MIERUNEで公開中&随時機能追加中のAmazon Location Serviceのサンプルの構成を紹介する記事です。 公開中のサンプルは以下のリンクからお試しいただけます! はじめに 本記事ではAmazon Location Service 及び AWS Amplify, Vue.jsを利用します。 なお、公開しているサンプル自体の解説はこの記事ではなく、以下の記事で行っていますのでご覧ください。 また、地図スタイルの日本語化に関しては以下の記事をご覧ください。 Amazon Location Service とは? Amazon Location Service とは Amazon Web Serviceが提供しているウェブ上・ネイティブアプリ上で使用される地図タイルの配信、ジオコーディングAPIやバーチャルフェンスやルート検索APIなど位置情報・地図に関係したいくつかのサービスをフルマネージドで提供するサービスのことで、2021/6/2にGAしたサービスです。 Amplify とは? AWS Amplify は 、Webアプリ・ネイティブアプリの開発を加速させるために作られたプラットフォームであり、AWS Amplify Frameworkを用いてAWSが提供しているいくつかのフルマネージドサービスを簡単に利用できるものです。また、環境自体はAmplify CLIを用いて簡単に初期設定等が行えます。 今回はAWS Amplify Frameworkの中でもAmplify-jsをVue.js上で利用しています。 構成図 Amazon Location Serviceの問題点 Cognitoを用いてトークンを取得して、リクエストにはすべてトークンを用いて署名する必要がある Amplifyと完璧に連携されているわけではない(2021/07/20現在) 手っ取り早くCognitoを利用するためにAmplifyを利用しています(現状Amplify Frameworkを用いないとCognitoがほぼ利用できない) AmplifyとAmazon Location Serviceの結び付け 今回は基本的なAmplifyの設定方法などは解説しません(たくさん良い記事がありますのでそちらをご覧ください) Cognito ユーザー のロールにALSのポリシーをアタッチする Amplifyを用いてCognito ユーザープールを生成すると amplify-${appId}-${backendEnvName}-${random}-authRoleというロールが自動生成されます。 このロールに対してポリシーをアタッチします。 Mapを表示するだけであれば Mapを表示するためのポリシー mapPolicy.json { "Version": "2012-10-17", "Statement": [ { "Sid": "MapsReadOnly", "Effect": "Allow", "Action": [ "geo:GetMapStyleDescriptor", "geo:GetMapGlyphs", "geo:GetMapSprites", "geo:GetMapTile" ], "Resource": "${Arn}" } ] } ジオコーディングAPIを利用するのであれば ジオコーディングAPIを利用するためのポリシー geocodingPolicy.json { "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": [ "geo:SearchPlaceIndexForText", "geo:SearchPlaceIndexForPosition" ], "Resource": "${Arn}" } ] } をそれぞれアタッチしましょう。 これを付けることでそれぞれのAPI、地図のスタイル・タイル・フォント・アイコンを取得することができるようになります。 また、今回はAuth Policy(認証済みポリシー)にアタッチしましたが、UnAuth Policy(未認証ポリシー)にアタッチするとログインなどをせずにMapの表示などを行えるようになります。(不正利用の可能性があるため、必ずCloudWatchなどで利用量 を確認してください) Amplify Analyticsの設定 Google Analyticsなどを利用してもいいのですが、せっかくAmplifyを利用しているのでAmplify Analyticsを利用します。 なお、裏で動作しているのはAmazon Pinpointです。 1. CLIでamplify analytics addを実行して質問に回答していくだけでInitは完了です。 2. Vue.jsでPageViewを計る場合には以下のコードをmain.jsに追加しましょう main.js // 省略 import Amplify, {Analytics} from 'aws-amplify'; import awsconfig from './aws-exports'; Amplify.configure(awsconfig); Analytics.autoTrack('pageView', { enable: true, type: 'SPA', getUrl: () => { return window.location.origin + window.location.pathname; } }); // 省略 たったこれだけでAnalyticsが利用できます CloudWatchの設定 Amplifyは基本的に自由にサインイン・サインアップを行うことができます。 そのため、Auth Policyにmapの読み込み等を設定していても利用量が一気にすごいことになっている場合があります。 利用量の跳ね上がりなどを検知するためにCloudWatchでAPIへのリクエスト量を監視しましょう。 Amazon Location Service (AWS/Location)ではCallCount,SuccessCount,ErrorCountに関しては合計、CallLatencyに関しては平均のみが正常に利用可能なようです。 https://docs.aws.amazon.com/location/latest/developerguide/monitoring-using-cloudwatch.html (こちらではCallLatencyについても合計と記載されていますが正常な値ではないように思います) 各種APIについては1000カウント辺りを閾値に、getTileに関しては100000カウント辺りでもよいように思います。 CI/CD AmplifyのCI/CDを利用しています。 Githubのmain/developブランチにPushされると自動的にビルドが走りデプロイされるように設定しています。 こちらもほかに丁寧な記事がありますのでそちらをご覧ください。 おわりに Amazon Location Serviceのサンプルの裏側をざっくり説明しました! Amplifyを利用することでとても簡単にWebアプリケーションを作成することができ、独自で実装すると大変なログイン等も簡単に実装できます。 また、現在Amplify jsではAmazon Location Serviceに対応するRFCが出てきており、今後Amplifyが対応する可能性も大きいかと思います! https://github.com/aws-amplify/amplify-js/issues/8553 今後もアプリケーションは更新していきます! (今後も更新のタイミングでQiitaなどでお伝えできるかと思います) おまけ 今回のサンプルサイトは社内ハッカソンにて制作されたものです そんな会社風土に憧れるという方、一緒に働いてみませんか?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon Location Service はじめました

目次 Amazon Location Service とは 住所検索 経路検索 逆ジオコーディング 地図の日本語化について サンプルサイト おまけ 1. Amazon Location Service とは そもそも Amazon Location Service とは何かという話なのですが AWS(Amazon Web Services)の各機能に統合できる地図および位置情報のサービスです GCP(Google Cloud Platform)におけるGoogle Maps Platformみたいなイメージですね 詳しくは以下ページをご覧いただければと思います https://docs.aws.amazon.com/ja_jp/location/latest/developerguide/what-is.html Googleの方は自身がデータプロバイダーであるのに対し、Amazonの方はEsriとHEREの2社から選択するような形式になっております 後述しますが、現状(2021年7月)では日本向けの地図スタイルは提供されておらず、日本の地図を表示しても、地名などが英語表記になってしまいますので、日本向けでの利用はこれからといった感じです 今回は住所検索、経路検索、逆ジオコーディングの3種類の機能について紹介したいと思います 2. 住所検索 あるキーワードを検索対象とし、地図の場所を検索結果として取得できる機能です レスポンスが非常に良く、テキストボックスのオートコンプリート機能と連携させて、リアルタイムな検索が可能です © MIERUNE | © OpenStreetMap contributors | © 2021 HERE 3. 経路検索 位置を複数指定すると、その経路情報を取得できる機能です 今回は2点間のみの経路を取得表示させるものを作りましたが、途中経由する位置も追加可能です 経路情報だけではなく、距離や概算所要時間も取得できます © MIERUNE | © OpenStreetMap contributors | © 2021 HERE 4. 逆ジオコーディング そもそも逆ジオコーディングとはなんぞやという話なのですが、住所から緯度経度を求めることをジオコーディングと言います その逆なので、緯度経度の情報から住所を求めることを逆ジオコーディングと言います この機能は日本語で住所の情報を取得できます © MIERUNE | © OpenStreetMap contributors | © 2021 HERE 5. 地図の日本語化について 現状でAmazon Location Serviceを日本国内向けに使うにあたって、最大の問題といえるのが、地図スタイルの日本語化です そこで、私の所属している株式会社MIERUNEにおきまして、日本語化された地図スタイルを絶賛開発しております 現状で進捗は20%ほど。。。という状況なのですが、どのくらい変わるのかをお見せしたいと思います ベースとしているのはHEREの2.5Dのスタイルです 左がオリジナルで、右が開発中のスタイルになります © MIERUNE | © OpenStreetMap contributors | © 2021 HERE 6. サンプルサイト 今回紹介した機能を全て体験できるwebサイトを公開しています (認証にAWSAmplifyを利用しておりますので、サインアップが必要です) こちらのサンプルサイトは今後も引き続き更新されていく予定です 7. おまけ 今回のサンプルサイトは社内ハッカソンにて制作されたものです そんな会社風土に憧れるという方、一緒に働いてみませんか? こちらの記事ですが 「AWSの基礎を学ぼう 特別編 最新サービスをみんなで触ってみる はじめての位置情報サービス」 このイベントのLTの内容を掲載しております
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Cloud9 - 既存のEC2インスタンスに接続する

まえがき 毎度のことながら環境構築に四苦八苦しております。 今回もうんうん唸りながら構築しました。 sshログイン時にNode.jsのインストール、ログインせずnodebrewでのインストール、 の両方とも経験した(ただ失敗しただけ)ので、どちらも投稿します。 結局新たにEC2インスタンスを作成し、SSH環境では使用していないので、 ゴンがカッツォを助けたみたいに、どなたかの助けになれば、、 Cloud9を使用する際は、AD Blockなどの拡張機能の停止または削除してから実行しましょう。 不具合が発生する模様です。 動作環境 ・MacBook Air (Retina, 13-inch, 2020) ・Big Sur11.4 ・ターミナルシェル:zsh 前提 VPCやEC2インスタンスの作成、 あるいはElastic IPアドレスの取得等は事前に済んでいることとします。 Cloud9のリージョンは東京にしました。 1.Node.jsをインストール Cloud9にはNode.jsが必要なので最初にインストールします。 (1)Amazon公式チュートリアルを参考にインストールしていきます ターミナルにて、 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash ※※ nvmのGitHubにて最新版のコードをコピペしてください ※※ (4)nvmを有効にします . ~/.nvm/nvm.sh (5)nvmを使用してNode.jsの安定版をインストールします nvm install --lts --latest -npm (6)安定版をデフォルトに設定します nvm alias default 'lts'/* (7)Cloud9で接続するディレクトリの権限を設定します ? Amazon公式:SSH環境ホスト要件 sudo chmod u=rwx,g=rx,o=rx ~ (8)gccが必要なので、 ? AWS Cloud9 で既存の EC2 インスタンスに接続する sudo yum -y gcc (10)つづけて、 curl -L https://raw.githubusercontent.com/c9/install/master/install.sh | bash (11)Node.jsが正しくインストールされ、実行されているかテストします node -e "console.log('Running Node.js ' + process.version)" -> 「Running Node.js VERSION」 と表示されればNode.jsのインストール、設定は完了です。 ec2-user@ip-○○○ $ node -e "console.log('Running Node.js ' + process.version)" Running Node.js v14.17.3 2. 既存のインスタンスのセキュリティグループを変更する (1)AWSコンソールにログインします EC2 -> 左メニューのセキュリティグループ -> EC2に割り当てているセキュリティグループを選択 (2)インバウンドルールに下記を追加し保存します タイプ プロトコル ポート番号 ソース 説明 SSH TCP 22 18.179.48.128/27 Cloud9 SSH TCP 22 18.179.48.96/27 Cloud9 ※※ コピー用 ※※ 説明欄は省略可能です ※※ (3)正常に保存されました 3-1. Cloud9の環境構築 (1)Cloud9に戻り、入力していきます Name:任意のお名前 Description:既存のEC2インスタンスに接続 Descriptionは省略可能ですが、分かりやすいように記述しました。 -> Next Stepをクリック (2)既存のEC2インスタンスに接続するため、一番下にある、 ?Connect and run in remote server (SSH)を選択します User:ec2-user(インスタンスのユーザー名) Host:パブリックIPアドレスまたはDNSホスト名※ ※EC2インスタンスに記載されていますが、DNSホスト名が無効になっていると、 「 - 」と表示されているので有効にします。無効の方は3-2を参考になさってください。 (3)Copy key to clipbord をクリックして公開鍵をコピーします Advanced settingsは空欄のままでOKです。 (4)ターミナルに戻り、 sudo vim ./. ssh/authrized-keys/ に移動し、さきほどCopy key to clipbordでコピーした鍵を貼り付け保存します。 (5)Cloud9に戻り、Next StepをクリックするとReviewが表示されます 確認後にCreate environmentをクリックするとCloud9が作成されます わたしのようにsshログインせずインストールしてしまっていたら、 エラーメッセージが表示されます。 Could not execute node.js on あなたのIPアドレス #訳:ssh環境にNode.jsないで ログインしてないと気付いたときにはゾッとしました。。 3-2.DNSホスト名が「無効」の方へ:DNSホスト名を有効にする (1)VPCコンソール -> VPC -> 使用しているVPCを選択 (2)下タブのDNSホスト名:「無効」を見届ける (3)選択したVPC -> アクション -> DNSホスト名の編集 -> DNSホスト名 ☑︎有効化 にチェックして保存 (4)「有効」になりました (5)EC2コンソール -> EC2で再度確認すると、DNSホスト名が表示されているはずです こちらをCloud9のHOSTに貼り付けます。 *1. nodebrewを使ったNode.jsのインストール (1)homebrewでnodebrewをインストールしていきます brew install nodebrew (2)nodebrewがインストールされたか確認します % nodebrew v8.9.4 (3)Nodebrewをセットアップします nodebrew setup (4)PATHの設定を書き込みます export PATH=$HOME/.nodebrew/current/bin:$PATH が表示されているはずなので、vim ~/.zshrcに移動し、貼り付けてPATHを通します :wq #保存 source =/.zshrc #反映 (5)同作確認を行います nodebrew -help ヘルプがブワァ〜と表示されればnodebrewのインストールは完了です。 *2. Node.jpをインストール (1)Node.jsのインストール可能なバージョンを調べます nodebrew ls-all (2)今回は安定版(LTS)をインストールしました。2021/07/15時点で、v14.17.3でした nodebrew install -binary stable ※※ 最新版をインストールしたい方はこちら↓ nodebrew install latest ※※ エラーが出てしまった方はこちら -> ? MacにNode.jsをインストールする方法 (3)インストールしたNode.jsのバージョンを確認します % nobebrew ls v17.17.3 current:none (4)バージョンが表示されましたが、cuurent(現在)がnoneになっているので、 使用するバージョンを選択し、currentに表示されるか確認しましょう % nodebrew use v14.17.3 use v14.17.3 % nodebrew ls v14.17.3 current: v14.17.3 (5)何度でも確認したいので、ターミナルを終了したあと、 再度ターミナルを開き、下記コマンドでもう一度バージョンを表示して確認します node -v (6)1の(8)と同じく、Cloud9の接続するディレクトリ権限を設定します sudo chmod u=rwx,g=rx,o=rx ~ (7)gccが必要なので、①yumでgccをインストールし、 ②curlでインストーラーをダウンロードして実行します ※※ gcc:さまざまなプログラミング言語のコンパイラ集のこと。? IT用語辞典バイナリ ① sudo yum -y gcc ② curl -L https://raw.githubusercontent.com/c9/install/master/install.sh | bash (8)本当にすごい量がブワァと表示されたのち、:Doneと表示されれば終了です! おつかれさまでした! ちなみになんですが、最後の | bash を省略してしまうと、 URL先に記述されているのコードがターミナル上に表示されるだけになります(経験者)。 あとがき SSH接続だと、セキュリティの設定やrvmのインストール等、自力でしなくてはなりません。 ノートに「マジでできたやっと 17:27:10」と書き残してあるのですが、 1日かかったやないか。。と学習が追いついていないことに愕然します。 未知の環境に複数手を出してはいけないと痛感しました。 phpとPython、サーバー系をとことんがんばります!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Fargate ResourceInitializationError: unable to pull secrets or registry auth: pull command failed: : signal: killed が出た時の対応

エラーの内容 新しくVPCを作成し、ECSからFargateでサービスを立ち上げようとしたら以下のエラーが出た。 AWS Fargate ResourceInitializationError: unable to pull secrets or registry auth: pull command failed: : signal: killed 解決方法 どうやら、VPC側の設定の問題。VPCのルートテーブルに、インターネットゲートウェイを設定する必要があるようだ。   EC2→ルートテーブルより、インターネットゲートウェイを紐付けることで解決した。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS構築手順】④S3・Route53・DNSの障害対策(メモ・備忘録)

AWS構築 - 完成全体像 - 第3回までの記事はこちら。 ①Route53設定 「blog.aws-demo-perlverity.ga」にてアクセス可能・動作確認完了。 ②S3を用いてセカンダリページ(Sorryページ)を設定する S3にセカンダリページ(Sorryページ)をアップロード。 セカンダリページ(Sorryページ)の設定。 バケットポリシーを編集。 セカンダリページ(Sorryページ)が表示されました。 ③Route53の設定でセカンダリページに切り替える インスタンスを停止すると、 しっかり、セカンダリページに切り替えることができました。 この記事はAWS初学者を導く体系的な動画学習サービス 「AWS CloudTech」の課題カリキュラムで作成しました。 https://aws-cloud-tech.com
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CloudFront FunctionsでIPアドレス制限をする CloudFormation templateの書き方例

はじめに CloudFront Functionsは CloudFront上で実行できる小さなFunctionで、Lambda@Edge よりも軽量な処理をちょっと行いたいときに便利そうです。 例えば、あるURLに対してIPアドレス制限をする、という用途が考えられます。 ググると色々記事もあります(例: CloudFront FunctionsでIP制限を試してみた)。 この方法は、IPアドレスを変更するたびにCloudFront Functionをデプロイしなおさないといけないので、社内のIPアドレスなどほぼ変化しないようなケースでしか使いづらいです。が、WAFを使う方法に比べて、お手軽に安くできるというメリットはあるかなと思います。 今回は CloudFormation でどう書いたら良いのか調べてみたので、書き残しておきます。 CloudFormation templateの書き方例 例としては以下のようになります。 ちょっと説明を書いておきます。 www.example.com への CloudFront に対する設定 /faq/ ページは 123.45.67.89 というIPからのみアクセスを許可 他のページは、全てアクセス許可 CachePolicy は適当なので、用途に応じて変える必要がある CacheBehaviors を追加していくことで、違うPathへの違う制限をすることができる AWSTemplateFormatVersion: "2010-09-09" Description: CloudFront with CloudFunction example Parameters: OriginName: Type: String Default: www.example.com AllowIPRegexp: Type: String Default: "^(123[.]45[.]67[.]89)$" Outputs: CDNUrl: Description: CDN URL Value: !Sub "https://${CloudFrontDistrib.DomainName}/" Resources: DefaultCachePolicy: Type: AWS::CloudFront::CachePolicy Properties: CachePolicyConfig: Name: SimpleConfig DefaultTTL: 3600 MinTTL: 600 MaxTTL: 86400 ParametersInCacheKeyAndForwardedToOrigin: CookiesConfig: CookieBehavior: all EnableAcceptEncodingGzip: true HeadersConfig: HeaderBehavior: none QueryStringsConfig: QueryStringBehavior: all IPRestrictionFunction: Type: AWS::CloudFront::Function Properties: Name: IPRestriction AutoPublish: true FunctionConfig: Comment: IP-Restriction-Function Runtime: cloudfront-js-1.0 FunctionCode: !Sub | function handler(event) { var request = event.request; var clientIP = event.viewer.ip; var regexp = new RegExp("${AllowIPRegexp}"); if (!regexp.test(clientIP)) { var response = { statusCode: 403, statusDescription: "Forbidden", // debug用に余計なHeaderを追加してみる headers: { "x-cloudfrontfunction-version": {value: "IPRestriction-Ver1"}, "x-ip-denied-from-cloudfront": {value: clientIP}, } }; return response; } return request; } CloudFrontDistrib: Type: AWS::CloudFront::Distribution Properties: DistributionConfig: Origins: - DomainName: !Ref OriginName Id: OriginGW CustomOriginConfig: OriginProtocolPolicy: "match-viewer" OriginSSLProtocols: [ "TLSv1.2" ] Comment: CloudFront distribution Enabled: true DefaultCacheBehavior: CachePolicyId: !Ref DefaultCachePolicy TargetOriginId: OriginGW ViewerProtocolPolicy: "redirect-to-https" CacheBehaviors: - PathPattern: "/faq/" TargetOriginId: OriginGW CachePolicyId: !Ref DefaultCachePolicy ViewerProtocolPolicy: "redirect-to-https" FunctionAssociations: - EventType: viewer-request FunctionARN: !GetAtt IPRestrictionFunction.FunctionMetadata.FunctionARN さいごに 最初のデプロイは数分程度かかりましたが、CloudFront FunctionのUpdateは1分くらいで終わるので、割と開発もやりやすかったです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS構築手順】③AutoScalingを用いたWordPress構築(メモ・備忘録)

AWS構築 - 完成全体像 - 以下のような構築。 CloudWatchで監視を行い、 CPU使用率が70%以上であれば、AutoScalingでEC2を4、 CPU使用率が30%以下であれば、AutoScalingでEC2を2とする。 第2回までの記事はこちら。 ①AutoScalingの設定 設定が完了すると、自動でインスタンスが立ち上がります。 また、インスタンスを停止すると、 起動合計インスタンス数が自動で2になるように、 復活します。 ②CloudWatchの設定 CPU使用率が70%以上の設定「CPU_High」 CPU使用率が30%以下の設定「CPU_Low」 スケーリングポリシーの設定。 ③AutoScaling&CloudWatchの動作確認 topコマンドでCPU使用率を確認。 負荷をかけるコマンド yes >> /dev/null & 上記を用いて動作確認する。 CPUの負荷がかなり高いことを確認。 ④CloudWatchによるアクセスログ監視 ログ監視は、自身で設定する必要がある。 sudo yum install awslogs cd /etc/awslogs sudo vim awscli.conf sudo vim awslogs.conf awslogs.conf [HttpAccessLog]![20_CloudWatch_14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/984321/5ee7539d-761b-852f-6ef3-5dce101b618b.png) file = /var/log/httpd/access_log log_group_name = HttpAccessLog log_stream_name = {instance_id} datetime_format = %b %d %H:%M:%S [HttpErrorLog] file = /var/log/httpd/error_log log_group_name = HttpErrorLog log_stream_name = {instance_id} datetime_format = %b %d %H:%M:%S sudo systemctl enable awslogsd sudo systemctl start awslogsd アクセスログが動作確認できました。 ※なお、以下をEC2のユーザーデータに設定することでEC2作成時に自動でCloudWatch Logsにログ送信が可能。 #!/bin/bash yum -y update yum -y install httpd php systemctl enable httpd.service systemctl start httpd.service mkdir /var/awslogs mkdir /var/awslogs/state yum -y install awslogs cat > /etc/awslogs/awslogs.conf <<EOF [general] state_file = /var/awslogs/state/agent-state [HttpAccessLog] file = /var/log/httpd/access_log log_group_name = HttpAccessLog log_stream_name = {instance_id} datetime_format = %b %d %H:%M:%S [HttpErrorLog] file = /var/log/httpd/error_log log_group_name = HttpErrorLog log_stream_name = {instance_id} datetime_format = %b %d %H:%M:%S EOF REGION=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone | sed -n 's/.$//p') sed -i "s/us-east-1/$REGION/g" /etc/awslogs/awscli.conf systemctl enable awslogsd systemctl start awslogsd この記事はAWS初学者を導く体系的な動画学習サービス 「AWS CloudTech」の課題カリキュラムで作成しました。 https://aws-cloud-tech.com
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS構築手順】②冗長性のあるWordPressを構築する(メモ・備忘録)

AWS構築 - 完成全体像 - 以下の記事にてVPC・EC2・RDS・WordPressを設置した。 今回はその続きで、冗長化を目標に、ELBを導入してみる。 ①AMI&インスタンス構築 ②ELB(Elastic Load Balancing)構築 ③RDSのDNS接続先をELBへ変更 mysql -h database-1.xxxxxxxxxxxxxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com -u wordpress -p USE wordpress SELECT * FROM wp_options WHERE option_name IN ('siteurl', 'home'); UPDATE wp_options SET option_value = 'http://xx.xx.xx.xx' WHERE option_name IN ('siteurl', 'home'); 現状だと、ELBを通らない通信が可能となっているため、 以下のような構築にする。 セキュリティグループの設定は以下の通り。 ④RDS冗長化 この記事はAWS初学者を導く体系的な動画学習サービス 「AWS CloudTech」の課題カリキュラムで作成しました。 https://aws-cloud-tech.com
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSに接続するグローバルIPを確認する

リモートでの案件となるとPACファイルの設定などで一般的なIPとAWSへ接続するときのIPが違うこともあるかと思います。 そういったときはAWSドメインに対してのグローバルIPを確認したくなります。 以下のURLでAWSに対してのグローバルIPが確認できました。 https://checkip.amazonaws.com/ curlの場合 curl checkip.amazonaws.com 以下のElasticsearchに関する設定を見ていた時にあったグローバルIP確認URLの共有でした。 参考URL https://aws.amazon.com/jp/premiumsupport/knowledge-center/anonymous-not-authorized-elasticsearch/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Aurora MySQLでIAM認証する手順と検証メモ

Aurora MySQLでIAM認証を検証したので、備忘録を兼ねて諸々メモ。 AuroraのIAM認証とは? DBユーザーに、個別のパスワード入力の代わりにIAMクレデンシャルで認証する機能。 DBごと、ユーザーごとのパスワード管理が不要になる。 スロットルのリスクがあるためアプリケーションの認証には不向きだが、運用に伴う人による認証については一考の価値がある。 Aurora側での設定 クラスターでIAM認証を有効化しておく。マネジメントコンソールでいうと、インスタンス設定(≠クラスター設定)の以下が該当設定。 データベースで、IAM認証を有効にしたDBユーザーを作成する。IAM認証は、あくまでこのDBユーザーに対して、AWSプロファイルのIAMクレデンシャルでログインする行為であって、DBユーザーがないと機能しない点に注意。 DBユーザーの作成 CREATE USER ssotest IDENTIFIED WITH AWSAuthenticationPlugin AS 'RDS'; 踏み台インスタンスでの設定 DB接続用のmysqlクライアントを事前にインストールしておく。 実は微妙にハマリポイント。Amazon Linux 2で普通にsudo yum install mysqlとすると、MariaDB互換クライアントがインストールされる(Amazon Linux 1だとMySQL純正クライアントが入る)のだが、MariaDB互換クライアントを使う場合、公式ドキュメントに記載のオプションではログインできない(後述の通り、別のオプションを使えば行けるが、そこに辿り着くまで大分かかった)。 MySQL純正クライアントをインストールしてもよいが、アークテクチャーを選んでローカルインストールなど若干面倒くさい手順が必要になるので、ここでは一行で済むMariaDB互換をインストールする。 IAM認証時は、TLS接続設定に関わらずAuroraのCA証明書が必要になるので、ホームディレクトリあたりにwgetしておく。 少し前はrds-ca-2019-root.pemだったが、いつのまにか変わったらしい。リージョンごとの差異を吸収する CA証明書の保存 % wget https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem 権限付与の方針決定 DBへのアクセスは一般に、パブリックアクセスは勿論、任意の端末からの接続を禁止するために、VPC内の特定のEC2インスタンス(踏み台)からのアクセスにセキュリティグループで縛ることが多いと思う。そうでないパターンも当然あり得るが、ここでは一応、踏み台パターンを前提とする。 IAM認証には接続トークンの生成が必要になるが、踏み台からのIAM認証パターンは以下の二種類が考えられる。違いは、「誰の権限で・どこでトークンを生成するか」の観点。 踏み台のインスタンスプロファイル(ロール)にIAM認証を許可するポリシーを付与。接続トークンは踏み台内で生成して環境変数に格納。 踏み台には権限を付与せず、それ以外のIAMロールにIAM認証を許可するポリシーを付与。接続トークンは別端末で生成してコピペ。 具体的には、以下のようなポリシーをどこのロールに割り当てるかの話になる。 IAM認証ポリシー例 { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "rds-db:connect" ], "Resource": [ "arn:aws:rds-db:ap-northeast-1:123456789012:dbuser:test-cluster/ssotest" ], "Condition": { "ForAnyValue:StringLike": { "aws:PrincipalOrgPaths": "o-oXXXXXXXXX/r-YYYY/ou-ZZZZZZZZZZZZZ/*" } } } ] } プロコンはざっくり以下のようなイメージ。今回は踏み台の多系統化を避けて「別のIAMロール案」とする。具体的にはAWS SSOを使い、アクセス権限セットにrds-db:connectを付与する。 インスタンスプロファイル案 別のIAMロールに付与する案 利点 認証操作がシンプル 踏み台は一系統でよい 欠点 権限ごとに踏み台を分岐する必要がある トークンを持ち込む必要がありひと手間かかる 接続 まずは認証トークン取得。前述の方針に従い、今回はAWS SSOでログイン後、踏み台の外でトークンを取得する(踏み台でトークン生成コマンドを叩くと、踏み台のインスタンスプロファイルの権限内で生成され、今回だとどこにも接続できないことになるため)。 認証トークン取得 % aws sso login --profile ssotest-profile % aws rds generate-db-auth-token \ > --hostname hoge \ > --region ap-northeast-1 \ > --port 3306 \ > --username ssotest \ > --profile ssotest-profile トークンが生成されたら、クリップボードにコピーする。本来は環境変数に格納するのが楽なのだが、今回は踏み台に持ち込む必要上、コピペで対応する。 踏み台にSSM Sessions Manager等でログイン後、DB接続を実行する。ここではMariaDB互換クライアントのパターンを使用するが、参考までにMySQL純正クライアントのパターンも併記しておく。 DB接続(MariaDB互換クライアントのパターン) % TOKEN='(先程取得したトークンの文字列をそのままコピペ)' % mysql -h <Auroraエンドポイント>.ap-northeast-1.rds.amazonaws.com \ > --port=3306 \ > --user=ssotest \ > --password=$TOKEN \ > --ssl-ca=~/global-bundle.pem \ > --default-auth=mysql_clear_password DB接続(MySQL互換クライアントのパターン) % mysql -h <Auroraエンドポイント>.ap-northeast-1.rds.amazonaws.com \ > --port=3306 \ > --user=ssotest \ > --password=$TOKEN \ > --ssl-ca=~/global-bundle.pem \ > --enable-cleartext-plugin 無事繋がったら完了。 なかなかMySQLのプロンプトが出ないな?という場合は、セキュリティグループを見直してみることをお薦めする。ちゃんと3306が許可されてるかとか、Aurora側でEC2側セキュリティグループをIngressに含めているか、など。 Access Deniedの際は以下のようなエラーが即時返ってくるので、そうでない場合はまずネットワークを疑う。 AccessDeniedな場合の接続エラー ERROR 1045 (28000): Access denied for user 'ssotest'@'XXX.XXX.XXX.XXX' (using password: YES) 小ネタ トークンは15分だけ有効。 一度生成したらいつでもどこでも再利用できるわけではなく、一定期間で切れるようになっている。 別アカウントで取得したトークンは使えない。 IAMポリシーがどうあれ、アカウントをまたいでトークンを再利用することはできない。例えば、開発アカウントでたまたまrds-db:connect権限を持っていてトークンを生成できたとしても、それを使って本番アカウントのDBにログインすることができないようになっている。 「インスタンスプロファイル案」を選択して踏み台からトークンを生成する場合、当たり前だが、RDSのAPIエンドポイントへの経路が必要になる。 踏み台のEgressをガチガチに縛っている場合は、VPCエンドポイントが必要になるかも。 AdministratorAccessなど、iam:*を保持している権限だと、ポリシー内容に拘わらず全部接続できてしまう。 検証する際は注意しておかないと、想定した挙動にならなくて時間を無駄にする。 AWS SSOでも使える。 今は改修されているが、以前は公式ドキュメントに「DBユーザー名はロール名と同じにせよ」という誤記があり、ロール名にアカウントごとのランダム文字列が付記されるAWS SSOでは運用上成立しないように見えていたが、原文は"Make sure the specified database user name is the same as a resource in the IAM policy for IAM database access."なので単なる誤訳だったらしい。 Resourceの部分はワイルドカードを使える。つまり、 Resource句(個別指定) "Resource": [ "arn:aws:rds-db:ap-northeast-1:123456789012:dbuser:test-cluster/ssotest" ], は、要件に応じて、以下のようにも書き変えることができる。 Resource句(ワイルドカード) "Resource": [ "arn:aws:rds-db:*:*:dbuser:*/ssotest" ], ただし、このままだといささか範囲が広すぎるので、せめてOrganizationsで縛るくらいはしておくこと。要件によっては、対象アカウントを本番とそれ以外に分けたり、対象クラスターを分けたりとかもあるかも知れない。 Condition句(OU縛り) "Condition": { "ForAnyValue:StringLike": { "aws:PrincipalOrgPaths": "o-oXXXXXXXXX/r-YYYY/ou-ZZZZZZZZZZZZZ/*" } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Lambdaの実行結果をメールで通知する

Lambdaを作ってEventBridgeで定期実行させてたけど、色々環境をいじってるうちに動かなくなってたってことがあります。そんなときにすぐに気づけるようにメール通知を実装したいと思います。 構成 サブスクリプションフィルター+Lambda+SNSで実現します。 実際に動作させるには権限周りや入れ子になっている設定が必要なのでこのようになります。(このあたりも後で解説します。) Lambdaコード こちらの記事を参考にカスタマイズしました。 https://dev.classmethod.jp/articles/notification_cloudwatchlogs_subscriptoinfilter/ import base64 import json import zlib import datetime import os import boto3 from botocore.exceptions import ClientError print('Loading function') def lambda_handler(event, context): data = zlib.decompress(base64.b64decode(event['awslogs']['data']), 16+zlib.MAX_WBITS) data_json = json.loads(data) log_entire_json = json.loads(json.dumps(data_json["logEvents"], ensure_ascii=False)) log_entire_len = len(log_entire_json) #メールの件名用にロググループ名を取得 logGroup = data_json['logGroup'] #ログ全体格納用 entire_message = '' print(log_entire_json) for i in range(log_entire_len): log_json = json.loads(json.dumps(data_json["logEvents"][i], ensure_ascii=False)) entire_message += log_json['message'] #複数のキーワードを設定 keywords = ['error', 'timed out'] #キーワードのいずれかを検知した時点でメールを送信する for keyword in keywords: if keyword in entire_message.lower(): try: sns = boto3.client('sns') #SNS Publish publishResponse = sns.publish( TopicArn = os.environ['SNS_TOPIC_ARN'], Message = entire_message, Subject = f'[{keyword.capitalize()}] {logGroup} Log stream' ) print(f"keyword:'{keyword}' in, send email") except Exception as e: print(e) finally: return print("keywords not in, don't send email") return 検知したキーワードを件名に入れ、ログ全体(対象Lambdaの開始から終了まで)をメール通知します。 最初は検知しない場合も通知するものを考えていたのですが、うっとうしかったり、Gmailのスレッド形式表示だと埋もれてしまったりするので、errorとタイムアウトのときのみ通知するようにしました。 参考にした記事もそうでしたが、よくあるのは検知したメッセージ部分だけをメール通知するというもの。でも、エラーの詳しい内容も確認できたらいいよなあと思い、全体を通知するようにしました。なので、サブスクリプションフィルターではあえてフィルターせずに全体をLambdaに送信し処理を行います。 今回は主にエラー検知を目的としていますが、正常に終了した場合でも実行過程や結果をprint出力し、それをキーワード検知してメール送信するといった使い方も視野に入れています。(エラーなしの場合も通知するバージョンも後で載せます。) サブスクリプションフィルター 通知を行いたいLambda(例えばEC2の起動停止Lambda)のロググループで設定を行います。フィルターパターンがこのように指定なしになっていればOKです。コンソールによる作成時に実際のログでフィルターのテストもできます。また、メール通知Lambdaへの権限も付与されます。作成後は編集できませんが、CloudFormationテンプレートも後で載せておきますので参考にしてください。 CloudWatch Logsにログが出力されていることが前提となりますので、対象のLambdaのCloudWatch Logsへのアクセス権限を確認し、一度テストで実行するなりしてログを出力しておきましょう。 IAMロール 以下のポリシーをアタッチしてメール通知Lambdaに使います。一応、メール通知Lambda自身もCloudWatch Logsにログを送信しています。 { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogStream", "sns:Publish", "logs:CreateLogGroup", "logs:PutLogEvents" ], "Resource": "*" } ] } リソース作成 後はSNSトピックにメールアドレスのサブスクリプションを設定しておけば、一通り揃いますので組み合わせていくだけですが…そのへんは面倒なのでCloudFormationテンプレートを載せておきます。ターゲットや名前はパラメータにしているので、任意の値を入力して使うこともできます。前述のLambdaコードも含んでいるので少し長くなっています。 AWSTemplateFormatVersion: "2010-09-09" Metadata: Generator: "former2" Description: "" Parameters: ManagedPolicyName: Type: String Default: lambda-error-send-email-policy RoleName: Type: String Default: LambdaErrorSendEmailRole TopicName: Type: String Default: LambdaErrorSendEmailTopic Endpoint: Type: String Default: info@example.com FunctionName: Type: String Default: LambdaErrorSendEmailFunction LogGroupName: Type: String Default: /aws/lambda/[TargetFunctionName] Resources: IAMManagedPolicy: Type: "AWS::IAM::ManagedPolicy" Properties: ManagedPolicyName: !Sub "${ManagedPolicyName}" Path: "/" PolicyDocument: | { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogStream", "sns:Publish", "logs:CreateLogGroup", "logs:PutLogEvents" ], "Resource": "*" } ] } IAMRole: Type: "AWS::IAM::Role" Properties: Path: "/" RoleName: !Sub "${RoleName}" AssumeRolePolicyDocument: "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}" MaxSessionDuration: 3600 ManagedPolicyArns: - !Ref IAMManagedPolicy Description: "" SNSTopic: Type: "AWS::SNS::Topic" Properties: DisplayName: "" TopicName: !Sub "${TopicName}" SNSSubscription: Type: "AWS::SNS::Subscription" Properties: TopicArn: !Ref SNSTopic Endpoint: !Sub "${Endpoint}" Protocol: "email" Region: !Ref AWS::Region LambdaFunction: Type: "AWS::Lambda::Function" Properties: Description: "" Environment: Variables: SNS_TOPIC_ARN: !Ref SNSTopic FunctionName: !Sub "${FunctionName}" Handler: "index.lambda_handler" Code: ZipFile: | import base64 import json import zlib import datetime import os import boto3 from botocore.exceptions import ClientError print('Loading function') def lambda_handler(event, context): data = zlib.decompress(base64.b64decode(event['awslogs']['data']), 16+zlib.MAX_WBITS) data_json = json.loads(data) log_entire_json = json.loads(json.dumps(data_json["logEvents"], ensure_ascii=False)) log_entire_len = len(log_entire_json) #メールの件名用にロググループ名を取得 logGroup = data_json['logGroup'] #ログ全体格納用 entire_message = '' print(log_entire_json) for i in range(log_entire_len): log_json = json.loads(json.dumps(data_json["logEvents"][i], ensure_ascii=False)) entire_message += log_json['message'] #複数のキーワードを設定 keywords = ['error', 'timed out'] #キーワードのいずれかを検知した時点でメールを送信する for keyword in keywords: if keyword in entire_message.lower(): try: sns = boto3.client('sns') #SNS Publish publishResponse = sns.publish( TopicArn = os.environ['SNS_TOPIC_ARN'], Message = entire_message, Subject = f'[{keyword.capitalize()}] {logGroup} Log stream' ) print(f"keyword:'{keyword}' in, send email") except Exception as e: print(e) finally: return print("keywords not in, don't send email") return MemorySize: 128 Role: !GetAtt IAMRole.Arn Runtime: "python3.8" Timeout: 60 TracingConfig: Mode: "PassThrough" LambdaPermission: Type: "AWS::Lambda::Permission" Properties: Action: "lambda:InvokeFunction" FunctionName: !GetAtt LambdaFunction.Arn Principal: "logs.amazonaws.com" SourceArn: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:${LogGroupName}:*" LogsSubscriptionFilter: Type: "AWS::Logs::SubscriptionFilter" Properties: LogGroupName: !Sub "${LogGroupName}" FilterPattern: "" DestinationArn: !GetAtt LambdaFunction.Arn エラーなしでも通知するLambda import base64 import json import zlib import datetime import os import boto3 from botocore.exceptions import ClientError print('Loading function') def lambda_handler(event, context): data = zlib.decompress(base64.b64decode(event['awslogs']['data']), 16+zlib.MAX_WBITS) data_json = json.loads(data) log_entire_json = json.loads(json.dumps(data_json["logEvents"], ensure_ascii=False)) log_entire_len = len(log_entire_json) logGroup = data_json['logGroup'] entire_message = '' print(log_entire_json) for i in range(log_entire_len): log_json = json.loads(json.dumps(data_json["logEvents"][i], ensure_ascii=False)) entire_message += log_json['message'] keywords = ['error', 'timed out'] result = 'notification' for keyword in keywords: if keyword in entire_message.lower(): result = keyword break try: sns = boto3.client('sns') #SNS Publish publishResponse = sns.publish( TopicArn = os.environ['SNS_TOPIC_ARN'], Message = entire_message, Subject = f'[{result.capitalize()}] {logGroup} Log stream' ) print(f'send [{result.capitalize()}] email') except Exception as e: print(e) 先ほどと同様に、error、タイムアウトの際は件名に入れてメール通知しますが、それらを検知しなかった場合も件名[Notification]としてログ全体をメール通知します。正常終了した場合でも結果をメールで確認したい場合は、Lambdaコードをこちらに書き換えればOKです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS CodeStar とは

勉強前イメージ 開発するのに使うAWSのサービス? 調査 AWS CodeStar とは 開発環境をすばやく構築できるサービスで、 codecommit, codebuild, codedeplpy, codepipeline をシームレスにつなげます。 クラウドベースの開発環境構築を構築でき、ブラウザで開発ができます。 また、複数人でアクセスして同時にPJをすすめることもできます。 AWS CodeStar の特徴 数分で開始できる 普通であればいろいろ構築しないといけないところ、 クリックのみでアプリケーションの構築環境を作成することができます。 1つの場所で管理 CodeStarのプロジェクトダッシュボードから1つのアクティビティを監視でき、 全段階にわたって進行状況を確認することができます。 セキュアに作業 CodeStarはIAMのポリシーが組み込まれており、 プロジェクトの所有者・コントリビューター・ビューワーのアクセスを管理できます。 プロジェクトテンプレートから選択可能 さまざまなアプリケーションを簡単に開発でき、 Java、JavaScript、PHP、Ruby、C#、Python などのプログラミング言語でプロジェクトを開始できます。 費用 AWS CodeStar自体の追加料金はないですが、 一緒に使用するEC2やlambdaの使用料金はかかります。 勉強後イメージ 作ってみたけど、追加でcloud9でIDE作れるってすごい。。。 参考 AWS CodeStarの特徴や使い方について AWS CodeStarとは
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む