Rails + rack-corsの設定をRSpecだけである程度お手軽にテストする

やりたいこと

  • CORSの設定ミスやエラーをできるだけ早期に発見する
  • CORSの設定のテストをできるだけブラウザを使った手動テスト以外の方法で実現する
  • 以上をできるだけ手軽に実現する

CORSのテストはとてもやっかい

CORS は非常にやっかいで、そもそも用語が request 側と response 側で分かりにくいというのもあるんだけど、何よりテストが面倒くさい。JavaScript からの CORS リクエストの動作を完全にテストしようとすると、例えば Rack アプリの場合、

  • cross-origin リクエストを生成するために JavaScript 向けに HTTP サーバを立てる
  • rack サーバを立てる
  • JavaScript のテストコードを書く
  • 以上を連動させる自動テストを作る

ということになってしまう。これはかなり面倒くさい。もちろん rack サーバ側は Rails アプリをそのまま使うとなると E2E になる。

rspec-railsだけでどこまでいけるのか?

ということで、どこまでだったら RSpec だけで頑張れるかをちょっと試してみた。環境は以下の通り。

  • rails 5.2.3
  • rspec-rails 3.8.2
  • rack-cors 1.0.3

rack-corsのdebugを有効に

config.middleware.insert_before 0, Rack::Cors do

で書いていく部分に debug 設定を追加。こんな感じ。

config.middleware.insert_before 0, Rack::Cors, debug: !Rails.env.production?

こうしておくと production の時以外は X-Rack-CORS* なヘッダが追加されるので、test 時にも利用できる。

ここで大事なのは

X-Rack-CORS: miss

というヘッダが現れるかどうか。これが現れたら少なくともサーバ側の動作としては CORS の要件を満たさないので目的の response を返してくれることはない。

request specを書く

これには以下のような理由がある。

  • Rails アプリが E2E で動作するように
    • rack-cors は rack レイヤーであって Rails の Controller, Routing などのテストでは不十分
  • ある程度自由に HTTP を喋れるように
    • Capybara のボキャブラリに縛られると自由が効かない

注意点。OPTIONS を直接呼べない。

RSpec::Rails::RequestExampleGroup の利用している ActionDispatch::Itegration::Runner で define_method で決まっているっぽい。

     %w(get post patch put head delete cookies assigns follow_redirect!).each do |method|
       define_method(method) do |*args|

こゆことです。つまり Preflight リクエストを直接テストできないことを意味している。ということで書けるのは Origin ヘッダ付きの GET とか POST とかそういうものになる。

get '/api/v1/posts.json', headers: { 'Origin' => 'http://example.net' }

みたいな感じ。例えば production で動く Origin をここで指定してあげると deploy する前にテストすることができる。

※ Rails 5.1 でオプションの与え方が変わっているらしいので注意が必要。ここは RSpec ではなく ActionDispatch の方に引っぱられているので、Rails のバージョンによって変わってしまう。

cf. Request tests fail after upgrading to Rails 5

この時、

response.headers

の中身は以下のようになる。

