検証環境用のアクセス制限をnginxで11行で設定する

  • 投稿日:
  • by
  • カテゴリ:

どんな状況での話か

  • 公開前や移行時の確認用のサーバーに、念のためパスワードを設定しておきたい
    • 厳密なアクセス制限は必要なく、「URLにアクセスしただけでは見られない」という程度の制限でよい
  • ウェブサーバーはnginxで、バックエンドのサーバーにプロキシしている
  • バックエンドのサーバーで、BASIC認証やDigest認証が使われている
    • つまり、nginxで単純にBASIC認証をかけてしまうと不都合がある

という稀によくありそうな状況で、既存の設定に影響をあたえること無く11行くらいで設定する方法についての話です。

注意事項

ユーザーが思った通りに認証情報を破棄できなかったりするので、本番運用でのアクセス制限として利用するものではありません。

設定前のファイル

な感じです。

設定後のファイル

11行(変数定義などで+4行)追加して以下のようになります。

流れとしては、

  1. サーバーでは、秘密のcookieが確認できない場合にはBASIC認証が必要とのレスポンスを返す
  2. ブラウザでは、BASIC認証が設定されているときのダイアログが表示される
  3. サーバーでは、正しいパスワードが送られてきた場合には秘密のcookieを設定する
  4. それ以降は、秘密のcookieが設定されていればBASIC認証の情報は必要なくアクセスできるようになる

となっています。

あえてBASIC認証のダイアログである必要はなく、セキュリティ的には期待される動作と異なるUIで好ましくはないのですが、403的なページを用意するよりもパスワードのダイアログを出してしまった方が楽だし説明も省けるので、一時的な制限ならこんなのもありではないかと思います。

therubyracerをAlpine linuxでも動くようにするDockerfileを書きました

  • 投稿日:
  • by
  • カテゴリ:

TL;DR

Alpine linux上では単純に$ gem install therubyracerしただけではtherubyracerは動かないのですが、これを動くようにするDockerfileを書きました。

DockerfileはDocker Hubのruby-with-therubyracerに置いてあり、usualoma/ruby-with-therubyracer:2.4.0-alpineという名前でbase imageとしても利用できるようになっています。(親のbase imageはruby:2.4.0-alpineです。)

何が必要だったか

therubyracerはlibv8というgemに依存しており、インストール時にlibv8に含まれるv8のライブラリにリンクされます。ここで、libv8は(ドキュメントにもあるのですが)通常だとコンパイル済みのバイナリで、Linuxの場合にはglibcにリンクされたものがインストールされるようになっているということのようです。そしてAlpine linuxではglibcではなくmusl libcがベースになっているという事情から、そのままでは動かないということでした。

そこまで分かれば後はlibv8をDockerfileでコンパイルすればよいだけなので既存のDockerfileを参考にしてlibv8をインストールして、その上でtherubyracerをインストールするようにしました。

docker image のサイズを抑えるためにやっていること

therubyracerではインストール時にlibv8のライブラリは静的にリンクされて利用時にはlibv8は参照されないようなので、libv8側のライブラリが含まれるディレクトリを$ rm -fr $(dirname $(dirname $(gem which libv8)))/vendorという形で削除しています。行儀のいいやり方ではありませんが、これだけで76MBほどかせぐことができます。

いずれにいしてもv8が入るのである程度大きくはなりますが、base imageから比較して55MB程度の増加で、debian8.0がベースのruby:2.4.0-slimで同じようにインストールしたものとと比較しても一応小ささは保たれているという感じです。

Movable Type のリスティングフレームワークの機能の一部である「フィルタ」を、MTタグやDataAPIのエンドポイントで利用することができるFilteredObjectsというプラグインを公開しました。

mt-plugin-FilteredObjects

記事やウェブページを複雑な条件で絞り込んで表示する場合、これまでは MTML で頑張るか、SearchEntriesで条件を指定していたと思います。FilteredObjects プラグインはこういったケースでのもう一つの選択肢になるものです。MTの「フィルタ」機能を利用するため(「フィルタ」は管理画面からGUIで条件を編集できるので)、プログラミングの知識がなくても条件を細かく指定することができ、MTMLの方ではそれを使ってシンプルな書式で結果を得ることができるようになります。

Getting sterted(とりあえず使ってみる)

プラグインのインストール

まずはFilteredObjectsプラグインをインストールしてください。

