dotenvで秘密情報を扱う件 〜 Ruby, PHP, PaaS, CI について 〜

.envファイルに環境変数を書いておく

dotenv って何かと一言で言うと Foreman の提供している .env ファイルから環境変数をセットする機能を Foreman 環境以外でも利用できるようにしたもの。

えーと。何のこと?って感じですね。

dotenv という言葉は意味する範囲がやや曖昧なんだけど、自分の場合は「.env ファイルに sh script のように環境変数を記述しておくと、アプリが実行時に自動的に環境変数として取り込むことができる系の機能の総称」と捉えている。

で、実装がそれぞれにある。いちばん有名な Ruby バージョンだと予想通り Dotenv gem で、他の言語でも似たようなものがいくつもある。

bkeepers/dotenv

なぜ.envファイルから環境変数をセットするのか

環境変数から設定を取得したり環境判別用の情報を取得しようという話は決して新しい話ではない。例えば Rails では以前から RailsEnv や RackEnv という環境変数を見て環境を切り替えるという機能があって、そのために Apache の httpd.conf に SetEnv とか書いたことのある人もいると思う。

つまり production では環境変数から情報を取得することができるというのは割とポピュラーな手法。しかし手元の開発環境ではこれらがセットされていないことが多く、設定用のコードで環境変数がセットされている場合とセットされていない場合を判別して処理する必要が出てきたりする。そうなると煩雑だしバグのもとになるし、環境変数をセットしつつ rails server を起動するのも面倒くさい。一度実行すれば履歴に残るだろうけど、そういう理解もコマンドライン環境への習熟度合いに大きく左右される。

そこでこの環境変数の扱いが抽象化されているといいよねとなって、.env ファイルから環境変数を自動でセットする方法が生まれた。書き方は sh script と同じ。rails server の起動のたびに環境変数をセットする必要もないし、楽ちん。

当初これを実現したのは Procfile を使って関連するプロセスを起動する Foreman だったようだ。しかしこの .env ファイルを扱う機能はプロセスの管理とは独立して便利なので Dotenv が生まれた。

cf. http://opensoul.org/2012/07/24/dotenv/

環境変数と秘密情報

なんでいま環境変数の話なのかというと、

秘密情報をどう扱うか?

が問題になるケースが増えてきたから。例えば AWS や Google はじめ各種 API を利用するのに key, secret, credential と呼ばれる秘密情報が必要になる。これら

秘密情報をアプリケーションコードの中、repositoryの中に直接置かずに利用したい。

直接 repository に入れてしまうと production と development で同じ秘密情報が利用されてしまったりするし、公開 repository で開発する場合や、private repository でも外部のパートナーと共有する場合はそもそも秘密情報を含むことはできない。

ということで環境変数経由でこういう情報は利用しましょうというのが最近の作法となっている。

cf. The Twelve-Factor App(日本語訳)

改めてdotenvと秘密情報

ということで、環境変数で秘密情報を渡すようにしてアプリや repository の中に入れないようにしましょう、そのために dotenv が使えるよということなんだけど、その時にこれを守っていないと台無しになってしまうのは、

.env は .gitignore しておくこと

これやらないとツールが増えるだけで、情報の扱いは変わらないので。

PaaS, CIと環境変数

自分で立てたサーバならアプリケーションの実行環境に環境変数をセットするのは自由にできるけど、昨今の Web 開発を支える便利サービス、PaaS や CI サービスはどうなっているのかと思い、ざっと調べてみた。

Heroku は Twelve Factor App という文書でアプリケーションを portable にするために環境依存の設定は環境変数を通じてセットしようと提唱しているように、Heroku はじめ各種 PaaS ではアプリケーションの実行環境に環境変数をセットする方法を提供している。とりあえず確認したところ Heroku, Mogok には Web UI から環境変数をセットする機能がある。

Sqale は 2015-04-05 時点では Web UI からの設定はできなくて、逆に .env ファイルを production で使うように奨めている。.env ファイルは基本的にプロジェクトルートに置くので、これは公開 repository では使えない方法である。そこで解決策として deploy ごとにクリーンになってしまわない領域に秘密情報を置いて、postinstall スクリプトで symlink を作るという方法を提供している。ただ ssh で作業する必要があり、Un*x 系サーバに不慣れな人にはちょっと向かない方法だ。また、この方法は ssh でアクセスできる人には秘密情報はバレてしまうということでもある。

Sqale - FAQ: 技術的な仕様に関する質問

EngineYard も Web UI はなくて Sqale のようにアプリケーション環境内の消えない領域にファイルを置く形のようだ。

Engine Yard CloudでPHPを利用する : Developer Center

CI については以前調べた時のついーとを貼付けておく。