{ "Content-Type" => "application/json; charset=utf-8",
  "Vary"         => "Origin",
  "X-Rack-CORS"  => "miss; no-origin",
..

この "X-Rack-CORS" => "miss" がポイント。これがあったら Rack のレベルで弾かれている。つまり Controller に処理が渡ることはない。

理由については rack-cors の中で以下のように定義されている。

module Rack
  class Cors

    protected
     class Result
       HEADER_KEY = 'X-Rack-CORS'.freeze

       MISS_NO_ORIGIN = 'no-origin'.freeze
       MISS_NO_PATH   = 'no-path'.freeze

       MISS_NO_METHOD   = 'no-method'.freeze
       MISS_DENY_METHOD = 'deny-method'.freeze
       MISS_DENY_HEADER = 'deny-header'.freeze

stub outなどは適宜ご自由に

E2E なので Model 側の準備も必要になってしまうが、CORS の部分だけをチェックしたいのであれば適当に Stub Out してあげればなんとかなる。これはいつもの通り。

RR.stub(Post).recents { [] }

みたいなのでたぶん十分。

まとめ - できること、できないこと

  • 少なくとも Origin をまたいだ HTTP リクエストのフリはできる
    • XHR のフリもできる
  • Origin が食い違っていることで動かない様子や Origin をオウム返しして動く様子など Rack サーバ側のテストはできる
  • JavaScript が動いているわけではないので、XHR の mode 設定はできない
    • つまり withCredentials 周りで引っかかるか否かはテストできない

欲を言えば XHR のモード設定とかしたいけれども、少なくとも今回の方法で 単純な HTTP のレベルと XHR や Fetch などの API レベルの切り分けはできる ので、そういう使い方になるのかなと思っている。

おまけ

今回 RSpec でどこまでやれるのかを確かめるきっかけになったのは、実は rack-cors が 1.0 で default の動作が変わったこと、および XHR に利用していたライブラリがなぜか withCredentials = true で動いていたことのコンボで CORS で取得していたコンテンツが表示できなくなっていたという問題だった。こうした問題に対してあれこれ試して分かったのは

  • CORS で意図通りにコンテンツを扱えないという問題を取得できる堅牢な API はない
    • XHR および Fetch API では通信の失敗とコンテンツが利用できないエラーは区別できない
  • ブラウザのconsole上には何か出ている
  • つまり production のモニタリングで失敗を検知するのは難しい

ということだった。1 少なくとも本気でやるなら最初に書いたようなテストの自動化を用意してあげる必要はありそうなんだけど、ちょっと今の環境でそこまでやる手間を掛けるのはアレだなと思って別な方法を探していた次第。

はー、思ったより疲れたよ。CORS ってほんとに狭間にあるので、意外と知見がまとまってないんだよなー。

参考

  1. もしかしたらイマドキの賢い Fetch クライアント的なものではうまく扱える可能性もないではないけど。 

秘密鍵を本体内からUSBメモリに移動した

職場の環境が変わったのでこれを機に以前から気になっていたことを試してみた。それは

秘密鍵を本体内に残さない

こと。

Zebedeeはそのまま移動するだけでよかった

トンネルを Zebedee で掘ってるんだけど、Zebedee に関しては

  • 実行バイナリが本体内
  • 設定ファイル、鍵などは USB メモリ

という組み合わせで普通に動いた。起動して detach してしまえば鍵や設定を入れた USB メモリは抜いてしまってもまったく問題ない。

OpenSSHはpermissionがキモ

もともと USB メモリに ssh の秘密鍵は入れてあった。しかしそれは

  • FAT でフォーマットしてある
  • 基本的に緊急時用のもので iniファイル版の putty と putty 用の鍵を用意
  • OpenSSH 用のものも入ってたけど入ってるだけ

という状態だった。今回はここにすでにある OpenSSH の秘密鍵を普段から利用するようにして本体内の秘密鍵は削除してしまおうという作戦。しかし、知ってたけど OpenSSH は鍵の permission をチェックしてくれるので、FAT 上の鍵は使えない。

MSのfilesystemは結局全滅

  • NTFS
  • exFAT 1
  • FAT32

を試したが、どれも OpenSSH の要求する permission を実現することができなかった。まぁ FAT がダメなのは予想通りなんだけど、NTFS もダメなんだね。

どうしたかというと、結局パーティションを分割して

  1. FAT で Windows 用のツールと鍵のパーティション
  2. ext で OpenSSH 鍵のパーティション

を用意した。

あ、Linux で使う気がないなら別に ext パーティションじゃなくて HFS+ でも UFS でもいいような気がするけど、たぶんそんな人いないよね?

MacFUSE + fuse-ext2でextパーティションを作る

ホストが Mac なのでそのままでは ext パーティションは作れない。そこで

の二つを利用して Mac 上で ext を読み書きできるようにした。

fuse-ext2の注意点

(OS や MacFUSE を含めた)バージョンの問題なのかもしれないけど、MacFUSE + fuse-ext2 の組み合わせでは

ext パーティションを自動で mount すると read only になってしまう

ようだ。今回は目的が OpenSSH の鍵を置くだけなので、普段は read only でまったく問題ない。

手作業でrwでmountする

とは言っても実際に鍵を置く作業をする際には read only では困る。そういうときには fuse-ext2 コマンドを mount コマンドに見立てて

$ fuse-ext2 -o rw+ device mountpoint

で mount してあれげば ok. めっちゃ EXPERIMENTAL って man には書いてあるけど。

MacFUSEの注意点

google code にある official version は 32bit で build されているので 64bit で動かしている場合は使えない。有志による 64bit build があるのでそちらを使う。

残念ながら unofficial version の置き場所は定まってないようなので、あっちこっちの blog などからたどって落として来る形になっているようだ。ちょっと怪しげな感じがするけど仕方ないのかなぁ。

なんでこんなことしたのか

もし秘密鍵を奪われた場合、ローカルでブルートフォースできまくるので、公開鍵暗号方式の接続もまったく安全とは言えなくなってしまう。

ということで銃と弾丸を別管理するように、本体と秘密鍵も別管理にできると良いと思い、やってみた。今のところ職場内のモラルやスキル的にこんな措置は必要ないんだけど、これを応用して、

  • 私物ノートの安全性向上
  • 共用アカウントの安全な運用

などを計っていきたいと思っている。

[2012-10-07 追記] もしLAN内の複数の端末で相互にログイン、認証したいので鍵をあちこちに置いておく必要がある、ということがあるなら「使い方を考え」た方がよいだろうし、それでもどうしても秘密鍵をどこかの端末に置いておく必要があるなら「エージェント転送(-A)」や「netcat mode」などが利用できる。

期待はしていなかったが、「netcat mode」では本来ログインできないはずの端末からログインできるようになる。エージェント転送はエージェント情報が途中の端末に残るので秘密鍵を残すのとどちらが危険か判断が必要になる。

  1. 10.6で対応しててパーティションの作成もできた 

OfficeファイルもRepository & Ticketベースで

ファイルがたくさんある

以前から思っていたのですが、ファイルベースの情報の管理というのは難しいです。ファイルという単位で扱えるものはしょせん1アプリケーションが表現可能なものに制限され、人間の脳内の活動をアウトプットするには非力すぎるからです。

なんて小難しいことが言いたいわけではなく、例えば Web を考えてみれば分かりますが、HTML をテキストファイルで、画像はそれぞれ画像フォーマットで表現します。画像が2つある文書を作ったらその時点でファイルは3つになってしまいます。

さらに画像は今のところ基本的にラスタ画像(PNG, GIF, JPEG)として表現する必要があり、「元画像」や「素材」と呼び分けたベクタ画像(AI, SVG などの XML形式)を別に保存しておく必要があったりします。同じラスタ画像でもより高解像度の画像を素材として残しておく場合もあります。

日付付きファイル名管理の限界

上のような特徴は HTML の場合に特に顕著ですが、Office 系の情報でも似たような状況にはなり得ます。

  • Word の提案文書
  • Excel の添付資料(比較資料や見積もりなど)
  • 製品のマニュアル PDF や関連情報の Web サイトのスクリーンショット

こういった構成の「資料」をやりとりすることは非常にありふれた話だと思います。そしてこれらの情報にはいわゆる「先方」の要望の変化や思い違い、あるいは単純なミスなどから細かく修正、訂正が入ります。つまり、Office 系のファイルでも

  • 意外に素材がある
  • 意外に変更が多い
  • 最終的には1ファイルではなく、セットが管理できないといけない

わけです。

私は以前からファイル名の頭にはほぼ必ず日付を入れて

20100601-hogehoge.xls

のようなファイルを作るようにしているのですが、複数のファイルを扱う場合、素材や、修正を加えた別バージョンと日付がずれていってしまい、頼りの日付の部分が返ってややこしいという問題が起きます。

もっとも、それだけであれば作業の開始日、あるいは締め切りの日付をフォルダ名に加えて、その中でファイルを管理することで回避することも可能です。素材が使い回しだったら単純にコピーでフォルダの中に持ってきても構いません。

共同管理が事態をややこしくする

いわゆる会社というところでこれらのファイルを扱っている場合、

共同編集はしなくても複数人で扱うことが多く、共有コストもバカにならない

という別の問題も発生します。例えば

窓口制作担当
上司部下
営業エンジニア、デザイナ

といった組み合わせで同じファイルに複数の人間が絡むことはよくあります。そしてファイルの共有方法が

  • 共有フォルダ
  • メールの添付ファイル

などになっており、

オリジナルの管理が難しい

といった事態を生むわけです。一人の作業としてもファイルがバラついて面倒なのにこれを共同管理するなんて考えただけでうんざりです。

共同管理なら Google Docs はどうなの?

  • 素材を含めての管理となると決して使いやすくはない
  • 最終的にはきれいな「印刷物」も大事
    • 印刷関連機能の充実したローカルアプリの重要性は減らない
  • ファイル管理の話にアカウント管理の話が加わってしまう

個人的には OOo を使うよりは Google Docs の方がひょっとして軽くていいかも、という気もするのですが、関わる人みんなを Google Docs に連れてくるのが大変だったり、アカウント管理どうするのよ、という別な問題が出てきます。組織の規模やカタさにもよりますが、解決したい問題に対して解決策が重すぎる場合はやはり不採用でしょう。

Officeファイルを中央集権的バージョン管理で

ということで前振りが長くなりましたが、今はこの方法で管理しようかと思い始めています。まだ安定して継続しているわけではありませんが。

  1. 「仕事の単位」でフォルダを作り、全部突っ込む
  2. ついでに ITS の milestone などに設定する
  3. commit はとりあえず分かってる人だけでやる

関わっている全員が commit できるのは理想かもしれませんが、そこまでの必要性は感じていません。どうせファイルを実際に編集して作業する人間はほとんど決まっています。

それよりもファイルの管理と同時に、雑多なファイルを取りまとめてやりとりする際のゴールを ITS の milestone で、残りの課題を ticket で見える化できることの方が効果としては大きいと思っています。

中央集権的と書きましたが、別に分散バージョン管理でも構いません。ただ、これを考えた段階で自分の環境では

  • どうせ作業者はある程度絞られる
  • 基本的にはシーケンシャルな使い方しかせず、複雑なマージなどは考える必要がない
  • ファイルの数もせいぜい多くて十数個までで、深い階層も必要ない
  • 出先作業は考慮しない

ので、別に何を使ってもいいです。基本的には

自分の使っている Trac がデフォルトで svn 対応だから

です。

まぁ、あえて言うなら「オリジナルの管理が難しい」という最初の課題に対する回答としては分散ではなく中央集権的なバージョン管理の方が分かりやすいのではないかと思います。

逆にほぼ自分一人が作業を行い、かつ出先など repository にアクセスできない環境でも変更を加えたいということなら当然分散バージョン管理の方がいいんですが、もうそうなるとそもそもの前提が違います1

実は開発の流れと同じ

こうやって道具を持ち替えてみると、意外なことに気づきます。

  • 複数の人間が絡んでいろんなファイルのバージョンを管理しなきゃいけない状況は単純なルーチンワークとは違う
  • ITSでワークフローや進捗を含めて見える化されると作業の漏れを減らせる
  • 要望が上がって作成 or 変更ののちリリース、のセットを回していく

これって何かに似てませんか?

そうです。開発の行程と同じです。いや、逆ですね。同じになるように道具を入れてるんです、ごめんなさい。

いやしかし、同じように回せることが分かればこっちのもの。開発で養ったノウハウが活きます。

  • 内容はともかくワークフローはある程度固めることができる
  • 定型タスクに落とし込んでどんどん自動化するとよい
    • 例えば相手の名前、例えば日付、こうしたチェックは自動化を考えよう
    • アーカイブなどのパッケージングも自動化すればメールで送る際も楽
  • テンプレート(パターンと呼び変えても可)を充実させるとよい
  • 道具の使い回しを考えてテキストから変換して生成するのは良いアイディア

ただし、

  • 残念ながらタイムボックスを取り入れた iteration とは相性の悪いケースもありがち

なのは現状致し方ないかなと思っています。一つ言えるのは

それでも手帳&メール添付ベースの進捗&バージョン管理よりはずいぶんマシ

ということです。

多少ツールが増えた分のコスト増はありますが、課題の見落としやどれが最新バージョンか分からない、regression などの問題は起きにくくなりますし、ゴールを明確化しやすくなるのでいわゆるビジネスパーソンの好きな PDCA の話にも落としこみやすくなります。

特に最後の効果は新しい道具の導入にあまり積極的でない人にもメリットを伝えやすくて、とてもいいんじゃないかなぁ。何より自分のやろうとしている方法では管理する側の手間は増えないし。とりあえずなんでもバージョン管理、なんでも Trac、を今後はもっと積極的に進めていこうと思っています。

名付けて Ticket Driven Documentation つまり TDD です :-)

  1. Office 系の文書もバージョン管理 & ITS 管理するというアイディア自体は別に複数人で関わらなくてもメリットありますけど。 

