Monday, February 1, 2016

MacにMackerelをインストールして負荷を可視化する (OS X El Capitan‎)

サーバのリソース可視化サービスのひとつであるMackerelを使って、Macの負荷をグラフで見れるようにします。エージェントプログラムをMacにインストールして、データを送信するところまでやってみます。
上のグラフはCPUの使用状況をMackerelで見れるようにしたときのキャプチャです。他にも、情報が色々取れます。これらは、Macのアクティビティモニタでも取れる情報ではありますが、Mackerelだと無料枠で、1日分の情報を保存しておけるので、より大きな範囲の情報が見ることができます。たとえば、午前と午後でのPCの使いかたの違いなど、面白い発見があるかもしれません。

以下、Mackerelのエージェントソフト(mackerel-agent)のインストール方法です。
OSは、OS X El Capitanです。
brewをあらかじめ使えるようにしてある前提です。

brewでmackerel-agentをインストール
$ brew tap mackerelio/mackerel-agent
==> Tapping mackerelio/mackerel-agent
Cloning into '/usr/local/Library/Taps/mackerelio/homebrew-mackerel-agent'...
remote: Counting objects: 8, done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 8 (delta 1), reused 4 (delta 0), pack-reused 0
Unpacking objects: 100% (8/8), done.
Checking connectivity... done.
Tapped 2 formulae (34 files, 140K)
$ sudo brew install mackerel-agent
==> Installing mackerel-agent from mackerelio/homebrew-mackerel-agent
==> Downloading https://github.com/mackerelio/mackerel-agent/releases/download/v0.27.1/mackerel-agent_darwin_amd64.zip
==> Downloading from https://github-cloud.s3.amazonaws.com/releases/19845168/6034e660-b61d-11e5-8055-d1fd7f0c32d6.zip?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAISTNZFOV
######################################################################## 100.0%
==> Caveats
You must append `apikey = {apikey}` configuration variable to /usr/local/etc/mackerel-agent.conf
in order for mackerel-agent to work.

