4
9

More than 5 years have passed since last update.

H2OをDocker上でNon rootで動かす

Last updated at Posted at 2018-03-18

H2Oをdocker上で80/443で動作させたい場合、起動後setuidによりh2o.confのuserとして動くため、Dockerfile中ではrootとしてH2Oを起動する

背景

http serverはnon rootで動かしたい。
そして複数のIPアドレスを持つ(IPv4 dhcp,autoip,IPv6など)ため、IPアドレスを指定せずに任意のアドレスINADDR_ANY(0.0.0.0)で待ち受けたい。
しかしport:80などの1024未満のポート番号は、CAP_NET_BIND_SERVICE権限を持つプロセスのみがbindできるとなっているため、権限がないとエラー(EACCES)になってしまう。
マニュアル

nginx

nginxはプロセス分離といった対策を取っている。rootで起動、port:80をbind,その後forkし、一般ユーザーで動く

H2O

H2Oはrootで起動、port:80をbind, その後setuidで一般ユーザーになる

with docker

しかしdocker上で、wwwといったユーザーを作り、port:80で待ち受けようとすると、H2Oで以下のエラーがでる。

$ ./h2o -c /usr/www/conf/h2o.conf
failed to listen to port ANY:80: Permission denie

$ ./h2o -m master -c /usr/www/conf/h2o.conf 
failed to bind to 0.0.0.0:80:Permission denied at /usr/www/share/h2o/start_server line 9

原因

原因はDockerfile内、および、docker run時のオプションでH2Oをwwwユーザーとして起動していたため。
INADDR_ANY(0.0.0.0)かつ1024未満のポート番号であるため、CAP_NET_BIND_SERVICE権限が要求されるが、wwwユーザーにはない。

対策

対策はrootユーザーで起動する事。もちろんH2O起動後によりsetuidによりNon rootとなるため、rootで動き続ける危険はない。

h2o.conf
user: www
listen: 80
listen:
  port: 443
  ssl:
    certificate-file: tls/server.crt
    key-file: tls/server.key
hosts:
  "*":
    paths:
      /:
        mruby.handler-file: mruby/header_add.rb
        file.dir: doc_root
        file.dirlisting: ON
    access-log: /dev/stdout

# buildに必要なtoolchain,h2oのbuildは以下を参照
# https://github.com/kawaway/h2o_docker

RUN groupadd www && useradd -m -d /usr/www -g www www
USER    www
WORKDIR /usr/www

RUN mkdir -p conf && \
    mkdir -p tls && \
    mkdir -p doc_root && \
    mkdir -p share/h2o && \
    mkdir -p mruby && \
    cp -r /usr/local/share/h2o/* share/h2o && \
    cp    /usr/local/share/doc/h2o/examples/h2o/server.key tls && \
    cp    /usr/local/share/doc/h2o/examples/h2o/server.crt tls

# H2O run as root for bind port:80/443(require CAP_NET_BIND_SERVICE, cause EACCES) first. then www by setuid
USER    root
ENV H2O_ROOT=/usr/www
CMD ["h2o", "-m", "master", "-c", "/usr/www/conf/h2o.conf"] 
docker-compose.yml
version: '3'
services:
   h2o:
      build: .
      ports:
         - "80:80"
         - "443:443"
      volumes:
         - ./doc_root:/usr/www/doc_root
         - ./conf:/usr/www/conf
         - ./mruby:/usr/www/mruby

docker-compose buildでbuild
docoker-compose up -dでコンテナ起動

参考

明日使えない Linux の capabilities の話
[Go] port 80を開いたあとroot権限を捨てるwebサーバの例

4
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
9