20211130のAWSに関する記事は18件です。

Pulumi (TypeScript) で Elastic Load Balancing account ID を取得する

はじめに AWS の Elastic Load Balancing (以外、 LB) にはアクセスログを S3 に出力する機能があり、出力先となる S3 の bucket policy の設定に Elastic Load Balancing account ID を利用します。 Elastic Load Balancing account ID はそう変更されることがないため、べた書きしてしまうこともあると思いますが、 Pulumi の AWS Provider から取得する方法があるためその方法を記載します。 ちなみに、 Elastic Load Balancing account ID はリージョンごと異なるため、複数リージョンにリソースを配置する際は注意が必要です。 Elastic Load Balancing account ID について 各リージョンの Elastic Load Balancing account ID については公式ドキュメントを参照してください ちなみに、 日本国内の東京、大阪リージョンそれぞれの Elastic Load Balancing account ID は以下となります。 Region Region name Elastic Load Balancing account ID ap-northeast-1 Asia Pacific (Tokyo) 582318560864 ap-northeast-3 Asia Pacific (Osaka) 383597477331 Elastic Load Balancing account ID を取得する account ID の取得方法は 2つあります。 Promise<GetServiceAccountResult> を返却する形式 ( getServiceAccount ) GetServiceAccountResult を返却する形式 ( getServiceAccountOutput ) GetServiceAccountResult は arn, id, region を返却します get_service_account_result.ts interface GetServiceAccountResult { arn: string id: stting region: string } 今回は GetServiceAccountResult を返却する形式 ( getServiceAccountOutput ) で Elastic Load Balancing account ID を取得します。 以下例だと region を指定していますが AWS provider で default region を指定している場合は region を指定する必要はありません。 elb_account_id.ts import * as pulumi from "@pulumi/pulumi" import * as aws from "@pulumi/aws" const serviceAccount = aws.elb.getServiceAccountOutput({ region: 'ap-northeast-1' }) export const serviceAccountArn = serviceAccount.arn // => 582318560864 ^ はTypeScriptですが Python, Go, C# は 以下マニュアルを参照してください 最後に 今回は Elastic Load Balancing account ID を例にしましたが CloudTrail Service Account や Redshift Service Account も同様に account ID を取得することができます。 CloudTrail Redshift
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS日記33 (DocumentDB)

はじめに 今回は Amazon DocumentDB を試します。 デベロッパーガイドのAmazon EC2 を使用したConnect の手順を参考に、EC2からDocumentDBに接続します。 Amazon EC2 インスタンスを作成 Amazon EC2 コンソールで、「インスタンスを起動」 を選択します。 今回は「Amazon Linux 2 AMI」 を選択します。 インスタンスタイプは「t2.micro」 を選択します。 SSH接続できる設定になっていることを確認します。 次の画面でAmazon EC2 key pairを作成・ダウンロードし、インスタンスを起動します。 セキュリティグループを作成 Amazon EC2 コンソールから「セキュリティーグループ」を選択します。 セキュリティーグループ名を入力し、VPCを選択します。 インバウンドルールから「ルールを追加」を選択します。 ポート範囲は「27017」に設定します。 ソースは先ほど作成したEC2のセキュリティーグループを選択します。 ページ最下部の「セキュリティーグループを作成」を選択します。 Amazon DocumentDB クラスターを作成 Amazon DocumentDB コンソールのクラスターから、「作成」を選択します。 今回はインスタンス数を「 1 」に設定します。 マスターユーザー名とマスターパスワードを設定します。 ページ最下部の「詳細設定の表示」を選択します。 Virtual Private Cloud(VPC)はEC2と同じ設定にします。 VPCセキュリティーグループは、先ほど作成したセキュリティーグループを選択します。 ページ最下部の「クラスターの作成」を選択します。 EC2に接続 EC2コンソールから、SSHクライアントでの接続方法を確認します。 EC2インスタンス作成時にダウンロードしたキーを使い、EC2にSSH接続します。 mongo シェルをインストール リポジトリ・ファイルを作成します。 echo -e "[mongodb-org-3.6] \nname=MongoDB Repository\nbaseurl=https://repo.mongodb.org/yum/amazon/2013.03/mongodb-org/3.6/x86_64/\ngpgcheck=1 \nenabled=1 \ngpgkey=https://www.mongodb.org/static/pgp/server-3.6.asc" | sudo tee /etc/yum.repos.d/mongodb-org-3.6.repo mongo シェルをインストールします sudo yum install -y mongodb-org-shell Amazon DocumentDB に接続 Amazon DocumentDBコンソールから、mongoシェルで接続する方法を確認します。 認証に必要な証明書をダウンロードします。 wget https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem mongoシェルのコマンドを実行し接続します。 動作確認 rs0:PRIMARY> help db.help() help on db methods db.mycoll.help() help on collection methods sh.help() sharding helpers rs.help() replica set helpers help admin administrative help help connect connecting to a db help help keys key shortcuts help misc misc things to know help mr mapreduce show dbs show database names show collections show collections in current database show users show users in current database show profile show most recent system.profile entries with time >= 1ms show logs show the accessible logger names show log [name] prints out the last segment of log in memory, 'global' is default use <db_name> set current database db.mycoll.find() list objects in collection mycoll db.mycoll.find( { a : 1 } ) list objects in mycoll where a == 1 it result of the last line evaluated; use to further iterate DBQuery.shellBatchSize = x set default number of items to display on shell exit quit the mongo shell 終わりに Amazon DocumentDB を試しました。 SSHトンネルを作成してAmazon VPC の外部から Amazon DocumentDB クラスターへの接続する方法も、今後試して行こうと思います。 参考資料 Amazon DocumentDB の使用開始 AWS DocumentDBのクラスタ構築からmongoコマンドで接続するまで Amazon DocumentDB (MongoDB 互換) の料金 Install MongoDB Community Edition on Amazon Linux Amazon DocumentDB を使って AWS Lambda ベースのアプリケーションを実行する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSへServerlessFrameworkでアプリケーションデプロイしている時のTips

Serverless Frameworkをいくつかのプロジェクトで使ってみたので、いくつかTipsをまとめておきます。 同じyaml定義で複数環境にデプロイする ServerlessFrameworkでは --stage develop のようにstageを指定してコマンドを実行することで複数環境をデプロイすることが可能になります。 ただ、 stage - division - team のように複数階層でデプロイかける時に少し工夫が必要でした。 stageがただの変数以上の意味を持つので何も考えず、以下のように実行してしまうと、Export名が重複してしまったり不具合が出てきました。 sls deploy --stage prod --division dev101 --team team101 stageの値ですが、いかのような優先順位で定義されます。 ${opt:stage, self:provider.stage, "dev"}. https://www.serverless.com/framework/docs/providers/aws/guide/variables/ https://github.com/serverless/serverless/blob/v2.67.0/lib/configuration/variables/sources/instance-dependent/get-sls.js#L25-L30 つまり極端な方法を採るとすると、以下のように実行すれば上手くいきます。ただ、この方法だと devision や team の変数の取り回しが上手くいきません。 sls deploy --stage prod-dev101-team101 そこで以下のように stage の値をyamlの中で定義する方法で対応することにしました。 custom: stageName: ${opt:stageName} division: ${opt:division} team: ${opt:team} stage: ${self:custom.stageName}-${self:custom.division}-${self:custom.team} この場合は --stage の代わりに --stageName として引数を渡します。 sls deploy --stageName prod --division dev101 --team team101 不要ファイルはパッケージから除外する デプロイ速度に関わるので不要ファイルを除外します。 package: patterns: - "!node_modules/**" - "!venv/**" デフォルトでも以下は除外されているようです。 .git/** .gitignore .DS_Store npm-debug.log .serverless/** .serverless_plugins/** 古いLambdaを削除 有名ですがLambdaの過去バージョンパッケージがストレージを圧迫する問題です。知らないと古いバージョンが蓄積されていって、75GB(2021/11/30時点)で時限爆弾のようにエラーを吐いて後で困ることになります。 こちらのserverless-prune-pluginを利用することで、未然に防ぐことが可能です。 plugins: - serverless-prune-plugin custom: prune: automatic: true number: 3 CloudWatchログの削除設定 CloudWatch Logも設定しないと永遠と溜まっていくので、適切な期間を設定して削除します。 provider: logRetentionInDays: 30 タグ付け 後でコスト管理できるようにしっかりタグ付けします。 serverless-plugin-resource-taggingのプラグインを利用すると、関連するもの一通りタグ付けしてくれます。 plugins: - serverless-plugin-resource-tagging provider: stackTags: division: devA team: teamA CloudFormationスタックの説明文修正 何も気にせずデプロイすると全てのスタックが 「The AWS CloudFormation template for this Serverless application」と説明文に表示されて何がデプロイされているのかわかりにくくなります。 結局はCloudformationなのでresoucesのところにDescriptionを入れることで説明文を変更可能です。 resources: Description: My API Stack ${self:custom.stage} 外部ファイルとすることも可能です。 resources: - ${file(./description.yml)} その場合、外部ファイルはDescriptionだけでもOKです。 description.yml Description: My API Stack ${self:custom.stage} デプロイ環境 以下のようなDockerfileからビルド用のコンテナを作成してデプロイを行っています。 PythonでLambdaを書いて lambci/lambda:build-python3.8 を使っています。 lambciのイメージは少し重いですがCの拡張ライブラリをLambdaで使う場合、このイメージからビルドすると動かしやすいので使っています。 FROM lambci/lambda:build-python3.8 ARG SLS_VERSION=2.67.0 # EC2のIAMロールを利用する場合は通常無効化 # ~/.aws/configを利用する場合は有効化 #ENV AWS_SDK_LOAD_CONFIG 1 RUN curl -sL https://rpm.nodesource.com/setup_17.x | bash - RUN yum install -y nodejs RUN npm install -g serverless@${SLS_VERSION} RUN mkdir /opt/sls WORKDIR /opt/sls
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS IoT1-Clickで家族へ「帰ります」通知を送信できるようにしてみました

