NGINX에서의 SELinux 보안 이슈 모두 모아봤습니다.

최신 RHEL(Red Hat Enterprise Linux) 및 관련 배포판의 SELinux(Security-Enhanced Linux)에 대한 기본 설정은 매우 엄격하여 편의성보다는 보안 측면에서 잘못될 수 있습니다. 기본 구성에서 NGINX Open Source 및 NGINX Plus의 기능을 제한하지 않지만 구성할 수 있는 다른 기능은 SELinux에서 명시적으로 허용하지 않는 한 차단될 수 있습니다. 이 포스트에서는 가능한 문제와 이를 해결하기 위한 권장 방법에 대해 설명합니다.

Note: 이 포스트는 NGINX Open Source와 NGINX Plus 모두에 적용됩니다. 읽기 쉽게 하기 위해 “NGINX”라는 용어가 전반적으로 사용됩니다.

CentOS는 원래 RHEL에서 파생된 관련 배포판이며 NGINX 및 NGINX Plus에서 지원됩니다. 또한 NGINX Plus는 관련 Amazon Linux 및 Oracle Linux 배포판을 지원합니다. 기본 SELinux 설정은 CentOS 및 RHEL과 다를 수 있습니다.

목차

1. NGINX SELinux 개요
2. NGINX SELinux 일시적으로 비활성화
3. SELinux 모드 변경
4. SELinux 보안 문제 해결

5. Issue 1: Proxy Connection is Forbidden
5-1. Boolean 옵션 이해
5-1-1. httpd_can_network_relay Boolean 옵션
5-1-2. The httpd_can_network_connect Boolean 옵션
6. Issue 2: File Access is Forbidden
6-1. Option 1: Modify the File Label
6-2. Option 2: httpd_t 도메인 권한 확인

7. Issue 3: NGINX가 추가 포트에 바인딩할 수 없음
8. Issue 4: Error: 너무 많은 파일이 열려있습니다.
8-1. NGINX Worker 프로세스가 오류를 생성하는 경우
8-2. NGINX Master 프로세스가 오류를 생성하는 경우
9. 추가 리소스

1. NGINX SELinux 개요

SELinux는 최신 RHEL 및 CentOS 서버에서 기본적으로 활성화됩니다. 각 운영 체제 객체(프로세스, 파일 Descriptor, 파일 등)에는 객체가 수행할 수 있는 권한 및 작업을 정의하는 SELinux 지시문으로 Label이 지정됩니다. RHEL 6.6/CentOS 6.6 이상에서 NGINX는 httpd_t 지시문으로 Label이 지정됩니다.

# ps auZ | grep nginx
unconfined_u:system_r:httpd_t:s0 3234 ? Ss 0:00 nginx: master process /usr/sbin/nginx \
                                                -c /etc/nginx/nginx.conf
unconfined_u:system_r:httpd_t:s0 3236 ? Ss 0:00 nginx: worker process

httpd_t 지시문은 NGINX가 일반 웹 서버 포트에서 수신하고, /etc/nginx의 구성 파일에 액세스하고, 표준 docroot location(/usr/share/nginx)의 콘텐츠에 액세스하도록 허용합니다. Upstream 위치로 Proxy 하거나 소켓을 통해 다른 프로세스와 통신하는 것과 같은 다른 많은 작업을 허용하지 않습니다.

2. NGINX SELinux 일시적으로 비활성화

httpd_t 지시문에 대한 SELinux 제한을 일시적으로 비활성화하여 NGINX가 Non‑SELinux OS에서와 동일한 작업을 모두 수행할 수 있도록 permissive 도메인에 httpd_t 지시문을 할당합니다.

3. SELinux 모드 변경

SELinux는 enforcing, permissive 또는 disabled 모드(도메인이라고도 함)에서 실행될 수 있습니다. 기본(strict) 권한을 위반할 수 있는 NGINX 구성 변경을 수행하기 전에 테스트 환경(사용 가능한 경우) 또는 Production 환경에서 SELinux를 enforcing 모드에서 permissive 모드로 변경할 수 있습니다. permissive 모드에서 SELinux는 모든 작업을 허용하지만 enforcing 모드에서 보안 정책을 위반했을 수 있는 작업은 기록합니다.

permissive 도메인 목록에 httpd_t를 추가하려면 다음 명령을 실행합니다.

# semanage permissive -a httpd_t

permissive 도메인 목록에서 httpd_t를 삭제하려면 다음을 실행하십시오:

