背景と根本的な原因
ホストマシンのコードディレクトリをDockerコンテナにマウントし、いざ git status を実行したところ、次のような冷や水を浴びせられた経験はないでしょうか。
fatal: detected dubious ownership in repository at '/workspace/my-app'
To add an exception for this directory, call:
git config --global --add safe.directory /workspace/my-app
この状況は、複数人の開発者が共同利用するステージングサーバーにSSH接続した際にもよく遭遇します。
リポジトリが壊れているわけでも、Gitのバグでもありません。これはCVE-2022-24765のセキュリティパッチ(2022年4月)で追加された防衛メカニズムです。
このパッチ以前のGitは、親ディレクトリを順次遡って .git ディレクトリを探索していました。そのため、マルチユーザーサーバーにおいて、悪意のあるユーザーが /tmp や /var/www などの共有ディレクトリに細工された偽のリポジトリを配置できてしまう脆弱性がありました。その .git/config 内に悪意のあるhookや危険なスクリプトを指す core.fsmonitor 設定を埋め込まれると、配下のサブディレクトリで任意のGitコマンドを実行しただけで、あなたの権限でマルウェアが即座に実行されてしまう危険性があったのです。
この脆弱性を塞ぐため、Gitは新たなルールを設けました。それは、「コマンドを実行するユーザー自身が、そのリポジトリディレクトリの所有者でなければならない」というものです。
問題はDockerを利用したときに生じます。ホスト上のソースコードは一般ユーザー(通常はUID 1000)が所有していますが、コンテナ内はデフォルトで root(UID 0)として動作します。この両者のUIDの不一致を検知し、Gitがリスク回避のために操作を即座にブロックするのです。
Gitのバージョン確認
safe.directory の仕組みは、Git 2.35.2以降で利用可能です。まずは、ホストまたはコンテナ内のバージョンを確認しましょう。
git --version
バージョンが古い場合は、パッケージをアップグレードする必要があります。Ubuntu/Debianの場合:
sudo apt update && sudo apt install -y git
Alpine Linux(Dockerでよく使われるイメージ)の場合:
apk update && apk add --no-cache git
安全策から根本対策までの4つのアプローチ
ネット上のコマンドをやみくもにコピー&ペーストしてはいけません。実際の運用環境に適した解決策を選びましょう。
1. 特定のディレクトリをホワイトリストに追加する(推奨)
最も手軽なのは、対象のディレクトリが完全に安全であることをGitに明示的に伝える方法です。
git config --global --add safe.directory /workspace/my-app
このコマンドを実行すると、現在のユーザーの ~/.gitconfig に1行追記されます。root権限で動作しているコンテナ内の場合は、/root/.gitconfig に書き込まれます。
2. DockerfileやComposeで事前設定する
開発用コンテナ(dev-container)やCI/CDランナー(GitLab CI、Jenkinsなど)を構築する場合、コンテナを再作成するたびに設定が失われてしまいます。そのため、Dockerfile 内に直接記述しておくのがスマートです。
FROM node:20-alpine
WORKDIR /app
# gitをインストールし、作業ディレクトリをホワイトリストに登録
RUN apk add --no-cache git \
&& git config --global --add safe.directory /app
CMD ["npm", "run", "dev"]
ローカル開発環境で docker-compose.yml を使用している場合は、entrypoint または command で次のように実行することも可能です。
version: '3.8'
services:
api:
image: python:3.11-slim
working_dir: /code
volumes:
- .:/code
command: >
sh -c "git config --global --add safe.directory /code &&
python main.py"
3. ホストとコンテナのUIDを同期させる(開発におけるベストプラクティス)
エラーの本質は、UIDの不一致(1000 vs 0)にあります。したがって、ホスト側のUID/GIDでコンテナを動かすのが最も根本的かつクリーンな解決策です。
docker run -it --rm \
-u $(id -u):$(id -g) \
-v $(pwd):/workspace \
-w /workspace \
node:20-alpine sh
この方法には一石二鳥のメリットがあります。Gitのdubious ownershipエラーが解消されるだけでなく、コンテナ内で生成されたファイル(node_modules やキャッシュなど)がホスト上でroot所有者になってしまう問題も防げます。
4. Linux上でディレクトリの所有権を修正する
共有サーバーでこのエラーが発生する場合、多くは誰かが誤って sudo git clone や sudo git pull を実行したことが原因です。所有権を自身のユーザーアカウントに戻すだけで解決します。
sudo chown -R $USER:$USER /var/www/my-app
警告:ワイルドカード「*」の使用は避ける
Stack Overflowなどの回答では、手軽さから次のようなコマンドが推奨されていることがあります。
# 危険:実環境では絶対に避けること
git config --global --add safe.directory '*'
ワイルドカード * を指定すると、システム全体のすべてのディレクトリでCVE-2022-24765による保護が無効化されてしまいます。この設定が許容されるのは、即座に破棄される一時的なCI/CDコンテナ(エフェメラルランナー)内部程度です。個人の作業マシンや本番サーバーでは決して適用しないでください。
確認とお手入れ
設定後は、Gitに正しく反映されているか確認しましょう。
現在設定されているsafe.directoryの一覧表示
git config --global --get-all safe.directory
設定が保存されているファイルパス(system、global、localのいずれか)を確認したい場合は、--show-origin フラグを追加します。
git config --list --show-origin | grep safe.directory
ステータスの確認
プロジェクトディレクトリでいつものコマンドを叩いてみましょう。
git status
エラーや警告が出ず、通常通りブランチ情報やコミット状況が表示されれば完了です。
不要になったパスの削除
使い終わった古いプロジェクトを削除した際は、~/.gitconfig を整理しておくのが理想的です。
git config --global --unset-all safe.directory /workspace/my-app
UIDの仕組みと safe.directory を正しく理解すれば、システムのセキュリティを犠牲にすることなく、権限エラーをわずか30秒でスマートに解決できます。
