fbpx

CL LAB

HOME > CL LAB > Chef > [和訳] 「install.sh の Curl パイプ Bash」問題を解決する 5つの方法 #getchef

[和訳] 「install.sh の Curl パイプ Bash」問題を解決する 5つの方法 #getchef

 ★ 70

本稿は 5 Ways to Deal With the install.sh Curl Pipe Bash problem (2015/07/16) の和訳です。

あなたは chef-client をインストールするのに、次のようなコマンドを実行するように書いてあるドキュメントを見たことがあると思います。


curl -L https://www.chef.io/chef/install.sh | bash -s -- -v 12

次のようにもできます。


curl -L https://chef.sh | bash

ChefDK では次のようにします。


curl -L https://www.chef.io/chef/install.sh | bash -s -- -v 12 -P chefdk

これらのコマンドが行うことは omnitruck と呼んでいるサービスと通信し、実行しているのはどの種類のディストリビューションなのかを判断するロジックを持つ 600 行くらいの短さのシェルスクリプトをダウンロードし、正しい s3 パッケージをダウンロードします(また、バージョン番号、ナイトリー、chefdk をインストールするコマンドラインオプションなどを取り扱っています)。

これらのコマンドすべては、インターネットからインストーラスクリプトをダウンロードして、bash シェルを通してローカルのマシンで実行するという形式に従っています。この方法は、多くの理由で非難されています。

この方法についてあなたが感じている心配の度合いと実際の危険性について、私はどう考えているかを整理し、それから代替策について説明しようと思います。これまで Chef をインストールするのに curl | bash を使ってはいけないと言った人は一人もいなかったでしょう? 同時に、このインストール方法を提供している理由と、新規ユーザにはこのインストール方法を最初に提示し続ける理由も説明しようと思います。

では、curl | bash に対して行える 5つの方法について説明しましょう。

気にしない

そう、install.sh を取り扱う最初の方法は、気にしない、ということです。もう少し説明しますが、これから説明する理由も気にしないのなら、1〜2節飛ばしてください。

はじめに、どうして気にしないのかという多くの根拠について書きました。curl | bash が悪いという議論を行う人々はさらに悪い議論を作り出すからです。私達が構築できる最高の curl | bash を作ったと言えることに満足しています。SSL を使っているし、すべてのバイナリのチェックサムを計算しているし、チェックサムは製品とは分離しているし、アカウントは異なる証明書を持っているし、すべてのスクリプトは git の履歴で確認できるし、PR を送ることでアクセスすることもできます。もし 600行の install.sh スクリプトをレビューしていないことが心配なら、10,000行の ruby コードの chef-client だってそれ以上に心配しないとおかしいでしょう? (アップグレードする前に RC 版をテストしていただけると嬉しいです。リモートからは十分でないと知っていますので)

しかしながら、install.sh による唯一の危険は、omnitruck サーバが侵入されて install.sh が改竄されるということです。そしてあなたはこのファイルをサーバにダウンロードし、root で実行することになります。もし install.sh の使用が自動化プロビジョニング手順の一部だったり、install.sh を実行する自動アップデートスクリプトならば、改竄を検知するまでの長い間、攻撃者はあなたのサーバに root で実行するコードを注入し放題になってしまうでしょう。RPM や Deb レポジトリの場合、root で実行するコードはあなたのディストリビューションの yum や apt-get バイナリで、ダウンロードしたパッケージは署名されていて、バイナリの検証はたいてい有効なので、改竄されたパッケージは検出されるため、この問題はありません (ダウンロードした公開鍵による署名が改竄されていないこと、私達が署名に使っている秘密鍵が誰かに盗まれていないこと、これらを信用しなければいけませんが)。

これが install.sh の弱点で、パッケージマネージャ経由でのインストールがよりよい方法であるということを隠さずに認めます。しかし、これについて心配する人々や私達のサーバが侵入されることを心配する人々は、パッケージマネージャのディストリビューションの経路の改竄についてはもちろん、私達の署名用の鍵も改竄されていることについてはおそらく心配していないということを述べました。ただし、omnitruck ruby アプリが壊れる可能性があることと、インターネットに直結したサーバに私達の秘密鍵を保存しないという追加的な予防手段があることは正しいです。

install.sh スクリプトをどこかにキャッシュする