はじめに 先日、AWSを使ってインフラの構築作業を行ったのですが、AWSにとても興味を持てたので、実際に生活で使えそうな何かを作成しようと思い、色々と考えてみました。 私の友人に「Lambda王」というあだ名の人がおり、私はlambdaを触ったことがなかったので、どんな感じで使えるのかを確かめようと思い、今回はLambdaを取り入れることにしました。 私はいつも仕事から帰る時に奥さんに「今から帰ります」と通知をしているので、これを簡単に実行してくれるものを作ってみました。Lambda関数を実行するトリガーとして「AWS iot-1-click」で関数が実行される仕組みにしています。 すでに、色々な情報が溢れており、今さら感が否めませんが、自分の勉強も兼ねてやってみました。 システム構成図 システム構成は以下の通りです lambdaランタイム設定 Ruby 2.7.0 使用したIotデバイス SORACOM LTE-M Button 開発の流れ 1 SORACOM LTE-M BUttonのデバイス登録 2 LINENotifyでアクセストークンの発行 3 lambda関数の作成 4 コードのアップロード 5 実験・感想 SORACOM LTE-M Buttonのデバイス登録 今回仕様したデバイスは、AWS IoT1-ClickというAWSサービスにデバイスの登録を行わないと使用できません。 デバイス登録に関する流れはこちらを参考にしました。 デバイス登録をする際にDNSという番号を入力しないといけないのですが、その番号がどこにあるのか分からず、苦戦したのでそちらについてご紹介します。 写真を見てもらうと一目瞭然ですが、SORACOM LTE-M Buttonの裏側の電池パックを入れるところにDNS番号が記載されています。 そちらをAWS IoT1-Clickのコンソール画面上に登録します。 LINENotifyでアクセストークンの発行 こちらからLINENotifyのトップページにアクセスし、ログインした後に、「マイページ」をクリックします。 遷移した画面で「トークンを発行」をクリックすると、LINENotifyのアクセストークンが表示されるので、後で使うので忘れないようにコピーして保管しておきます。 このアクセストークンを使ってLINEの特定の通知先に通知を送ることができます。 AWS Lambda AWS Lambdaは、サーバを構築することなく、Lamda関数と呼ばれるコードを実行することができるサービスで、Faas(Function as a Service)と呼ばれるものです。 サーバを構築、管理しなくていい上に、料金も、関数が実行される短い時間だけであり、EC2を構築して運用するよりもメリットが大きいサービスです。 Lambda関数のトリガー Lambda関数を実行するためには、何かしらのきっかけ(トリガー)が必要です。 AWS iot-1-clickのイベントリソースの種類はプッシュ型の中の非同期呼び出し(処理の実行命令だけを送る)になり、今回はこちらがトリガーになります。 Lambda関数のコーディング方法(3つ) コンソール上で直接コードを書く ローカル環境でコードを書いて、zipファイルにしてアップする(容量は10MBまで) S3にアップしたzipファイルを参照する ruby 2.7.0での開発環境構築 lambda関数のRuby対応バージョンがruby2.7.0だったので、開発環境も統一させます。 普段使っているバージョンが異なっているので、今回の開発についてはruby 2.7.0で実現させたいので、バージョンを一時的に変更させます。 d@dMacBook-Pro ~ % rbenv versions system * 2.6.5 (set by /Users/xxxxxxx/.rbenv/version) # rubyバージョンが2.6.5しかなかったので、2.7.0をインストールしていきます d@dMacBook-Pro ~ % brew update d@dMacBook-Pro ~ % rbenv install 2.7.0 d@dMacBook-Pro ~ % rbenv versions system * 2.6.5 (set by /Users/xxxxxxxx/.rbenv/version) 2.7.0 # 現在のディレクトリのプロジェクトのみに適用 $ rbenv local 2.7.0 # 開発環境のみruby 2.7.0になってるのを確認します d@dMacBook-Pro lineNotify_ruby % rbenv local 2.7.0 d@dMacBook-Pro lineNotify_ruby % ruby -v ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-darwin20] # ルートディレクトリではバージョンは元の状態です d@dMacBook-Pro ~ % ruby -v ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin20] lambda関数の作成 コードの作成は、こちらの公式を参考にさせていただきました。 https://notify-bot.line.me/doc/ja/ こちらも参考にしています。 https://docs.ruby-lang.org/ja/latest/library/net=2fhttp.html require 'net/http' require 'uri' class LineNotify NOTIFY_TOKEN = ENV["NOTIFY_TOKEN"] APIURI = URI.parse("https://notify-api.line.me/api/notify") def make_request(notify_message) # postリクエスト作成 request = Net::HTTP::Post.new(APIURI) request["Authorization"] = "Bearer #{NOTIFY_TOKEN}" request.set_form_data({message: notify_message}) return request end def send(notify_message) request = make_request(notify_message) response = Net::HTTP.start(APIURI.host, APIURI.port, use_ssl: APIURI.scheme == "https") do |https| https.request(request) end end end line = LineNotify.new notify_message = "今から帰らせていただきます" response = line.send(notify_message) puts response.code puts response.body AWSLambdaにコードをアップする まずは関数を作成します。 この時にランタイム設定のバージョンが開発環境と同じであることを確認します。 関数を作成したら、秘匿情報を環境変数に定義して、関数実行時に読み込めるようにします。 AWSLambdaコンソール画面上の「設定」項目に「環境変数」の項目があるので、ここから設定できます。 AWSLambdaにコードをアップする zipコマンドでファイルを圧縮する d@dMacBook-Pro lineNotify_ruby % zip -r lineNotify_ruby.zip . 詰まりポイント 今回は、ローカルで作成したディレクトリをzipファイルで圧縮してlambdaにアップしました。 コードをアップする際は、作成したプロジェクトディレクトリ内でzipファイルを作成しないと、パスの読み込みがローカル上とlambda上で異なってしまい、エラーが発生するので、注意してください。 また、作成したコードが記載されているファイルは、zipファイルのルートに配置して圧縮するようにします。 AWSLambdaコンソール画面に戻り、圧縮したファイルをアップします。 圧縮ファイルをアップすると、コンソール画面上にアップしたコード類が表示されるようになります。 詰まりポイント このままのコードでは、Lambda関数は実行できません。 Lambda関数が実行される際、コードが記載されているファイル名とランタイム設定に記載されているメソッド名が呼び出されて、関数が実行されます。 コード内には、Lambda関数を実行する際に、呼び出されるメソッドを定義しておく必要があります。 よって、作成したコードを以下のように修正します。 send_iamgoinghome_notify.rb require 'net/http' require 'uri' class LineNotify NOTIFY_TOKEN = ENV["NOTIFY_TOKEN"] APIURI = URI.parse("https://notify-api.line.me/api/notify") def make_request(notify_message) request = Net::HTTP::Post.new(APIURI) request["Authorization"] = "Bearer #{NOTIFY_TOKEN}" request.set_form_data({message: notify_message}) return request end def send(notify_message) request = make_request(notify_message) response = Net::HTTP.start(APIURI.host, APIURI.port, use_ssl: APIURI.scheme == "https") do |https| https.request(request) end end end # ここを修正 def lambda_handler(event:, context:) line = LineNotify.new notify_message = "今から帰らせていただきます" response = line.send(notify_message) puts response.body end lambda_handlerメソッドを定義して、引数にevent:, context:を渡せるようにします。 この引数を記載しないとエラーになります。 LineNotifyクラスを作成するメソッドを定義し、ラインタイムに記載されている呼び出しメソッドがファイル名.定義したメソッド名になっていることを確認してください。 今回の場合は、send_iamgoinghome_notify.lambda_handlerが実行されます。 設定が異なっている場合は、編集して、呼び出されるメソッド名を統一させます。 Lambda関数のテスト 以上の手順でLambda関数を設定したら、テスト実行して、きちんとコードが実行されるか確認します。 デバイスにLambda関数を登録 AWS Iot1-Clickに登録したデバイスに作成したLambda関数を登録します。 まずは、プロジェクトを作成します。 「プロジェクト名」と「説明」を適宜入力します。 プロジェクトを作成した後は、デバイステンプレートを定義し、作成したLambda関数を登録します デバイステンプレートを定義したら、プレイスメントを作成して、完成です。 実験 完成はこのような感じです。 感想 とても簡単に実生活で使えそうなものが完成しました。 いやー、とても面白かったです。 実際に何かを作って完成したら、おー!ってなります。色々と詰まりポイントもあり、自分の勉強にもなりました。 長文記事をご覧くださり、ありがとうございました。また、何か勉強がてら作ってみたいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】ザックリ解説!世界トップシェアの理由に迫る!