あとこんなこと言ってる人がいた。

えーとまとめるとメジャーなCIサービスは環境変数を Web UI からセットできるようだ。Drone.io もできるってさ。

dotenv系ツール

最後はツールを紹介しておしまい。

Dotenv gem

この Dotenv gem は foreman のようにコマンドとしても使え、dotenv コマンド経由でアプリを起動すれば Ruby 以外の言語でも同じように .env から環境変数をセットできるようだ。

bkeepers/dotenv

使い方は、

1. require 'dotenv' して Dotenv.load(*args)

require 'dotenv-rails' してしまうと load は引数を受け取れないので注意。

2. dotenv bundle php -S localhost:3000

この場合は dotenv 経由で起動したプロセスに環境変数がセットされる。

direnv

Dotenv は ruby のコード内で .env からの読み込みを明記したり、dotenv コマンド経由でアプリケーションを起動する必要がある。

対して Direnv は rvm, rbenv が用意しているような、shell を hook して特定のディレクトリに入った時だけ特定の環境変数をセットすることができる Go 製のツール。

つまり、アプリケーションコード内で env のロードを明示する必要がなく、アプリケーションの言語に依存しない代わりに shell の設定が必要になる。

これは伝統的な PHP アプリ開発者には向いていないかも。あと、アプリケーションコード内に環境変数の設定に関する記述がなくなるので、そのノウハウの共有は十分注意して行う必要があるような気がする。

phpdotenv

vlucas/phpdotenv

..env が読み込めない時に例外が上がっちゃう。ちょっとちょっと。しかも超汎用的な InvalidArgumentError なのでハンドリングが難しい。なーーーんでそんなことするの。

php-dotenv

josegonzalez/php-dotenv

読み込みの記述が Dotenv gem に比べると冗長。

Laravel + Dotenv

Laravel は 4 の時点では自前で .env.php を扱っていたが、Laravel 5 では Dotenv を使うように変わっている。

うーん、簡単に Fatal Error になっちゃう方を採用したのね。なんか方法考えないとな。

あと、当然気づいてると思うけど npm にもある。 dotenv ま、イマドキだいたいどの環境でも使えるんだと思う。

More

Categories

Tool 日々 Web Biz Net Apple MS ことば News Unix howto Food PHP Movie Edu Community Book Security Text TV Perl Ruby Music Pdoc 生き方 RDoc ViewCVS CVS Rsync Disk Mail FreeBSD Cygwin PDF Photo Zebedee Debian OSX Comic Cron Sysadmin Font Analog iCal Sunbird DNS Linux Wiki Emacs Thunderbird Sitecopy Terminal Drawing tDiary AppleScript Life Money Omni PukiWiki Xen XREA Zsh Screen CASL Firefox Fink zsh haXe Ecmascript PATH_INFO SQLite PEAR Lighttpd FastCGI Subversion au prototype.js jsUnit Apache Trac Template Java Rhino Mochikit Feed Bloglines CSS del.icio.us SBS qwikWeb gettext Ajax JSDoc Rails HTML CHM EPWING NDTP EB IE CLI ck ThinkPad Toy WSH RFC readline rlwrap ImageMagick epeg Frenzy sysprep Ubuntu MeCab DTP ERD DBMS eclipse Eclipse Awk RD Diigo XAMPP RubyGems PHPDoc iCab DOM YAML Camino Geekmonkey w3m Scheme Gauche Lisp JSAN Google VMware DSL SLAX Safari Markdown Textile IRC Jabber Fastladder MacPorts LLSpirit CPAN Mozilla Twitter OpenFL Rswatch ITS NTP GUI Pragger Yapra XML Mobile Git Study JSON VirtualBox Samba Pear Growl Mercurial Rack Capistrano Rake Win RSS Mechanize Sitemaps Android JavaScript Python RTM OOo iPod Yahoo Unicode Github iTunes God SBM friendfeed Friendfeed HokuUn Sinatra TDD Test Project Evernote iPad Geohash Location Map Search Simplenote Image WebKit RSpec Phone CSV WiMAX USB Chrome RubyKaigi RubyKaigi2011 Space CoffeeScript Nokogiri Hpricot Rubygems jQuery Node GTD CI UX Design VCS Kanazawa.rb Kindle Amazon Agile Vagrant Chef Windows Composer Dotenv PaaS Itamae SaaS Docker Swagger Grape WebAPI Microservices OmniAuth HTTP 分析基盤 CDN Terraform IaaS HCL Webpack Vue.js BigQuery Middleman CMS AWS PNG Laravel Selenium OAuth OpenAPI GitHub UML GCP TypeScript SQL Hanami Develop Document Jekyll