blog
« いまさらながらPHP_EOL定数と改行コードについて |  TOPへ  | Jettyのdeploy仕様がおかしいと思う »
Jetty

JettyはTomcatと比べて、ファイルサイズも小さく、起動が早いので最近、僕のお気にいりのWebサーバ兼サーブレットコンテナなんですが、困った点が1つあります。 デフォルトPortは8080なんですが、Portを80に変更して運用するには管理者権限(root)が必要になってしまうようです。一般Shellユーザー(例えばjettyとかユーザーを作成した場合)でPort:80で試してみましたところ、起動はできるんですが途中でExceptionを吐いてコケテしまいます。
本来はApacheのように実行権限のないnologinのWebサーバ用ユーザー(daemon、 nobody...)で起動するのが理想です。というかセキュリティ面を考えると当たり前だと思うんですよね。管理者権限(root)、一般ShellユーザーでJettyを起動してサービスを運用したくない。万が一JettyにBugがあった場合、バッファーオーバーフローをつかれて、権限を奪取されサーバ内に侵入されてしまうわけですから。そこで JettyをPort:80でnologinユーザー(実行権限の無い)で起動する方法をまとめてみました。下記のJettyのFAQページを参考にしました。

http://docs.codehaus.org/display/JETTY/port80
(※)Jetty7や他のバージョンでは試していないので同じ方法かどうかはご注意

今回使用したミドルウェア情報・補助情報

[ミドルウェア]
OS    :2.6.31-17-generic(Ubuntu、CentOS)
Jetty :6.1.22(STABLE)
Java  :1.6.0_15(OpenJDK) 

[補助用語]
$JETTY_HOME  :Jettyのソースを設置したディレクトリ
$JAVA_HOME   :Java SDKをインストールしたディレクトリ
               (コンパイルが発生するのJava Runtimeだけでは駄目かも)

導入方法は次の2パターンが有ります

(1):一般ShellューザでPort:8080で起動してOSの機能である、
ipchains、iptablesを使って、Port:80へのリクエストを8080へフォワードする

(2):JNIを使って、Nativeライブラリを起動時に読み込みnologinユーザーにsetuid (and setumask)を行う

(1):iptables、ipchainsを使用して実現する方法

iptables

/sbin/iptables -t nat -I PREROUTING -p tcp --dport 80 \
 -j REDIRECT --to-port 8080

ipchains

/sbin/ipchains -I input --proto TCP --dport 80 \
-j REDIRECT 8080