はじめに 2021年6月よりSESでのエンジニアデビューを果たし、これからスキルアップに向けてIT全般の知識を学んでいく所存。 今回はそのアウトプットの一環として、クラウド事業において世界トップシェアを誇るAWSに関する記事を書いてみる事にした。 そもそもクラウドとは?聞いたことはあるけど。。。    クラウドという言葉自体は聞いたことがあるかもしれませんが、具体的に何?と聞かれると困ります。 そこでざっくりですが、言語化していきたいと思います。 一言で言うと、 「ユーザーがインフラ※やソフトウェアを持たなくても、インターネットを通じて、サービスを必要な時に必要な分だけ利用する考え方」になります。    もっと具体的に掘り下げてみると、利用にあたって、コンピューター(サーバー)の所在地(どこ?)が意識されない点が特徴として挙げられます。    従来のコンピューターの利用形態では、利用者は手元のパソコンの中にあるソフトウェアやデータを利用していました。しかしクラウドサービスでは、ネットワークを経由して、雲(クラウド)の中にあるソフトウェアやデータをサービスの形で使います。    では、本題のAWSについて AWSとは? 2006年に、Amazon 社内のビジネス課題を解決するために生まれた IT インフラストラクチャのノウハウをもとに、アマゾン ウェブ サービス(AWS)という名称でサービスがスタートしました。世界で最も利用されているクラウドインフラサービス(※)です。 これらのサービスはIaaS(Infrastructure as a Service)と呼ばれており、Googleが提供するGCP(Google Cloud Platform)やMicrosoftが提供するMicrosoft AzureもIaaSの1つです。 AWSで実現できること 豊富なサービス 世界最上級の運用専門知識 最もセキュアなセキュリティ基準 安心できる情報網(コミュニティやパートナーの存在) まったく新しいテクノロジーを最速で提供し続けるサービスコンセプト コスト削減や俊敏性を高めビジネスの効率化、ビジネススピードの加速化しイノベーションの加速を実現します。 AWSの大きな魅力のひとつが、初期費用がかからないことです。機材やソフトウェアライセンスなどの初期費用なしで、必要なときに、必要なだけ、低価格で多くの選択肢のなかから最適なITリソースをすぐに利用することができます! AWS学ぶべき理由 ・2020年代はビッグデータ活用が進められるため ビッグデータの活用が進むことでAWSの需要が上がると考えられるのも、インフラエンジニアがAWSを勉強するべき理由の一つです。 AWSでもさまざまなデータを蓄積するためのサービスがあり、集めた大量のデータを分析することでマーケティングの分野に活かすことができます。 また大量のデータ運用を行う際にも必要に合わせてデータ量の増減を行い、運用費用を最小限に押さえることができるのもAWSのメリットです。    ビッグデータやプログラミングの知識を得られる AWSではサーバーやストレージの他にも、ビッグデータを活用したアプリケーションを構築することができます。 ビッグデータを活用するメリットは、ビックデータを用いて解析したデータをマーケティングの分野に活かすことができるという点です。 またAWSのLambdaというサービスを活用することで、サーバーレスアプリケーションの構築やその他AWSサービスとの連携を行い、インフラ周りの構築・管理を楽にすることができます。 AWS Lambdaでは実際にコードを書く必要があるため、Lambdaを使うことでプログラミングの知識を身に着けることもできるでしょう。 まとめ AWSについての説明は以上になります。 AWSのこれからの需要であったり、エンジニアとして学ぶべき理由を何となく理解出来ましたでしょうか。 次はよりサービスに踏み込んだ記事を投稿していきますのでよろしくお願いします! ※今回参考にさせて頂いた記事 (https://aws.amazon.com/jp/what-is-aws/) インフラエンジニアにAWSのスキルは必要なのか?習得メリットを解説
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IPアドレス完全に理解した

過去に開いたAWS勉強会のスライドが元になっています!こちらからご覧ください! https://speakerdeck.com/hokuo/awsmian-qiang-hui-day1-ec2-rds?slide=26 IPアドレスとは IPアドレスというのは送信データに「送信元IP」「送信先IP」としてそれぞれ1つずつ設定できるもので、これがないとルーターを介した通信ができません。 私達がよく見る192.168.33.13のようなIPアドレスはIPv4という部類であり、最近ではIPの数が足りていないのでIPv6という規格も主流になっています。 ここではIPv4をベースにお話をしていきます。 IPアドレスの構造 IPアドレスの構造は最大3桁の数字がピリオド区切りで4つ並んでいます。 これは10進数で表されたもので、実態は「2進数8桁の数字をピリオド区切りで4つ並べたもの」になります。 サブネットマスク よくIPアドレスの後ろに「/16」などと書かれたものがあり、見かけたことがある方もいると思いますが、これはサブネットマスクと呼びます。 サブネットマスクの役割 IPアドレスに近い構造ではありますが、後に説明する「IPアドレスのネットワーク部とホスト部の境目」を示すものになります。 同じように2進数の8桁のピリオド区切りで4つ並べたもので、16という数字は左から16桁目までが1だよということを示してくれています。(1になっている部分はネットワーク部を示す。つまり2進数表記におけるIPアドレスの左から16桁目までがネットワーク部になるということ) 同じネットワークに所属しているIP同士はルータを使わなくても通信できます。社内で同じルーターを使っていれば、みんなは同じネットワークに所属しているはずですので、ネットワーク部を割り出して確認してみてください。 IPアドレスとサブネットマスクの確認の仕方 「ifconfig」コマンドでen0の欄を見ると自分のIPとサブネットを確認することができます。 netmastkがサブネットマスクのことですが、ここでは16進数になっているので10進数では「255.255.255.0」となりCIDR表記では「/24」になります。 IPが決まると、ネットワーク内に割り当てられるホストの数が決まる このネットワークとホストの境目が決まると、同じネットワークに所属しているホストの数が決定されます。/16であればネットワーク部が16桁で、ホスト部が残りの16桁になるのでホスト部の数字の組み合わせは2の16乗となり65536ホストとなります。 実際の設計では、このホスト(PCやサーバー)の使用する台数によって数値を絞るようにサブネット小さくを切ります。(/24など。ホストが1台の場合は/32など) 中には割り当てることができない特殊なIPも存在します。 一部の特殊なIPアドレスの紹介 特にループバックアドレスは馴染みがありそうですね。localhostのIPアドレスであり、自分のローカルIPアドレスとなります。 実際の通信はどうなっているのか 実際にルータを使用した通信の例を見てみます。 まずARPというプロトコルで先ほど紹介した「ブロードキャストアドレス」を使用して同じネットワークのすべてのホストに対して「このアドレス知ってる人います?」と質問します。 それがわかると、機器固有のMACアドレスを質問者にわたし、それをもとにその危機にデータをわたします。はじめに説明の通り、送信先のIPは一つしか持てないので、中継する機器はMACアドレスを渡して「こっちにパスしてくれー」と合図を送っているわけです。 これらを繰り返して目的地までのデータ送信を進めていきます。 終わりに 今回はIPアドレスに関する簡単な解説しながら実際の通信を覗いてみました。 もう少し実践的な使い方や説明はこちらのスライドをご覧ください! https://speakerdeck.com/hokuo/awsmian-qiang-hui-day1-ec2-rds また、AWSの勉強も兼ねて学んでみたい方は下記の書籍がおすすめですのでぜひ! 基礎知識 パターン構築
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【CloudFront】カスタムエラーレスポンスを設定し、ログインページに飛ばしてみよう

1. はじめに  以下の画像が何かわかりますでしょうか?  そうです。署名付きクッキーで制限したCloudFrontにアクセスした際、クッキーが正しく設定でいていない時に表示されるエラーです。わかりましたか?  さて、署名付きクッキーがない時に毎回この画面が表示されたんじゃ中々ばつが悪いことがあります。特に、「ログインページからログインして(署名付きクッキーを設定して)ページに飛べば正常通りに表示されるのに、直接URLで入ったらこんな画面が表示された。。。」なんて思われてしまうかも。。。ちなみに、署名付きクッキーとは公式ではこういったものです。 2. カスタムエラーレスポンスの設定  カスタムエラーレスポンスとは何でしょうか。簡単に言うと、CloudFrontディストリビューションにアクセスした際に上記のようなエラー画面ではなく、自分で設定した画面を表示する処理を行う設定になります。公式の説明はこちらへ。  ではでは、カスタムエラーレスポンスの設定を行いましょう.  まず前提として、エラーが発生した場合に飛ばすログイン画面は、S3の'/login/inedx.html'に格納されているとしましょう。署名付きクッキーでディストリビューションへのアクセスを制限している場合、パス'/login/*'への制限がないようにビヘイビアを編集する必要があります。 やり方1 直接ログインページを表示する  こちらは、エラーが発生したページで直接ログインページを開くやり方になります。下記とは違い、エラーが発生したページのURLのまま、ログインページを開くことになります。  手順としては、任意のディストリビューションを選択し、エラーページのカスタムエラーレスポンスの作成を選択します。今回対象となるエラーは認証情報が足りない場合の403エラーになるため、エラーコードで403を選択します。最小TTLのキャッシュエラーは、変に残っていると再度アクセスした際に認証情報が足りててもログインページに飛ぶことがあるため0にしました。レスポンスページのパスでは表示したい'/login/index.html'を選択します。HTTPレスポンスコードはHTTPエラーコードと同様の403を選択しましょう。200を選択した場合、ログインページがそのURLにキャッシュとして残り、何度アクセスしてもログインページが表示されるようになります。  カスタムエラーレスポンスの作成をクリックして作成しましょう。これで403エラーが発生した時に/login/inedx.htmlが表示されるようになりました。  しかし、先ほども言ったようにこのやり方はエラーが発生したページのURLのまま、ログインページを表示します。/login/inedx.htmlで他の画像などを参照していた場合、参照元URLが変わるため、相対パスで参照していた場合うまく他の情報を参照できなくなります。そこで、/login/inedx.htmlの参照先を絶対パスに変更しておきましょう。 やり方2 ログインページにリダイレクトさせる  こちらは、エラーが発生した際にそのURLで別のページを開いて、ログインページにリダイレクトさせるやり方になります。上記とは違い、どのページでエラーが発生しても、ログインページのURLでログインページが表示されます。  やり方は手順は1とほとんど同じです。ただし、一度別のページを開く必要があるため、S3'/error/error.html'にログインページにログイン画面に飛ばすよう仕込んでおく必要があります。  まずは、カスタムエラーレスポンスの設定を行いましょう。下記の画像のように設定すればOKです。  次に、パス'/error/*'への制限がないようにビヘイビアを編集します。最後にS3/error/'の中に下記のように書きerror.htmlを格納しましょう。 error.html <!DOCTYPE html> <html lang="ja"> <title>403</title> <script>location.href='https://~~~~/login/index.html'</script>  これで、403エラー時に/error/error.htmlにアクセスし、'/login/index.html'に飛ばされるようになりました。  ちなみに、もしもIEでこの動作を行いたい場合は注意が必要です。IEでは、設定されているエラーページが500バイト以下の場合に、IEで標準に設定されているエラーページを表示するという仕様があります。なので、もしIEでも使用される場合は、error.htmlを下記のように訂正します。 error.html <!DOCTYPE html> <html lang="ja"> <title>403</title> <script>location.href='https://~~~~/login/index.html'</script> <a>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</a>  上記のerror.htmlをS3に格納した時に500バイトを超えるまで「a」を適当に増やしてください。 3. CloudFormationでも設定方法  CloudFormationのテンプレートを用いて、カスタムエラーレスポンスの設定を行ってみました。手順1の場合、AWS::CloudFront::DistributionプロパティのDistributionConfigに下記のように設定を追加すればOKです。 CustomErrorResponses: - ErrorCachingMinTTL: 0 ErrorCode: 403 ResponseCode: 403 ResponsePagePath: /login/index.html せっかくなんで、CloudFront全体のテンプレートファイルを公開します。すでにS3は作成済、バケットポリシーは何も記入無し、ACMに証明書登録済という前提で行います。やり方は手順1、アクセス制限は署名付きクッキーで行います。また、他の設定項目はおおむね適当です。すきに設定してください。 AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: > Create CloudFront Distribution Resources: #S3とCloudFrontを繋ぐようのOAI CloudFrontOriginAccessIdentity: Type: "AWS::CloudFront::CloudFrontOriginAccessIdentity" Properties: CloudFrontOriginAccessIdentityConfig: Comment: access-identity-<S3バケット名> #S3に追記するバケットポリシー BucketPolicy: Type: "AWS::S3::BucketPolicy" Properties: Bucket: <S3バケット名> PolicyDocument: Statement: - Action: "s3:GetObject" Effect: Allow Resource: arn:aws:s3:::<S3バケット名>/* Principal: CanonicalUser: !GetAtt CloudFrontOriginAccessIdentity.S3CanonicalUserId #クラウドフロントディストリビューション作成 CloudFrontDistribution: Type: "AWS::CloudFront::Distribution" Properties: DistributionConfig: Comment: <コメント> #証明書関係 ViewerCertificate: AcmCertificateArn: <証明書のarm> MinimumProtocolVersion: TLSv1.2_2019 SslSupportMethod: sni-only PriceClass: PriceClass_All #オリジンの設定 S3との接続の設定 Origins: - DomainName: <S3バケット名>.s3.amazonaws.com Id: S3origin-<S3バケット名> S3OriginConfig: OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity}" #Behaviorsの設定 著名付クッキー等の設定 DefaultCacheBehavior: #デフォルトの接続の設定 TargetOriginId: S3origin-<S3バケット名> ViewerProtocolPolicy: redirect-to-https AllowedMethods: - GET - HEAD CachedMethods: - GET - HEAD DefaultTTL: 3600 MaxTTL: 86400 MinTTL: 60 Compress: true ForwardedValues: QueryString: false TrustedKeyGroups: - <署名付きクッキーのキー> CacheBehaviors: - TargetOriginId: S3origin-<S3バケット名> #login/*の接続の設定 ViewerProtocolPolicy: redirect-to-https AllowedMethods: - GET - HEAD CachedMethods: - GET - HEAD DefaultTTL: 3600 MaxTTL: 86400 MinTTL: 60 Compress: true ForwardedValues: QueryString: false PathPattern: login/* #カスタムエラーレスポンスの設定 CustomErrorResponses: - ErrorCachingMinTTL: 0 ErrorCode: 403 ResponseCode: 403 ResponsePagePath: /login/index.html HttpVersion: http2 Enabled: true 4. 終わりに  いかがだったでしょうか。カスタムエラーレスポンスの設定よりも、CloudFrontのテンプレートファイルの方が需要があるかもしれませんね。。。今回紹介しているカスタムエラーレスポンスでは、署名付きクッキーがない際の403エラーを例に挙げていますが、他にもWAFによるエラー等も使用できると思われますので、ぜひ使用してみてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Linux】【zcat】圧縮ファイルの中身を解凍せずに確認する方法

awsのs3環境で作業をしていると、gzファイルなどの圧縮ファイルを扱うことが多いです。 大体はファイルの移動で終わりますが、データが正しいか中身を確認したい時もあります。 ファイルを別ディレクトリにコピーし、解凍後にcatコマンドやviコマンドで確認することもできますが、正直そんな事をしていたら非効率ですので、そんな時に以下コマンドが役に立ちます。 zcat 使い方は簡単で、cpコマンドのオプションとして語尾に付けるだけです。 aws s3 cp s3://xxxx/xxxx/test.tsv.gz - | zcat 下記のようにオプションを付け足すことで、レコード件数を確認出来たりもします。 aws s3 cp s3://xxxx/xxxx/test.tsv.gz - | zcat | wc -l awsではなく普通のLinux環境でしたら、下記のようにして確認可能です。 zcat test.tsv.gz 以上です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CloudWatch Insightsのクエリまとめ

背景・目的 CloudWatchLogs Insightで思ったようにクエリが使えないので学習します。 内容 JSON形式のログを取り扱う 事前準備 以下のログを用意します。 検証 1. デフォルトのクエリ まずはデフォルトのクエリをそのまま実行する。 fields @timestamp, @message | sort @timestamp desc | limit 20 結果 ----------------------------------------------------- | timestamp | message | |-------------------------|-------------------------| | 2021-11-24 14:09:30.882 | {"id":3,"name":"zzzzz"} | | 2021-11-24 14:09:18.371 | {"id":2,"name":"yyyyy"} | | 2021-11-24 14:08:58.528 | {"id":1,"name":"xxxxx"} | ----------------------------------------------------- 2.filter fields @timestamp, @message | filter @message like /"name":"xxxxx"/ | sort @timestamp desc | limit 20 結果 ----------------------------------------------------- | timestamp | message | |-------------------------|-------------------------| | 2021-11-24 14:08:58.528 | {"id":1,"name":"xxxxx"} | ----------------------------------------------------- 3.項目追加 ingestionTimeを追加します。 ingestionTimeは、ログストリームに追加された時間になります。 fields @timestamp, @ingestionTime, @logStream, @message, @log | sort @timestamp desc | limit 20 結果 ------------------------------------------------------------------------------------------------------------------------------------ | timestamp | ingestionTime | logStream | message | log | |-------------------------|-------------------------|------------|-------------------------|---------------------------------------| | 2021-11-24 14:09:30.882 | 2021-11-24 14:09:31.267 | pattern1 | {"id":3,"name":"zzzzz"} | 012345678901:/cloudwatch/insight/test | | 2021-11-24 14:09:18.371 | 2021-11-24 14:09:18.895 | pattern1 | {"id":2,"name":"yyyyy"} | 012345678901:/cloudwatch/insight/test | | 2021-11-24 14:08:58.528 | 2021-11-24 14:09:02.269 | pattern1 | {"id":1,"name":"xxxxx"} | 012345678901:/cloudwatch/insight/test | ------------------------------------------------------------------------------------------------------------------------------------ 4.Parse 成功版 fields @timestamp, @ingestionTime, @logStream, @message, @log | parse @message "\"id\":*," as id | sort @timestamp desc | limit 20 結果 ----------------------------------------------------------------------------------------------------------------------------------------- | @timestamp | @ingestionTime | @logStream | @message | @log | id | |-------------------------|-------------------------|------------|-------------------------|---------------------------------------|----| | 2021-11-24 14:09:30.882 | 2021-11-24 14:09:31.267 | pattern1 | {"id":3,"name":"zzzzz"} | 012345678901:/cloudwatch/insight/test | 3 | | 2021-11-24 14:09:18.371 | 2021-11-24 14:09:18.895 | pattern1 | {"id":2,"name":"yyyyy"} | 012345678901:/cloudwatch/insight/test | 2 | | 2021-11-24 14:08:58.528 | 2021-11-24 14:09:02.269 | pattern1 | {"id":1,"name":"xxxxx"} | 012345678901:/cloudwatch/insight/test | 1 | ----------------------------------------------------------------------------------------------------------------------------------------- 試行錯誤 JSONメッセージの中の抽出がうまくできない。 JSONフィールドの区切り文字まで含めないと切り出せない。 fields @timestamp, @ingestionTime, @logStream, @message, @log | parse @message "\"name\":*" as name | sort @timestamp desc | limit 20 結果 ----------------------------------------------------------------------------------------------------------------------------------------------- | @timestamp | @ingestionTime | @logStream | @message | @log | name | |-------------------------|-------------------------|------------|-------------------------|---------------------------------------|----------| | 2021-11-24 14:09:30.882 | 2021-11-24 14:09:31.267 | pattern1 | {"id":3,"name":"zzzzz"} | 012345678901:/cloudwatch/insight/test | "zzzzz"} | | 2021-11-24 14:09:18.371 | 2021-11-24 14:09:18.895 | pattern1 | {"id":2,"name":"yyyyy"} | 012345678901:/cloudwatch/insight/test | "yyyyy"} | | 2021-11-24 14:08:58.528 | 2021-11-24 14:09:02.269 | pattern1 | {"id":1,"name":"xxxxx"} | 012345678901:/cloudwatch/insight/test | "xxxxx"} | ----------------------------------------------------------------------------------------------------------------------------------------------- 上記と同様に、JSONフィールドの区切り文字まで含めないと切り出せない。 fields @timestamp, @ingestionTime, @logStream, @message, @log | parse @message "\"id\":*" as id | sort @timestamp desc | limit 20 結果 -------------------------------------------------------------------------------------------------------------------------------------------------------- | @timestamp | @ingestionTime | @logStream | @message | @log | id | |-------------------------|-------------------------|------------|-------------------------|---------------------------------------|-------------------| | 2021-11-24 14:09:30.882 | 2021-11-24 14:09:31.267 | pattern1 | {"id":3,"name":"zzzzz"} | 012345678901:/cloudwatch/insight/test | 3,"name":"zzzzz"} | | 2021-11-24 14:09:18.371 | 2021-11-24 14:09:18.895 | pattern1 | {"id":2,"name":"yyyyy"} | 012345678901:/cloudwatch/insight/test | 2,"name":"yyyyy"} | | 2021-11-24 14:08:58.528 | 2021-11-24 14:09:02.269 | pattern1 | {"id":1,"name":"xxxxx"} | 012345678901:/cloudwatch/insight/test | 1,"name":"xxxxx"} | -------------------------------------------------------------------------------------------------------------------------------------------------------- JSONは直接フィールドを指定すると取得できるらしい(サポートされるログと検出されるフィールド) 変わらず後ろの値も取れてしまう。 fields @timestamp, @ingestionTime, @logStream, @message, @log | parse @message "id" as id | sort @timestamp desc | limit 20 結果 ---------------------------------------------------------------------------------------------------------------------------------------------------------- | @timestamp | @ingestionTime | @logStream | @message | @log | id | |-------------------------|-------------------------|------------|-------------------------|---------------------------------------|---------------------| | 2021-11-24 14:09:30.882 | 2021-11-24 14:09:31.267 | pattern1 | {"id":3,"name":"zzzzz"} | 012345678901:/cloudwatch/insight/test | ":3,"name":"zzzzz"} | | 2021-11-24 14:09:18.371 | 2021-11-24 14:09:18.895 | pattern1 | {"id":2,"name":"yyyyy"} | 012345678901:/cloudwatch/insight/test | ":2,"name":"yyyyy"} | | 2021-11-24 14:08:58.528 | 2021-11-24 14:09:02.269 | pattern1 | {"id":1,"name":"xxxxx"} | 012345678901:/cloudwatch/insight/test | ":1,"name":"xxxxx"} | ---------------------------------------------------------------------------------------------------------------------------------------------------------- 5.stats count IDごとのカウントをとる。 fields @timestamp, @ingestionTime, @logStream, @message, @log | parse @message "\"id\":*," as parsed_id | stats count(*) by parsed_id | sort @parsed_id asc | limit 20 結果 ------------------------ | parsed_id | count(*) | |-----------|----------| | 3 | 1 | | 2 | 1 | | 1 | 1 | ------------------------ 考察 もっとJSONの抽出方法を調べる必要がある。 結論 参考 サポートされるログと検出されるフィールド
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

