Certificate Managerを試す、、、ためのメール受信の設定

ケース

AWS Certificate Managerを使用して、SSL/TLS証明書を取得して、HTTPS接続を確立したい。

ドメインは、(お名前.comとかで)適当に取得しておいたものを使う。

問題

AWS Certificate Managerは、証明書の発行のときに、検証Eメールを送信する。自分(もしくは関係者)が、証明書の発行を承認する必要があるから。そりゃ当然なわけ。

送信先は、ヘルプにかかれている。2種類あって、WHOISに書かれたメールアドレスと、登録しようとしているドメインに適当なアカウント名をくっつけたメールアドレス。

Email is sent to the following three registered contact addresses in WHOIS:

  • Domain registrant
  • Technical contact
  • Administrative contact

Email is also sent to the following five common system administration addresses where your_domain is the domain name that you entered when you initially requested the certificate.

  • administrator@your_domain
  • hostmaster@your_domain
  • postmaster@your_domain
  • webmaster@your_domain
  • admin@your_domain

で、エンタープライズな人たちなら特に問題じゃないわけだけど。。。

その検証Eメールを受信できる状態じゃないと、前へ進めない。

お名前.comなんかでドメインを取得すると、whoisへの情報登録を代行してくれる。

代行してくれた登録情報は、お名前.comの会社の情報になるので、whoisの情報には、自分個人の情報は含まれていない、ということ。

個人利用にはありがたい代行サービスなんだけど、今回だけは、それが微妙。

目的

ということで、

AWS Certificate Managerが送信する送信先をつくります。

やり方

同じくAWSのサービスの、SESを使って、メールを受信する。

例として、example.jpというドメインを持っていて、そのサブドメインapp.example.jpSSL/TLS証明書を発行する。ために、admin@app.example.jpを受信できるようにする。

SESはこの記事を書いている時点(2016/11/08)では、まだ日本に提供されていないっぽいので、リージョンは適当に選んで使う。

事前準備:Route 53 の設定

もしDNSを設定していない状態であれば、Route 53にexample.jpを登録する。

とりあえずHosted Zoneにexample.jpを登録するだけでOK。

(ということで、Route 53を使う前提で書きます。)

ドメインの登録

  1. 左ペインのDomainsを選ぶ。
  2. Verify a New Domainボタンを押す。
  3. Domainを入力する。
    • 今回のケースで登録するドメインは、app.example.jp
  4. DNSの設定について書かれている画面に切り替わる。
  5. Use Route 53で先に進む。
  6. Domain Verification Record(TXTレコード) と Email Receiving Reocrd(MXレコード)どちらも登録したいので、両方チェックを入れて、Create Record Sets
  7. 少し待つと、ドメインのステータスがverifiedに変わる。

受信設定

  1. 左ペインのRule Setsを選ぶ。(Email Receivingの下)
  2. View Active Rule Setボタンを押す。
  3. Create Ruleボタンを押す。
  4. Recepientは空のままNext Step。(今回の場合は。本当は絞ったほうが良いはず。)
  5. Add actionで、S3を選ぶ。
    • S3のバケットとかは適当に。新しく作るなり、
    • prefixは、必要に応じて。app.example.jp/みたいにアカウント名を入れておくと便利そう。
    • 今回の場合は暗号化は不要。(本当は暗号化したほうが良いはず。)
  6. Next Stepで先に進む。
  7. Rule nameは適当に。
  8. 他、デフォルトのままで、Next Step
  9. Create Rule

テスト

  1. 左ペインのDomainsを選ぶ。(戻る)
  2. 登録したドメイン(例:app.example.jp)を選ぶ。
  3. Send a Test Emailを押す。
  4. メールの内容を適当に入力して、送信。
    • Toアドレスをadmin@app.example.jpみたいにしておくと、Certificate Managerが送信先に指定するメールアドレスみたくなる。
  5. S3をチェック。
    • Ruleで設定したバケット、prefixの場所にメールがあるか?
    • メールの内容が先ほど適当に入力したメールの内容に一致するか?

後は

Certificate Managerであれこれするだけ。

ELBの資料を読んだメモ

自分が業務で使いそうな範囲でメモに起こす。

aws.typepad.com