リスティングフレームワークを拡張するプラグインのインストール(必要な場合だけ)

FilteredObjectsはリスティングフレームワークを拡張する別のプラグインと組み合わせるとより強力になります。(プラグインによっては組み合わせることができない可能性もあります。)そして特に、カスタムフィールドで複数の条件を指定して絞り込みができるとここでは格段に便利になると思うので、おすすめはCustomFieldsListingプラグインです。(ただ実は、CustomFieldsListingプラグインはFilteredObjects側からだいぶ無理して利用させてもらう形になっているので、同時に利用した場合CustomFieldsListingの機能が正常に動作しなくなってしまう可能性があります。同時利用で不具合が起きる場合にはFilteredObjectsプラグインを削除してください)

「フィルタ」の準備

一覧ページで「フィルタ」の新規作成へと進み、条件を指定したあと、「識別子」を指定して「フィルタ」を保存してください。

screen-shot 2016-12-20 4.15.34.png

screen-shot 2016-12-20 4.17.36.png

MTMLで出力する

「識別子」を付けて保存した「フィルタ」は mt:FilteredEntries と mt:FilteredPages を使って以下のように出力することができます。

Data API で取得する

Data API では /filtered-objects/entries に filter パラメータを指定して取得することができます。

Advanced Usage(もう少し凝った使い方)

MTの「フィルタ」では、「または(OR)」という条件を指定することができないのですが、FilteredObjects では複数の「フィルタ」を「または(OR)」でつなげることができます。

やや複雑になるので、具体的な例で考えてみます。

複数のニュースのブログから条件を指定してピックアップする

以下のように、複数のニュース用のブログがあるとします。

  • 会社全体のニュース
  • 製品毎のニュース

ここで、ウェブサイトのトップページに、

  • 会社全体のニュースからは全部
  • 製品毎のニュースからは「全体のニュースに表示する」にチェックを入れたものだけ

という条件で表示するケースを考えます。

この場合は「フィルタ」を2つ、それぞれ上の条件と同じものを指定して作成します。(特定のブログを指定するための「ブログID」の条件はMTの標準では表示されませんが、FilteredObjects をインストールすると表示されるようになります。)

screen-shot 2016-12-20 5.00.56.pngscreen-shot 2016-12-20 5.03.40.png

screen-shot 2016-12-20 5.09.59.pngscreen-shot 2016-12-20 5.10.34.png

スクリーンショットの内容で、以下の2つのフィルタを作成したとします。

  • blog-general-news
  • show-in-general-news

ここまでできれば MTML 側は簡単で、 mt:FilteredEntries タグの filter に2つのフィルタを「OR」でつなげて指定することで目的の記事を表示することができます。

また、Data API の /filtered-objects/entries エンドポイントで取得することもできます。

制限事項など

  • MTタグ、Data APIのエンドポイントの両方で、公開されている記事のみが対象になります
  • 誤って削除してしまうことのないように、「識別子」を付けた「フィルタ」ではゴミ箱アイコンが表示されなくなります
    • 「システム > フィルタの管理」で削除することができます
  • 種類が「日付と時刻」のフィールドでは「今より前」「今より後」が表示されますが、これは CustomFieldsListing を独自に拡張しているもので、うまく動かない場合は FilteredObjects 側の問題です
    • 日付の場合「今より前」は「今日の23時59分59秒より前」となり、「今より後」は「今日の0時0分0秒より後」となります
  • 「自分の記事」の条件には未対応です
  • 対象となるブログは「フィルタ」からいい感じに選択されるはずですが、うまくいかない場合には明示的に指定してください
    • mt:FilteredEntries/mt:FilteredPages では、マルチブログのモディファイアを指定することができます
    • /filtered-objects/entries エンドポイントでは、blogIds パラメータにブログのIDを「,」(カンマ)区切りで複数指定することができます

FilteredObjects プラグインでの「フィルタ」の運用について

MTでは「フィルタ」は「ユーザ」毎に管理されるデータなので、FilteredObjectsのようにテンプレートやData APIでで利用する道具としては向いていない部分があります。(他の「ユーザ」の「フィルタ」を見ることができない、など。)

「識別子」のついた「フィルタ」がいろいろな「ユーザ」のデータとして散らばるのは好ましくないと思うので、現時点としては『専用の「ユーザ」を作成して、全ての「フィルタ」はそこで管理する』が現実的な一つの回避策ではないかと思っています。

