みなさんこんにちは。ジョブセンスでエンジニアをしている小沼です。初めまして。 16新卒で入社して現在2年目になります。 去年はSQLで円グラフを書いたりしていました。
さて、本題に入ります。先日新規でRailsリポジトリを立ち上げたのですが、その時に秘匿情報の取り扱いについて考えたことがあったので紹介いたします。
Rails5.1から導入されたEncrypted Secretsについて
Rails5.1から標準でEncryptedSecretsという機能を使うことができるようになりました。
参考: Ruby on Rails — Rails 5.1: Loving JavaScript, System Tests, Encrypted Secrets, and more
使い方はざっくりと以下の通りです。
# 鍵とsecretsを準備 # 鍵ファイルは自動で.gitignoreに追記される $ bundle exec rails secrets:setup # 編集 $ bundle exec rails secrets:edit
上記の操作で秘匿情報をセキュアにgit管理することができます。 暗号化された情報はconfig/secrets.yml.enc
に保存されていて、アプリケーションの初期化に際してconfig/secrets.yml
の内容とmergeされます。
実際にアプリケーション上で取り扱う際は、
config/application.rb
またはconfig/environments/*.rb
でconfig.read_encrypted_secrets = true
とした上、Rails.application.secrets.hogehoge
で参照することができます。
正直なところ、かゆいところに手が届かない
新規で作成したリポジトリではRails5.1を使っていたので、EncryptedSecretsを使うことができる環境でした。 しかしながら実際の運用を始めてみると、EncryptedSecretsは少々使いにくいなと思うことがでてきました。
そのように感じた主な理由は以下3点です。
- 暗号化された情報は一行で記述されるため、コンフリクトが起きやすい
- 当然、変更のたびにファイル全体が変更されるため、どの情報がいつ変更されたのか確認できない
- ファイル全体が暗号化されるため、そもそもなんの情報が暗号化されているのかすらわからない
- せめて
ansible-vault show
みたいな閲覧するだけのコマンドが欲しい
なのでぼくの欲求を満たしてくれるGemを作った
実現したいことは以下の通りです。
- 暗号化するのはYAMLの末端(葉)で、かつ
key: value
のvalue
のみ- (それによってセキュアさが落ちることは許容する)
- 暗号化したあともYAMLの構造は守る
- 複合化した情報を手軽に参照できるコマンドを提供する
そしてできたのがleml(Leaf Encrypt YAML)です。
lemlの概要
インストールと使い方
lemlはRailsのプラグインとして実装されていて、秘匿情報はrakeタスクによって閲覧、編集を可能にしています。 実際のRailsプロジェクトはGemfile/Gemfile.lockによってgemを管理することが多いと思います。通常のgemをインストール手順と同じく、
# Gemfile gem 'leml'
とした上で、$ bundle install
。
初期化
その後、初期化を行います。
$ bundle exec rake leml:init Complete! create config/leml.key create config/leml.yml Caution Don't forget add key file in gitignore
※ 自動的に鍵ファイルは.gitignoreに追加されません(今後機能として追加していく予定です)
※ デフォルトでYAMLのシンタックスハイライトが効いて欲しかったので本家のファイル名にある.enc
サフィックスは削除させてもらいました
編集/閲覧
暗号化されたファイルの編集、閲覧は以下のように行います。
$ EDITOR=vim bundle exec rake leml:edit
これでvimが立ち上がり秘匿情報の編集ができるようになります。環境変数'EDITOR'を利用するのでお好きなエディタをセットしてください。 保存して終了すると、内容を暗号化した上でconfig/leml.yml
が更新されます。編集する必要はなく、内容を確認できればいい場合は以下のコマンドで標準出力に出力することも可能です。
$ bundle exec rake leml:show
実際に使ってみると以下のようになります。
元のYAML
--- development: gem: leml: author: onunu github: https://github.com/onunu favorite_precure: twinkle
上記の内容を暗号化すると、
$ cat config/leml.yml --- development: gem: leml: author: aFR1RWUyT0RoVTJzSlgrZ0ptVmlqdz09LS0vL2hzZXVLMzZtTFJ5dk1ZSzdqQ2lRPT0=--bced3fd4f521287b819edb677460e2518bcefb8b github: ZW9PcTBYMy8xakZQUXhqVmhWNlNxNXBKMmM3Z3FLTDZKVEdoRmFzUnB4Z2JScTNKMlo1VktiK0NZS01JVlpLWC0tc2hHTTlrTytOYXpJckxIMmhhNWV5QT09--5340c65706b5619afcc81318dca47c033aecf65f favorite_precure: T21uN2R0NjhsNjdHOHgzY1d6WHp5RDRibnNYVHBOY2l4TC9rcmc1UGZ5OD0tLXllQnQrTXk0Q3lVdmtTdzAzQVprVXc9PQ==--e2568a8cfa84055f367dce531f5e287ac5182033
階層構造があっても問題ありません。何が暗号化されているのか一目瞭然! 実際のアプリケーションから参照する時は、通常のsecretsと同様に扱うことができます。
$ bundle exec rails c Running via Spring preloader in process 68212 Loading development environment (Rails 5.1.2) irb(main):001:0> Rails.application.secrets.favorite_precure => "twinkle"
なにをしているのか
Railtieについて
lemlはRailsの初期化プロセスに相乗りする形でsecretsをmergeしていますが、これはrailtieを使うことで比較的簡単に実装することができます。 lemlでは以下のようにしてRailsの初期化プロセスを拡張しました。
# leml/lib/leml/railtie.rb module Leml class Railtie < Rails::Engine initializer 'Decrypt Leml file' do require 'leml/core' Leml::Core.new.merge_secrets end end end
Railtie
はRails公式のプラグイン機構です。Railtieに基づいて各コンポーネントを組み合わせることで、Railsの様々な処理を手軽に拡張することができます。自作のgem(今回でいうところのLeml
モジュール内)でRails::Engine
クラスを継承したRailtie
クラスを定義することで、Railsが処理を行う際に相乗りさせてもらえます。
参考: Rails::Railtie
今回は初期化プロセスを拡張したいので、内部でinitializer
メソッドを定義しました。これにより、Railsが実行する初期化と同時に、initializer
メソッド内で記述した処理をRailsが行ってくれます。
各タスクについて
lemlでは各操作をrakeタスクで提供しています。 Rails pluginではleml(gem名)/lib/tasks
配下にrakeファイルを設置することで、Railsプロジェクトのタスクとして読み取ってもらえるだけでなく、タスクの実行時にはRailsの初期化プロセスが行われた後でタスクを実行してくれます。
lemlのrakeファイルは以下のようになっています。
# leml/lib/tasks/leml_tasks.rake require 'leml/core' namespace :leml do desc 'initialize secrets yaml' task :init => :environment do Leml::Core.setup end desc 'edit encrypted yaml' task :edit => :environment do Leml::Core.new.edit end desc 'show encrypted yaml' task :show => :environment do Leml::Core.new.show end end
終わりに
いかがでしたか? 自分で書いたファイル数は4つくらいでしたし、それぞれ基本的なことしかしていません。 それでも自分のやりたかったことはある程度実現できました。Railsの懐の深さに驚きです。
また本家のEncryptedSecretsの実装、Railtieの仕組みなどを理解するのにソースコードを読みましたが大変勉強になりました。やはり一次資料はソースコードですね。
lemlはオープンソースで提供しているので、みなさん使ってみてくださいね! まだまだ至らぬ所はありますが、メンテ頑張っていきたいと思います。ご要望やバグ報告、その他ご指摘などはissueを立ち上げていただけると幸いです。
ご注意
lemlはYAMLファイル中のkey: value
のうちvalueだけを暗号化します。当然、keyは平文のまま残るため、keyからvalueの値を推測されてしまう危険性をもっています。 そのリスクを許容できない場合はRails標準のEncryptedSecretsを利用するか、あるいは併用するのがよいでしょう。
ご利用の際にはご注意ください。