.git を小さくする git gc って結構効果あるのね

例によってログから。

19:07:23 >wtnabe< ちょっと bundle ファイルが無駄にでかくなってきた
19:07:46 >wtnabe< git の repository の comapction みたいなことってでき
                  るんだっけ
19:08:32 <eban> @wtnabe git gc
19:08:51 >wtnabe< @eban すばやいw ありがとうございます。
19:09:36 >wtnabe< うっかり手抜き発言をすると光の早さでツッコミが入る
                 (ただしものすごく一部の情報だけ)のはありがたい

いやもうほんと、そんだけっす。知識はあったけどやってみたことなかった。

ずっとやってなかったせいもあるけど期待以上に .git が小さくなってビックリ!っていうか、意外に .git は無駄が多いんだなってことを初めて知った。今度からちょこちょこ実行するようにしよう。

今ごろ NeoOffice 2.1 入れる

ダウンロードしただけで手元の機械は 2.0 beta 3 + patch 15 のままだったのに気づいたので改めてインストール。

なんでこんなことになるかというと大物はすべてサーバ上で wget しているからです。落として満足し続けてた。アホすぎる。

ちょっと beta の頃より動きが軽いか? 今度こそ本当に満足満足。

find + cpio でほしいファイルだけツリー構造を保持して取り出す