# semanage permissive -d httpd_t

모드를 전체적으로 permissive으로 설정하려면 다음을 실행하십시오.

# setenforce 0

모드를 전체적으로 enforcing로 설정하려면 다음을 실행하십시오.

# setenforce 1

4. SELinux 보안 문제 해결

permissive 모드에서 보안 문제는 기본 Linux Audit Log인 /var/log/audit/audit.log에 기록됩니다. NGINX가 enforcing 모드에서 실행 중일 때 문제가 발생하면 permissive 모드로 로그인된 예외를 검토하고 이를 허용하도록 보안 정책을 업데이트합니다.

5. Issue 1: Proxy Connection is Forbidden

기본적으로 SELinux 구성은 다음과 같은 audit Log 메시지에 표시된 것처럼 NGINX가 원격 HTTP, FastCGI 또는 기타 서버에 연결하는 것을 허용하지 않습니다.

type=AVC msg=audit(1415714880.156:29): avc:  denied  { name_connect } for  pid=1349 \
  comm="nginx" dest=8080 scontext=unconfined_u:system_r:httpd_t:s0 \
  tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket
type=SYSCALL msg=audit(1415714880.156:29): arch=c000003e syscall=42 success=no \
  exit=-115 a0=b \a1=16125f8 a2=10 a3=7fffc2bab440 items=0 ppid=1347 pid=1349 \
  auid=1000 uid=497 gid=496 euid=497 suid=497 fsuid=497 egid=496 sgid=496 fsgid=496 \
  tty=(none) ses=1 comm="nginx" exe="/usr/sbin/nginx" \
  subj=unconfined_u:system_r:httpd_t:s0 key=(null)

audit2why 명령은 메시지 코드(1415714880.156:29)를 해석합니다.

# grep 1415714880.156:29 /var/log/audit/audit.log | audit2why
type=AVC msg=audit(1415714880.156:29): avc:  denied  { name_connect } for  pid=1349 \
  comm="nginx" dest=8080 scontext=unconfined_u:system_r:httpd_t:s0 \
  tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket
 
        Was caused by:
        One of the following booleans was set incorrectly.
        Description:
        Allow httpd to act as a relay
 
        Allow access by executing:
        # setsebool -P httpd_can_network_relay 1
        Description:
        Allow HTTPD scripts and modules to connect to the network using TCP.
 
        Allow access by executing:
        # setsebool -P httpd_can_network_connect 1

audit2why의 Output은 httpd_can_network_relayhttpd_can_network_connect Boolean 옵션 중 하나 또는 둘 다를 활성화하여 NGINX가 Proxy 연결을 수행하도록 허용할 수 있음을 나타냅니다. 일시적으로 또는 영구적으로 활성화할 수 있으며 후자는 출력에 표시된 대로 ‑P 플래그를 추가하여 활성화할 수 있습니다.

5-1. Boolean 옵션 이해

sesearch 명령은 Boolean 옵션에 대한 자세한 정보를 제공하며 settools 패키지(yum install settools)를 설치하는 경우 사용할 수 있습니다. 여기서는 httpd_can_network_relayhttpd_can_network_connect 옵션에 대한 출력을 보여줍니다.

5-1-1. httpd_can_network_relay Boolean 옵션

다음은 httpd_can_network_relay 옵션에 대한 sesearch 명령의 Output입니다.

# sesearch -A -s httpd_t -b httpd_can_network_relay
Found 10 semantic av rules:
   allow httpd_t gopher_port_t : tcp_socket name_connect ;
   allow httpd_t http_cache_client_packet_t : packet { send recv } ;
   allow httpd_t ftp_port_t : tcp_socket name_connect ;
   allow httpd_t ftp_client_packet_t : packet { send recv } ;
   allow httpd_t http_client_packet_t : packet { send recv } ;
   allow httpd_t squid_port_t : tcp_socket name_connect ;
   allow httpd_t http_cache_port_t : tcp_socket name_connect ;
   allow httpd_t http_port_t : tcp_socket name_connect ;
   allow httpd_t gopher_client_packet_t : packet { send recv } ;
   allow httpd_t memcache_port_t : tcp_socket name_connect ;

이 Output은 httpd_can_network_relayhttpd_t 지시문(예: NGINX)로 Label이 지정된 프로세스가 http_port_t 유형을 포함하여 다양한 유형의 포트에 연결할 수 있음을 나타냅니다.