プラグインのインストール

  1. GitHubからzipファイルをダウンロードしてください。リリース一覧
  2. ダウンロードしたファイルを展開してください。
  3. pluginsディレクトリにアップロードしてください。

    インストール後のディレクトリの配置は以下のようになります。

    $MT_HOME/
        plugins/
            FilteredObjects/

サポートしている機能

  • スタティックパブリッシング
  • Data API

サポートしていない機能

  • ダイナミックパブリッシング

動作環境

  • MT6

この記事について

この記事は Movable Type Advent Calendar 2016 の20日目の記事です。

CSS ではできないこと

この記事について

NSEG Advent Calendar 2015 の 22 日目の記事です。
2日目の CSS Bird や8日目のサンプルコードで学ぶ CSS プログラミングの基本テクニックでは CSS でなんでもできそうな記事ばかりだったので、今日は CSS でできないことについて書いてみます。(今日のは短いです)

親要素、兄要素でしか絞り込むことができません

CSS では他の DOM 要素との関係で絞り込むための、

  • Descendant combinator ( body div )
  • Child combinator ( body > div )
  • Next-sibling combinator ( div + div )
  • Following-sibling combinator ( div ~ div )

がありますが、これはいずれも、右側の要素に対して「親」または「兄」の要素で絞り込むためのセレクタです。

つまり、チェックボックスで状態を管理する場合、その状態にしたがって表示を変化させることができるのは「弟」または「子」要素のみで、CSS では「親」や「兄」に影響を与えることはできません。 CSS Bird のようなアクションゲームでは、当たり判定のための子要素を用意しておいて、「子どもの .enemy 要素に :hover したら、親の #game-over を表示させる」というようなことができると嬉しいのですが、そういったことはできないのです。サンプルコードで、「DOM の先頭に INPUT 要素が兄弟として並ぶ」形になっていたのはこのためです。

ちなみに、Selectors Level 4 の Editor's Draft では、:has() という「セレクタにマッチする子や孫を持っている要素」を表現するための擬似クラスが含まれていて、これを使うと「#game-over:has(.enamy:hover) { display: block }」のように子要素から親要素に影響を与えるように CSS も書けるようになりそうです。

状態は DOM 構造に従ってしか伝播しません

CSS では :hover などの状態は DOM 構造にしたがってしか伝播しません。例えば次のような CSS があったとき、

「#div2」が「#div1」子要素になっていれば、「#div2」の :hover が「#div1」に伝わります。

しかし「#div1」と「#div2」が兄弟要素の場合には、見た目は同じでも「#div2」の :hover は「#div1」には伝わりません。

また逆に「#div2」が「#div1」の子要素になってさえいれば、見た目としては重なっていなくても「#div2」の :hover は「#div1」に伝わります。

Fastly - MT と Fastly を連携させるプラグイン

  • 投稿日:
  • by
  • カテゴリ:

Movable Type と Fastly を連携させるための Fastly というプラグインを公開しました。

mt-plugin-Fastly

このプラグインを使うと認証を必要としないパブリックな Data API のレスポンスをキャッシュさせることができます。これにより先頃から AWS で利用できるようになった t2.nano でも、Data API をフロントエンドで利用しているサイトを運用することも可能になります。(なると思います)

また昨年公開した mt-plugin-DataAPIFormatPHP とあわせて使えば、外部のサイトから PHP で以下のように MT のデータにアクセスをしても、現実的な速度で動作するようになります。(なると思います)

このブログは Fastly と mt-plugin-Fastly を使って運用されています。

できること

  • MT でコンテンツを更新した際に、対応する Fastly のキャッシュを即時にクリアすることができます
    • 記事、ウェブページ、アイテム、の全てに対応しています
    • ダイナミックパブリッシングにも対応しています
  • 管理画面 (mt.cgi) と、認証付きの Data API (mt-data-api.cgi) を Fastly でキャッシュされないようにします
  • 認証なしの Data API の結果をキャッシュさせ、かつコンテンツを更新した際に即時にクリアすることができます
    • mt-search.cgi の検索結果についても同様にキャッシュ、クリアすることができます
    • キャッシュのクリアを非同期に処理させることもできます
  • テンプレートモジュールを ESI で処理させることができます
    • これにより、スタティックパブリッシングでもページ内の部分的な更新が可能になります
    • (ダイナミックパブリッシングでは未対応です)
  • PURGE リクエストに任意のヘッダーを指定することができます
    • リクエストヘッダーでアクセス制限をかけている場合に、プラグインの設定で認証情報を追加することができます。(Fastly 側の設定例)