さばぴょい伝説

位置について ROBOCOPY どん! Woooooooo(さばおっち) Woooooooo(さばぴょい さばぴょい) Woooo(うつだっち) Woooo(さばぽい) さばさばうつうつ 5 1 0 RAID!! オレンジぱっぱか怪ストレージ(はいっ) ちょこちょこなにげに(そーわっ So What) 第一第零第五RAID(だんだんだんだん異音が近づき) めんたまギラギラ障害でーす(はいっ) 今日もめちゃめちゃはちゃめちゃだっ(ちゃー) がち追い込み(睡眠カット) 家帰りてー(でも帰れなーい) DATは(からっぽ)HDDは(消した) どいつもこいつも あらら(リワインド) 泣かないで(はいっ) 拭くんぢゃねー(おいっ) 業者呼んでも(なおらないっ)(はーっ?) きょうの総務の女帝は あたしだけに注意する サーバルームへゆこう 電源を切って マウント外して 業者のなかに 光ともす (どーきどきどきどきどきどきどきどき) 機密ファイルが! ずきゅんどきゅん 戻らないー(ふっふー) ばきゅんぶきゅん 消えてーゆーくーよー こんなー思いはー はーじめてー(5 1 0 RAID!) ピピーピピー アラートが鳴りー(ふっふー) ピピーピピー 冷や汗ーだーよー 今日もーかなでーるー 発狂発狂 部長 3 2 1 Go Fight 鬱々 メンタル 3 2 1(うーーClinic!) なんだこの様は…くそっ… 社内全体で使用していたファイルサーバが老朽化し、さらに出張やリモートワークにも対応するため、某クラウドストレージサービスに移行することになりました。 AWSのS3をベースとしたもので、そのままS3として利用することもできたので、ファイル移行はS3に直接ファイル転送することにしました。 全部で数TBの容量だったのでAWS Snowballの利用も考えましたが、なるべくコストかけたくなかったので、Windows ServerからS3をネットワークドライブとしてマウントして、ROBOCOPYで全転送することにしました。 このときAWS Snowballを利用していれば・・・。 RUBOCOPYは1週間経っても終わりませんでした。S3へのアップロードって意外と転送速度出ないんですよね。 RUBOCOPY実行から1週間以上経過したある日、社内のサーバルームから嫌なWooooという異音と、ピーピーというアラートオンらしきビープ音が・・・。 ファイルサーバをみると、2台のHDDにオレンジのLEDがぱっかぱっか点灯していました。そう、HDD故障のランプです。 RAID5のストレージのHDDの2台が故障したということは・・・、普通には復旧できないことを意味します。 管理コンソールにアクセスすることもできず、どうやらRAIDコントローラごと故障したようです・・・。 契約書や営業資料など、社内の全ての機密情報が入ったファイルサーバが死亡したことで、社内業務は停止。 総務の女帝からは激しく怒られることになりました。 バックアップをデイリーでDATにとっていたので、真っ先にこれからの復旧を試みたのですが、実は機械が壊れていたのかバックアップはずっとうまく取れておらず、中身は空っぽ。こここからの復旧はできませんでした。 過去にメンテナンスのため一時バックアップをとっていた外付けHDDに古い資料だけでも残っていないか見てみましたが、やはり機密文書含むため綺麗に消し去っていました。 他にもあれこれ試すも結局復旧できず、最後の頼みの綱で数百万円かけてデータ復旧業者に希望の光をともすことにしました。 祈る思いでどきどきしながら業者の連絡を待ちましたが・・・、結局復旧はできないとの連絡。 結局データ復旧はあきらめ、各社員のPCやメールの履歴などからファイルをかき集めることになりました。 この結果、次回に活かすとしよう 元々老朽化していたファイルサーバに1週間以上連続してHDDにアクセスし続ける処理を行わせることが、今考えるとそもそも無茶がありました。 AWS Snowballのコストをケチったせいで、データ復旧業者への支払いに数百万かかった挙句にデータも全て失うことになりました。 失敗したら困るようなものはケチらず、安全と安心と買う意味で厚めにコストをかけましょう。 またHDDが故障した際は余計なことはせず電源をすぐに切った方が、データ復旧業者で復旧できる可能性が高いそうです。 信念を曲げなければ理想は必ず芽吹く それを示せなかった私が“悪い例”として皆の人柱となろう ©︎エアグルーヴ(ウマ娘) 女帝フリーアイコン - ことうのイラスト - pixiv
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

いいかんじにAWS上のネットワーク構成図を作りたいから手鍋した話