昨日ハマったので別解を探す。rsync の include, exclude には頼らずに必要なファイルだけのツリーを作るというところまでは同じなんだけど、ファイル群が巨大すぎて全体のコピーなんてやってられませんという場合には必要なファイルだけを取り出す作業が必要になる。

[2007-06-07 追記]

以前 tar 方式を試したが予想通りファイルが多すぎたらダメになったので、ちゃんと cpio について調べた。そしたら以下のように超簡単に書けることが分かった。

find PATH \( EXPRESSION \) -a print0 | cpio -p0d DEST

find の -print0 は書いた瞬間に true が返ってしまい、すべてのファイルが対象になってしまうので最後に書く。

cpio の -0 オプションは GNU cpio じゃないと使えないですね…。BSD だと pax の -s \0 とかで代用できるのかな?


※ こっから下はエントリの日付通りの日に試したみた結果。案の上 tar -cf ではファイルの数が多すぎたときに途中でアーカイブがクリアされてしまう。

一カ所にコピーしたいんじゃなくて階層も保持したいのでとりあえず tar に突っ込む方式にした。つーことはこんな感じか。

find PATH EXPRESSION | xargs tar -cf ARCHIVE

実際にはアーカイブを作成せずに一気に別な場所にコピーすることも可能だが、まぁそれは次の段階ということで。ファイルの数が多すぎると -c で作っているんで問題が起きるような気がする1んだけど、そこら辺をどうやって回避したらいいかまでは考えてないです。(最初にアーカイブを空っぽで作成して append していかなくていいのか?とか)

