セキュリティのためのApache Solrアクセス制限あれこれ | A Day In The Boy's Life

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

Apache Solrをデフォルトのままで利用しようとすると、管理サイトへのアクセスや検索や情報の更新もどこからでもできたりします。

これは、セキュリティ的にまずいので、Solrへのアクセスを制御する方法をまとめてみます。

なお、環境はRedHat5.9にApache Solr4.2を利用した環境です。

Solrは付属のJettyで動かしているので、その他の環境で動かしている場合は設定が異なるかと思います。



Solr(Jetty)がListenするIPアドレスを限定したアクセス制御


管理サイトをつぶしてしまってかまわないのであれば、JettyがListenするIPアドレスを下記のようにローカルホストからのみに変更するのが手っ取り早い方法かもしれません。


<New class="org.eclipse.jetty.server.bio.SocketConnector">
  <Set name="host"><SystemProperty name="jetty.host" /></Set>
  <Set name="port"><SystemProperty name="jetty.port" default="8983"/></Set>


jetty.xmlの54行目あたりにある上記の箇所を


<Set name="host"><SystemProperty name="jetty.host" default="127.0.0.1" /></Set>
または
<Set name="host">127.0.0.1</Set>


※ 編集後は要Solr再起動。


のように設定することで、localhostからしかアクセスできなくなります。

要はListenするサーバーのIPアドレスが、127.0.0.1になるのでサーバーローカルからしかアクセスできなくなります。

もちろんそのサーバーにGUIでアクセスできる環境があるのであれば、そこからは管理サイトへアクセス可能です。

そして、検索結果の取得やSolr内の情報の更新もlocalhostからの接続に限定できます。


もしサーバーに複数のN/Wの足が出ていて、サービス用(ユーザーがアクセスする)とマネジメント用(管理者がアクセスする)のN/Wに接続されているようなサーバーであればマネジメント用のIPアドレスをListenさせることで、Solrへのアクセスを限定できたりします。


こういった制限を加えた状態で、Solrのサンプルでついているpost.shを利用してUPDATEしようとしてみると、

$ ./post.sh foo.xml
Posting file foo.xml to http://192.168.0.1:8983/solr/collection1/update
curl: (7) couldn't connect to host


というように、接続できないことがわかります。



Solr(Jetty)へ接続するIPアドレスを限定したアクセス制御


もう1つのやり方は、接続元のIPアドレスを限定するやり方です。

このIPアドレスを持つクライアントからしか接続させない、というやり方です。


まず、先ほど書いたListenするIPアドレスはデフォルトの状態(全てで受付ける)に戻しておきます。

次に、jetty.xmlの108行目あたりを以下のように編集します。


<Array type="org.eclipse.jetty.server.Handler">
  <Item>
<!--
             <New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
-->
    <New class="org.eclipse.jetty.server.handler.IPAccessHandler">
      <Call name="addWhite">
        <Arg>192.168.0.1</Arg>
      </Call>
      <Set name="handler">
        <New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
      </Set>
    </New>
  </Item>

※ 編集後は要Solr再起動。


コメントアウトしている箇所は、元々設定ファイルに書かれていた内容ですので、削除してもかまいません。

「addWhite」属性のArgタグに書かれているのが許可したい接続もとのIPアドレスです。

N/Wで制限したい場合は、


<Call name="addWhite">
  <Arg>192.</Arg>
</Call>


という風に書いておきます。
これで、許可していないIPから接続しようとすると


$ ./post.sh foo.xml
Posting file foo.xml to http://localhost:8983/solr/collection1/update

<html>
<head>
<meta

 http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/>
<title>Error 403 Forbidden</title>
</head>
<body>
<h2>HTTP ERROR: 403</h2>
<p>Problem accessing /solr/collection1/update. Reason:
<pre>    Forbidden</pre></p>
<hr /><i><small>Powered by Jetty://</small></i>
</body>
</html>


というようにHTTPステータスコード403が返ってきて拒否されます。



Solrの管理サイトやUPDATE実行時にBasic認証をかける


N/W制限に加えて管理サイトやSolrの情報をUPDATEする箇所にパスワードをかけられたらよりセキュリティは高まるでしょう。

ただし、この方法はBasic認証を使うやり方なので、それほど堅牢ではありませんが。

まず、jetty.xmlの設定ファイル内の適当な箇所(一番最後とか)に下記の設定を埋め込みます。


<Call name="addBean">
  <Arg>
    <New class="org.eclipse.jetty.security.HashLoginService">
      <Set name="name">Admin Realm</Set>
      <Set name="config"><SystemProperty name="jetty.home" default="."/>/etc/realm.properties</Set>
      <Set name="refreshInterval">0</Set>
    </New>
  </Arg>
</Call>


続いて、同フォルダ内にあるwebdefault.xmlの適当な箇所に下記の情報を追加します。


<login-config>
  <realm-name>AdminAuth</realm-name>
</login-config>
<security-constraint>
  <web-resource-collection>
    <web-resource-name>Admin</web-resource-name>
    <url-pattern>/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
    <role-name>admin-role</role-name>
  </auth-constraint>
</security-constraint>


最後に、jetty.xmlで指定したユーザー用の設定ファイル(realm.properties)を同じくetcディレクトリ直下に作成します。


admin:admin, admin-role

書式としては、「ユーザー名:パスワード, ロール名」です。

これで、Apache Solrを再起動し管理サイトにアクセスしてみるとBasic認証がかかっているはずです。


