開発と設計の思想
そもそも、bwsfというコマンドを探してたどり着いた人に、このコンテンツは不要かもしれません。
しかし、そうでない方のために、bwsfがどういうコンセプトで、どういう動機の元で開発されたのかを、ここに記しておきます。
環境変数とは
bwsfや.envファイルを語る前に、そもそも環境変数について知っておく必要があります。
環境変数とは、OSが保持している変数のことです。
「とはいっても…」という方もいらっしゃるかもしれません。
そもそも変数とは、コンピューターやプログラミング言語において、値の入れ物となるものです。
例えば、PHPというプログラミング言語では
$value = "Hello world";これで、値「value($は、変数の印)」は「Hello world」という文字が入っている(代入)という意味になります。
従って、PHPではこれをechoというコマンドで実行すると
echo $value;
// -> Hello worldと、「Hello world」が出力されます。
上記はあくまでPHPの例で、プログラミング言語によって書き方は異なりますが、変数という概念は、コンピューターの世界では共通です。
環境変数は、プログラミング言語のものではなく、OSそのもので扱われるものです。OSもいろいろありますが、LinuxやmacOSだと
export VALUE="Hello world"
echo $VALUE
# -> Hello worldと出力されます。
この環境変数には、以下のメリットとデメリットがあります。
- メリット: 変数名で、その内容を秘匿することができる
- デメリット: メモリ内に保存されるので、再起動すると変数の値がリセットされる
こういう特性があります。
The Twelve-Factor App
The Twelve-Factor Appという概念があります。
これは、長年のWebアプリケーション開発の中で形作られてきた、ベストプラクティスの集合と言うべきものです。
例えば、Ruby on Railsなどは、この思想を広くベースとしています。
Ruby on Railsや、Railsの思想を継承している他のフレームワーク(LaravelやDjango)なども、同じくこの思想をベースとしていることになります。
これらいわゆる Rails way と呼ばれるフレームワークたちは、多くのプロダクトに採用され、安定した稼働を行っています。
言い換えれば、The Twelve-Factor Appは、堅牢なWebアプリケーションを開発するための、非常に重要な考え方と言えます。
The Twelve-Facotr App と言うので、実際には 12個のファクター で構成されています。
そのうちの3つ目に 設定 - 設定を環境変数に格納する というのがあります。
詳細はリンク先を読んで欲しいのですが、簡単に言えば
- データベースの接続先
- ユーザー名・パスワード
- APIキーなど
こういった情報は、環境変数で管理しましょう、というルールです。
例えば、これをconfig.rbのような設定ファイルに書いてしまった場合、現在のアプリ開発では欠かすことのできない**CVS(コード・バージョンコントロールシステム、現代のデファクトでは、Git)**によって、その設定値が記録されてしまいます。
仮にGitHubのような公開型のリポジトリを使っていなかったとしても、設定値やパスワードなどの機密情報が、バージョン管理システムに残ってしまい、他の開発ユーザーなどに共有され、そこから情報が流出してしまう、というリスクがあります。
そこで、The Twelve-Factor Appでは、設定値は環境変数に保存する、という原則が定められました。
dotenvという考え方
ただ、先ほど述べたように、環境変数には扱いづらい側面もあります。
echoコマンドで出力しないと中身を見ることはできないですし、そもそも端末の中にどういった環境変数の変数名(キー名)があるかも、パッとはわかりません。
ちなみに、printenvというコマンドがあり、これで一覧にすることができますが、逆に端末内全体の環境変数を出力してしまうので扱いづらいというのもあります。
そもそも、端末を再起動してしまえば消えてしまう情報でもあり、環境変数を使った開発は色々と面倒があるのです。
そこで、dotenvという考え方が生まれました。
.envというファイルを、開発ディレクトリのルートに置いておきます。
次に、開発しているプロダクトの開発言語で、dotenvというパッケージをインストールします。これらは各言語にポートされていて、PHP/Node.js/Python/Ruby/Go/Dockerなどで利用できます。
dotenvパッケージは、プロジェクトルートにある.envファイルを読み取り、そこに記載されている情報を、環境変数としてエクスポートします。
.envファイルの一般的な内容としては
DATABASE_URL=postgres://user:password@db.example.com:5432
DATABASE_USER=john
DATABASE_PASSWORD=secretpasswordこういったものです。コマンドラインで、exportするのと、同じ内容が記載されています。
大変重要なポイントとして、これら**.envファイルは、バージョン管理システムから除外する**ことが大切です。
なぜなら、これをしなければ、先ほどのconfig.rbのケースと同じく、設定値がバージョン管理されてしまうからです。
つまり、.envファイルは、ユーザーの端末の中だけに存在する、ということが大切になります。
Gitであれば、.gitignoreファイルに、以下のように記載すれば、Gitの管理対象から外すことができます。
.env開発者は複数名いるのが当たり前
さて、一人で開発する分には、.envファイルをディレクトリの中に保存しておけばいいのですが、現代のソフトウェア開発では複数人で行うことも当たり前です。
そこで、これら.envのファイルを共有する必要が出てきます。
しかし、ちょっと待って下さい。
既に、そういった経験がある人もいるかもしれません。
例えば、「メール/Slackなどのチャットツール/DropboxやGoogleDriveなどのファイル共有ツールで、.envファイルを共有」したことはありませんか?
これは非常に危険な行為です。
The Twelve-Factor Appの考えから言えば、設定値は秘匿されないといけない情報です。
しかし、メールでは受信ボックスに溜まりますし、Slackなどでも同様です。
DropboxやGoogleDriveなどはどうでしょう。後から削除すればいいかもしれませんが、リビジョンとして残ってしまいます。
都度都度リビジョンを削除するのでしょうか。めんどくさいですよね。もし、削除し忘れたら??
このように、.envファイルを共有するというのは、設定値を秘匿するというベストプラクティスから真逆の行為なのです。
環境ごとに設定値が違う問題
この時点でエンジニアたちの頭を悩ませていたのですが、もっと複雑な問題も現れました。
それは環境ごとに設定値が違うという問題にも向き合わないといけない、ということです。
特に、Webシステムの開発では、さまざまな開発環境がありますが、一般的には以下に分けられることが多いでしょう。
- ローカル開発環境:開発者の手元のPC/Mac等で開発する環境
- ステージング環境:開発したものを、本番と同じ状態で再現した環境(クラウド上)
- プロダクション環境:本番環境とも。実際にビジネスやソリューションとして提供されている環境
開発現場によっては、さらにここから複数の開発環境に分かれることもありますが、概ねこういった構成が一般的です。
しかし、これらの環境は、すべて別の環境として構築されます。そうでないと意味がないからです。
例えば、ステージング環境は、複数の開発者が作り上げたシステムを、本番と同じ状況でテストするためにあるわけですが、一方で本番であるプロダクション環境とは分けられていないといけません。
そうしないと、本番環境にバグが生じた場合、ビジネスやソリューションとして稼働している本番環境に、悪影響が生じかねないからです。
そこで、サーバーやデータベースといった接続情報は、環境ごとに情報が異なることになります。
先ほどの例で行けば、ローカルでは
DATABASE_URL=postgres://user:password@localhost:5432ですが、
ステージングでは
DATABASE_URL=postgres://user:password@staging.example.com:5432となるでしょう。
さらに、プロダクション版も必要です。
このように、環境変数の名前(キー)としては同一だけど、環境によって値が変わる、ということになります。
dotenvでは、この考え方に対し、以下のようなアプローチを取ります
.env.local.env.staging.env.production
ファイル名を.envではなく、その後に.続けて、環境名をつけるのです。
環境名は、上記で挙げたのは一般的な例で、プロジェクトによっては.env.developとか.env.testなどもあるでしょう。
しかし、共通しているのは、.env.your_enviroment_nameをバージョン管理システムに含めては行けない、ということです。
そうすると、.gitignoreには、どう書けばいい? こうです。
.env
.env.**(アスタリスク)は、ワイルドカードの意味があり、.env.の後に、どんな文字列が来てもマッチする(対象とする)という意味になります。 つまり、.envというファイルと.env.(ここはどんな文字列でもいい)という名前のファイルは、Gitから除外されます。
秘匿する情報が増えすぎる現代
さて、ここまでで我々は多くのことを学びました。
- 設定値は環境変数で秘匿する
- 環境変数をファイルで管理するために、dotenvと.envファイルを活用する
- しかし、メールやチャット、ファイル共有サービスでの共有は危険
- .envファイルはGitなどのバージョン管理システムに含めない
- 環境ごとに、.env.stagingなど、.envファイルを複数作る
さらに難問が増えます。
設定値がありすぎるという問題です。
先ほどの例では、データベースのURLを1つ定義しただけですが、実際の開発現場では、もっと大量の環境変数を扱います。
複数のサーバーを並行して扱うことも一般的ですし、キャッシュなどのために、より早いKV型のデータベースを追加することもあります。
それぞれに、URLやユーザー名、パスワードが存在します。
そうすると、設定値はすぐに、10, 20と増えていきます。
何という名前の設定値が、何の情報なのかもわかりにくくなります。
.envには、行頭に#をつければ、それは自動的にコメントとなります。
しかし一方で、.envそのものを共有するのが大変という課題が残っています。
そこで考え出されたのが、.env.exampleという考え方です。
.env.exampleの書き方
例えば、.envは以下のように書くとします。
# メインデータベースのURL。Postgresを使用
DATABASE_URL=postgres://user:password@localhost:5432開発者の手元開発環境では、これで接続できるはずです。
そして、.env.exampleというファイルを用意しておきます。
内容は以下のとおりです。
# .env.exampleをコピーし、.envにリネームしてから、値を書き換えて利用して下さい
# メインデータベースのURL。Postgresを使用。
DATABASE_URL=postgres://sample_user_name:sample_password@example.com:5432どうでしょうか。見るからにサンプルであり、これを書き換えて使用するのだな、というのがコメントからも伝わります。
そして、大事なことですが、.env.exampleは、Gitなどのバージョンコントロールシステムで管理して良いということです。
exampleということは見本ですので、そこに書かれている値はあくまでサンプルでなければなりません。
しかし変数名(キー名)は、.envと共通ですから、開発者はこれを見て、どういう環境変数名が用意されているのか理解することができます。
先ほどの、環境ごとに.envファイルを用意するのであれば、以下のようになります。
- .env.local ←バージョン管理しない
- .env.local.example ←バージョン管理する
- .env.staging ←バージョン管理しない
- .env.staging.example ←バージョン管理する
- .env.production ←バージョン管理しない
- .env.production.example ←バージョン管理する
ややこしいですね、でもここは大変重要なポイントです。
では、.exampleだけはバージョン管理するには、どうしたらいいでしょうか。
.gitignoreでは、以下のように書きます。
.env
.env.*
!.env.example
!.env.*.exampleこうなります。.gitignoreでは、行頭に!がつくと、その行の指定は否定されます。ややこしいですが「バージョン管理を無視する項目を無視する=バージョン管理する」という意味です。
この.envカオスをなんとかしないといけない
ここまでの状況は、筆者の個人的な体感ですが、2010年代中頃には、既にあったと思われます。
ですが、ここまで効率的にロジックを組み立て、環境変数→.env→環境ごとの.env→.exampleの発明→.gitignoreの利用と進化してきたのに、我々はまだ環境変数そのものをセキュアに送信するという課題は根本的には解決できていないのです。
逆に言えば、この課題は2010年代中頃から現在に至るまで、現場や個人のアイデアによって、その場その場で各々のスタイルによって行われてきました。
つまり、場当たり的で、これがスタンダード、というベストプラクティスは、まだないと言えます。
もっとも、こういう課題解決のアプローチとして、
などがあります。
ただ、これらは、AWSやGCPでプロダクション環境を動作させるために環境変数を管理するためのツール、という色合いが強いです。
また、AWSやGCPはサービスが広範なため、アカウント権限の管理も神経を使います。
つまり、管理をするための管理が比較的大きな負担となりやすいサービスです。(.envの管理だけするには、明らかに過大)
改めて、.envファイルの送信方法をチェックする
今の業界スタンダードはどうなのか筆者にはわかりかねますが(自社サービスの開発がほとんどなため)、.envファイルといえば、現場に参画すれば、上司や先輩などからチャットで送られてくる、というイメージでした。
今もそういうところがあるかもしれません。
筆者の経験で言えば、チャットで送られてきた情報は、受け取ったら削除しますが、本当に削除されたかどうかはわかりません。
例えばチャットでいえばSlackやChatworkなどですが、これらの情報を「削除」ボタンを押して削除したとて『このメッセージは削除されました』などと出ます。
分かっているエンジニアであれば「それは論理削除では?」と思うわけです。
本当に物理削除されたいたとしても、一時的にはSlackやChatwork社のサーバーに、秘匿情報を保存していることになります。
開発の秘密保持契約などで、機密情報を他社のサーバーに置かない、というルールになっていれば、これはとんでもないことになります。
メールで送る? 論外ですね。メールは覗き見される可能性がありますから、絶対にやってはいけません。
暗号化されたUSBメモリで渡す? なかなかレトロな方法ですが、リモートワークの時代には難しいですね。いや、同じフロアでもちょっと面倒かも。USBメモリが禁止なオフィスも多いですし。
一番セキュアなのは、同じオフィスで、付箋にメモをして渡し、.envに書き込んだところで、メモをシュレッダーにかけることです。
うーん、20世紀かな?
とまあ、こういうように、環境変数をセキュアに送る方法というのは、いまだにどれもリスクのある方法なのです。
もっと、手軽に送信、そして管理したい
送信方法の解決決定打が見えてないところに申し訳ないのですが、ここまで読んできた方の中には気づいた人もいるかもしれません。
「これ、送信も問題だけど、管理するのめっちゃ大変じゃね?」
そうです。そのとおりです!
環境ごとに、ファイルが異なり、それが複数ある。
また、環境変数というのは、開発が進むごとに、環境変数の数も増えていく傾向があります。
また、色々な事情で環境変数の値が変更されることがあります。
ある日git pullしたら、.env.exampleに新しい変数が追加されていて、その新しい値を誰かに聞かないといけない。
という状況も発生します。
送信だけではありません。アップデートされたら、何度もその新しい情報を送信しなおしたり、書き直したりしないといけないのです。
さすがに、AWS SSMや、GCP SecretManagerはその辺りのことは考えられていますが、前述のように、ちょっと過大なサービスです。
やはり、環境変数をクラウドで管理するしかない(ただし安全に)
ここまでじっくり読まれた方は、いかに設定値が重要で、秘匿されるべきものかは理解できたかと思います。
一方で、.envというローカルで管理するには、できるっちゃできるけど面倒な課題がたくさんある仕組みで進めないといけない、という現状の課題もあります。
そこで、やはり.envはローカルだけで管理せず、クラウド(リモート)で管理するべきでは、というのが私たちの発想です。
実質、AWSもGCPもクラウドで管理しているわけですから、環境変数の管理=クラウドNGというわけではありません。
一つ重要なのは平文で環境変数をクラウドで管理するのはNGということだと思います。
もちろん、ローカルだけで管理できればいいのですが、それはもう限界に来ています。
では、どうやって安全に環境変数をクラウドで管理するかということになります。
これはもう、強力な暗号化しかありません。
実は、先ほどのチャットやファイル共有サービスでは、既にE2Eの暗号化が施されているものもあります。 そういうものであれば、送信→物理削除(本当に物理削除なのなら)でもいいかもしれません。
しかし、環境変数を簡便に管理したいという目的は満たせません。
.env.exampleファイルが変更したたびにチームのチャットに送信し、数時間後にリーダーが手で削除しないといけないのでしょうか。
開発メンバーは、チャットが来たら、手でコピペして、.envを上書きするのでしょうか。
この時代、それはちょっとナンセンスです。
そこで、私たちはbwsfを開発するにあたり、以下のコンセプトを作りました。
bwsf 開発コンセプト
- 環境変数は、クラウド(リモート)で管理される(ただし、強力な暗号化がなされること)
- 環境変数は、CLIで、手元環境に適用できること
- 環境変数は、CLIで、クラウド(リモート)に送信できること
- .exampleファイルは対象外とし、それらはGit管理すること
これらはコンセプトですが、実装にあたり、詳細なテックスタックを検討しました。
特に、環境変数を「クラウド」に保存する場合、どういう方策がベストなのか。色々考えました。
まず、マネージドなSaaS、という選択肢は最初に浮かんだ強力な選択肢でした。
しかし、すぐ検討から外さざるを得ない、重要な考慮事項も出てきました。
- そのサービスが終わったらどうなるのか
- 暗号強度は十分か
- 仕様変更に振り回される可能性がある
といったものです。
一方で、セキュアなバックエンドサービスを自己開発するにも、そこそこ手間がかかります。
また、bwsfは、.envの管理を簡便化するのが目的であり、オープンソースのセキュアストレージを作るのが目的でもありません。
やはり、バックエンドは既存の何か、特にデータの構造の持たせ方にある程度自由度があり、かつ暗号強度が高い既存の何かを使う、という方針に決めました。
そこで最終的に採用されたのがBitwardenです。
Bitwardenは、パスワードマネージャーの1つで、オープンソースでも提供されています。
暗号化の方式ですが、AES-256を採用し、PBKDF2の反復回数が60万回ということですから、これは私たちの要求を満たすものと考えました。
次に、機能です。
基本的にBitwardenはパスワードマネージャーなので、ログイン項目(ID,パスワード,TOTP)などの項目を保存しやすくなっています。
一方、パスワード以外の情報(例えばクレジットカード情報や、身分証の画像など)も保存できるようになっています。
私たちが注目したのは「ノート」という形式です。
これは、タイトルと、本文という、非常に簡単なブログのような形式で保存されます。
私たちは、本文に「プロジェクト名」を、「ノート本文」に、.envファイル一式をJSON形式で保存することにしました。
また、Bitwardenには「組織」という概念があり、その「組織」に紐づいたユーザーに対して、アクセス権限を持たせることもできます。
すなわち、特定の開発者のみに、特定のプロジェクトのみbwsf/Bitwardenのノート項目にアクセスさせることができます。
開発から外れたメンバーがいれば、Bitwarden上で解除すればいいだけのことになります。
また、先述の通りBitwardenはオープンソースです。
bwsf自体は、クラウド版のBitwardenでも利用できますが、もちろんオープンソース版でも利用可能です。
これは、機密情報を他社のサーバーに置かないという要件の場合に非常に有効です。
自社のオンプレミスサーバーやVPSなどにBitwardenをホスティングし、そこで管理することができます。
これは、AWS SSMや、Google SecretManagerでも出来ない要件です。
bwコマンドの存在も大きかったです。
bwコマンドは、BitwardenのCLIであり、全機能ではありませんが、一般的なBitwardenの操作をコマンドラインから行えます。
bwsfは、現時点でログインなどの機能において、bwコマンドに依存しています。
しかし、このおかげで、bwsfを早期に開発することができました。
最後に
bwsfは、まだ開発途上です。付加したい機能も、まだまだたくさんあります。
バックエンド開発をほとんど行わずに済んだという点で、Bitwardenおよび、Bitwardenオープンソースコミュニティの皆様には、多大なる感謝を申し上げます。
また、bwsfに可能性を感じ、開発のお手伝いをしていただける方は、いつでも歓迎です。ぜひ、コントリビュートガイドラインをお読みの上、ご参画ください。
Kohki SHIKATA, CEO of Bicycle for Mind LLC Dec 1st 2025