mruby_nginx_module を使用して,Riak CS や Amazon S3 のオブジェクトをブラウザから取得する
Riak CS や Amazon S3 に置いたファイルを非公開にした状態で,Nginx を介した場合にのみ,アクセス元のユーザは Access Key ID も Secret Access Key も使用せずに,非公開設定のファイルを取得します.
環境準備
# git clone https://github.com/cubicdaiya/mruby_nginx_module.git # cd mruby_nginx_module # git submodule update --init # cd mruby # make # cd # git clone https://github.com/simpl/ngx_devel_kit.git # curl -O http://nginx.org/download/nginx-1.4.4.tar.gz # tar zxvf nginx-1.4.4.tar.gz # cd nginx-1.4.4 # ./configure --prefix=/opt/nginx/1.4.4 --add-module=../mruby_nginx_module --add-module=../ngx_devel_kit --with-http_ssl_module # make && make install
nginx.conf の設定
location / { if ( $request_method != GET ) { return 400; } set $aws_access_key_id "aws_access_key_id"; set $aws_secret_access_key "aws_secret_access_key"; set $bucket_name "bucket"; set $base_url "example.com"; mruby_set_code $date "Nginx::Time.http_time(Nginx::Time.time)"; mruby_set_code $authorization ' content_md5 = "" content_type = "" request = Nginx::Request.new var = Nginx::Var.new string_to_sign = request.method + "\n" + content_md5 + "\n" + content_type + "\n" + var.date + "\n" + "/" + var.bucket_name + request.uri hmac_sha1 = Nginx::Digest.hmac_sha1(string_to_sign, var.aws_secret_access_key) "AWS " + var.aws_access_key_id + ":" + Nginx::Base64.encode(hmac_sha1) '; proxy_set_header Host $bucket_name.$base_url; proxy_set_header Date $date; proxy_set_header Authorization $authorization; proxy_pass http://$bucket_name.$base_url:80$uri; }
elasticsearch + Kibana でログの可視化
動かしただけで,理解していないので,作業した内容だけをメモしておきます.
ab で Apache に負荷をかけて,その access.log のグラフを表示します.
elasticsearch の準備/起動
# aptitude -y install openjdk-7-jdk # curl -O https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-0.90.7.tar.gz # tar zxvf elasticsearch-0.90.7.tar.gz # cd elasticsearch-0.90.7 # ./bin/elasticsearch -f
Kibana3 の準備
# aptitude install apache2 # cd /var/www # curl -O https://download.elasticsearch.org/kibana/kibana/kibana-3.0.0milestone4.tar.gz # tar zxvf kibana-3.0.0milestone4.tar.gz # mv kibana-3.0.0milestone4 kibana
logstash を使用する場合
# mkdir logstash # cd logstash # curl -O https://download.elasticsearch.org/logstash/logstash/logstash-1.2.2-flatjar.jar # cat <<__EOT__ >logstash.conf > input { > file { > type => "apache" > path => "/var/log/apache2/access.log" > } > } > > filter { > grok { > type => "apache" > pattern => "%{COMBINEDAPACHELOG}" > } > } > > output { > elasticsearch { > embedded => true > } > } > __EOT__ # java -jar logstash-1.2.2-flatjar.jar agent -f logstash.conf -- web
fluentd を使用する場合
# gem install fluentd # gem install fluent-plugin-elasticsearch # cat <<__EOT__>fluent.conf > <source> > type tail > format apache > path /var/log/apache2/access.log > pos_file /var/log/apache2/access.pos > tag apache2-access > </source> > > <match apache2-access> > type elasticsearch > host 192.168.1.2 > port 9200 > logstash_format true > </match> > __EOT__ # fluentd -c fluent.conf
グラフの確認
下記のように ab で適当に負荷をかけつつ,ブラウザで http://192.168.1.2/kibana/index.html#/dashboard/file/logstash.json にアクセスして、グラフを確認します.
# ab -c 10 -n 1000000 http://192.168.1.2/index.html
Ansible のメモ
Ansible 使用時に手間取った点がいくつかあったので書いておきます.
ディストリビューション毎に処理を分ける
ディストリビューションによって,yum と apt-get 等を使い分けたい場合は,
centos.yml や ubuntu.yml 等を作成して,yum や apt-get 等の処理を書いておきます.
main.yml
--- - include: ubuntu.yml when: ansible_distribution == "Ubuntu"
ubuntu.yml
--- - shell: apt-get -y update - shell: apt-get -y install aptitude - shell: aptitude -y install {{ item }} with_items: - make - gcc - curl - git
環境変数を指定する
環境変数を指定したい場合には environment: を指定します.
- shell: make environment: PATH: /opt/local/bin:/usr/bin:/usr/local/bin
PATH の場合は,追加だけしたいので,次のように指定した方が良いかもしれません.
- shell: make environment: PATH: "/opt/local/bin:{{ ansible_env.PATH }}"
ansible_facts で取得できない対象サーバの情報を指定したい場合には,stdout の値を使用することもできます.
- shell: echo $PATH register: path - shell: echo $PATH environment: PATH: "/opt/local/bin:{{ path.stdout }}"
git clone
Ansible には git モジュールがあるため,簡単に git リポジトリを clone できますが,
GitHub 等から ssh で clone する場合にはいくつか注意点があります.
- ホストキーのチェックによる処理の停止
- 公開鍵認証に使用する鍵の管理
ホストキーのチェックによる処理の停止
仮想環境等で、必要になった場合にのみインスタンスを作成するような場合には,
~/.ssh/known_hosts に Git/GitHub のサーバにホスト鍵は追加されていないことがよくあります.
このような環境で,StrictHostKeyChecking のデフォルト値が ask の場合には,git clone を実行するとホスト鍵の確認が入るため,Git/GitHub のサーバへのアクセスで処理が止まります.
これを回避するために,.ssh/config や /etc/ssh/ssh_config に StrictHostKeyChecking no を設定しておきます.
git モジュール を使用する前に,Git/GitHub のサーバへのアクセスする際の設定に StrictHostKeyChecking no を記載した ssh_config を, copy モジュール でアップロードしておきます.
- copy: src=ssh_config dest=/etc/ssh/ssh_confg - git: repo=... dest=/home/user/...
公開鍵認証に使用する鍵の管理
自分以外の誰かがアクセスできるサーバ上には、秘密鍵は置きたくないです.
そのため,ssh でログインしてリポジトリを clone する場合には ForwardAgent をすることが多いと思います.
ansible を使用する場合でも,通常の ssh の場合と同じように ForwardAgent を使用して,サーバ上に秘密鍵を置かずに,リポジトリを取得します.(ansible.cfg の設定以外は ssh でログインする場合と同じような手順です)
1. ansible.cfg の準備
$ cat ansible.cfg [ssh_connection] ssh_args = -o ForwardAgent=yes
$ ssh-aget -t 3600 bash $ ssh-add ~/.ssh/id_rsa
3. ansible-playbook の実行
$ ansible-playbook -i hosts playbook.yml
Riak CS の SSL を有効にして AWS SDK for Ruby でファイルをアップロードする
手順
1. 証明書を作成する
2. bucket を決めて,DNS にドメインを登録する
3. Riak CS の SSL 機能を有効にして起動させる
4. curl を使用して,admin ユーザのアカウントを作成する
5. AWS SDK for Ruby をインストールする
6. ファイルをアップロードする
7. boto で確認してみる
1. 証明書を作成する
Amazon S3 と同様で,2 つの URL を使用します.
- example.com
- <bucketname>.example.com
そのため,サーバ証明書の X509v3 Subject Alternative Name に example.com と,<bucketname>.example.com を指定しておきます.
後で bucket を追加する場合を考えて,Amazon S3 のサーバ証明書と同様に * を使用して,下記のように指定しておくと便利です.
X509v3 Subject Alternative Name: DNS:*.example.com, DNS:example.com
2. bucket を決めて,DNS にドメインを登録する
HTTP の場合と異なり,SSL を使用する場合には,クライアントの HTTP プロキシの指定は使用できません.
HTTPS の場合,クライアントは,はじめに HTTP プロキシサーバに向かって CONNECT メソッドのリクエストを送り,トンネル接続の確立を要求するためです.
そのため,リクエストのホスト名の名前解決が必要になります.
例えば,bucket1 という bucket を使用する場合には,example.com と bucket1.example.com の 2 つを DNS サーバに登録しておきます.
簡易の動作確認のための unbound への設定例:
local-data: "example.com A 192.168.1.6" local-data: "bucket1.example.com A 192.168.1.6"
3. Riak CS の SSL 機能を有効にして起動させる
下記のドキュメントを参考にして,Riak CS を起動させます.
また,今回はドメインを example.com で設定しているため,/etc/riak/app.config の下記の箇所も変更します.
< {cs_root_host, "s3.amazonaws.com"}, --- > {cs_root_host, "example.com"},
4. curl を使用して,admin ユーザのアカウントを作成する
--cacert オプションに,サーバ証明書を検証する CA 証明書を指定します.
# curl --cacert ca.pem -H 'Content-Type: application/json' -X POST https://example.com:8080/riak-cs/user --data '{"email":"admin@example.com", "name":"admin user"}' {"email":"admin@example.com","display_name":"admin","name":"admin user","key_id":"BSNKJWA28Q9QYHRAQZVS","key_secret":"I3N_Nfh58X_Ypva4nb7_NtY-0p1RrEQabKKNtg==","id":"9062458e400b4faa5052dc8aae54dd5da58d38e6f958e555570804cc73af9588","status":"enabled"}
6. ファイルをアップロードする
ファイルアップロードの例です.
#!/usr/bin/env ruby # -*- coding: utf-8 -*- require 'aws-sdk' ACCESS_KEY_ID = 'BSNKJWA28Q9QYHRAQZVS' SECRET_ACCESS_KEY = 'I3N_Nfh58X_Ypva4nb7_NtY-0p1RrEQabKKNtg==' BUCKET_NAME = 'bucket1' FILE_NAME = 'foo.txt' s3 = AWS::S3.new( access_key_id: ACCESS_KEY_ID, secret_access_key: SECRET_ACCESS_KEY, s3_endpoint: 'example.com', use_ssl: true, s3_port: 8080, ssl_ca_file: '/tmp/ca.pem', ssl_verify_peer: true ) bucket = s3.buckets.create(BUCKET_NAME) object = bucket.objects[FILE_NAME] object.write(File.open(FILE_NAME, 'rb'))
Riak CS のログ(/var/log/riak-cs/access.log)を見てみると PUT できていることが分かります.
192.168.1.104 - - [16/Aug/2013:15:29:07 +0000] "PUT /buckets/bucket1 HTTP/1.1" 200 0 "" "aws-sdk-ruby/1.15.0 ruby/2.0.0 x86_64-darwin12.3.0" 192.168.1.104 - - [16/Aug/2013:15:29:07 +0000] "PUT /buckets/bucket1/objects/foo.txt HTTP/1.1" 200 0 "" "aws-sdk-ruby/1.15.0 ruby/2.0.0 x86_64-darwin12.3.0"
7. boto で確認してみる
AWS SDK for Ruby で確認してもいいのですが,他のツールで確認してみます.
.boto の設定:
[boto] ca_certificates_file = "/tmp/ca.pem" https_validate_certificates = True
#!/usr/bin/env python from boto.s3.connection import S3Connection conn = S3Connection( aws_access_key_id = "BSNKJWA28Q9QYHRAQZVS", aws_secret_access_key = "I3N_Nfh58X_Ypva4nb7_NtY-0p1RrEQabKKNtg==", is_secure = True, host = 'example.com', port = 8080) bucket_name = 'bucket1' bucket = conn.get_bucket(bucket_name) key = bucket.get_key("foo.txt") key.get_contents_to_filename("bar.txt")
期待通りに,アップロードした foo.txt の内容で bar.txt が作成されていました.
念のため,Riak CS のログも確認
192.168.1.104 - - [16/Aug/2013:15:29:29 +0000] "GET /buckets/bucket1/objects?max-keys=0 HTTP/1.1" 200 252 "" "Boto/2.10.0 (darwin)" 192.168.1.104 - - [16/Aug/2013:15:29:29 +0000] "HEAD /buckets/bucket1/objects/foo.txt HTTP/1.1" 200 14 "" "Boto/2.10.0 (darwin)" 192.168.1.104 - - [16/Aug/2013:15:29:29 +0000] "GET /buckets/bucket1/objects/foo.txt HTTP/1.1" 200 14 "" "Boto/2.10.0 (darwin)"
SPDY パケット読むまでのメモ
最近は SPDY プロトコルが流行っているようなので,"SPDYと「やったー、net-http-spdyできたよー」の話 - I am Cruby!" の記事の net-http-spdy を動かして,パケットを取得しようとしてみたら,手間取った点がいくつかありましたので,設定した内容等を適当に書いておきます.
環境
- Web サーバ
- クライアント
- Ruby 2.0.0-rc1
- net-http-spdy (authorNari/net-http-spdy · GitHub)
- Ruby 2.0.0-rc1
実行
example を参考にして,上記環境のサーバにアクセスしてみる
とりあえず,下記のようにしてみました.
require 'net/http/spdy' fetch_threads = [] Net::HTTP::SPDY.start(uri.host, uri.port, use_ssl: true) do |http| 10.times do uri = '/index.html' req = Net::HTTP::Get.new(uri) fetch_threads << Thread.start do http.request(req) end end fetch_threads.each(&:join) end
実行すると下記のエラーになりました.
/opt/ruby/2.0.0-rc1/lib/ruby/2.0.0/net/http.rb:917:in `connect': SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed (OpenSSL::SSL::SSLError)
Wireshark でも見てみます.
Alert (Level: Fatal, Description: Unknown CA)
ルート CA 証明書を指定していないので当然ですね.
ca_file オプションを指定してみる
下記のように ca_file を追加します.
Net::HTTP::SPDY.start(uri.host, uri.port, use_ssl: true, ca_file: './ca.pem') do |http|
エラーにならずに,Application Data の送受信が繰り返され続けます.
$ ruby spdy.rb
ここで,Wireshark で Application Data を復号して,送受信されているパケットを確認しようとしてみましたが,Wireshark に秘密鍵を設定しても復号できませんでした.
そこで,Server Hello を見てみると,Cipher Suite は下記になっていました.
Cipher Suite: TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (0x009f)
DHE の場合は復号されないのかな?
ciphers オプションを指定してみる
とりあえず,Cipher Suite を変更してみます.
Cipher Suite は,クライアントが Client Hello で Cipher Suites をサーバに送って,
その中から 1 つを選んでサーバが Server Hello でクライアントに送るので,
クライアントから送る Cipher Suites に,使用したい Cipher Suite だけを指定するか,
または,サーバ側で,使用したい Cipher Suites だけを返すように設定します(mod_ssl の場合は SSLCipherSuite ディレクティブを設定します).
SSLCipherSuite ディレクティブについては,下記を参照してください.
今回はクライアントからの Cihper Suite をしてしてみます.
下記のように ciphers を追加します.
Net::HTTP::SPDY.start(uri.host, uri.port, use_ssl: true, ca_file: './ca.pem', ciphers: 'AES256-SHA') do |http|
Wireshark で確認してみます.
Client Hello
Cipher Suites には下記の 2 つが含まれていました.
TLS_RSA_WITH_AES_256_CBC_SHA (0x0035) TLS_EMPTY_RENEGOTIATION_INFO_SCSV (0x00ff)
TLS_EMPTY_RENEGOTIATION_INFO_SCSV (0x00ff) は,SSL および TLS の renegotiation の脆弱性対応です.
renegotiation の脆弱性や TLS_EMPTY_RENEGOTIATION_INFO_SCSV の詳細については,下記を参照してください.
- RFC 5746 - Transport Layer Security (TLS) Renegotiation Indication Extension
- 情報処理推進機構:情報セキュリティ:調査・研究報告書:情報セキュリティ技術動向調査(2009 年下期)
Server Hello
Cipher Suite が TLS_RSA_WITH_AES_256_CBC_SHA (0x0035) であることが確認できます.
復号(ssl でフィルタ)
"Decrypted SSL data" のタブが増えていますので,spdy パケットを確認できるようになっています.
参考にしたページ
- SPDYと「やったー、net-http-spdyできたよー」の話 - I am Cruby!
- GettingStarted - mod-spdy - Getting started with mod_spdy. - Apache SPDY module - Google Project Hosting
- authorNari/net-http-spdy · GitHub
- mod_ssl - Apache HTTP Server
- RFC 5746 - Transport Layer Security (TLS) Renegotiation Indication Extension
- 情報処理推進機構:情報セキュリティ:調査・研究報告書:情報セキュリティ技術動向調査(2009 年下期)
LXC の導入
Ubuntu 12.04.1 LTS に LXC を導入したので、設定内容を書いておきます
Bridge の設定
## Bridge 用のインターフェースを追加 brctl addbr br0 ## bridge forward delay に 0 秒を設定する brctl setfd br0 0 ## 物理インターフェースの eth0 に Bridge のインターフェース br0 をバインドする brctl addif br0 eth0 ## eth0 をアクティブにする ifconfig eth0 0.0.0.0 up ## DHCP で br0 に IP アドレスを割り当てる dhclient br0
LXC
インストール
# aptitude install lxc
設定
/etc/lxc/lxc.conf
lxc.network.type = veth lxc.network.flags = up lxc.network.link = br0 lxc.network.ipv4 = 0.0.0.0/24 lxc.network.name = eth0
コンテナの作成
例
# lxc-create -f /etc/lxc/lxc.conf -t ubuntu -n u200
- -f: 設定ファイル
- -t: テンプレート
- ubuntu 等
- -n: コンテナ名
コンテナの起動
例
# lxc-start -n u200
- -n: コンテナ名
コンテナのシャットダウン
例
# lxc-shutdown -n u200
- -n: コンテナ名
コンテナのクローン
例
# lxc-clone -o u200 -n 2001
- -o: クローン元のコンテナ名
- -n: クローン先のコンテナ名
自動起動
ホスト起動時に自動的にゲストを起動させるには、/etc/lxc/auto/ 以下にゲストの config のシンボリックリンクを張ります
# ln -s /var/lib/lxc/u200/config /etc/lxc/auto
Unbound
ちょっとした検証をするときに,DNS サーバが欲しくなることがよくあるのですが,BIND をインストールして,設定して,,が面倒になってきたので,簡単に準備できそうな,Unbound をインストールして試してみました
インストール
# aptitude install unbound
設定
/etc/unbound/unbound.conf を下記のように設定します
server: interface: 0.0.0.0 port: 53 access-control: 192.168.1.0/24 allow local-data: "app1.hexa.diary A 192.168.1.2" local-data: "app2.hexa.diary A 192.168.1.3" local-data: "app3.hexa.diary A 192.168.1.4"
local-data: を設定すれば,設定した内容で A レコードを返してくれます
設定は,この 1 ファイルで済むので簡単です
dig で確認
$ dig @192.168.1.10 app1.hexa.diary ; <<>> DiG 9.8.3-P1 <<>> @192.168.1.10 app1.hexa.diary ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8789 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: ;app1.hexa.diary. IN A ;; ANSWER SECTION: app1.hexa.diary. 3600 IN A 192.168.1.2 ;; Query time: 41 msec ;; SERVER: 192.168.1.10#53(192.168.1.10) ;; WHEN: Tue Jan 15 00:55:29 2013 ;; MSG SIZE rcvd: 49