ELB = L4のロードバランサー(Classic Load Balancer = CLB) + L7ベースのロードバランサー(Application Load Balancer = ALB)

ELB

ELBは複数AZをまたいで負荷分散が可能。

ヘルスチェックは細かく指定可能。以下の表は引用。 ウェブアプリケーションで未熟な状態なら、PingパスでDBの接続とかあれこれチェックできる状態にしておくのも一つの手か?

項目 メモ
Ping プロトコル HTTP 200が返るか?
Ping ポート 80
Ping パス /index.html HTTP/HTTPS利用の場合
タイムアウト時間 20秒
ヘルスチェック間隔 30秒
異常判定までの回数 4回
正常判定までの回数 2回

正常との判定が遅いと追加したインスタンスが使える前に時間がかかる。 逆に異常との判定が厳しすぎても過負荷時に処理できるインスタンスを減らしてしまうことも。

Route 53以外でDNSを使うときは、CNAMEで設定してELBに向ける。

サーバー証明書はELBにアップロード。Let's Encryptを使うときはどうする?的な話は別途調べる。→AWS Certificate Managerを使用する。証明書に対しての料金がかからない理解。アップロード作業も不要。

サーバーへの接続のソースIPはELBのIPアドレスになるので、X-Forwarded-Forヘッダーを見る。

ELBにもセキュリティグループを設定できるので、今までEC2に設定していたセキュリティグループはELBだけを受け付けるようにして、ELB側で制限をかける。

ALB

コンテクストベースルーティングで、ルールにURLのパスが使える。振り分け先にはターゲットグループ(バックエンドサーバーをまとめたグループ)を指定。

WebsocketやHTTP/2にも対応。

料金 ALBの使用時間と、Load Balancer Capacity Units (LCU) の使用量で課金。

LCU 以下の3つのディメンションを測定し、使用量が最も高いディメンションのみ請求。

とりあえず変なことしなければ、CLBよりちょろっと安く済ませられるらしい。

ディメンションの項目から考えると、HTTP/2にちゃんと対応したほうが安くなるのか?

Git(hub)でフォークしたリポジトリに、オリジナルリポジトリの変更を取り込む

いつも忘れて調べなおすことになるので、メモ。

ケース

例えば、こんなリポジトリ

github.com

オリジナルのプラグインでほんのすこしだけカスタマイズしたいから、ちょっとだけ修正して使ってる、みたいな。

で、時間が経って、変更されているものもあるから取り込みたい。

というときの作業。

参照

結論、Githubのヘルプを見ればいいわけですね。はい、いつも見ます。いつもたどり着くのに時間がかかりますが。

https://help.github.com/articles/fork-a-repo/help.github.com

https://help.github.com/articles/syncing-a-fork/help.github.com

この記事のこの後の内容は、ヘルプの内容をただただ自分のためにメモしたものです。

作業

ざっくり書くとこんな感じでしょうか。

git clone ${forked_repo} ${forked_repo_dir}
cd ${forked_repo_dir}
git remote add upstream ${original_repo}
git fetch upstream
git checkout master # forked master
git merge upstream/master

自分の(忘れた後の)理解のために、細かく書いていくと、、、

フォークしてカスタマイズしたリポジトリGitHub - sugilog/molokai: Molokai color scheme for Vimに、オリジナルのリポジトリGitHub - tomasr/molokai: Molokai color scheme for Vimの修正内容を取り込む。

自分のリポジトリclone

git clone git@github.com:sugilog/molokai.git ~/workspace/molokai
cd ~/workspace/molokai

オリジナルのリポジトリupstreamという名前でリモートブランチとして登録。名前はかぶらなきゃOK。

git remote add upstream https://github.com/tomasr/molokai.git
git fetch upstream

自分のリポジトリのブランチをcheckout。とりあえずmaster

git checkout master

最後に、オリジナルの内容をマージ。

git merge upstream/master

あとはコンフリクトを直すなりなんなり。

結論、マージ作業なわけです。

GithubのウェブUIからのプルリク作業に慣れてきった生活で、簡単なマージ作業でさえ忘れてしまう。。。

最後に

molokai、お世話になってます。

github.com

tmux 2.3のキーバインディングでちょっと困った

macでtmuxを使っていてエラーが出て解決してたら次の問題にハマって、ということがあったので一連の話をメモ。