# semanage port -l | grep http_port_t
http_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000

http_port_t에 대해 허용된 포트 세트에 더 많은 포트(여기서는 8082)를 추가하려면 다음을 실행하십시오.

# semanage port -a -t http_port_t -p tcp 8082

이 명령의 출력에 다음 예와 같이 포트가 이미 정의되어 있다고 표시되면 해당 포트가 다른 집합에 포함되어 있음을 의미합니다. 다른 서비스에 부정적인 영향을 미칠 수 있으므로 http_port_t에 다시 할당하지 마십시오.

# semanage port -a -t http_port_t -p tcp 8080
/usr/sbin/semanage: Port tcp/8080 already defined
# semanage port -l | grep 8080
http_cache_port_t              tcp      3128, 8080, 8118, 8123, 10001-10010

5-1-2. The httpd_can_network_connect Boolean 옵션

다음은 httpd_can_network_connect 옵션에 대한 sesearch 명령의 Output입니다.

# sesearch -A -s httpd_t -b httpd_can_network_connect
Found 1 semantic av rules:
   allow httpd_t port_type : tcp_socket name_connect ;

이 Output은 httpd_can_network_connecthttpd_t 지시문으로 Label이 지정된 프로세스(예: NGINX)가 port_type 속성이 있는 모든 TCP 소켓 유형에 연결할 수 있음을 나타냅니다. 이를 나열하려면 다음을 실행하십시오.

# seinfo -aport_type -x

6. Issue 2: File Access is Forbidden

기본적으로 SELinux 구성은 다음과 같은 audit Log 메시지에 표시된 것처럼 NGINX가 잘 알려진 승인된 location 외부의 파일에 액세스하는 것을 허용하지 않습니다.

type=AVC msg=audit(1415715270.766:31): avc:  denied  { getattr } for  pid=1380 \
  comm="nginx" path="/www/t.txt" dev=vda1 ino=1084 \
  scontext=unconfined_u:system_r:httpd_t:s0 \
  tcontext=unconfined_u:object_r:default_t:s0 tclass=file

audit2why 명령은 메시지 코드(1415715270.766:31)를 해석합니다.

# grep 1415715270.766:31 /var/log/audit/audit.log | audit2why
type=AVC msg=audit(1415715270.766:31): avc:  denied  { getattr } for  pid=1380 \
  comm="nginx" path="/www/t.txt" dev=vda1 ino=1084 \
  scontext=unconfined_u:system_r:httpd_t:s0 \
  tcontext=unconfined_u:object_r:default_t:s0 tclass=file
 
    Was caused by:
        Missing type enforcement (TE) allow rule.
 
        You can use audit2allow to generate a loadable module to allow this access.

파일 액세스가 금지된 경우 두 가지 옵션이 있습니다.

6-1. Option 1: Modify the File Label (NGINX SELinux)

NGINX(httpd_t 지시문으로 Lable이 지정된 프로세스)가 파일에 액세스할 수 있도록 파일 Lable을 수정합니다.

# chcon -v --type=httpd_sys_content_t /www/t.txt

기본적으로 이 수정 사항은 파일 시스템의 Lable이 재지정될 때 삭제됩니다. 변경 사항을 영구적으로 적용하려면 다음을 실행하십시오.

# semanage fcontext -a -t httpd_sys_content_t /www/t.txt
# restorecon -v /www/t.txt

파일 그룹의 파일 Lable을 수정하려면 다음을 실행하십시오.

# semanage fcontext -a -t httpd_sys_content_t '/www(/.*)?'
# restorecon -Rv /www

6-2. Option 2: httpd_t 도메인 권한 확인 (NGINX SELinux)

추가 파일 location에 대한 접근를 허용하도록 httpd_t에 대한 정책을 확장합니다.

# grep nginx /var/log/audit/audit.log | audit2allow -m nginx > nginx.te
# cat nginx.te
 
module nginx 1.0;
 
require {
        type httpd_t;
        type default_t;
        type http_cache_port_t;
        class tcp_socket name_connect;
        class file { read getattr open };
}
 
#============= httpd_t ==============
allow httpd_t default_t:file { read getattr open };
 
#!!!! This avc can be allowed using one of these booleans:
#     httpd_can_network_relay, httpd_can_network_connect
allow httpd_t http_cache_port_t:tcp_socket name_connect;

컴파일된 정책을 생성하려면 -M 옵션을 포함합니다.

# grep nginx /var/log/audit/audit.log | audit2allow -M nginx

