Container'ı non-root çalıştırınca mount edilen storage dizinine yazamıyorum, izinleri nasıl çözerim?
Soru
Laravel uygulamamı Docker'da çalıştırıyorum ve güvenlik için image'ı root yerine kendi oluşturduğum bir kullanıcıyla (non-root) çalıştırmaya geçtim. Ama bunu yaptıktan sonra `storage/` altına yazma işlemleri patlamaya başladı: log yazamıyor, cache ve session dosyaları oluşmuyor, sürekli "Permission denied" alıyorum. Kurulumum şöyle: `php:8.3-fpm-alpine` tabanlı bir image, uygulama kodu image'ın içinde, ama `storage/` dizinini kalıcılık için bir volume olarak mount ediyorum. Container root iken hiç sorun yoktu. Non-root kullanıcıda izinleri, `chmod 777` yapmadan, doğru şekilde nasıl çözerim?
Cevap
Kısa cevap: Sorun izin bitlerinde değil, UID eşleşmesinde. Container’daki kullanıcının sayısal UID’si, mount edilen dizinin sahibiyle uyuşmuyor; root iken çalışmasının sebebi de root’un (UID 0) her şeye yazabilmesiydi. Çözüm chmod 777 değil, sahipliği hizalamak.
- Kök neden: UID, kullanıcı adı değil. Linux izinlerinde önemli olan sayısal UID/GID’dir. Image’daki
appkullanıcısı UID 1000 olabilir; ama mount ettiğiniz dizin host’ta başka bir UID’ye aitse kernel yazmayı reddeder. İsim eşleşmesi hiçbir şey ifade etmez. - Named volume mü, bind mount mu? Named volume Docker tarafından yönetilir ve ilk oluşumda içeriği root sahipliğiyle doldurur; bind mount ise host dizininin sahipliğini olduğu gibi taşır. İkisinde de çözüm farklı.
- Entrypoint’te hedefli chown + drop. Container’ı root olarak başlatın, yalnızca yazılabilir olması gereken dizinleri chown edin, sonra kullanıcıya düşün:
#!/bin/sh
# entrypoint (root ile başlar, sonra app'e düşer)
chown -R app:app storage bootstrap/cache
exec su-exec app "$@"
- Bind mount’ta build-time UID’yi hizalayın. Dockerfile’da
ARG UID/ARG GIDile kullanıcıyı host’unuzun UID’siyle oluşturun, koduCOPY --chown=app:appile kopyalayın. Böylece entrypoint hilesine bile gerek kalmadan sahiplik baştan doğru olur. - chmod 777’den kaçının. Bu bir güvenlik gerilemesidir ve non-root’a geçme amacınızı boşa çıkarır. Group-writable (
g+w) izin ile doğru grup üyeliği yeterli; yeni dosyaların grubunu korumak için dizine setgid bit’i (chmod g+s) verin. - Kalıcı state’i doğru yere koyun. Birden çok replica çalıştıracaksanız
storage/’ı paylaşımlı bir dosya sistemi üzerinden değil, session/cache için Redis’e ve dosyalar için S3 gibi bir object storage’a taşıyın; volume izin derdi tamamen kaybolur.
Sonuç: Ben olsam build-time ARG UID ile kullanıcıyı sabitler, COPY --chown kullanır ve named volume’ler için entrypoint’te yalnızca storage ve bootstrap/cache’i chown edip su-exec ile app’e düşerdim. Uzun vadede ise kalıcı veriyi mümkün olduğunca S3/Redis’e taşıyıp container’ı gerçekten stateless tutardım.
Yorumlar
Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.