タイトルにしたキーバインディングは結局ググってもよくわからずソースを見て解決した話。

github.com

流れ

macOS Sierra にバージョンアップ。そしたら変なWARNINGメッセージが出るようになった。

qiita.com

Qiitaのまとめどおり、tmuxをアンインストール&再インストールしてみた。

ちなみにこの作業をした時のHEADはDo not force symbols to width 1, from Yen Chi Hsuan. · tmux/tmux@178894b · GitHub

この時点で、tmuxのバージョンが2.3に更新された。元のバージョンは忘れたので省略。

tmux2.3を起動したら、またもや変なエラーメッセージが出始めた。

nyangryy.hatenablog.com

このときの自分のtmux.confdotfiles/.tmux.conf at 42f0b71a673aad45921912766acc963948d0027b · sugilog/dotfiles · GitHubこんな感じ。

ブログにまとめられているとおり、-t vi-copy-T copy-mode-viにしてあれこれして、使い始めた。

dotfiles/.tmux.conf at 33c847acf64889f012859e234576fc85d8e1d36c · sugilog/dotfiles · GitHub

で、本題

copy-modeのときに、スクロールアップがうまくできない。正確には、

  • copy-modeでのj, kでのカーソル移動でのスクロールはできる。
  • copy-modeでのCtrl+fでのスクロールダウンはできる。
  • copy-modeでのCtrl+bでのスクロールアップができない。

バインディングはvi)。

ググっても情報が見つからないのでソースにあたる。。。と見つけたコミット。

github.com

Ctrl+bはコンフリクトがあるから、初期設定するのやめよう、とのこと。なるほどね。

ちなみに自分は関係ない人なので、自分で追加しちゃいます。

github.com

ちなみに自分のtmuxで使うprefixキーはCtrl+tC-t)です。

解決!

お粗末さまでした。

git logをltsvにする

コミットの情報をもとにファイルチェックとかしてみよう、という感じからgitだとどうしようということでログから情報を取り出す。

subversionで、svn ls -v <URL>してとれるような情報のイメージ。

ついでにプログラムから加工しやすいようにLTSVにする。

git log -n 1 --format="hash:%H%x09author_date:%at" <file>

分解して理解

git log : 省略

-n 1 : とりあえず今回は、指定したファイルに対する最新のコミットを取得できればいいので、1つだけ。

--format : ログのフォーマットを指定する。続く引数内で、%なんとかみたいにすると情報を埋め込めたりする。くわしくは、git help log内のPRETTY FORMATSを参照。

hash: : ここまでは単なる文字列。LTSVのキー。

%H : コミットハッシュ。 %hにすると省略形(7桁くらいになってるやつ)のコミットハッシュ。

%x09 : %xはあとに続く16進数のコードに対応した文字を埋め込む。09はタブ。故にLTSVの区切り文字。

author_date: : ここも単なる文字列。LTSVのキー。

%at : author dateをunix timestampで表示する。rubyからなら、Time.atを使えばTimeオブジェクトに変換できる。comitter dateを使うかどうかみたいな話はここの主題ではないので省略。

使用上の前提条件というか、現在のこの記事の状況。

  • リモートリポジトリの情報をfetchしたりするのは別途必要ということで。
  • コミットメッセージ内にタブが含まれてたらどうしようとか思って、まだLTSV化するログの中にコミットメッセージを入れることは試していない。

Dockerを使って、AWS Lambda用のPhantomJSとfontconfigをビルドする

dockerを使ってみる。 やりたいことはこれ

AWS LambdaでPhantomJSを使ってあれこれしたいけど、日本語フォントを扱えるようにしないと、キャプチャがうまくできない。 日本語フォントを扱える夜にするのはリクルートさんの記事のとおりにできるけど、じゃあそのためのfontconfigのビルドは?

さらに、npmのphantomjs(現在はphantomjs-prebuilt)macの環境でインストールしてしまうと、lambdaの環境では動かない。

ということで、Linux環境であれこれして、Lambda用のパッケージのビルドする。

そのための簡易環境としてdockerを使ってみようという魂胆。

fontconfigやらPhantomJS自体のビルドの時間はそこそこかかるけど、それを待ちさえすれば、がっつり自動化できた。