(2):nologinユーザーにsetuidを使用して実現する方法

  1. setuidのNativeライブラリをJettyにあてるために解凍したJettyのソースに移動する

  2. cd $JETTY_HOME/extras/setuid
    
  3. (a):Mavenを使ってNativeライブラリ(org_mortbay_setuid_SetUID.c)をインストールする場合

    $JETTY_HOME/extras/setuid配下にMavenの設定ファイル
    (pom.xml)があるので、mvn installを実行する
    

    (b):手動でNativeライブラリ(org_mortbay_setuid_SetUID.c)をインストールする場合

  4. mkdir -p modules/native/target/generated
    
    cp modules/native/src/main/native/ \
     org_mortbay_setuid_SetUID.c  
     modules/native/target/generated  
    
    javah -d modules/native/target/generated \
     -classpath modules/java/target/ \ 
     jetty-setuid-java-6.1.22.jar
     org.mortbay.setuid.SetUID
    
    gcc -I$JAVA_HOME/include/ \
      -I$JAVA_HOME/include/linux/  \
      -shared \
      modules/native/target/generated/ \
      org_mortbay_setuid_SetUID.c -o
      ../../lib/ext/libsetuid.so
    
  5. libsetuid.soファイルができているか確認する

  6. ls $JETTY_HOME/lib/ext/libsetuid.so
    

    ただし、何故かJetty6.1.22のバージョンでは解凍した段階でlibsetuid.soも同梱されていた・・・どの環境によるコンパイルか不明のため、自分でコンパイルは行った方がよいと思います

  7. Jettyを実行させたいnologinユーザのユーザーIDとグループIDを設定ファイルに指定する

  8. vi $JETTY_HOME/etc/jetty-setuid.xml
    

    nobodyユーザーのuid、gidに修正する。uid、gidはお使いの環境によってことなるので/etc/passwdなどで確認して適時値を変更してください

    <Configure id="Server" class="org.mortbay.setuid.SetUIDServer">
     <Set name="startServerAsPrivileged">false</Set>
     <Set name="umask">2</Set> 
     <Set name="uid">jetty</Set> (変更)=> <Set name="uid">6553</Set> 
     <Set name="gid">jetty</Set> (変更)=> <Set name="gid">6553</Set>
    
     <!--↑↑uid、gidの値をjettyから6553に変更する↑↑-->
    
  9. jetty.xmlのPort指定箇所を8080から80に変更する

  10. <Call name="addConnector">
     <Arg>
      <New class="org.mortbay.jetty.nio.SelectChannelConnector">
      <Set name="host"><SystemProperty name="jetty.host" /></Set>
      <Set name="port"><SystemProperty name="jetty.port" default="8080"/></Set>
    
      <!--↑↑defaultの値を8080から80に変更する↑↑-->
    
      <Set name="maxIdleTime">30000</Set>
      <Set name="Acceptors">2</Set>
      <Set name="statsOn">false</Set>
      <Set name="confidentialPort">8443</Set>
      <Set name="lowResourcesConnections">5000</Set>
      <Set name="lowResourcesMaxIdleTime">5000</Set>
      </New>
     </Arg>
    </Call>
    
  11. jetty-setuid.xmlを指定して、Jettyを起動させる

  12. $JAVA_HOME/bin/java -jar $JETTY_HOME/start.jar \ 
      $JETTY_HOME/etc/jetty-setuid.xml \
      $JETTY_HOME/etc/jetty.xml
    

    この時の注意点として、読み込ませる設定ファイルの中で、jetty-setuid.xmlを一番最初に指定すること。指定順番が大事です

設定、コンパイルが上手くいかない場合

java.net.SocketException: Permission denied
 at sun.nio.ch.Net.bind(Native Method)
 at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:119)
 at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:59)

Permission deniedで一般ユーザーでは権限がないというメッセージがでて起動途中でとまってしまいます。この場合は設定ファイル、コンパイル内容などを疑ってください

結論

(1)のほうが、Jettyのソースを変更することなく、Port:80で起動を可能にしますし、お手軽だと思います。ですがPortフォワードというOSの機能を使って実現するあたりが、スマートではないように思います。リクエストのたびにOSに多少なりともフォワード負荷が余計にかかる事が、本来ならアプリケーション処理に割り当てられるはずの、サーバのリソースを無駄に消費している感がなんとも損した気持ちになってしまいます。実際Portフォワードでサーバが消費するリソースは微々たるものだと思いますが・・・。 なにより一般Shellユーザーで結局はPort:8080で起動しないといけないので、セキュリティ的な不安は残ってしまいます。
やはり個人的には(2)がベストだと思います、GCCによるコンパイルなど、ちょっと面倒ではありますが、 Mavenがインストールされている環境であれば、ワンステップで処理が完了するので、そこまで苦にはならないだろうし、nologinのWebサーバ用ユーザーで実行するので、通常のApacheとかの起動内容と同じになりますので、実行ユーザーに関する点のセキュリティ的リスクは限りなく低くなると思います。

 
投稿日:2010/03/11 | カテゴリ:Java | コメント・TrackBack:(0)



Trackback URL

http://blog.fukaoi.org/2010/03/11/jetty-port-80?tb=y&entry_id=23

コメントはこちらからどうぞ

 
 
              
入力された内容
確認内容として表示されます