정책을 Load 하려면 semodule -i를 실행한 다음 semodule -l로 성공을 확인합니다.

# semodule -i nginx.pp
# semodule -l | grep nginx
nginx 1.0

이 변경 사항은 재부팅 후에도 지속됩니다.

7. Issue 3: NGINX가 추가 포트에 바인딩할 수 없음

기본적으로 SELinux 구성은 NGINX가 http_port_t 유형에 허용 목록에 있는 기본 포트 이외의 TCP 또는 UDP 포트를 수신 대기(bind()) 하는 것을 허용하지 않습니다.

# semanage  port -l | grep http_port_t
http_port_t                    tcp      80, 443, 488, 8008, 8009, 8443

허용 목록에 없는 포트(NGINX 구성의 http, Stream 또는 mail 지시문에 있는 listen 지시문 사용)에서 수신하도록 NGINX를 구성하려고 하면 NGINX를 확인(nginx -t) 하거나 Reload 할 때 오류가 발생합니다. 이 NGINX Log 항목에 표시된 대로 구성:

YYYY/MM/DD hh:mm:ss [emerg] 46123#0: bind() to 0.0.0.0:8001 failed (13: Permission denied)

semanage를 사용하여 원하는 포트(여기서는 8001)를 http_port_t 유형에 추가할 수 있습니다.

새 구성으로 NGINX를 Reload합니다.

# nginx -s reload

8. Issue 4: Error: 너무 많은 파일이 열려있습니다.

열린 파일 수(RLIMIT_NOFILE)에 대한 제한을 초과하면 Error Log에 다음 메시지가 나타납니다.

Too many files are open

8-1. NGINX Worker 프로세스가 오류를 생성하는 경우 (NGINX SELinux)

대부분의 경우 NGINX Worker 프로세스에서 이 오류를 보고하지만 SELinux는 setrlimit() 시스템 호출을 허용하지 않기 때문에 NGINX worker_rlimit_nofile 지시문을 사용하여 제한을 늘릴 수 없습니다. 오류 및 audit Log의 다음 메시지에 보고됩니다.

  • CentOS/RHEL 7.4+의 경우 /var/log/nginx/error.log에서:
YYYY/MM/DD hh:mm:ss [alert] 12066#0: setrlimit(RLIMIT_NOFILE, 2342) failed (13: Permission denied)
  • CentOS/RHEL 8.0+의 경우 /var/log/nginx/error.log에서:
YYYY/MM/DD hh:mm:ss [alert] 3327#0: setrlimit(RLIMIT_NOFILE, 65535) failed (1: Operation not permitted)
  • CentOS/RHEL 7.4+의 경우 /var/log/audit/audit.log 및 CentOS/RHEL 8.0+의 경우 /var/log/messages에서:
type=AVC msg=audit(1437731200.211:366): avc:  denied  { setrlimit } for pid=12066 \
  comm="nginx" scontext=system_u:system_r:httpd_t:s0 \
  tcontext=system_u:system_r:httpd_t:s0 tclass=process

제한을 늘리려면 대신 다음 명령을 root 사용자로 실행하십시오.

$ setsebool -P httpd_setrlimit 1

8-2. NGINX Master 프로세스가 오류를 생성하는 경우 (NGINX SELinux)

NGINX Master 프로세스에서 오류를 보고하면 NGINX용 systemd unit file을 업데이트해야 합니다. 이것은 Master 및 Worker 프로세스 모두에 대한 파일 Descriptor 제한을 설정합니다.

1. nginx.service 구성을 위한 디렉토리를 만듭니다.

$ mkdir /etc/systemd/system/nginx.service.d

2. /etc/systemd/system/nginx.service.d/nofile_limit.conf에 다음 줄을 추가합니다.

[Service]
LimitNOFILE=65535

3. systemd 데몬 구성을 reload하고 NGINX를 다시 시작합니다.

$ systemctl daemon-reload
$ systemctl restart nginx.service

9. 추가 리소스

SELinux는 운영 체제 권한을 관리하기 위한 복잡하고 강력한 기능입니다. 추가 정보는 다음 포스트에서 확인할 수 있습니다.

NGINX Plus의 SELinux 보안과 성능을 직접 테스트 및 사용해 보려면 지금 30일 무료 평가판을 신청하거나 사용 사례에 대해 최신 소식을 빠르게 전달받고 싶으시면 아래 뉴스레터를 구독하세요.