この記事はディップ株式会社 Advent Calendar 2021の 5日目の記事ですっ 4日目は @bikun_bikunさんの SendGridのDynamicTemplateでハマった話 でした。 これはなかなか気づけない仕様ですね・・linterが欲しいレベルです・・ やったこと ネットワーク構成図だけを描くシンプルなツールを書いた。 やってみたかっただけのスカンクワーク 現状の課題 クラウドに移行したサーバーのネットワーク管理がぱっと見レベルの可視化ができていない 簡略化した可視化が出来ていないので、セキュリティレベルを上げるための許可ポートのリファクタとか 何かあったときの侵入経路みたいなのがサクッとレベルで分からない。 実現したい事の分析 リスクがある通信の流れ リソース障害発生時に影響をうける個所 これらをうまいとこ資料化出来ていればいいと思った。つまり、 サブネットの種類 ゲートウェイやサブネットをまたいだ通信の向き セキュリティグループ が、あればいい。何のサーバーが何台あって、何の機能があって、どのくらいの性能か、は不要。 むしろそれはサーバー側のリソース管理でやってる。 色々ソリューションあるやん? AWS Perspective アーキテクチャを作図するツール。ネットワーク構成とリスクの観点で見れるものじゃなくね? CloudCraft、CDK-Diaも同じ部類。ネットワーク視点じゃない。 aws-security-viz SGの相関図を描くやつ。かなり惜しいんだけど、侵入リスクで考えたいのでEC2単位で見たい。 SaaSとかシステムとかじゃなくて、シュっとアミダ状に構成みれるやつ。そういうシンプルなの無いんだよね。。 というわけで ↓押してみろ 飛ぶぞ ↑押してみろ 飛ぶぞ 作りました! ※ネットワークIDみたいなのが見えている所を念のため黒塗りしてます 特徴 ・アウトプットフォーマットがGraphviz形式なのでDOT言語で出力される。つまり、SVG等に出来るのでパワポぽく編集可能! 対応している出力フォーマット ・awscli+シェルスク+Go言語+Graphvizと連携して生成するので自動生成が出来る。つまりバッチ処理、リアルタイム更新が出来る! どういうことだってばよ・・? GraphVizをECSでHTTPサービス化して社内どこからでも使えるようにした話 ↑こういう世界です。素敵!! ・Go言語だからワンバイナリで環境構築が要らないという大正義&大勝利 で? 小分けにプロダクト/コンポーネント単位で作って可視化・管理していきまっしょ!という事です。 サブネットの種類 同じサブネットに所属する事で外部との通信が出来るか、それは片方向か、出来ないかを判別 ゲートウェイやサブネットをまたいだ通信の向き ハックされたのと逆算でGatewayまでのルートを追う事でどのサーバーを跨いできたかを判定できる セキュリティグループ セキュリティグループ内で共通の通信が出来ることから、どのようなプロトコル/ポートを使った通信かを調査できる。 これにより、 情報の漏洩 xxがハックされるとどのルートでアクセスされるので何が漏れるか 情報の改ざん xxがハックされるとどのルートでアクセスされるので何が改ざんされるか 業務の停止 xxがハックされ止まると、システム的に何が起こるか この視点で可視化出来ました。めでたしめでたし 続編を期待  このツールを使う欠点として横断的な見方が難しいというのがある。最終防御壁がセキュリティグループなので、セキュリティグループが攻略された場合は全構成をチェックしていかないとならない。 けど、どうせ全構成チェックが起きる規模のハッキングなので手間としては大して変わらないと思う。  あとセキュリティグループ名から実際に空いてるセグメント、ポート番号を直観的に見れないよねってのもある。こっちは図に直接書くと文字が多すぎて見辛くなるので、そこまではやってないって意図がある。命名規則とかでカバーするのがよろし、と思ってる 明日は 6日目、@horihori_natsumiさんの 何か書きます〜!!! です。 何でしょうwおったのしみに!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

はじめてのAWS CloudFormation -IAMユーザー作成編-

はじめに 前回の記事:はじめてのAWS CloudFormation -S3バケット作成編- 前回に引き続きCloudFormationを学ぼうという試みと取り組んだ内容の記録です。 今回はCloudFormationでIAMユーザー、グループ、ポリシーを作成してみます。 IAMユーザーのパラメータ設計 作成するIAMユーザーを考えます。 社内でアカウントを払い出す時にありがちなユーザーと権限を想定して以下のようにしました。 方針 IAMポリシーは権限管理を容易にするため、原則IAMグループに対してポリシーをアタッチする(個々のユーザーに対して直接ポリシーをアタッチしない)。 運用ユーザー相当のOpeUserはec2インスタンスの起動・停止のみ操作可能とし、その他のサービスについては読み取りのみ可能とする。 各IAMユーザーのパスワードは、共通のものをデプロイ時にパラメーターとして指定。初回ログイン時に変更させる。 IAMグループ IAMグループ名 マネージドポリシー カスタムポリシー CFn-test-AdminGroup AdministratorAccess - CFn-test-PowerUserGroup PowerUserAccess - CFn-test-OpeGroup ReadOnlyAccess CFn-test-ec2-OpePolicy IAMユーザー IAMユーザー名 所属グループ CFn-test-AdminUser CFn-test-AdminGroup CFn-test-PowerUser CFn-test-PowerUserGroup CFn-test-OpeUser CFn-test-OpeGroup カスタムポリシー:CFn-test-ec2-OpePolicy CFn-test-ec2-OpePolicy { "Version": "2012-10-17", "Statement": [ { "Sid": "ActionInstances", "Effect": "Allow", "Action": [ "ec2:DescribeInstances", "ec2:StartInstances", "ec2:StopInstances" ], "Resource": "*" } ] } テンプレート用パラメータの確認 公式ドキュメントから各リソースで指定可能なPropertiesを確認します。 IAMグループ AWS公式ドキュメント - AWS::IAM::Group IAMGroupProperties Type: AWS::IAM::Group Properties: #グループ名を指定 GroupName: String #付与するマネージドポリシーのARNを指定 ManagedPolicyArns: - String #パスを指定 Path: String #インラインポリシーを記載 Policies: - Policy IAMユーザー AWS公式ドキュメント - AWS::IAM::User IAMUserProperties Type: AWS::IAM::User Properties: #所属グループ名を指定 Groups: - String #パスワードに関する設定を指定 LoginProfile: LoginProfile #付与するマネージドポリシーのARNを指定 ManagedPolicyArns: - String #パスを指定 Path: String #PermissionsBoundaryを指定 PermissionsBoundary: String #インラインポリシーを記載 Policies: - Policy #タグを指定 Tags: - Tag #ユーザー名を指定 UserName: String IAMポリシー AWS公式ドキュメント - AWS::IAM::ManagedPolicy IAMポリシー作成時に指定するリソースタイプとしては、「AWS::IAM::ManagedPolicy」と「AWS::IAM::Policy」が存在します。 紛らわしいのですがカスタムポリシー(カスタマー管理ポリシー)を作成する場合は「AWS::IAM::ManagedPolicy」を指定します。 「AWS::IAM::Policy」で作成するとインラインポリシーの作成になってしまいます。 IAMPolicyProperties Type: AWS::IAM::ManagedPolicy Properties: #説明を記載 Description: String #アタッチするIAMグループを指定 Groups: - String #ポリシー名を指定 ManagedPolicyName: String #パスを指定 Path: String #ポリシーの内容を記載 PolicyDocument: Json #アタッチするIAMロールを指定 Roles: - String #アタッチするIAMユーザーを指定 Users: - String Propertiesを記載 リソースごとにPropertiesを記載します。以下のようになりました。 IAMグループ IAMGroup #"CFn-test-AdminGroup" AdminUserGroup: Type: AWS::IAM::Group Properties: GroupName: !Ref AdminUserGroupName #マネージドポリシーのアタッチ ManagedPolicyArns: - arn:aws:iam::aws:policy/AdministratorAccess #"CFn-test-PowerGroup" PowerUserGroup: Type: AWS::IAM::Group Properties: GroupName: !Ref PowerUserGroupName #マネージドポリシーのアタッチ ManagedPolicyArns: - arn:aws:iam::aws:policy/PowerUserAccess #"CFn-test-OpeGroup" OpeUserGroup: Type: AWS::IAM::Group Properties: GroupName: !Ref OpeUserGroupName #マネージドポリシーのアタッチ ManagedPolicyArns: - arn:aws:iam::aws:policy/ReadOnlyAccess #AWS :: IAM :: ManagedPolicyリソースは戻り値としてARNを返すため、!RefでARNを取得 - !Ref OpePolicy IAMユーザー IAMUser #CFn-test-AdminUser AdminUser: Type: AWS::IAM::User Properties: UserName: !Ref AdminUserName Groups: - !Ref AdminUserGroupName #初期パスワードとリセット設定 LoginProfile: Password: !Ref Passwd PasswordResetRequired: true #CFn-test-PowerUser PowerUser: Type: AWS::IAM::User Properties: UserName: !Ref PowerUserName Groups: - !Ref PowerUserGroupName #初期パスワードとリセット設定 LoginProfile: Password: !Ref Passwd PasswordResetRequired: true #CFn-test-OpeUser OpeUser: Type: AWS::IAM::User Properties: UserName: !Ref OpeUserName Groups: - !Ref OpeUserGroupName #初期パスワードとリセット設定 LoginProfile: Password: !Ref Passwd PasswordResetRequired: true IAMポリシー IAMPolicyProperties OpePolicy: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: "CFn-test-ec2-OpePolicy" PolicyDocument: { "Version": "2012-10-17", "Statement": [ { "Sid": "ActionInstances", "Effect": "Allow", "Action": [ "ec2:DescribeInstances", "ec2:StartInstances", "ec2:StopInstances" ], "Resource": "*" } ] } 完成したCloudFormationテンプレート CreateIAMUsers AWSTemplateFormatVersion: 2010-09-09 Description: Create IAM Users Parameters: AdminUserGroupName: Type: String Default: CFn-test-AdminGroup Description: AdminUserGroupName PowerUserGroupName: Type: String Default: CFn-test-PowerUserGroup Description: PowerUserGroupName OpeUserGroupName: Type: String Default: CFn-test-OpeGroup Description: OperationUserGroupName AdminUserName: Type: String Default: CFn-test-AdminUser Description: AdminUserName PowerUserName: Type: String Default: CFn-test-PowerUser Description: PowerUserName OpeUserName: Type: String Default: CFn-test-OpeUser Description: OpeUserName Passwd: Type: String Description: Password NoEcho: True Resources: AdminUserGroup: Type: AWS::IAM::Group Properties: GroupName: !Ref AdminUserGroupName ManagedPolicyArns: - arn:aws:iam::aws:policy/AdministratorAccess PowerUserGroup: Type: AWS::IAM::Group Properties: GroupName: !Ref PowerUserGroupName ManagedPolicyArns: - arn:aws:iam::aws:policy/PowerUserAccess OpeUserGroup: Type: AWS::IAM::Group Properties: GroupName: !Ref OpeUserGroupName ManagedPolicyArns: - arn:aws:iam::aws:policy/ReadOnlyAccess - !Ref OpePolicy AdminUser: Type: AWS::IAM::User Properties: UserName: !Ref AdminUserName Groups: - !Ref AdminUserGroupName LoginProfile: Password: !Ref Passwd PasswordResetRequired: true PowerUser: Type: AWS::IAM::User Properties: UserName: !Ref PowerUserName Groups: - !Ref PowerUserGroupName LoginProfile: Password: !Ref Passwd PasswordResetRequired: true OpeUser: Type: AWS::IAM::User Properties: UserName: !Ref OpeUserName Groups: - !Ref OpeUserGroupName LoginProfile: Password: !Ref Passwd PasswordResetRequired: true OpePolicy: Type: AWS::IAM::ManagedPolicy Properties: ManagedPolicyName: "CFn-test-ec2-OpePolicy" PolicyDocument: { "Version": "2012-10-17", "Statement": [ { "Sid": "ActionInstances", "Effect": "Allow", "Action": [ "ec2:DescribeInstances", "ec2:StartInstances", "ec2:StopInstances" ], "Resource": "*" } ] } Outputs: UserName: Description: UserName Value: !Join [",", [!Ref AdminUserName, !Ref PowerUserName, !Ref OpeUserName]] LoginURL: Description: LoginURL Value: !Join ["", ["https://", !Ref AWS::AccountId, ".signin.aws.amazon.com/console"]] 実行後の確認 IAMユーザー IAMグループ CFn-test-OpeGroupに付与されたポリシー 考慮した点や途中発生したエラーについて Parametersセクション グループ/ユーザー名は固定値でも良かったのですが、Parametersセクションでデプロイ時に指定可能にしています。 Default:の値として「IAMユーザーのパラメータ設計」で決めた値を記載しているため、最初から入力された状態になっています。 Outputsセクション Outputsセクションで、作成されたIAMユーザー名の表示とログインURLの生成を行いデプロイ完了後に「出力」タブで確認できるようにしています。 機能的にはなくても良いのですが、前回のS3バケット作成で使うことがなかったOutputsセクションを使ってみたかっただけです。 NoEcho属性 Parametersセクションのパスワードを指定するPasswd:について、NoEcho: Trueを指定しています。 最初はNoEcho属性を記載していなかったのですがVSCode上で[cfn-lint]: Parameter Passwd used as Password, therefore NoEcho should be True という警告が表示されていました。 NoEcho属性を記載しない場合、以下のように「パラメータ」タブに指定した値がマスクされずに表示さるため、NoEcho: Trueを記載した方が良い旨の警告でした。 NoEcho属性を記載した場合以下のようになります。 CloudFormationに対して読み取り権限があればパラメータとして渡したパスワード情報を見ることができてしまうので、 ログインパスワード等の重要情報をパラメータとして渡す場合はNoEcho: Trueを指定してマスクした方が安全性が高まります。 参考:AWS公式ドキュメント - パラメータ おわりに CloudFormationを使ってIAMユーザー、グループ、ポリシーを作成してみました。 今回は自分のAWSアカウント内で完結していますが、 AWS Organizationsを利用している場合、Organizations管理アカウントからCloudFormationのStackSetsを利用することで、 OU単位で簡単に複数のAWSアカウントに対して同じリソースをデプロイすることもできるようです。 (Organizations環境でなくても、StackSetsを利用することで別アカウントに対してのClodFormation実行は可能) 会社で検証やプロジェクトごとのアカウント払い出し時に、標準化されたIAMユーザーや権限のセットがあればテンプレート化することで払い出しにかかる時間を短縮できそうですね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amplify APIのfunctionディレクティブでLambdaを呼び出す

