본문으로 바로가기

웹서비스를 위한 퍼미션 문제

category 리눅스/Linux 일반 2013. 9. 7. 15:18

워드프레스를 설치하다가 config.php 파일을 생성할 수 없다(쓰기 권한이 없어서)는 에러를 접하게 되곤 합니다. 대부분 이와 같은 경우, config.sample.php 파일을 복사해서 수정해서 쓰곤 합니다.

왜 이런 현상이 나타나는지에 궁금증이 생겨 이에 대해 파헤쳐 보기로 했습니다.

전반적인 문제

이 문제는 Apache와 PHP의 파일생성 권한과 연관되어 있다. Apache의 데몬실행 권한은 Apache 설정파일에 설정되어 있는데 우분투의 경우는 www-data이고 기타 다른 배포판에서는 nobody, 혹은 apache로 명명되어 있다(설명을 위해 우분투의 www-data를 기준으로 한다).

이말인즉, 서버에서 웹서비스를 구동할때 그 웹사이트를 구동하는 Owner(소유주)가 서버계정의 소유주가 아니라 Apache의 www-data가 된다는 것이다.

이로인해 PHP를 실행하게 되는 소유주 또한 www-data이기에 PHP를 이용해 파일을 생성하게 되는 경우, 예를들어 fopen을 이용한 파일생성, 업로드를 통해 생성되는 파일등의 소유주가 www-data가 된다.

개념의 혼동이 좀 올 수가 있는데, 한가지 예를 들어보자.

  • 홈디렉토리 경로 : /home/unclepapa/public_html
  • 홈디렉토리 권한 : drwxr-xr-x 6 unclepapa unclepapa

일반적으로 위와 같이 사용자(unclepapa)가 디렉토리에 대한 소유자이며 그룹사용자이다.
디렉토리 퍼미션은 755 로 일반적으로 권장되는 퍼미션이다.(umask 022)

위와 같은 디렉토리에서 서비스되는 웹페이지는 원래 uncelpapa의 권한으로 서비스가 되어야 하지만 보안과 모종의 이유로 이를 www-data로 실행하게 되는 것이다.

계정의 소유주와 퍼미션이 서버에 이미 정해져 있기에 www-data가 파일을 생성할때 퍼미션 755의 마지막 5(Other)의 권한으로 실행되기에 쓰기 권한이 없다는 오류를 접하게 되는 것이다. 이로인해 국내의 XE보드, 그누보드등이 설치시에 설치디렉토리의 퍼미션을 777 혹은 707을 요구하게 된다.

또한 이렇게 PHP등을 이용해 생성하게 되는 파일들의 소유권은 아래와 같이 www-data:www-data 가 된다.

rwxr--r-- www-data www-data  config.php

위와 같이 생성된 파일들은 퍼미션이 644로 이 파일의 소유권자(www-data)만이 이를 수정/삭제가 가능하기에 서버계정의 소유주 조차도 이 파일에 대한 수정 권한이 없게 된다(Other의 권한뿐이 부여받지 못하기에).

물론, 대부분의 업로드관련 소스들은 umask()chmod()함수로 업로드시에 파일및 디렉토리 퍼미션을 666 / 777 으로 설정하여 Other 권한으로 접근하여 수정할수 있게 하고 있다. 하지만 이 또한 보안상으로 위험을 감수해야 하는 방식이다.

얽힌 실타래 풀기

보안과 편의라는 두가지 시점에서 이를 풀어보자.

보안을 중시한 방법

웹서비스의 홈디렉토리의 소유자를 www-data:www-data 로 변경하는 방법이 있다.

sudo chown -R www-data:www-data /home/unclepapa/public_html

위와 같은 방식은 www-data의 권한을 탈취당해도 www-data가 가진 권한이 매우 적기에 보안상 추천되고 있다. 하지만 이는 FTP 접속에 제한이 될 수 밖에 없는데 계정사용자가 홈디렉토리에 접속해도 소유권이 모두 www-data 이기에 업로드/수정이 불가능하게 된다.

FTP 사용에 편의를 위해서는 어떻게든 디렉토리와 파일의 소유권이 계정사용자를 포함해야 하는데, Owner 값은 id 값으로 중복될 수 없기때문에 www-data 값을 변경할 수는 없다.

