Apache suexec 설정
Gentoo에서 Apache 설정중에 SUEXEC를 이용하여 특정 디렉토리의 스크립트 권한을 apache가 아닌 다른 소유자 및 그룹의 권한으로 실행시키게 할수 있는 방법이 있는데, 매번 apache:apache 권한으로 CGI 스크립트를 실행되도록 하다가 실제 해당 User의 권한으로 실행되도록 설정하다가 몇가지 문제점에 부딪혀서 그 해결방법을 메모해 둔다.
Suexec는 Apache에서 예제로 아래와 같은 방식으로 설정한다.
<IfDefine SUEXEC>
<Directory /home/test/cgi-bin>
Options +ExecCGI
Require all granted
AllowOverride All
Options +MultiViews -Indexes +SymLinksIfOwnerMatch +IncludesNoExec
AddHandler cgi-script .sh
</Directory>
</IfDefine>
위의 내용을 보면 알겠지만, SUEXEC를 사용하려면 플래그가 선언되어 있는지 확인해야 하는데, /etc/conf.d/apache.conf 를 확인해 보면 다음과 같은 플래그가 있어야 한다.
# /etc/conf.d/apache2: config file for /etc/init.d/apache2
# When you install a module it is easy to activate or deactivate the modules
# and other features of apache using the APACHE2_OPTS line. Every module should
# install a configuration in /etc/apache2/modules.d. In that file will have an
# <IfDefine NNN> directive where NNN is the option to enable that module.
#
# Here are the options available in the default configuration:
#
# AUTH_DIGEST Enables mod_auth_digest
# AUTHNZ_LDAP Enables authentication through mod_ldap (available if USE=ldap)
# CACHE Enables mod_cache
# DAV Enables mod_dav
# ERRORDOCS Enables default error documents for many languages.
# INFO Enables mod_info, a useful module for debugging
# LANGUAGE Enables content-negotiation based on language and charset.
# LDAP Enables mod_ldap (available if USE=ldap)
# MANUAL Enables /manual/ to be the apache manual (available if USE=docs)
# MEM_CACHE Enables default configuration mod_mem_cache
# PROXY Enables mod_proxy
# SSL Enables SSL (available if USE=ssl)
# STATUS Enabled mod_status, a useful module for statistics
# SUEXEC Enables running CGI scripts (in USERDIR) through suexec.
# USERDIR Enables /~username mapping to /home/username/public_html
#
#
# The following two options provide the default virtual host for the HTTP and
# HTTPS protocol. YOU NEED TO ENABLE AT LEAST ONE OF THEM, otherwise apache
# will not listen for incomming connections on the approriate port.
#
# DEFAULT_VHOST Enables name-based virtual hosts, with the default
# virtual host being in /var/www/localhost/htdocs
# SSL_DEFAULT_VHOST Enables default vhost for SSL (you should enable this
# when you enable SSL)
#
APACHE2_OPTS="-D DEFAULT_VHOST -D INFO -D SSL -D SSL_DEFAULT_VHOST -D LANGUAGE -D PHP -D USERDIR -D WSGI -D DAV -D SUEXEC -D SVN -D SVN_AUTHZ -D DAV_FS -D PROXY"
APACHE2_OPTS=”-D DEFAULT_VHOST -D INFO -D SSL -D SSL_DEFAULT_VHOST -D LANGUAGE -D PHP -D USERDIR -D WSGI -D DAV -D SUEXEC -D SVN -D SVN_AUTHZ -D DAV_FS -D PROXY”
SUEXEC를 사용하려면 -D SUEXEC를 추가하면 되겠지만, 만약 아래에서 설명하고자 하는 SuexecUserGroup를 사용하려면 apache emerge 할때 suexec 플래그를 make.conf에 추가하고 재컴파일을 해야 한다.
USE="systemd cgi phpdbg ... suexec"
SUEXEC_DOCROOT="/home"
그리고 SUEXEC_DOCROOT 환경변수는 “/home”으로 추가가 되어 있는데, 이 /home을 추가해야 하는 이유가 궁극적인 이 블로그 내용을 작성하게된 이유가 된다.
Apache의 USERDIR를 사용하면서, SUEXEC 플래그를 추가한 상태로 emerge를 하게 되면 00_mod_userdir.conf 파일이 다음과 같이 업데이트 되게 된다. 물론 emerge가 완료된 이후 etc-update를 통한 업데이트이다.
$ cat /etc/apache2/modules.d/00_mod_userdir.conf
# Settings for user home directories
<IfDefine USERDIR>
# UserDir: The name of the directory that is appended onto a user's home
# directory if a ~user request is received. Note that you must also set
# the default access control for these directories, as in the example below.
UserDir public_html
# Control access to UserDir directories. The following is an example
# for a site where these directories are restricted to read-only.
<Directory /home/*/public_html>
AllowOverride FileInfo AuthConfig Limit Indexes
IndexOptions NameWidth=*
IndexOptions +Charset=UTF-8
Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
<Limit GET POST OPTIONS>
Require all granted
</Limit>
<LimitExcept GET POST OPTIONS>
Require all denied
</LimitExcept>
</Directory>
# Suexec isn't really required to run cgi-scripts, but it's a really good
# idea if you have multiple users serving websites...
<IfDefine SUEXEC>
<Directory /home/*/public_html/cgi-bin>
Options ExecCGI
SetHandler cgi-script
</Directory>
</IfDefine>
</IfDefine>
# vim: ts=4 filetype=apache
위의 내용대로라면 /home/user/cgi-bin/ 하위의 디렉토리에 스크립트가 해당 유저의 권한으로 스크립트가 실행될수 있다는 것으로 이해할수 있는데, 실제로 http://example.com/~user 라는 userdir 접근 방식으로 유저 디렉토리의 페이지를 읽게 되면 실제로 cgi-bin 내의 CGI, Perl 스크립트등이 해당 user의 권한으로 실행할수 있다는 것을 알수 있다.
문제는 이것이 아니고 별도의 도메인을 갖고 있는 VirtualHost 의 웹페이지가 문제가 된다. VirtualHost로 sub.example.com 이라는 도메인을 설정하고 그 설정내에서 SUEXEC를 설정해봐야 당연히 apache(기본) 권한으로 실행될수 밖에 없다. 따라서 아파치 설정중에 “SuexecUserGroup”를 이용해서 어떤 User/Group으로 실행시킬 것인지 지정할수 있게 된다.
골때리는 상황은 이거다.
<VirtualHost *:80>
ServerName cgi.user.example.com
DocumentRoot "/home/user/public_html/cgi-bin"
RewriteEngine On
DirectoryIndex index.sh
SetEnv LANG ko_KR.UTF-8
SetEnv LC_ALL ko_KR.UTF-8
SetEnv LC_LANG ko_KR.UTF-8
<IfDefine SUEXEC>
SuexecUserGroup user user
<Directory /home/user/public_html/cgi-bin>
Options +ExecCGI
Require all granted
AllowOverride All
Options +MultiViews -Indexes +SymLinksIfOwnerMatch +IncludesNoExec
AddHandler cgi-script .sh
</Directory>
</IfDefine>
</VirtualHost>
위의 내용대로 cgi.user.example.com이라는 호스트 설정내에 SuexecUserGroup 추가하고 해당 디렉토리 내의 스크립트를 실행시키도록 하게 되면, /var/log/apache2/suexec_log (suexec 추가된 apache 경우의 log 파일 위치) 기록에 이런 메세지를 확인할수 있다.
[2024-05-11 12:12:52]: command not in docroot (/home/user/public_html/cgi-bin/index.sh)
?? docroot가 뭐야 ??
docroot 디렉토리에 안에 없으면 실행 안시켜준다는 거절의 내용이다. 500에러를 경험하게 될것이다. 그럼 docroot가 지금 어디냐? 아래의 내용은 make.conf에 환경변수 SUEXEC_DOCROOT를 추가 하기 전의 내용이다.
# suexec -V
-D AP_DOC_ROOT="/var/www"
-D AP_GID_MIN=100
-D AP_HTTPD_USER="apache"
-D AP_LOG_EXEC="/var/log/apache2/suexec_log"
-D AP_SAFE_PATH="/usr/local/bin:/usr/bin:/bin"
-D AP_SUEXEC_UMASK=077
-D AP_UID_MIN=1000
-D AP_USERDIR_SUFFIX="public_html"
위와 같이 DOC_ROOT는 아파치가 컴파일되었을때 지정된 SUEXEC의 설정 값인데, 보통 apache의 default 웹페이지의 DocumentRoot를 의미하는 것 같다. 일단, 확실한건 VirtualHost 설정의 DocumentRoot “/home/user/public_html/cgi-bin” 와는 다른 것이다.
apache를 컴파일 할때 suexec 플래그가 추가된 상태로 emerge가 끝날때 아래와 같은 메세지가 나온다. 본인이 필요하면 SUEXEC_DOCROOT를 추가하라는 건데, 이게 default가 /var/www 라고 안내하고 있다.
* You can manipulate several configure options of suexec
* through the following environment variables:
*
* SUEXEC_SAFEPATH: Default PATH for suexec (default: '/usr/local/bin:/usr/bin:/bin')
* SUEXEC_LOGFILE: Path to the suexec logfile (default: '/var/log/apache2/suexec_log')
* SUEXEC_CALLER: Name of the user Apache is running as (default: apache)
* SUEXEC_DOCROOT: Directory in which suexec will run scripts (default: '/var/www')
* SUEXEC_MINUID: Minimum UID, which is allowed to run scripts via suexec (default: 1000)
* SUEXEC_MINGID: Minimum GID, which is allowed to run scripts via suexec (default: 100)
* SUEXEC_USERDIR: User subdirectories (like /home/user/html) (default: public_html)
* SUEXEC_UMASK: Umask for the suexec process (default: 077)
*
* Please note that you need SysV IPC support in your kernel.
* Make sure CONFIG_SYSVIPC=y is set.
*
* Attention: cgi and cgid modules are now handled via APACHE2_MODULES flags
* in make.conf. Make sure to enable those in order to compile them.
* In general, you should use 'cgid' with threaded MPMs and 'cgi' otherwise.
그래서 apache emerge 하기전에 make.conf에 설정에 위에서 설명한 대로 SUEXEC_DOCROOT 변수값을 추가 하라고 넣은 것이다.
SUEXEC_DOCROOT를 ‘/home’ 디렉토리로 변경하고 emerge가 끝나 컴파일되면 suexec -V 커맨드로 확인시에 다른 경로로 잡힌 것을 확인할수 있다.
# suexec -V
-D AP_DOC_ROOT="/home"
-D AP_GID_MIN=100
-D AP_HTTPD_USER="apache"
-D AP_LOG_EXEC="/var/log/apache2/suexec_log"
-D AP_SAFE_PATH="/usr/local/bin:/usr/bin:/bin"
-D AP_SUEXEC_UMASK=077
-D AP_UID_MIN=1000
-D AP_USERDIR_SUFFIX="public_html"
이제 VirtualHost의 SuexecUserGroup가 설정된 /home 디렉토리의 하위 디렉토리는 모두 문제없이 지정된 소유자의 권한으로 실행되는 것을 확인할수 있을 것이다.
보안상의 이유던 어떤 다른 이유가 되었던 위의 방식이 보안상의 논점이 되는 것을 떠나, 개인적인 개발서버로 사용할때 등의 상황에서는 매우 불편한 사유가 될수 있기 때문에 다른 누군가가 같은 문제에 부딪혔을때 위의 방법으로 쉽게 해결하기를 바란다.
SUEXEC_DOCROOT=”/var/www:/home” 등의 중복 패스를 넣으면 안된다. 단 1개의 PATH만 허락한다. 추가해서 테스트했더니 디렉토리가 매칭되지 않는다고 에러를 뱉었다.
link: https://kldp.org/node/137163
비슷한 문제를 겪었던 10년전 게시글이 있어 일단 링크를 걸어본다.