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.
Kısa cevap
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. İmajı non-root ve mümkün olduğunca dar kurmanın diğer yüzünü çok aşamalı build ve distroless kaydında ele almıştım.
Neden
-
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 image’daki mount noktasının sahiplik/izinlerini kopyalar (çoğunlukla root çıkar ama bu garanti değildir); bind mount ise host dizininin sahipliğini olduğu gibi taşır. İkisinde de çözüm farklı.
-
chmod 777neden çözüm değil. 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 zaten yeterlidir; sorun izin bitlerinin darlığı değil, sahipliğin yanlış yerde olmasıdır.
Ne yapmalı
-
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. Yeni dosyaların grubunu korumak için dizine setgid bit’i (chmod g+s) verin. -
Named volume’de entrypoint’te hedefli chown + drop yapın. 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 "$@" -
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.