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.jp
のSSL/TLS証明書を発行する。ために、admin@app.example.jp
を受信できるようにする。
SESはこの記事を書いている時点(2016/11/08)では、まだ日本に提供されていないっぽいので、リージョンは適当に選んで使う。
事前準備:Route 53 の設定
もしDNSを設定していない状態であれば、Route 53にexample.jp
を登録する。
とりあえずHosted Zoneにexample.jp
を登録するだけでOK。
(ということで、Route 53を使う前提で書きます。)
ドメインの登録
- 左ペインのDomainsを選ぶ。
- Verify a New Domainボタンを押す。
- Domainを入力する。
- 今回のケースで登録するドメインは、
app.example.jp
- 今回のケースで登録するドメインは、
- DNSの設定について書かれている画面に切り替わる。
- Use Route 53で先に進む。
- Domain Verification Record(TXTレコード) と Email Receiving Reocrd(MXレコード)どちらも登録したいので、両方チェックを入れて、Create Record Sets。
- 少し待つと、ドメインのステータスが
verified
に変わる。
受信設定
- 左ペインのRule Setsを選ぶ。(Email Receivingの下)
- View Active Rule Setボタンを押す。
- Create Ruleボタンを押す。
- Recepientは空のままNext Step。(今回の場合は。本当は絞ったほうが良いはず。)
- Add actionで、S3を選ぶ。
- S3のバケットとかは適当に。新しく作るなり、
- prefixは、必要に応じて。
app.example.jp/
みたいにアカウント名を入れておくと便利そう。 - 今回の場合は暗号化は不要。(本当は暗号化したほうが良いはず。)
- Next Stepで先に進む。
- Rule nameは適当に。
- 他、デフォルトのままで、Next Step。
- Create Rule。
テスト
- 左ペインのDomainsを選ぶ。(戻る)
- 登録したドメイン(例:
app.example.jp
)を選ぶ。 - Send a Test Emailを押す。
- メールの内容を適当に入力して、送信。
- Toアドレスを
admin@app.example.jp
みたいにしておくと、Certificate Managerが送信先に指定するメールアドレスみたくなる。
- Toアドレスを
- S3をチェック。
- Ruleで設定したバケット、prefixの場所にメールがあるか?
- メールの内容が先ほど適当に入力したメールの内容に一致するか?
後は
Certificate Managerであれこれするだけ。
ELBの資料を読んだメモ
自分が業務で使いそうな範囲でメモに起こす。
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のヘルプを見ればいいわけですね。はい、いつも見ます。いつもたどり着くのに時間がかかりますが。
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、お世話になってます。
tmux 2.3のキーバインディングでちょっと困った
macでtmuxを使っていてエラーが出て解決してたら次の問題にハマって、ということがあったので一連の話をメモ。
タイトルにしたキーバインディングは結局ググってもよくわからずソースを見て解決した話。
流れ
macOS Sierra にバージョンアップ。そしたら変なWARNINGメッセージが出るようになった。
Qiitaのまとめどおり、tmuxをアンインストール&再インストールしてみた。
ちなみにこの作業をした時のHEADはDo not force symbols to width 1, from Yen Chi Hsuan. · tmux/tmux@178894b · GitHub。
この時点で、tmuxのバージョンが2.3に更新された。元のバージョンは忘れたので省略。
tmux2.3を起動したら、またもや変なエラーメッセージが出始めた。
このときの自分のtmux.conf
はdotfiles/.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)。
ググっても情報が見つからないのでソースにあたる。。。と見つけたコミット。
Ctrl+b
はコンフリクトがあるから、初期設定するのやめよう、とのこと。なるほどね。
ちなみに自分は関係ない人なので、自分で追加しちゃいます。
ちなみに自分のtmuxで使うprefix
キーはCtrl+t
(C-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種類。passwd
でパスワード消したり、sshd_config
でセキュリティ弱めているのは、ファイルを取り出すときにパスワードとかいろいろ聞かれたくないから。- 本番環境では「ダメ、絶対」な設定だけど、今回はこれで。
pip
、six
は、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 -R
でknown-hosts
をクリアして、StrictHostKeyChecking
をしないようにする。
AWS API GatewayのBody Mapping Templateで使えるテンプレート
AWSのAPI 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のログを監視していてもわからない問題になるので、検証は自己責任ということで。