🚪

80 番ポートで Permission Denied - Capability 設定

2023/05/25に公開

記事に書く内容

Docker コンテナで 80 番ポートをバインドするサーバープログラムを実行すると listen tcp :80: bind: permission denied というエラーが起きたので、その理由と対処方法を書き残します。

Sample プログラム

原因

サーバープログラムを非rootユーザーで実行する Alpine ベースのコンテナを立ち上げたとき Permission Denied が表示されていました。
どうやら well-known ポートをバインドするには非rootユーザーでは権限が足りないようです。

Dockerfile
FROM alpine

WORKDIR /app

COPY ./serve /app/serve

# 非ルートユーザー appuser を追加する 
RUN adduser -u 1000 -D -H appuser \
 && chown appuser:appuser /app/serve

USER appuser

# 80 番ポートで実行するサーバープログラム
CMD ["/app/serve"]
docker image build -t serve .
docker container run --rm -p 80:80 serve

対処方法

rootユーザーで実行してしまえばエラーは解消しますが、なるべく少ない権限で解決するために Capability という Linux のセキュリティ機能を使ってポートをバインディングできる権限を追加することにしました。
Capability を簡単に説明すると、ルートユーザーの権限を細かく分けて割り当てる機能です。より詳しい内容は man コマンドで確認できます。

man capabilities

Capability はファイルに直接割り当てることができるので、今回は CAP_NET_BIND_SERVICE というポートバインディング権限を与える Capability を /app/serve プログラムにセットすることにしました。setcap コマンドで設定できます。

setcap 'cap_net_bind_service=+ep' /app/serve

Alpine Linux で setcap コマンドを実行するには libcap パッケージを追加します。
最終的に Dockerfile は以下のようになりました。

Dockerfile
FROM alpine

WORKDIR /app

COPY ./serve /app/serve

+ # serve ファイルにポートバインディングを許可する
+ # https://jessicadeen.com/posts/2020/how-to-solve-the-listen-tcp-80-bind-permission-denied-error-in-docker/
+ RUN apk add libcap \
+  && setcap 'cap_net_bind_service=+ep' /app/serve

# 非ルートユーザー appuser を追加する 
RUN adduser -u 1000 -D -H appuser \
 && chown appuser:appuser /app/serve

USER appuser

# 80 番ポートで実行するサーバープログラム
CMD ["/app/serve"]

参考

How to solve the "listen tcp :80: bind: permission denied" error in Docker
コンテナセキュリティ コンテナ化されたアプリケーションを保護する要素技術

株式会社ROBONの技術ブログ

Discussion