なんか最初無理に複雑にして BSD と GNU/Linux の違いにハマっていきましたが、実は単純でした。ありがとう > 隣の席の人

  1. xargs から tar が複数回に分けて呼び出されるとまずいってこと。 

もう10年間フルタイムノートユーザーですけど

ノートPCが身体に悪影響–米研究者が警鐘 (CNET Japan)

自分は力一杯健康だなんてことは申しませんがね。1日8時間なんてレベルじゃないですよ? 少なくともこの7年くらいは起きてから寝るまで PC に向かっているような生活です。まぁ肩こりや、場合によっては手首から肘に痛みを感じることはありますが、これといって問題はないですよ?(問題だらけという突っ込みも入るかもしれませんが。)

ただ首の角度よりも個人的に気になるのはパッド。これね、絶対手首に負担掛かります。マウスに持ち変えるより作業そのものは楽ですが、手首をひねって使う形になるので、実際の負荷は大きいような気がします。負荷を考えれば絶対にトラックポイントかボールの方がいいです。

あと、PC 以前のことを考えるとキーボードと画面が近いのはむしろ手書きに近いので姿勢的には不自然じゃないと思うんですが。

CraftLaunch

ついでに CraftLaunch も試してみた。どうも以前も試したことがあるみたいで、見覚えがある。