install.sh 問題のもっとも単純な解決策は、スクリプトをキャッシュすることです。一旦キャッシュしてしまえば、誰かが私達のサーバに侵入しても、あなたのスクリプトまでは改竄できません。これで、あなたのディストリビューションで動作させている apt-get が「任意のバイナリ」であるのに比べれば、あなたがインターネットからダウンロードするような「任意のスクリプト」ではありません。誰かがディストリビューションのミラーサイトの 1つに侵入することと、おおよそ同レベルの攻撃があなたに対して行われるという危険に晒されています。もちろんスクリプトが古くなることがありますが、長い間とても安定していて API も安定しており、github の履歴を精査できるし、必要ならば必要なときにバグ修正を取り込むこともできます。

スクリプトはダウンロードする製品のチェックサムを計算しており、そして製品とチェックサムは別々のレポジトリに内部的には別々の証明書で格納されているので、すべてが SSL で行われるため、ダウンロードした情報が改竄されるようなほとんどの方法は事前に軽減されています。製品の署名の頂点で行われるので (MacOSX と Windows で修正されています)、パッケージマネージャのパッケージ検証を有効にしていれば、パッケージレポジトリを使っていて私達の公開鍵をインポートしているなら、改竄されたパッケージも拒絶します。

packagecloud.io レポジトリを使用する

オーケー、install.sh が単に嫌いなら、私達が packagecloud.io に持っている RPM や deb ファイルレポジトリを使うという次の解決策を聞いて嬉しくなるでしょう。packagecloud サイトに packagecloud レポジトリをインストールする方法のドキュメントがあります。しかし、デフォルトではおそらくやりたくないであろう(笑)、「Bash Scripts」タブに困惑するでしょう。まあ、「Chef」タブを叩いてみてください。Chef の packagecloud Cookbook が行う作業をとても短く親切に説明しています。

Chef の Recipe の metadata.rb に次のように記載します。


depends "packagecloud"

そして、omnibus_update Cookbook からちょっと借りてきた、多少複雑な Recipe を提案します。


packagecloud_repo "chef/stable" do
type "deb" # or "rpm"
end

package "chef" do
version node['myorg']['chef-client-version']
action :install
notifies :run, 'ruby_block[omnibus chef killer]', :immediately
end

# Continuing with a chef client run after the client has been upgraded will give undetermined results
ruby_block 'omnibus chef killer' do
block do
raise 'New omnibus chef version installed. Killing Chef run!'
end
action :nothing
end

しかし、これは Chef をインストールするのに Chef が動作していることを要求するので、初期ブートストラップの問題は解決していません。なので、次の 2つの方法のうちの 1つを組み合わせて使う必要があります。このコードは、(皆が deb/rpm を使っているなら) omnibus_update Cookbook の最も一般的なベストプラクティスであるユースケースの置き換えだと思います。

Knife Bootstrap の独自テンプレート

これは文字通り、ブートストラップする Node 上で knife bootstrap が実行するコードを、お好みのシェルスクリプトコードを置き換えます。デフォルトで Chef が使うテンプレートは「chef-full」(「フルスタック omnibus インストーラ」の略)と呼ばれており、Chef のソースコード内にあります。Chef 12.4.1 での chef-client をインストールするコードブロックはここにあり、好きなように変更できます。


<% if knife_config[:bootstrap_install_command] %>
<%= knife_config[:bootstrap_install_command] %>
<% else %>
install_sh="<%= knife_config[:bootstrap_url] ? knife_config[:bootstrap_url] : "https://www.opscode.com/chef/install.sh" %>"
if test -f /usr/bin/chef-client; then
echo "-----> Existing Chef installation detected"
else
echo "-----> Installing Chef Omnibus (<%= latest_current_chef_version_string %>)"
do_download ${install_sh} $tmp_dir/install.sh
sh $tmp_dir/install.sh -P chef <%= latest_current_chef_version_string %>
fi
<% end %>

このコードを編集して、packagecloud レポジトリを設定するシェルコードを挿入したり、特定の Chef バージョンを S3 バケットにアップロードしてあなたのプライベート URL から直接ダウンロードするなどができます。これに満足してテストしたくなったら、~/.chef/bootstrap/my-chef-full.erb に配置し、knife bootstrap の引数に -t my-chef-full を渡します。よい例を出せないのですが、このコードを置き換えるのに単純なコードは次の通りです。