はじめに 例えば認証付きでAWS上で関数を実行したい時に、Amplify APIのfunctionディレクティブがとても便利でした。 今回はフロントエンドから特定のLambdaを呼び出す流れについてまとめさせていただきたいと思います。 https://docs.amplify.aws/cli/graphql-transformer/function/ 実装 API導入 amplify add api amplify add apiで追加します。 設定の詳細は省きますが、今回はfunctionディレクティブを利用するのでGraphQLにしてください。 schema.graphql amplify/backend/api/gqllambda/schema.graphqlが生成されているので、こちらのファイルを編集して定義していきます。 schema.graphql type Query { signup( email: String! password: String! ): String @function(name: "signup-${env}") } 今回はLambda上のsignup-${env}関数を呼び出すようなイメージとなります。 email/passwordのように引数を渡すことも出来ますし、${env}を付与することで環境に応じてLambda関数を呼び分けることも出来ます。 また、他にも便利なディレクティブが多々あり、例えば@aws_cognito_user_poolsを付与することでユーザープール認証済みでないと呼び出せないようにしたりも出来ます。 https://docs.amplify.aws/cli/graphql-transformer/directives/ Push amplify push amplify pushで設定した内容をPushします。 呼び出し フロントからの呼び出しです。 Push時に設定したファイルにschema.graphqlの内容が実装されているのでそれを利用します。 import { API, graphqlOperation } from 'aws-amplify' import { signup } from '../graphql/queries' await API.graphql(graphqlOperation(signup, { email: '', password: '' })) TypeScriptで型を明確にしたい場合は、Push時に生成されたAPI定義ファイルをimportすることで対応出来ます。 おわりに 個人的に初めてサーバーレスでフロントエンドアプリケーションの開発をしたのですが、今回のように「なんかLambda用意してフロントから呼び出したいなー」というユースケースはままありました。 誰かの役に立てば幸いです。 何か改善や問題等あればコメントいただけると嬉しいです! 宣伝 株式会社start-up studioでは、コーポーレートガバナンス領域のDX推進を目指しております。 その第一弾として、複雑・膨大・専門的という三重苦を抱える決算開示業務のプロセスを劇的に効率化する「Uniforce -決算開示業務ナビゲーション- 」をリリースしました。 ご興味のある方はお問い合わせください! https://startup-studio.co.jp/news/e4spzygrs/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【コスト削減】検証環境をSlackから停止できるようにした話【QA止める君】

記事の内容 CAMPFIRE社内で使用している検証環境(社内では、QA環境と呼ばれている)をSlackから停止して、コスト削減した話 自己紹介 こんにちは、CAMPFIRE SREの榊原です。 本日はCAMPFIREアドベントカレンダーの一つとして、QA止める君(SREチームの方が命名してくれました笑)についてお話したいと思います。 CAMPFIREの検証環境について 検証環境って大切ですよね。 新しい機能をリリースする際に、ローカルだけではなく、ちゃんと本番と同じ構成のインフラ上で動作させて、挙動を確認したり。 企業によっていろいろな検証環境があると思います。 例えば、 小規模なスタートアップでしたら、プロダクション環境とステージング環境のみがあって、ステージングで動作確認をする 中規模な企業でしたら、ステージング・プロダクションとは別に、検証環境用のインフラがあり、そこにリリースして、検証が終了したら、他のエンジニアに譲る(一つの検証環境をみんなで使い回す) などなど。 CAMPFIREの検証環境(QA環境) CAMPFIREでは、githubのブランチに qa というプレフィックスをつけてpushすると、自動で検証環境が一つ出来上がるようになっています。 そのため、アプリケーションエンジニアは、自分が作りたいだけ検証環境を作成することができます。 この環境は、非常にエンジニアフレンドリーで、QA環境の取り合いが起こることがないのですが、1点問題があります。 コスト問題 それは、コストが肥大化すること。 CAMPFIREのインフラ構成は、ECS(EC2)で動いているため、検証環境の数だけEC2が増えていきます。 定期的に検証環境を削除するバッチが走っているのですが、データを保持し続けたい検証環境も存在するため、高頻度で削除バッチを回すことができず、ちょっとした修正の検証環境も1週間ほど残り続け、コストが肥大化する問題がありました。 QA止める君 そこで、QA止める君(SREチームの方が命名してくれました笑)を作成しました。 QA止める君の概要 QA止める君は slackのアクションにて、現在のQA環境一覧を取得する 停止したいQA環境を選択 停止ボタンを押す だけで、作成したQA環境を停止することができます。 コスト削減できたのか? はい! CAMPFIREでは、DatadogとAWSを連携して、毎月のコストを確認しています。そこで、QA環境のみのEC2のコスト推移を確認しているのですが、QA止める君の導入以降、QA環境のコストが削減できました。 QA止めるくんの技術構成 slack API Gateway Lambda にて作成しています。 slackからAPI Gatewayのエンドポイントを叩き、API Gatewayと連携したLambdaが停止の処理を行っています。 まとめ コスト削減は大事! SREチームは基本的にユーザーが直接使う機能をリリースしないため、サービスの売上への貢献度が曖昧になりがちです。 しかし、コスト削減は確実に会社の利益に直結するかつ、SREの腕の見せどころでもあるため、これからもコスト削減に取り組んでいきたいと思います! We are hiring CAMPFIREでは、一緒にCAMPFIREの信頼性を担保してくれるSREエンジニアを募集しています。 少しでも、興味がある方は、是非、応募してください!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

boto3からの解放。python3の標準ライブラリのみでAWSサービスを取り扱うには