To have launchd start mackerelio/mackerel-agent/mackerel-agent at login:
  ln -sfv /usr/local/opt/mackerel-agent/*.plist ~/Library/LaunchAgents
Then to load mackerelio/mackerel-agent/mackerel-agent now:
  launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mackerel-agent.plist
Or, if you don't want/need launchctl, you can just run:
  mackerel-agent -conf /usr/local/etc/mackerel-agent.conf
==> Summary
  /usr/local/Cellar/mackerel-agent/0.27.1: 4 files, 8.1M, built in 6 seconds
※ 以下のエラーが出るときは、Xcodeのライセンスに同意する必要があります。
You have not agreed to the Xcode license agreements, please run 'xcodebuild -license' (for user-level acceptance) or 'sudo xcodebuild -license' (for system-wide acceptance) from within a Terminal window to review and agree to the Xcode license agreements.
Error: Failure while executing: /usr/bin/otool -L /usr/bin/install_name_tool
表示に従ってコマンドを打ちます。
$ xcodebuild -license
ライセンスが表示されます。[q]で抜けます。
By typing 'agree' you are agreeing to the terms of the software license agreements. Type 'print' to print them or anything else to cancel, [agree, print, cancel] agree
ライセンスに同意するか聞かれるので [agree] と入力してEnter。
これでもう一度、mackerel-agentをインストールしてみてください。

設定ファイルを編集
MackerelのAPIキーをコピーして、apikeyを入力します。
$ sudo emacs /usr/local/etc/mackerel-agent.conf
apikey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

deamon化して自動で起動
launchctlの設定ファイル(plistファイル)が添付されているので、これを~/Library/LaunchAgentsディレクトリに配置。
launchctl loadでサービスを開始します。
$ sudo ln -sfv /usr/local/opt/mackerel-agent/*.plist ~/Library/LaunchAgents
$ sudo launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mackerel-agent.plist
プロセスを確認します。
$ sudo launchctl list | grep mackerel
8563 0 homebrew.mxcl.mackerel-agent
エラーログを確認します。
$ tail -f /usr/local/var/log/mackerel-agent.log
※以下の様なエラーが出ているときは、データの送信に失敗しています。設定ファイルのapikeyなどを見なおしましょう。
2016/01/29 11:20:15 INFO 
Starting mackerel-agent version:0.27.1, rev:06d85e6, apibase:https://mackerel.io 2016/01/29 11:20:15 WARNING API error. status: 401, msg: status code is not 200 2016/01/29 11:20:15 CRITICAL
Failed to prepare host: Failed to find this host on mackerel (You may want to delete file "Library/mackerel-agent/id" to register this host to an another organization): API error. status: 401, msg: status code is not 200
ストップするには以下のコマンドを打ちます。
$ sudo launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.mackerel-agent.plist
$ sudo launchctl stop homebrew.mxcl.mackerel-agent
しばらくすると、Mackerelのコンソール上に情報が表示されてきます。
うまく表示されたでしょうか?

まとめ
MacにMackerelエージェントをインストールして、Mackerelでリソース情報を見れるようにしました。brewを使ってインストールできるので、Mackerelエージェント起動までの手順が少なくてよかったです。
MackerelのAPIの仕様がわかりやすいので、たとえば、室温や湿度センサーの情報をMacに入力して、Mackerelで見たりしたら面白そうだと思いました。

See Also
mackerel-agentをMacに簡単にインストール可能になりました、ホスト数の表示の改善しました・ほか - Mackerel ブログ #mackerelio
http://blog-ja.mackerel.io/entry/2015/02/27/141105
A launchd Tutorial
http://launchd.info/
Undocumented Mac OS X:第1回 initを置き換えるlaunchd【前編】 (1/3) - ITmedia エンタープライズ
http://www.itmedia.co.jp/enterprise/articles/0704/26/news009.html

Tuesday, January 19, 2016

SPF + DKIMを設定して、Postfixでメールを送信(CentOS)

Postfixを使ってメール送信できるように設定をする手順です。
素のPostfixでもメール送信はできますが、SPFやDKIMといったメールの信頼性を高める認証技術を利用してメールを送信できるようにします。

実は、Amazon SESにSPF + DKIMを設定してメール送信をしようとしていたのですが、Yahooメールにテストメールを送信したところ、迷惑メールに振り分けられていたりしてAmazon SESの使用を断念した経緯がありました。メールヘッダを見ると一部のAmazon SESメールサーバのIPアドレスがYahooのフィルタでブロックされている記述がありました。これでは、使いものにならないので、EC2上にMTAを構築してメール送信をすることにしました。[AWSイスターシリーズ] Amazon Simple Email Service の選択肢 1 です。

以下の例では、EC2にMTAとしてPostfixを利用してメール送信するにはどうしらよいか?調べたメモになります。
なお、メール受信については取り扱っていません。

環境は EC2 CentOS 6 (x86_64) - with Updates HVM です。
IPアドレスは 192.0.1.1、ドメインは example.com としています
ドメインの設定は AWS Route53 を使用しています。別のDNSサービスを使っているときは、サービスにフォーマットを合わせてください。
以下、設定手順です。

Postfix

IPV4のみを使うようにします。
# emacs /etc/postfix/main.cf
inet_protocols = ipv4

SPFレコード

AWS Route53 を利用してSPFレコードを設定します。
Create Record Setから2つのレコードを登録します。
Name: 入力なし
Type: TXT
Value: "v=spf1 +ip4:192.0.1.1 -all"
Name: 入力なし
Type: SPF
Value: "v=spf1 +ip4:192.0.1.1 -all"
digコマンドで設定を確認。
$ dig example.com txt
example.com. 48 IN TXT "v=spf1 +ip4:192.0.1.1 -all"

※ 既にSPFレコードを登録している場合。
1つのドメインに対して複数行のspf1レコードは公開できません。そのばあいは、以下のフォーマットで1行にまとめて登録します。
"v=spf1 ip4:192.0.1.1 ip4:192.0.2.2 include:_spf.google.com ~all"
※ 既に別のTXTレコードがある場合。
複数行併記すれば良いです。
"google-site-verification=xxxxxxxxxxxxxx"
"v=spf1 ip4:192.0.1.1 ip4:192.0.2.2 include:_spf.google.com ~all"

DKIM レコード

SELINUXを無効にする。
# getenforce
Enforcing
# setenforce 0
Permissive
# emacs /etc/selinux/config
SELINUX=enforcing
->
SELINUX=disabled

OpenDKIMをインストール。
# rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
# yum -y install opendkim
鍵ファイルの作成先ディレクトリを用意して、鍵を作成。 -s で指定するセレクタは自由に設定できますが、ここでは、dkimselector としました。
# mkdir /etc/opendkim/keys/example.com
# opendkim-genkey -D /etc/opendkim/keys/example.com -d example.com -s dkimselector
# chown opendkim:opendkim /etc/opendkim/keys/example.com/*
鍵の中を確認します。
# cat /etc/opendkim/keys/example.com/dkimselector.txt
dkimselector._domainkey IN TXT ( "v=DKIM1; k=rsa; "
   "p=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" )  ; ----- DKIM key dkimselector for example.com
Route 53に2つのレコードを登録します。 公開鍵レコードを登録。
Name: dkimselector._domainkey
Type: TXT
Value: "v=DKIM1; k=rsa; p=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
ADSPレコードを登録。
_adsp._domainkey.example.com.  IN TXT "dkim=unknown"
登録したレコードが正しく引けるかを確認します。
# dig dkimselector._domainkey.example.com txt
# dig _adsp._domainkey.example.com txt 
DNSの設定はこれで完了です。

OpenDKIMの設定をしていきます。設定ファイルを編集。
# cp /etc/opendkim.conf /etc/opendkim.conf.org
# emacs /etc/opendkim.conf
---
Mode    v
->
Mode    sv

KeyFile /etc/opendkim/keys/default.private
->
# KeyFile /etc/opendkim/keys/default.private

# KeyTable      /etc/opendkim/KeyTable
->
KeyTable        refile:/etc/opendkim/KeyTable

# SigningTable  refile:/etc/opendkim/SigningTable
->
SigningTable   refile:/etc/opendkim/SigningTable

# ExternalIgnoreList    refile:/etc/opendkim/TrustedHosts
->
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts

# InternalHosts refile:/etc/opendkim/TrustedHosts
->
InternalHosts  refile:/etc/opendkim/TrustedHosts
DKIM署名に使う秘密鍵を指定。
# emacs  /etc/opendkim/KeyTable
dkimselector._domainkey.example.com example.com:dkimselector:/etc/opendkim/keys/example.com/dkimselector.private
DKIM署名を行うドメイン名を指定。
# emacs  /etc/opendkim/SigningTable
*@example.com  dkimselector._domainkey.example.com
メール送信を行うPostfixサーバのIPアドレスを指定。デフォルトで127.0.0.1が記述してあるので、このままで良いです。
# emacs  /etc/opendkim/TrustedHosts
127.0.0.1
OpenDKIMを起動。
# service opendkim start
OpenDKIMの自動起動を設定しておきます。
# chkconfig opendkim on
PostfixからOpenDKIMを使えるように設定します。最終行に追加。
# emacs /etc/postfix/main.cf
smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = $smtpd_milters
milter_default_action = accept
Postfixをリロードします。
# service postfix reload
DKIM認証の設定が完了しました。

動作確認

Gmailなどへメールを送信してテストしてみます。
メールコマンドをインストールして、メール送信。
送り主を info@example.com、あて先を my-email-address@gmail.com としました。自身のメールアドレスに書き換えてください。
# yum -y install mailx
# echo "Test Mail." | mail -s "test mail" -r info@example.com my-email-address@gmail.com

メール送信ログ
DKIMフィールドが追加されている。
# tail -f /var/log/maillog
Jan 19 08:37:25 ip-192-0-1-1 opendkim[6565]: F182261A0A: DKIM-Signature field added (s=dkimselector, d=example.com)

メールヘッダ
下記のような表示があればOKです。
ヘッダーの Authentication-Results にGoogleが許可した認証を spf=pass, dkim=passのように追記しています。
Received-SPF: pass (google.com: domain of info@example.com designates 192.0.1.1 as permitted sender) client-ip=192.0.1.1;
Authentication-Results: mx.google.com;
       spf=pass (google.com: domain of info@example.com designates 192.0.1.1 as permitted sender) smtp.mailfrom=info@example.com;
       dkim=pass header.i=@example.com
DKIM-Filter: OpenDKIM Filter v2.10.3 ip-192-0-1-1CAD5961A13
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com;
 s=dkimselector; t=1453192772;
 bh=XXXXXX=;
 h=Date:From:To:Subject:From;
 b=XXXX/XXXX
  XXXX
  XXXX=

まとめ

SPFとDKIMを設定してメール送信できるようにしました。
これで、認証付きのメール送信は出来るようになりましたが、携帯キャリアなどに送信テストしていません。用途によって、ちゃんとメール送信・受信テストする必要があります。
他にも、メールサービスによっては、フィルタがきつくて受信できないところもあるかもしれません。また、大量メール配信はできないでしょう。
クリティカルな用途でなかったら、これで十分かもしれませんが。用途によっては、メールの到達率、可用性く、大量のワークロードを処理できる、などといった指標をクリアする商用のMTAを使うことをおすすめしたいです。

See Also.
間違いから学ぶSPFレコードの正しい書き方 : 迷惑メール対策委員会
http://salt.iajapan.org/wpmu/anti_spam/admin/operation/information/spf_i01/
複数の SPF レコード - Google Apps 管理者 ヘルプ
https://support.google.com/a/answer/4568483
Postfix DKIM 認証 設定メモ(CentOS6.5+OpenDKIM) | あぱーブログ
https://blog.apar.jp/linux/856/
SE奮闘記: PostfixにDKIMを設定して送信メールサーバーの信頼度を上げる
http://se-suganuma.blogspot.jp/2015/01/postfixdkim.html
Top photo from Bill Ward Mail Trucks

Monday, December 21, 2015

Let's EncryptのSSL証明書を導入して、A+評価にする

無料でSSL証明書を自動発行するサービス Let's Encrypt を利用して、nginxにSSL証明書を組み込む手順を紹介します。
通常、Let's Encryptの証明書を発行するには、Pythonやモジュールをインストールして、Lets's Encrypt Clientを使えるようにする必要がありますが、これらの環境構築を簡素化するためにDockerを利用します。
以下導入手順です。OSはCentOS 6を使用しています。

Dockerをインストール
# yum install docker-io
# service docker start

Let's Encryptで証明書を発行
コマンドを実行すると、--webroot-path で指定したパス以下に、.well-known/acme-challenge/* ディレクトリを作って認証ファイルが設置されます。
Let's Encryptの認証クローラが、このファイルにアクセスして認証処理をします。
Dockerコンテナから、外部ファイルへアクセスする必要があるので、 -v オプションに --webroot-path と同じパスを指定しておきます。

以下の例では、--webroot-path に /var/www/historia を指定。
同じく、-v "/var/www/historia":"/var/www/historia"を指定。
-d にドメイン historia.hitokoto.co を指定。
-m にメールアドレスを指定します。
# docker run -it --rm --name letsencrypt \
            -v "/etc/letsencrypt:/etc/letsencrypt" \
            -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
            -v "/var/www/historia":"/var/www/historia" \
            quay.io/letsencrypt/letsencrypt:latest certonly \
            --agree-tos \
            --webroot \
            --webroot-path /var/www/historia \
            -d historia.hitokoto.co \
            -m yako.takeshi@googlemail.com
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/historia.hitokoto.co/fullchain.pem. Your cert
   will expire on 2016-03-20. To obtain a new version of the
   certificate in the future, simply run Let's Encrypt again.
 - If you like Let's Encrypt, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le
証明書ができた旨が示されます。
この例では、/etc/letsencrypt/live/historia.hitokoto.co/ 以下にファイルができています。 確認してみましょう。
# ls -al /etc/letsencrypt/live/historia.hitokoto.co
total 8
drwxr-xr-x 2 root root 4096 Dec 21 13:48 .
drwx------ 3 root root 4096 Dec 21 13:48 ..
lrwxrwxrwx 1 root root   44 Dec 21 13:48 cert.pem -> ../../archive/historia.hitokoto.co/cert1.pem
lrwxrwxrwx 1 root root   45 Dec 21 13:48 chain.pem -> ../../archive/historia.hitokoto.co/chain1.pem
lrwxrwxrwx 1 root root   49 Dec 21 13:48 fullchain.pem -> ../../archive/historia.hitokoto.co/fullchain1.pem
lrwxrwxrwx 1 root root   47 Dec 21 13:48 privkey.pem -> ../../archive/historia.hitokoto.co/privkey1.pem

OpenSSLのサーバコマンドで証明書をチェック
s_server を起動。オプションで証明書を指定します。さらに、-wwwオプションを付けて HTTPS サーバをエミュレートします。
# cd /etc/letsencrypt/live/historia.hitokoto.co
# openssl s_server -cert cert.pem -key privkey.pem -CAfile chain.pem -www
Using default temp DH parameters
Using default temp ECDH parameters
opensslのs_serverがポート 4433 番で接続待ちをしている状態になりました。

クライアントを起動。
$ openssl s_client
CONNECTED(00000003)
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X1
verify return:1
depth=0 CN = historia.hitokoto.co
verify return:1
---
Certificate chain
 0 s:/CN=historia.hitokoto.co
   i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X1
 1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X1
   i:/O=Digital Signature Trust Co./CN=DST Root CA X3
---
Server certificate
以下略
ハンドシェイクの様子が流れて、入力待ちとなります。

Curlで確認してみます。
# curl https://historia.hitokoto.co:4433/ -H 'Host: historia.hitokoto.co' -v
* About to connect() to historia.hitokoto.co port 4433 (#0)
*   Trying 153.126.157.62... connected
* Connected to historia.hitokoto.co (153.126.157.62) port 4433 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: /etc/pki/tls/certs/ca-bundle.crt
  CApath: none
* SSL connection using TLS_DHE_RSA_WITH_AES_256_CBC_SHA
* Server certificate:
*  subject: CN=historia.hitokoto.co
*  start date: Dec 21 03:49:00 2015 GMT
*  expire date: Mar 20 03:49:00 2016 GMT
*  common name: historia.hitokoto.co
*  issuer: CN=Let's Encrypt Authority X1,O=Let's Encrypt,C=US
> GET / HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Accept: */*
> Host: historia.hitokoto.co
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 ok
< Content-type: text/html
<
以下略
SSLで通信ができていることが確認できました。