rpm -ivh https://myawsbucket.s3.amazonaws.com/chef-12.4.1-1.el5.x86_64.rpm

独自の --bootstrap-install-command

前述の単純な方法で、賢明な読者のみなさんは他にやり方があることに気がついているでしょう。テンプレートをフォークすると、私達の何らかの修正や強化といった、新しい修正をフォークしたテンプレートにインポートするコストを支払わなければいけないという問題があります。フォークしたテンプレートのユーザは、テンプレートの変更が必要となる、新しい validator なしのブートストラップ機能を使うことが難しいです。それらが chef-full に行われたら、独自テンプレートを使っているユーザは自力でテンプレートを修正しなければ使えません。みなさんが install.sh スクリプトを他のコマンドで置き換えたいだけならば、knife bootstrap コマンドライン (か、knife.rb ファイル) に、そうできる機能を 11.14.0 で追加しています。install.sh を置き換えるフラグを knife bootstrap に追加し、前述の例を使い回すとするなら、--bootstrap-install-command='rpm -ivh https://myawsbucket.s3.amazonaws.com/chef-12.4.1-1.el5.x86_64.rpm' となります。コマンドがスペースを含んでいるので、コマンドライン引数をシングルクォートでくくることに注意してください。また、複雑なことを始めようとするなら、他のシェル拡張の問題について何度も直面するでしょう (Windows ユーザは経験が必要です)。

もし、curl | bash を別のコマンドに置き換えたいだけならば、これが最も簡単な解決策で、将来もずっと使い続けられる方法です。テンプレートをフォークするよりもお勧めの方法です。ブートストラップテンプレートをこの記事で最初に説明したのは、より説明しやすくるすためです。

まとめと歴史的な経緯

本記事が curl | bash 問題に不満を抱いている人々に対して代替を示すことができたらと思います。代替のすべての違いについて総合的な一覧となっていないかもしれませんが、手始めとして有用な情報を提供できたと思います。

また、心配事をきっぱりと取り除くことも意図していました。curl | bash をみなさんに提供するには、みなさんにお勧めの解決策を示しておく必要があると考えたからです。knife が実行される Workstation はホストに何をブートストラップしようとしているか「知る」必要性がないという問題と、ホストは chef や ohai をインストールしていなかったので、knife bootstrap が多くのプラットフォームにわたって omnibus 製品をインストールする方法が必要であるという困難な問題を解決するために install.sh を書きました。私達は、増加するブートストラップテンプレートの問題と、(異なる対象にブートストラップしようとするときに、人々にキーボードで ohai を叩かせたり、knife bootstrap コマンドの互換性をなくしたりといった) ブートストラップされるディストリビューションを含むすべてのブートストラップコマンドの必要性という問題に直面していました。Ubuntu マシンだけを管理しているような Chef ユーザの視点からだと、解決するにはあまりにも複雑な問題でしょう。異なる Linux ディストリビューションがごたまぜで、ときどき Solaris や AIX が混ざるような顧客を抱えた Chef 開発者の視点からだと、必ず解決する必要のある問題でした。packagecloud レポジトリという解決策は Ubuntu と RHEL ユーザにとっては有効ですが、AIX や Solaris で問題を抱えている顧客にはまったく無意味なものです。

knife bootstrapinstall.sh の組み合わせは、個々の事案に特に最適化されたものでは必ずしもありませんが、おおよそすべてをカバーする解決策です。これが、なぜそうなのか、なぜ存在しているのか、なぜしばらくの間出回り続けるのか、という理由です。もし他の方法が登場したら使ってみて、是非フィードバックかサムアップをお寄せください。

近日公開予定

test-kitchen、vagrant-omnibus、chef-provisioning、など、omnibus 製品をインストールするための独自の方法を組み込んでいるその他すべてのコード間の違いを解決する、mixlib-install をちょっと見てみましょう。

CL LAB Mail Magazine

CL LABの情報を逃さずチェックしよう!

メールアドレスを登録すると記事が投稿されるとメールで通知します。

メールアドレス: 登録

※登録後メールに記載しているリンクをクリックして認証してください。

Related post

新規CTA