概要 こんにちは! KDDIアジャイル開発センターの小板橋です。 今回の記事は、KDDI Engineer & Designer Advent Calendar 2021の3日目の記事となります。 ある日、こんな要望が舞い降りてきました。 「python3の標準ライブラリのみでAWSサービスに対して操作(今回は、S3に対するCRUD操作-GetとPostに限定)できるようにして欲しいな。」 そんな時に、どうやってあの便利なboto3を使用せずにこの要件を満たせるかを検証してみたというものです。 boto3のDEBUGログ boto3依存をやめるとなると、まずやらなければならないのはboto3が何を隠蔽してくれているのかを確認する必要があります。 まあ、単純に言えばBoto3がどんなREST APIのリクエストを投げているのかを確認すればよいのです。 そこで、Boto3でDEBUGログを出力してあげると簡単にREST APIのリクエストの内容が確認できます。 DEBUGログの設定 debug.py import boto3 # boto3が対象で、ログレベルはDEBUG boto3.set_stream_logger() # boto3/botocoreの詳細指定まで可能で、ログレベルの変更も可能 boto3.set_stream_logger('boto3.resources', logging.WARN) # パッケージの指定を''にすると # boto3/botocore全てのログが出力される。 boto3.set_stream_logger('') S3に対するGet処理 まず、AWSへのAPIリクエストを行う場合(AWS SDKや、AWS CLI、Boto3などのAWSツールを使わない場合)、リクエストの署名するためのコードを含める必要があります。 基本的には、この署名を気にすることはありません。(AWS SDK、AWS CLI、Boto3などのAWSツールは、ツールの設定時に指定するアクセスキーを使用してAPIリクエストに署名します。) そもそも何で署名が必要なの?? 簡単に言えば、署名によってリクエストのセキュリティ確保をしたい為です。 AWSのリクエストに対する署名では以下の点でセキュリティの確保を行なっています。 リクエスタのIDの確認 署名により、有効なアクセスキーを持っている人がリクエストを送信したことを確認できます。 送信中のデータの保護 送信中のリクエストの改ざんを防ぐために、リクエストの要素からハッシュ値を計算し、得られたハッシュ値をリクエストの一部として含めます。 AWSがリクエストを受け取ると、同じ情報を使用してハッシュを計算し、リクエストに含まれているハッシュ値と比較します。ハッシュ値が一致しない場合、AWSはそのリクエストを拒否します。 => Canonical Request この時、HTTP Authorization ヘッダーを使用します。 潜在的なリプレイ攻撃の防止 リクエストに含まれるタイムスタンプの5分以内にAWSに到達する必要があります。その条件を満たさない場合、AWSはリクエストを拒否します。 署名のバージョン AWSでは、署名バージョン4と署名バージョン2がサポートされています。AWS CLI, AWS SDKは、署名バージョン4をサポートするすべてのサービスに対して自動で署名バージョン4を使用します。 今回も、使用する署名のバージョンは署名バージョン4にします。 S3におけるCanonical Requestでは何が必要?? Canonical Requestでは、次のStepで署名の検証が行われます。 ①: 署名するための文字列を決めます。 ②: 署名キーを使用して、署名する文字列のHMAC-SHA256ハッシュを計算します。 ③: s3は認証されたリクエストを受信すると、署名を計算しリクエストで指定した署名と比較します。 (そのため、s3と同じ方法で署名を計算する必要があります。=> ここで、署名のために合意された形式でリクエストを送信するプロセスは、正規化と呼ばれます。 ①:Canonical Requestの作成 まず、下記にあるのがS3におけるcanonical requestのフォーマットになります。 <HTTPMethod>\n <CanonicalURI>\n <CanonicalQueryString>\n <CanonicalHeaders>\n <SignedHeaders>\n <HashedPayload> HTTPMethodは、GET/PUT/HEAD/DELETE等のHTTPメソッドの1つです。 CanonicalURIは、URIのURIエンコードバージョンです。ドメイン名に続く「/」で始まり、文字列末尾まで、または疑問符文字( '?')までのすべてを指定します。 CanonicalQueryStringは、URIエンコーディングされたクエリパラメータを指定します。 CanonicalHeadersは、リクエストヘッダーとその値のリストです。個々のヘッダー名と値のペアは、改行文字( "\ n")で区切られます。 ヘッダー名は小文字にする必要があります。また、CanonicalHeadersは下記のものを必ず含めなければなりません。 HTTPホストヘッダー Content-Typeヘッダーがリクエストに存在する場合は、追加 リクエストに含める予定のx-amz-*ヘッダーも追加。たとえば、一時的なセキュリティクレデンシャルを使用している場合は、リクエストにx-amz-security-tokenを含める必要があります。 下記、CanonicalHeadersのサンプル。 host:s3.amazonaws.com x-amz-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b785 2b855 x-amz-date:20130708T220855Z SignedHeadersは、アルファベット順にソートされたセミコロンで区切られた小文字のリクエストヘッダー名のリストです。 リスト内のリクエストヘッダーは、CanonicalHeaders文字列に含めたものと同じヘッダーです。 HashedPayloadは、リクエストペイロードのSHA256ハッシュの16進値です。 ちなみに、GETリクエストを使用してオブジェクトを取得する場合、空の文字列ハッシュを計算します。 ②: 署名する文字列を作成 下記が署名する文字列の例になります。 "AWS4-HMAC-SHA256" + "\n" + timeStampISO8601Format + "\n" + <Scope> + "\n" + Hex(SHA256Hash(<CanonicalRequest>)) AWS4-HMAC-SHA256は、ハッシュアルゴリズムHMAC-SHA256を使用していることを示します。 timeStampは、ISO8601形式の現在のUTC時刻を入れます。 Scopeは、結果の署名を特定の日付、AWSリージョン、およびサービス名を連結したものを入れます。 結果の署名は、特定の地域および特定のサービスでのみ機能し、署名は指定された日付から7日間有効です。 ちなみに、Scopeで連結したものの例が下記になります。 date.Format(<YYYYMMDD>) + "/" + <region> + "/" + <service> + "/aws4_request" ③: 署名を計算 AWS署名バージョン4では、AWSアクセスキーを使用してリクエストに署名する代わりに、最初に特定のリージョンとサービスを対象とする署名キーを作成します。 下記が作成する署名キーの例になります。 DateKey = HMAC-SHA256("AWS4"+"<SecretAccessKey>", "<YYYYMMDD>") DateRegionKey = HMAC-SHA256(<DateKey>, "<aws-region>") DateRegionServiceKey = HMAC-SHA256(<DateRegionKey>, "<aws-service>") SigningKey = HMAC-SHA256(<DateRegionServiceKey>, "aws4_request") 実装コード(Python3) それでは、上記の内容を元にPython3の標準ライブラリのみでS3の特定のbucketに対してGetのリクエストを投げてみましょう。 下記がその時のコードになります。 sample.py import sys, os, base64, datetime, hashlib, hmac import urllib.request, urllib.response import urllib.parse method = 'GET' service = 's3' host = 'xxxxbucket.s3.xxxregion.amazonaws.com' region = 'us-east-1' request_parameters = '' # Key derivation functions. See: # http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-python def sign(key, msg): return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest() def getSignatureKey(key, dateStamp, regionName, serviceName): kDate = sign(('AWS4' + key).encode('utf-8'), dateStamp) kRegion = sign(kDate, regionName) kService = sign(kRegion, serviceName) kSigning = sign(kService, 'aws4_request') return kSigning access_key = os.environ.get('AWS_ACCESS_KEY_ID') secret_key = os.environ.get('AWS_SECRET_ACCESS_KEY') if access_key is None or secret_key is None: print('No access key is available.') sys.exit() # Create a date for headers and the credential string t = datetime.datetime.utcnow() amzdate = t.strftime('%Y%m%dT%H%M%SZ') datestamp = t.strftime('%Y%m%d') # Date w/o time, used in credential scope # ************* TASK 1: CREATE A CANONICAL REQUEST ************* canonical_uri = "https://%s%s" % (host, "s3_bucketのkey") canonical_querystring = request_parameters payload_hash = hashlib.sha256(("").encode("utf-8")).hexdigest() canonical_headers = 'host:' + host + '\n' + 'x-amz-content-sha256:' + payload_hash + '\n' + 'x-amz-date:' + amzdate + '\n' signed_headers = 'host;x-amz-content-sha256;x-amz-date' canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash # ************* TASK 2: CREATE THE STRING TO SIGN************* algorithm = 'AWS4-HMAC-SHA256' credential_scope = datestamp + '/' + region + '/' + service + '/' + 'aws4_request' string_to_sign = algorithm + '\n' + amzdate + '\n' + credential_scope + '\n' + hashlib.sha256(canonical_request.encode('utf-8')).hexdigest() # ************* TASK 3: CALCULATE THE SIGNATURE ************* signing_key = getSignatureKey(secret_key, datestamp, region, service) signature = hmac.new(signing_key, (string_to_sign).encode('utf-8'), hashlib.sha256).hexdigest() # ************* TASK 4: ADD SIGNING INFORMATION TO THE REQUEST ************* authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature headers = {'x-amz-date':amzdate, 'x-amz-content-sha256': payload_hash, 'Authorization':authorization_header} request = urllib.request.Request( canonical_uri, headers=headers, method="GET", ) try: with urllib.request.urlopen(request) as response: print("リクエスト送信に成功" + response) except (ValueError, Exception): print("リクエスト送信に失敗") raise (参考先コード) S3に対するPost処理 S3に対するアップロードの処理で気をつけなければならないのは、アップロードするデータの種類を固定させるわけにはいかないので、複合データ型(=multipart)を扱える、multipart/form-dataという形式でアップロードしなければなりません。 multipart/form-dataでの送信 multipart/form-dataは、複数の種類のデータを一度に扱える形式です。 気をつけなければならないのは、下記のようにboundaryをコンテンツの境界を示す文字列として入れてあげなければなりません。 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryO5quBRiT4G7Vm3R7 今回は、Python3の標準ライブラリのみを使用して、s3に対してアップロードしたいと考えています。 外部ライブラリで有名なrequestsというものがありますが、こちらを使うと簡単にmultipart/form-data形式の送信ができますが、今回はurllibで頑張って実装していきます。 requestsを使わずにurllibでmultipart/form-data送信する multipart/form-data送信するクラスが下記になります。 multipart_post_handler.py #!/usr/bin/env python3 __all__ = ["MultipartPostHandler"] from email.generator import _make_boundary from os.path import basename from io import IOBase as FILE_TYPE from urllib.parse import urlencode from urllib.request import BaseHandler def b(str_or_bytes): if not isinstance(str_or_bytes, bytes): return str_or_bytes.encode("utf-8") else: return str_or_bytes NEWLINE = "\r\n" class MultipartPostHandler(BaseHandler): handler_order = BaseHandler.handler_order - 10 def _encode_form_data(self, fields, files): boundary = _make_boundary() parts = [] for name, value in fields: parts.append(b("--%s" % boundary)) parts.append(b("Content-Disposition: form-data; name=\"%s\"" % name)) parts.append(b("")) parts.append(b(value)) for name, fp in files: filename = basename(fp.name) fp.seek(0) parts.append(b("--%s" % boundary)) parts.append(b("Content-Disposition: form-data; name=\"%s\"; " \ "filename=\"%s\"" % (name, filename))) parts.append(b("")) parts.append(fp.read()) parts.append(b("--%s--" % boundary)) data = b(NEWLINE).join(parts) return boundary, data def http_request(self, req): data = req.data if data and isinstance(data, dict): fields = [] files = [] for key, value in data.items(): if isinstance(value, FILE_TYPE): files.append((key, value)) else: fields.append((key, value)) if files: boundary, data = self._encode_form_data(fields, files) req.add_header("Content-Type", "multipart/form-data; " \ "boundary=\"%s\"" % boundary) req.add_header("Content-Length", len(data)) else: data = urlencode(fields, doseq=True) req.data = data return req https_request = http_request あとは、このクラスを使用しS3に対してmultipart/form-dataでアップロードを実行してあげれば完成です。 sample_post.py #!/usr/bin/env python3 from typing import Any, Dict, Optional, Tuple, BinaryIO from module.multipart_post_handler import MultipartPostHandler import urllib.request, urllib.response import urllib.parse def main(): response = s3_presigned_post("urlを入れてください", "formを入れてください", "file(バイナリで入れてください。)") logging.debug(response) def s3_presigned_post( url: str, form: Dict[str, str], file: BinaryIO, verify: bool = True ): opener = urllib.request.build_opener(MultipartPostHandler()) params = form.copy() params["key"] = params["key"].encode("utf-8") params["file"] = file return opener.open(url, params) if __name__ == "__main__": main() まとめ いかがだったでしょうか。 もし、何かの参考になれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Serverless Framework の使い方を初心者にも分かりやすく説明する