Group 값은 중복될 수 있기때문에 www-data의 group에 FTP 계정을 사용할 사용자를 추가할 수 있다.

sudo usermod -a -G www-data unclepapa

이 경우 기본적인 리눅스 umask 값에 의해 디렉토리 퍼미션이 755이기에 umask 값을 002로 변경하여 생성하는 디렉토리나 파일들이 775 / 664의 값을 갖도록 해야 파일의 업로드/수정이 가능해진다.

sudo chmod -R 775 /home/unclepapa/public_html

혹은 umask 값을 수정( 특정사용자의 umask 값을 변경하려면 ~/.bashrc 에 값을 적용)

sudo vi /etc/profile

  umask 002

관리의 편의를 위한 방법

Apache의 모듈중에 mod_ruid2가 있는데 이 모듈은 소유권과 퍼미션 조정을 수월하게 함으로써, 편의를 적용하지만 몇가지 제약사항도 있고 보안상으로 추천하는 방법은 아니다. 오직 리눅스에서만 작동한다.

mod_ruid2는 자바 서블릿이나 jsp에 대한 요청을 제외한 모든 http 요청에 적용된다. 많은 호스팅업체와 서버관리자들은 편의를 위해 이 방법을 사용하곤 한다.

비호환성

mod_ruid2는 복잡한 보안 모델이기에 mod_ruid2와 함께 사용할때 모듈 취약점을 소개한다. mod_ruid2를 활성화할 경우 아래에 언급하는 것들을 사용할수 없게 된다.

option Module Identifier Description
Cache cache_module This module caches content that is keyed to URIs. Local and proxy content can be cached. mod_ruid2 caused problems with the ownership of cache lock files. It may be possible to patch the cache modules or mod_ruid2 to solve the issue, but the patch may introduce security vulnerabilities.
Disk Cache disk_cache_module This module caches content that is keyed to URIs. This module uses disk-based storage management and is usually used with cache_module. mod_ruid2 caused problems with the ownership of cache lock files. It may be possible to patch the cache modules or mod_ruid2 to solve the issue, but the patch may introduce security vulnerabilities.
FastCGI mod_fcgi This module is a language-agnostic extension of CGI that allows Apache to serve PHP faster. When using FastCGI, requests are processed by a separate and persistent process. mod_ruid2 cannot force the processes to change UID or GID based on the domain.
MemCache  mod_mem_cache This module caches content that is keyed to URIs. This module requires the cache_module and provides a memory-based storage management system. mod_ruid2 causes problems with the ownership of cache lock files. It may be possible to patch the cache modules or mod_ruid2 to solve the issue; however, the patch may introduce security vulnerabilities.
MPM Worker

mpm_worker_module This is a multi-processing module that allows Apache to serve additional requests by off-loading processing work to supporting threads. Thread-based MPMs will not work with mod_ruid2, because they alter the UID and GID at the process level.
MPM Event mpm_event_module This module is a variant of the worker MPM that allows Apache to serve additional requests by off-loading processing work to supporting threads. Thread-based MPMs will not work with mod_ruid2, because they alter the UID and GID at the process level.
MPM Leader mpm_leader_module This is an experimental variant of the worker MPM that allows Apache to serve additional requests by off-loading processing work to supporting threads. Thread-based MPMs will not work with mod_ruid2, because they alter the UID and GID at the process level.
MPM Perchild  mpm_perchild_module This is a multi-processing module that allows Apache processes to use different UIDs. Thread-based MPMs will not work with mod_ruid2, because they alter the UID and GID at the process level.
MPM Threadpool  mpm_threadpool_module This module is an experimental variant of the worker MPM that allows Apache to serve additional requests by off-loading processing work to supporting threads. This module queues idle worker threads and passes accepted connections to the next available worker. Thread-based MPMs will not work with mod_ruid2, because they alter the UID and GID at the process level.
Mono  mod_mono This module provides ASP.NET support for Apache 2.0 and 2.2. This applies to .NET 1.x and 2.x. Enabling mod_ruid2 prevents mod_mono from building. We may look into this further if there is sufficient demand.
UserDir mod_userdir The mod_userdir module allows visitors to access a site on your server using the http://example.com/~account syntax. This method of accessing websites causes a conflict with mod_ruid2
ModSecurity mod_security Under heavy load, an AcceptMutex can be held by another UID. This causes ModSecurity to fail and exit, which then causes Apache to crash. We are aware of this issue and are working on a solution. In the meantime, do not use ModSecurity with mod_ruid2.
Tomcat mod_jk The Apache Tomcat Connectors (mod_jk) allow Apache to communicate with Tomcat. There are compatibility issues between mod_jk and mod_ruid2 which cause Tomcat to fail.
설치