nginxにLet's Encryptの証明書を組み込む
DH鍵交換に使用するパラメータファイルを作成。
# openssl dhparam 2048 -out dhparam.pem
nginx設定ファイルを編集。serverディレクティブを以下のようにしました。
nginxをリスタート。
# service nginx restart
Stopping nginx:                                            [  OK  ]
Starting nginx:

Qualys SSL LabsでSSLの評価を試してみます。
https://www.ssllabs.com/ssltest/index.html

A+となりました。

SSL証明書を更新
証明書を発行する時とほぼ同じコマンドです。--renew-by-defaultオプションを付けています。
# docker run -it --rm --name letsencrypt \
            -v "/etc/letsencrypt:/etc/letsencrypt" \
            -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
            -v "/var/www/historia":"/var/www/historia" \
            quay.io/letsencrypt/letsencrypt:latest certonly \
            --webroot \
            --webroot-path /var/www/historia \
            -d historia.hitokoto.co \
            --renew-by-default
※ 現時点(Entering Public Beta)では、証明書の期限が90日と短いです。そのため、cronなどでSSL証明書を自動更新するようにしておくと良いでしょう。

See Also.
Title image from Let's Encrypt - Overview
User Guide — Let's Encrypt 0.2.0.dev0 documentation
https://letsencrypt.readthedocs.org/en/latest/using.html#running-with-docker
Let's Encrypt を支える ACME プロトコル - Block Rockin’ Codes
http://jxck.hatenablog.com/entry/letsencrypt-acme
Linux - Let's Encrypt で証明書を取得した時の手順備忘録 - Qiita
http://qiita.com/TsutomuNakamura/items/4166423699061e38d296
letsencrypt - DockerでLet’s Encryptしよっか - Qiita
http://qiita.com/sawanoboly/items/9fdde1707de5e975dd15
Let's EncryptのSSL証明書で、Qualys SSLTestでA+評価を獲得するには - Qiita
http://qiita.com/dseg/items/bab80f6f14349fcd9c22
Dockerを導入する(CentOS版) - Qiita
http://qiita.com/zwirky/items/991f61a231f4e198a320