はじめに Ateam Brides Inc. Advent Calendar 2021の2日目は 株式会社エイチームブライズのエンジニア、寒くなってきて膝が痛みだした@mkinが担当します さて、今回は業務でServerless Frameworkを使ったので基本的な使い方を紹介したいと思います Serverless Frameworkってどうやって動いてるの? 全体のイメージを持って取り掛かると理解が早いと思い、概要図を描きました 開発者は1〜3をするだけで、必要なAWSのリソースを作成してくれます補足概要図では最終的にLambdaとAPI Gatewayのリソースを作成していますが、API Gatewayの部分はLambdaが動くトリガーだったり使用するリソースになるので、作りたいものによって変わります。今回は、実際に作ってみる雛形に合わせたリソースを概要図に描いています。 今までAWSコンソールでポチポチしてリソース作成したり、バラバラだったLambda等の運用方法が統一され開発速度が上がることが Serverless Framework を導入するメリットかと思います。 開発環境を準備する それでは、早速、動かしてみましょう。私の実行環境は以下の通りです 実行環境 OS: macOS Big Sur v11.6 node: v14.17.1 (v6より高ければOKです) 1. Serverless Framework をインストール まずは公式ドキュメントに従って、自分のマシンにServerless Frameworkをインストールしましょう ServerlessFrameworkをインストール # インストール $ npm install -g serverless # バージョン確認 serverless --version > Framework Core: 2.66.2 > Plugin: 5.5.1 > SDK: 4.3.0 > Components: 3.18.1 2. Serverless Framework が使う IAMユーザーを作る 概要図にも書いた通り、Serverless Frameworkはデプロイコマンド一つでAWSの各リソースを作成してくれます。その際に、リソースを作成する権限をもったIAMユーザーが必要になります それでは早速、AWSコンソールからIAMユーザーを作成してみましょう。 AWSコンソールから IAMユーザーを追加 ユーザー名: serverless-servicename-agent AWS アクセスの種類を選択: アクセスキー - プログラムによるアクセス のみ 既存のポリシーから直接アタッチ ポリシーの作成 JSON 公式ドキュメントで紹介されている設定からコピペします(最終的には必要な権限のみ残して運用してください) ポリシー名: serverless-servicename-policy ポリシーを作成後、リロードして作成したポリシーにチェックをつけてユーザを作成 ユーザーが作成されたら「アクセスキー ID」と「シークレットアクセスキー」をメモしておいてください(後で使います) 3. 作成した IAMユーザーのクレデンシャル情報を自分のマシンに設定する 手順2で作成した IAMユーザーを使って自分のマシンからデプロイするためには、クレデンシャル情報(「アクセスキーID」と「シークレットアクセスキー」)を認証情報ファイルに記載する必要があります 下記のコマンドの「アクセスキーID」と「シークレットアクセスキー」の箇所を先程メモした値に書き換えて実行してください。 profileオプションを指定することで名前付きプロファイルにしています。(今回は分かりやすいようにIAMユーザー名に合わせています。) AWSクレデンシャル情報を認証情報ファイルに設定する $ serverless config credentials --provider aws --key アクセスキーID --secret シークレットアクセスキー --profile serverless-servicename-agent # 確認(コマンドqで終了) less ~/.aws/credentials # profileオプションに指定したプロファイル名のクレデンシャル情報が追加されていることを確認 > [default] > aws_access_key_id=アクセスキーID > aws_secret_access_key=シークレットアクセスキー > > [serverless-servicename-agent] > aws_access_key_id=アクセスキーID > aws_secret_access_key=シークレットアクセスキー プロジェクトを作成 開発環境が準備できたら、峠は超えました。 下記のコマンドを実行してプロジェクトを作成しましょう プロジェクトを作成 $ mkdir my-special-service $ cd my-special-service $ serverless create --template aws-nodejs-typescript --name my-special-service $ npm install nameオプションで指定したサービスを作成します サービス名にスネークケースは使わないほうがよいでしょう 余談:私はサービス名をmy_special_serviceのようにスネークケースで設定したのですが、デプロイでS3バケットが自動作成される際に、S3の命名規則(小文字、数字、ドット (.)、およびハイフン (-) のみ)に違反しておりエラーになってしまいました。もちろん、S3バケット名は任意の名称を設定できるので回避策もありますが、最初は公式ドキュメント通りがオススメです。 下記のフォルダ構成でひな形が作成されます。 知っておきたいserverless.tsファイル このファイルがServerless Frameworkのconfigファイルであり、とて〜も重要なファイルになります。 このファイルを自由に扱えたらServerless Framework中級者なのかなと思います(私は初心者)。 ざっくりと設定項目を洗ってみたいと思います key 内容 使い所 provider どこのクラウドサービスをどんな設定で動かすか(AWS以外もGCPとか対応してる) slsコマンドのデフォルト値とか設定しておくとオプションで指定しなくてもよくなる functions lambdaファンクションとそのトリガーの設定 layers lambdaファンクションが使うlayerの設定 node_moduleや共通処理を切り出してくれる resources lambdaファンクションが使うリソースを事前に定義 定義したリソースはデプロイ時に自動作成してくれる plugins 機能拡張できるプラグインを設定します 有名なのだとserverless-offlineプラグインを入れてローカルで動かしたり サービスをデプロイする 概要図の3.デプロイコマンドを実行に該当します。 ここで紹介するsls deployというコマンドはこれからいっぱい使うことになります。(slsはservelessの略) しかし、まだ下記のコマンドは実行しないでください デプロイコマンド(実行しないで!) $ sls deploy --aws-profile serverless-servicename-agent --region ap-northeast-1 --stage dev --verbose aws-profileオプションの値は先に設定した名前付きプロファイルの名前を指定します aws-profileやregion、stageオプションはserverless.tsに設定しておけばCLIオプションで指定する必要がなくなります(後述します) serverless.ts の設定を調整 先述のとおり、デプロイのたびにCLIオプションを書くのは面倒ですね。 そこで、CLIオプションの値をserverless.tsに設定することで、毎回書く手間を省いてくれます serverless.tsにオプション値を設定してCLIオプションを省略する ... provider: { name: 'aws', runtime: 'nodejs14.x', + profile: 'serverless-servicename-agent', + region: 'ap-northeast-1', + stage: "${opt:stage, 'dev'}", apiGateway: { minimumCompressionSize: 1024, shouldStartNameWithService: true, }, environment: { AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1', NODE_OPTIONS: '--enable-source-maps --stack-trace-limit=1000', }, lambdaHashingVersion: '20201221', }, ... stageに指定した${opt:stage, 'dev'}ですが、これは${可変のソース, デフォルト値}の形になっています。opt(オプション)以外にもenv(環境変数)など色んなソースを参照できます。 書いてみて分かると思うのですが、typescriptの型によってインテリセンスが効くので書きやすいですし、他にも用意されている設定を知ることもできて面白いです。 さて、これで先程のデプロイコマンドはだいぶスッキリ書くことができます。デプロイしてみましょう すっきりしたデプロイコマンド $ sls deploy --verbose --verboseオプションは任意です。つけておくとServerless Frameworkがどうやって動いているか理解しやすくなるので最初はオススメです デプロイの実行結果 先程のデプロイコマンドの出力ログを見てみます 〜 改めて概要図を見ると、--verboseオプションで出力したログの流れと合っていることが確認できるかと思います。 ローカルディレクトリを見るとデプロイ用に.serverlessが作成されていることが分かります 実際にAWSコンソールを見ると、各リソースが作成されています(Lambda関数が見つからない場合は東京リージョンになっていないかもしれません) ローカルから実行してログを見る デプロイしたLambda関数をローカルから実行してみましょう。 テンプレートにあるhello関数(src/functions/hello/handler.ts)を見ると、引数にeventを受け取り、event.body.nameを出力していることが分かります。 src/functions/hello/handler.ts const hello: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async (event) => { return formatJSONResponse({ message: `Hello ${event.body.name}, welcome to the exciting Serverless world!`, event, }); } このeventに同階層にあるmock.jsonを渡したい src/functions/hello/mock.json { "headers": { "Content-Type": "application/json" }, "body": "{\"name\": \"Frederic\"}" } このeventは、--pathor-pオプションでjsonファイルを指定することで渡すことができます。コマンドは以下の通りです。 ローカルから実行するコマンド $ sls invoke -f hello -p src/functions/hello/mock.json > Serverless: Running "serverless" installed locally (in service node_modules) > { > "statusCode": 200, > "body": "{\"message\":\"Hello Frederic, welcome to the exciting Serverless world!\",\"event\":{\"headers\":{\"Content-Type\":\"application/json\"},\"body\":{\"name\":\"Frederic\"},\"rawBody\":\"{\\\"name\\\": \\\"Frederic\\\"}\"}}" > } ログを見るコマンド $ sls logs -f hello > Serverless: Running "serverless" installed locally (in service node_modules) > START RequestId: 89c96351-37fb-4806-8bef-07eee054540d Version: $LATEST > END RequestId: 89c96351-37fb-4806-8bef-07eee054540d > REPORT RequestId: 89c96351-37fb-4806-8bef-07eee054540d Duration: 2.93 ms Billed Duration: 3 ms Memory Size: 1024 MB Max Memory Used: 57 MB Init Duration: 185.99 ms Lambdaの実行ログがローカルからも確認できました サービスを削除する お金かかるしね、消しときましょう $ sls remove --verbose おわりに 初めて業務でServerless Frameworkを触りましたが、公式ドキュメントが丁寧に書いてあるので理解しやすかったです。サーバーに負荷の高い画像処理なんかを任せちゃえるし、今後も使っていきたいなと思いました Ateam Brides Inc. Advent Calendar 2021の3日目は、 @Shuni がお送りします!!どんなネタを用意してくるのか楽しみです!! 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS SDK for JavaScript v3 で S3 の操作を行う方法

はじめに 現在Node.jsでAWSのサービスは「AWS SDK」というパッケージで操作することが可能になっています。 大まかに「v2」「v3」に分かれていて、v3は操作するサービスごとに個別にパッケージをインストールすることができたりTypescriptのサポートを進めたことから公式もv3の使用を推奨しているようです。 しかし、現状情報はv2の情報が多いこと、v2とv3で違いが大きかったりして使用する際にハマったことが多かったこと、また公式ドキュメントも正直わかりづらく情報を得るのに苦戦したので、v3でS3を操作する方法をここに記したいと思います。 パッケージのインストール 前述したようにv3は操作するサービスごとに個別にパッケージをインストールできるので、S3を操作するパッケージをインストールします。 npm npm i -S @aws-sdk/client-s3 yarn yarn add @aws-sdk/client-s3 S3の操作方法 1・インスタンスの生成 import {S3Client} from '@aws-sdk/client-s3'; const s3 = new S3Client({ region: 'ap-northeast-1', credentials: { accessKeyId: 'sample', secretAccessKey:'sample', }, }) 2・バケットの作成 import { S3Client, CreateBucketCommand } from '@aws-sdk/client-s3'; const s3 = new S3Client({ region: 'ap-northeast-1', credentials: { accessKeyId: 'sample', secretAccessKey:'sample', }, }); //S3バケットを作成する s3.send( new CreateBucketCommand({ Bucket: '作成したいバケット名' }) ); 3・バケットの削除 import { S3Client, DeleteBucketCommand } from '@aws-sdk/client-s3'; const s3 = new S3Client({ region: 'ap-northeast-1', credentials: { accessKeyId: 'sample', secretAccessKey:'sample', }, }); //S3バケットを削除する s3.send( new DeleteBucketCommand({ Bucket: '削除したいバケット名' }) ); 4・バケットにオブジェクトを追加 import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'; const s3 = new S3Client({ region: 'ap-northeast-1', credentials: { accessKeyId: 'sample', secretAccessKey:'sample', }, }); s3.send( new PutObjectCommand({ Bucket: '保存したいバケット名', Key: 'キーを設定。取り出す際はこのキーで参照する', Body: '保存したいオブジェクト本体' }) ) 5・複数のバケット内オブジェクトを取得 import { S3Client, ListObjectsV2Command } from '@aws-sdk/client-s3'; const s3 = new S3Client({ region: 'ap-northeast-1', credentials: { accessKeyId: 'sample', secretAccessKey:'sample', }, }); await s3.send( new ListObjectsV2Command({ Bucket: 'バケット名', MaxKeys: 10, //取得件数を指定。最大1000件まで }) ); 6・バケット内の特定のオブジェクトを取得 ここが私が一番苦戦したところでした。 「GetObjectCommand」で取得したオブジェクトは「Result.Body」に格納されているのですが、このオブジェクトは「ReadableStream」オブジェクトになっているためそのままでは取得できません。 「fs.createWriteStream」などで書き込み専用のストリームを定義し、.pipe()メソッドで少しづつデータを渡してあげる必要があります。 import fs from 'fs'; import { Readable } from 'stream'; import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'; const s3 = new S3Client({ region: 'ap-northeast-1', credentials: { accessKeyId: 'sample', secretAccessKey:'sample', }, }); const result = await s3.send( new GetObjectCommand({ Bucket: 'バケット名', Key: '取得したいオブジェクトのキー' }) ); const readableObj = result.Body as Readable; const writableObj = fs.createWriteStream('ファイル名'); //readableObjをwritableObjに少しづつ書き込む readableObj.pipe(writebleObj); expressの場合は、レスポンスオブジェクトにS3から取得したオブジェクトを.pipe()メソッドで書き込むことが可能です。 import fs from 'fs'; import { Readable } from 'stream'; import { Request, Response, Router} from 'express'; import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'; //画像を取得するhandler const getImage = async (req: Request, res: Response) => { const s3 = new S3Client({ region: 'ap-northeast-1', credentials: { accessKeyId: 'sample', secretAccessKey:'sample', }, }); const result = await s3.send( new GetObjectCommand({ Bucket: 'バケット名', Key: '取得したいオブジェクトのキー' }) ); const readableObj = result.Body as Readable; res.setHeader('Content-Type', 'image/png'); res.setHeader('Content-Length', result.ContentLength as number); //readableObjをresに少しづつ書き込む readableObj.pipe(res); } const router = Router(); router.get('/image/get', getImage); 7・バケット内のオブジェクトを削除する import { S3Client, DeleteObjectCommand } from '@aws-sdk/client-s3'; const s3 = new S3Client({ region: 'ap-northeast-1', credentials: { accessKeyId: 'sample', secretAccessKey:'sample', }, }); await s3.send( new DeleteObjectCommand({ Bucket: 'バケット名', Key: '削除したいオブジェクトのキー' }) ); まとめ 今回はAWSSDKのV3でS3の操作をする方法を紹介しました。 今後も随時情報を更新していきます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Certified SysOps Administrator - Associate に合格しました

2021/11/29に SOA に合格しました。新試験になりラボが追加されたので、どうなるのか不安でしたが、ぎりぎりですが無事に合格できました。 ラボ対策 新試験となって日が浅いので、まだまだラボ試験対策の情報が少ないですが、可能な限り情報を集めました。その上で、下記のような対策・ヤマをはりました。 SQS, SNS, Lambda, S3, RDS は、サービスを新規作成できるようにする。プラスアルファで、下記のような問題を想定しました。 SQS は、メッセージの送信とポーリングを開始してメッセージを受信する Lambda は、バージョン管理、メモリの調整、IAMロールを作成して CloudWatch Logs への書き込み権限 RDS は、KMS を使用した暗号化、スナップショットの作成、スナップショットからの復号方法 S3 は、オブジェクトのライフサイクル CloudFormation はリソースの変更ができるようする。 ネットワーク周りだと、VPC を作成して Public サブネットと Private サブネットを作成できるようにする。 2021/12/31 までに試験の登録をするとラボ試験のサンプルが利用できるので、SOA を受験する予定がある人は、期限が迫っているのでお早めに登録した方がいいです。そして、ラボ試験のサンプルを受験してどんな感じの問題がでるのかを確認すると、さらに対策を立てやすくなります。 感想 ラボ問題は、問題文中に誘導(センター試験での数学みたいなやつ)があればそこまで難しくない、あるいは、そこまで点数を落とすことはないと思いますが、誘導がなくてかつ一度も作成したことがないサービスの場合は、キビシイと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む