A Day In The Boy&#39;s Life-Solr-Basic認証


ちなみに、この状態だと検索やUPDATEもBasic認証がかけられるため、今まで使っていたプログラムも変更する必要があります。

そのため、付属のpost.shでUPDATEするシェルスクリプトも下記のように変更しておきます。


FILES=$*
URL=http://localhost:8983/solr/collection1/update

for f in $FILES; do
  echo Posting file $f to $URL
  curl $URL -u admin:admin --data-binary @$f -H 'Content-type:application/xml'
  echo
done

curl -u admin:admin "$URL?softCommit=true"
echo


変更箇所は、curlの実行オプションに「-u」をつけ、Basic認証用のユーザー/パスワードを指定しているだけです。

データの送信とコミットは分けて実行しているので2箇所とも変更する必要があります。
(1箇所しか変更しないでBasic認証が通らずにはまりました・・・)


Basic認証に失敗すると下記のようにHTTPステータスコード401が返ってきます。


$ ./post.sh foo.xml
Posting file foo.xml to http://localhost:8983/solr/collection1/update
<html>
<head>
<meta
 http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
<title>Error 401 Unauthorized</title>
</head>
<body><h2>HTTP ERROR 401</h2>
<p>Problem accessing /solr/collection1/update. Reason:
<pre>    Unauthorized</pre></p><hr /><i><small>Powered by Jetty://</small></i><br/>
</body>
</html>


もし、管理サイトとUPDATEのBasic認証用のユーザーを分けたい、もしくはSolrからの検索はBasic認証なしに通したい、という場合は下記のように項目を分けて設定することもできます。


<login-config>
  <realm-name>AdminAuth</realm-name>
</login-config>
<security-constraint>
  <web-resource-collection>
    <web-resource-name>Admin</web-resource-name>
    <url-pattern>/admin/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
    <role-name>admin-role</role-name>
  </auth-constraint>
</security-constraint>

<security-constraint>
  <web-resource-collection>
    <web-resource-name>FileUpload</web-resource-name>
    <url-pattern>/collection1/update/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
    <role-name>guest-role</role-name>
  </auth-constraint>
</security-constraint>


前半分は、管理サイト用のURL(/admin/*)に、後半部分がUPDATEをする際のURL(/collection1/update/*)に対してBasic認証をかけています。


パスワードファイルも、下記のようにユーザーを追加し、ロールを分けておきます。


guest:guest, guest-role
admin:admin, admin-role

これで、guestユーザーはUPDATE時にだけ使え、adminユーザーは管理サイトへのアクセスできるユーザーとして登録できます。



Solr(Jetty)の起動・停止時のポート番号を変更する


Apache Solrはデフォルトでポート8983を使うので、何も制限をかけてない状態でそのまま利用しておくと管理サイトや情報検索・更新が外部から可能になってしまいます。

ということで、他で利用していないオリジナルのポート番号に変更することで、変にアクセスされるリスクを幾分か低下させることができます。


まず、起動時のポート番号の変更ですが、jetty.xmlを編集します。
今回は、8080番ポートを利用するように変更してみます。


<Call name="addConnector">
  <Arg>
      <New class="org.eclipse.jetty.server.bio.SocketConnector">
        <Set name="host"><SystemProperty name="jetty.host" /></Set>
        <Set name="port"><SystemProperty name="jetty.port" default="8080"/></Set>
        <Set name="maxIdleTime">50000</Set>
        <Set name="lowResourceMaxIdleTime">1500</Set>
        <Set name="statsOn">false</Set>
      </New>
  </Arg>
</Call>

port属性のdefault値を8080変更します。
続いて、solr.xmlを編集します。


<?xml version="1.0" encoding="UTF-8" ?>
<solr persistent="true">
  <cores defaultCoreName="collection1" adminPath="/admin/cores" zkClientTimeout="${zkClientTimeout:15000}" hostPort="8080" hostContext="solr">
    <core schema="schema.xml" loadOnStartup="true" instanceDir="collection1/" transient="false" name="collection1" config="solrconfig.xml" dataDir="data"/>
</solr>


編集箇所は、hostPort値の箇所を8080に変更しています。
これで、起動時のSolrの利用するポートを変更できます。


続いて、停止時のポート番号ですが、「Apache Solrを利用して本格的な検索エンジンを導入する 」にて紹介した起動スクリプトを見るとわかりますが、Solrを起動する際に「DSTOP.PORT」というポート番号(8079)を指定しています。

これは、Jettyの仕様で停止コマンドを受付けるポートを起動時に指定できるというもので、「DSTOP.KEY」で指定したキーの値がパスワード代わりとなり、セットで揃っていればJettyの停止ができるというものです。


ですので、停止時のポート番号は起動スクリプトを編集するなどして、起動時のオプションを変更することで変える事ができます。


KEY=stopkey
CORE=solr
cd $JETTY_HOME_DIR
start() {
  $JAVA -Dsolr.solr.home=$CORE -DSTOP.PORT=6060 -DSTOP.KEY=$KEY -jar start.jar >> $LOG_FILE 2>&1 &
  echo "Solr started!"
}
 
stop() {
  $JAVA -DSTOP.PORT=6060 -DSTOP.KEY=$KEY -jar start.jar --stop
  echo "Solr stopped!"
}


上記の場合は、停止時のポート番号を6060に指定してSolrを起動しています。
ポート番号は適当に空いているものを利用してください。



ここで紹介したことも含め、Solrのマニュアル の方も確認してみてください。