設定画面を始め、随所に「可愛くなさ」が満載されているのでやっぱ bluewind にしとく。

Dolphin 3000位突破

見た段階で

  • 世界 2966 位
  • 日本 34 位

なんだかここまでくるとすでに喜んでいいのかどうかすらあやしい感じではあるけれど。。。(周囲にやってるやついねーし。)

やっぱ ck ステ

Emacs で作業していると画面で表示されているのと実際の編集状態が食い違っていく。。。

怖くて使えない。

MoonLight → bluewind

言い方は悪いが MoonLight のパクリのような bluewind にランチャを変えた。デフォルトでは若干動作がもたつく。

  • 優先度の設定があって、よく起動するアプリの優先度が上がるような仕組みが動いている
  • 履歴なんか残しちゃってる

のでこの辺の機能をカット。少しスッキリした感じ。アプリの登録は MoonLight の方が簡単だ。bluewind の方が多機能だが、登録時のアクションが分かりやすくないと、ちょっとどうかなぁって感じ。

でもめでたく AVG と同居できるようになった。eTrust はやっぱちょっと重いので非力なマシンには AVG だろ。

あとは MoonLight にお願いしていた残りメモリの表示機能なんだけど。。。なくてもいいかなぁ? 9x 系じゃないからメモリの解放機能なんかも必要ないんだし。かわいい顔も必要ない、、、か。

あ、bash からアプリを起動するのはバックグラウンドに bash のプロセスが山のように溜まっていくのでやめました。

ck および rxvt 謎解決

答えは

  • .Xdefaults に日本語を含む場合 sjis でないとダメぽ
  • Osaka の等幅フォントを指定する正しいスペル(?)は「Osaka−等幅」

この両方にハマっていた。

行間を指定する方法は、なんと用意されていた。これ、0.9.3 でもあった? ちゃんとオプション全部読んでたはずなんだけどなぁ。

これで rxvt でも ck でも .Xdefaults だけで設定全部できるようになりました。ck の半透明はやはり重過ぎて使いものになりませんでした。。画面サイズに対してビデオ性能が弱すぎるのが原因ですな。

何にしてもこれで terminal から terminal を起動して何の支障もない状態になったので、これで晴れて MoonLight をステることができそうです。これでリアルタイムなアンチウィルスとバッティングする問題に悩む必要がなくなる。(リソースモニタやメモリクリーナとしての MoonLight は惜しいけど、これは他を探すか。。。)

あぶないあぶない

自分サーバに外から入ろうとして ssh を叩いたらどうも違う人のサーバに繋がっちゃったらしい。パスワード認証を許可していないはずなのに パスワードを聞かれて、反射的にパスワードを打とうとしてやばい、と思ってやめた。

一瞬、え、もうクラック?と思ったが ssh -v すると動いている OpenSSH のバージョンから全然違う。(バージョンが落ちてる)だいたい、RSA 鍵認証しか許可してないし、まだ全然余計なプロセス動かしてないんだから、乗っ取られるには早すぎるよなぁ。

DynamicDNS の落とし穴? DynamicDNS のアカウントを乗っ取られてるのかな? それともタイミングの問題? 自動で更新するはずだからしばらく待つか。とりあえず ssh_config の方でパスワード認証を切っておこう。勢いでパスワード打ったら大変だ。

と思ったらよく似た別な名前のサーバに繋いだだけらしい。自分が悪いだけじゃないか(^^; なんだかあほすぎだ。致命的なポカしないように気をつけよう。

MATRIX RELOADED を見た。

先行上映。さすがに混んでた。

nmap …分からなかった;;

ssh は分かったけど、何の脆弱性をついたとか、あれで分かるのか? オタク恐るべし。

話がすっかり大きくなったのと、ちょっと想像を越えたアクションで面白かった。反則だとも思うけど、一作目の設定の段階でほとんどどんな反則も折り込み済みなんだし、それは言っちゃだめでしょ。

ラストが楽しみ。

今回はやたらと人間が前面に出ていたけど、この調子でいくのかなぁ?

/* 最近の映画は全般的に映像の何割 CG なんだー?とか思ってしまう。もう少しストーリーに集中できるものを見ないと視点を奪われてダメだなぁ。 */

About

例によって個人のなんちゃらです