ESI について

ESI は耳慣れない言葉かもしれないので、簡単に補足します。(私も詳しくはありません)

ESI は Edge Side Includes の略で、CDN やキャッシュサーバーの層で include の処理をさせるための仕様です。つまり ESI を使うと、(静的な内容ではありますが) PHP の include や、SSI の #include あるいは、Web Components のようなことを、CDN やキャッシュサーバーの層で行わせることができるようです。

Fastly では (2015年12月現在) バックエンドでは varnish (2.1) を利用しているとのとこで、varnish の ESI に関するドキュメント の機能を利用することができるようです。esi:remove はあまり利用することはないと思うので、実質的には「esi:include のみが利用できる」というところだと思います。

Fastly で ESI を利用するために必要なこと

Fastly で ESI を利用するためには、custom VCL. を使って有効にする必要があります。この custom VCL. の機能は登録直後は利用できず、サポートにメールをして有効にしてもらう必要があり、英語が苦手(私は苦手です)な場合には若干敷居が高いです。が、おそらく特別な審査があるということではなく、私は「custom VCL」「request」を含んだ2行くらいのメールを送ったところ数時間程度で有効にしてもらえたので、基本的にはお願いすればすぐに使えるようになるものだろうと思います。

costom VCL が設定できるようになったら、以下の VCL をアップロードすれば ESI を利用できるようになります。

もう少し細かいところについては「How do I enable basic ESI in my VCL?」などを参照するとよいと思います。

プラグインのインストール

  1. GitHubからzipファイルをダウンロードしてください。リリース一覧
  2. ダウンロードしたファイルを展開してください。
  3. pluginsディレクトリにアップロードしてください。

    インストール後のディレクトリの配置は以下のようになります。

    $MT_HOME/
        plugins/
            Fastly/

Getting sterted (とりあえず使ってみる)

とりあえず使ってみるだけであれば、難しい設定は必要ありません。

Fastly

  • 公開サイトを Fastly 経由で配信されるようにする

MT

  • プラグインをインストールする
  • 「ブログのプラグイン設定」を開き、「有効」のチェックボックスにチェックを入れて更新する

以上の設定を行うだけで、以下の機能が有効になります

  • 記事やアイテムの更新のタイミングでキャッシュがクリアされる
  • 認証が必要な Data API がキャッシュの対象から外される
  • mt.cgi に関しては全てキャッシュの対象から外される

ESI を使う

ESI を使う場合にも特別な設定は必要ありません。以下の手順で利用することができます。

  1. ブログの「全般設定」を開き、「モジュール設定 > サーバーサイドインクルード」で「Edge Side Includes」を選択して保存する
  2. ESI でインクルードしたいテンプレートモジュールを開き「ESIのインクルードとして処理する」にチェックをいれて保存する

以上の準備ができていると、インデックステンプレートやアーカイブテンプレートから mt:Include[module="module-name"] でそのモジュールを呼び出した場合に、ESI で処理されるようになります。

ESI を使うとどんなことが実現できるか

  • スタティックパブリッシングを使いながら
  • PHP や SSI などサーバー上の仕組みに頼ること無く
  • MT の作法で mt:Include を書くだけで
  • 全てのページに埋め込まれるようなモジュールを、全体を再構築すること無く
  • 通常の記事の更新に合わせて全てのページで更新する

ということができるようになります。

Data API や検索結果のキャッシュをクリアする

Data API や検索結果のキャッシュをクリアする場合には、Fastly の API キー等を設定する必要があります。

MTの設定

  1. mt-config.cgi に、FastlyAPIKey という名前で Fastly の API キーを追加する
  2. 「ブログのプラグイン設定」を開き、「Service」の欄に Fastly の管理画面で表示されている ID を入れて更新する

以上の設定を行うと、ブログ記事やアイテム、またはテンプレートなどの更新時に Data API や検索結果のキャッシュがクリアされるようになります。

サポートしている出力タイプ

  • スタティックパブリッシング
  • ダイナミックパブリッシング
    • ESI には未対応です

動作環境

  • MT6

この記事について

この記事は Movable Type Advent Calendar 2015 の21日目の記事です。