아래의 명령어를 한줄씩 입력

sudo apt-get install libcap2-dev
sudo apt-get install apache2-prefork-dev
sudo apt-get install gcc
wget http://downloads.sourceforge.net/project/mod-ruid/mod_ruid2/mod_ruid2-0.9.8.tar.bz2
tar xjf mod_ruid2-0.9.8.tar.bz2
cd mod_ruid2-0.9.8
sudo apxs2 -a -i -l cap -c mod_ruid2.c
sudo service apache2 restart

/etc/apache2/mods-enabled 디렉토리에 ruid2.load 가 존재하면 설치가 정상적으로 된 것이다.

옵션
  • RMode : mod_ruid의 적용방법을 설정 (stat : default, config : 유저설정)
  • RCoreDump : CoreDump 여부를 설정 (default : off)
  • RCoreDumpSize : Dump파일의 사이즈를 설정
  • RGroups : 변경 가능 대상의 group을 설정 (최대 지정개수 : 4개)
  • RMinUidGid : 변경 가능 UID와 GID의 제한선을 설정
  • RDefaultUidGid : 변경 UID, GID의 dafault 값을 설정
  • RUidGid : 변경할 USER와 GROUP을 설정
예제

간단히 가상호스트를 설정할 경우

<VirtualHost *:80>
  ...	
  RMode config
  RUidGid unclepapa unclepapa
  ...
</VirtualHost>

기본 아파치 설정파일에서 기본값을 셋팅하고 필요한 구간에서만 사용하는 경우

User                     www-data
Group                    www-data
RMode                    stat
RGroups                  apachetmp
RDocumentChRoot          /home /example.com/public_html

NameVirtualHost 192.168.0.1

<VirtualHost example.com>
  ServerAdmin    webmaster@example.com
  RDocumentChRoot /home /example.com/public_html
  ServerName     example.com
  ServerAlias    www.example.com
  RMode          config		# unnecessary since config is the default
  RUidGid        user1 group1
  RGroups        apachetmp

   <Directory /home/example.com/public_html/dir>
       RMode stat
   </Directory>



   <Directory /home/example.com/public_html/dir/test>
       RMode config
       RUidGid user2 group2
       RGroups groups1
   </Directory>

   <Directory /home/example.com/public_html/dir/test/123>
       RUidGid user3 group3
   </Directory>

   <Location /yustadir>
       RMode config
       RUidGid user4 user4
       RGroups groups4
   </Location>

 </VirtualHost>

 <VirtualHost example.net>
  ServerAdmin    webmaster@example.net
  DocumentRoot   /home/example.net/public_html
  ServerName     example.net
  ServerAlias    www.example.net
 </VirtualHost>

TroubleShooting

Some users have encountered problems with mod_ruid2 while using mutual exclusion. Lines similar to the following will appear in the Apache error logfile (/usr/local/apache/logs/error_log):

[Wed Sep 12 20:21:50 2012] [emerg] (13)Permission denied: couldn't grab the accept mutex

[Wed Sep 12 20:21:51 2012] [alert] Child 27585 returned a Fatal error... Apache is exiting!

[Wed Sep 12 20:21:51 2012] [emerg] (43)Identifier removed: couldn't grab the accept mutex

[Wed Sep 12 20:21:51 2012] [emerg] (22)Invalid argument: couldn't release the accept mutex

[Wed Sep 12 20:22:25 2012] [emerg] (22)Invalid argument: couldn't grab the accept mutex

To resolve this issue, you may add the following line to /usr/local/apache/conf/mod_ruid2.conf:

AcceptMutex posixsem