dockerの設定

Dockerfile

Linux環境にはCentosを使いたいのでDocker HubのCentosのページを参考にする。

# https://hub.docker.com/_/centos/
FROM       centos:7
MAINTAINER "YOU" <you@example>
ENV        container docker
RUN        (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \
           rm -f /lib/systemd/system/multi-user.target.wants/*;\
           rm -f /etc/systemd/system/*.wants/*;\
           rm -f /lib/systemd/system/local-fs.target.wants/*; \
           rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
           rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
           rm -f /lib/systemd/system/basic.target.wants/*; \
           rm -f /lib/systemd/system/anaconda.target.wants/*; \

           yum install -y openssh-server openssh-clients; \
           yum install -y iproute iproute-devel; \
           yum -y install epel-release; \
           yum -y install gperf freetype-devel libxml2-devel python-lxml git-all libtool; \
           yum -y install npm bzip2; \

           passwd root -d; \
           sed -i "s/^.*PermitEmptyPasswords.*$/PermitEmptyPasswords yes/g" /etc/ssh/sshd_config; \
           sed -i "s/^.*PasswordAuthentication.*$/PasswordAuthentication yes/g" /etc/ssh/sshd_config; \

           mkdir -p /src/python; mkdir -p /src/node; \

           cd /src/python; \
           curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py; \
           python get-pip.py; \
           pip install -U six; \

           cd /src/; \
           git clone http://anongit.freedesktop.org/git/fontconfig; cd /src/fontconfig; \
           ./autogen.sh --prefix=/tmp/fontconfig --sysconfdir=/tmp/fontconfig/etc --bindir=/tmp/fontconfig/usr/bin --enable-libxml2; \
           make; make install; \

           cd /src/node; \
           npm install phantomjs-prebuilt; \
           mv /src/node/node_modules/phantomjs-prebuilt /tmp/phantomjs-prebuilt;

VOLUME     [ "/run", "/tmp" ]
CMD        [ "/usr/sbin/init" ]

RUNの部分の話を自分が忘れないようにするレベルでまとめておく。

  • 前半のrmしまくってるのは、systemdまわりの対応。Docker HubのCentosのページを参照。
  • yumでインストールしているのは2種類。
    • opensshはあとでscpでファイルを取り出したいので、sshdがほしいからくらい。clientはいらないけどついで。
    • iprouteは、困ったときのipコマンドのため。デバッグ用途なので、実際には使わない。
    • 残りはfontconfigとPhantomJSをビルドするために必要なもの(いらないもの混じってるかもだけど精査してない)。
  • passwdでパスワード消したり、sshd_configでセキュリティ弱めているのは、ファイルを取り出すときにパスワードとかいろいろ聞かれたくないから。
    • 本番環境では「ダメ、絶対」な設定だけど、今回はこれで。
  • pipsixは、fontconfigのautogenのタイミングで必要なので入れておく。
  • あとは、fontconfigとPhantomJSをビルド。
  • ファイルを取り出しやすいように、それぞれtmpディレクトリに移動させてる。

デバッグ

簡易的に接続してみるなら、attachは使わずに、ssh感覚でexec+bash-it(= interactive, tty)オプションをつけて実行。 本来の用途ではないらしいので、あくまで簡易的に。

docker exec -it {container_id_or_name} /bin/bash

実行

以下の様なシェルスクリプトを作って実行してみた。

docker pull centos:7
docker build --rm -t local/centos7 .
docker create --name phantom --privileged -p 20022:22 local/centos7
docker start phantom

sleep 2

ssh-keygen -R "[localhost]:20022";
scp -o 'StrictHostKeyChecking no' -r -P 20022 root@localhost:/tmp/fontconfig fontconfig
scp -o 'StrictHostKeyChecking no' -r -P 20022 root@localhost:/tmp/phantomjs-prebuilt phantomjs-prebuilt

docker rm -f phantom
docker rmi local/centos7

ポイントといえば、、、

  • privilegedをつけてcreateしないとうまく動かない。systemdまわりの話(+SELinuxが絡んでたっけな?)
  • start後すぐにscpしてもうまく接続できない。1秒か2秒くらいあける。
  • scpで認証周りのことをきかれたくないから、ssh-keygen -Rknown-hostsをクリアして、StrictHostKeyCheckingをしないようにする。

AWS API GatewayのBody Mapping Templateで使えるテンプレート

AWSAPI Gatewayでは、リクエストをどう受けて、どう返すか、ということが設定できる。中間処理にLambdaを使う場合は、リクエストの情報をJSONオブジェクトに変換しておいて貰わないと、情報が来ない。

ということで、Body Mapping Template(後処理に渡すデータを生成するためのテンプレート)を使うわけだけど、以前には多分なかったテンプレートが追加されていたので試している。

Method Request Passthrough

2016/05/11時点では以下のような形をしている。cognitoの情報も追加できるようになっているので、おそらく今後も変化していくのだろうと想定。

##  See http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
##  This template will pass through all parameters including path, querystring, header, stage variables, and context through to the integration endpoint via the body/payload
#set($allParams = $input.params())
{
"body-json" : "$input.json('$')",
"params" : {
#foreach($type in $allParams.keySet())
    #set($params = $allParams.get($type))
"$type" : {
    #foreach($paramName in $params.keySet())
    "$paramName" : "$util.escapeJavaScript($params.get($paramName))"
        #if($foreach.hasNext),#end
    #end
}
    #if($foreach.hasNext),#end
#end
},
"stage-variables" : {
#foreach($key in $stageVariables.keySet())
"$key" : "$util.escapeJavaScript($stageVariables.get($key))"
    #if($foreach.hasNext),#end
#end
},
"context" : {
    "account-id" : "$context.identity.accountId",
    "api-id" : "$context.apiId",
    "api-key" : "$context.identity.apiKey",
    "authorizer-principal-id" : "$context.authorizer.principalId",
    "caller" : "$context.identity.caller",
    "cognito-authentication-provider" : "$context.identity.cognitoAuthenticationProvider",
    "cognito-authentication-type" : "$context.identity.cognitoAuthenticationType",
    "cognito-identity-id" : "$context.identity.cognitoIdentityId",
    "cognito-identity-pool-id" : "$context.identity.cognitoIdentityPoolId",
    "http-method" : "$context.httpMethod",
    "stage" : "$context.stage",
    "source-ip" : "$context.identity.sourceIp",
    "user" : "$context.identity.user",
    "user-agent" : "$context.identity.userAgent",
    "user-arn" : "$context.identity.userArn",
    "request-id" : "$context.requestId",
    "resource-id" : "$context.resourceId",
    "resource-path" : "$context.resourcePath"
    }
}

ざっくりと展開すると、以下の様な感じになる。だいたい自分でわかった範囲で説明もつけておく。雰囲気なので間違ってたらごめんなさい。

{ "body-json": [object], "params": { "path": [Empty Object], "querystring": [object], "header": [object], }, "stage-variables": [object], "context": [object] }

Key Description Type
body-json BodyをJSON.parseしたもの。 object, array, ...
params リクエスト時のパラメーター情報。 object
params.path (おそらく)基本的に空のオブジェクト。 {}
params.querystring URLクエリを処理したオブジェクト。配列クエリ、ネストされたクエリには対応されてなさそう。 object
params.header ヘッダ情報。 Host, Origin, User-Agent, などなど。 object
stage-variables API Gateway で設定したstageの情報。 object
context API Gatewayでリクエストを受けた時の状態。 object
context.http-method リクエストメソッド。GET, POST, など。 string
context.stage API Gateway でデプロイしているステージのラベル。 string
context.source-ip リクエストしてきたIPアドレス string
context.user-agent ユーザーエージェント文字列。 string
context.resource-path URLパスの文字列。基本的にAPI Gatewayのリソースに一致するはず。 string

バグ?

テンプレートにはバグっぽい挙動が含まれていて、POST時のリクエストBodyのJSONで文字列を含んでいるとエラーする。ダブルクオーテーションで囲ってしまっているため。ダブルクオーテーションを外してしまったほうが多分良いと思われる(思われるけど落とし穴があるかも)。

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

-"body-json" : "$input.json('$')",
+"body-json" : $input.json('$'),

ちなみにエラーすると、Lambdaは起動されない。Lambdaのログを監視していてもわからない問題になるので、検証は自己責任ということで。