audit2why로 SELinux 에러 조회하기
SELinux 사용을 어려워하는 이유중 하나는 어디에 에러 로그가 남는지 모르고 로그의 위치를 알아도 내용을 해석하기가 어려운 점입니다.
이는 문제 발생시 원인 분석과 처리를 어렵게 만드므로 에러 로그의 위치와 내용 분석은 원활한 SELinux 사용을 위해 꼭 넘어야 할 벽입니다.
다행히 RHEL 계열의 배포판에는 에러를 손쉽게 확인할 수 있게 해주는 다양한 유틸리티가 제공되므로 기본적인 원리를 안다면 에러 처리가 어렵지 않습니다.
aureport
aureport 명령어는 audit 패키지에 포함된 유틸리티로 audit 데몬이 남기는 로그의 요약 리포트를 편리하게 볼 수 있게 해줍니다.
audit2why
audit2why 는 시스템의 감사(audit) 로그에서 SELinux 가 왜 차단했는지 보여주는 유틸리티로 전 절인 보안 정책 관리 에서 설치한 policycoreutils-python 패키지에 포함된 유틸리티입니다.
audit2why는 파라미터로 파이프를 사용하여 감사 로그 파일의 경로를 지정해 주면 됩니다. SELinux 의 보안 로그는 audit daemon 이 구동되었을 경우에는 /var/log/audit/audit.log 에 기록되며 그렇지 않을 경우 커널의 로그 파일인 /var/log/messages 남게 됩니다.
audit 데몬 구동
대다수의 리눅스 시스템은 audit daemon 이 기본적으로 구동되므로 /var/log/audit/audit.log 를 우선 확인하면 됩니다.
SELinux 차단 로그는 중요한 보안 사항이므로 관리자 권한이 있어야 볼 수 있습니다.
에러 로그를 확인하기 위해 먼저 에러 상황을 발생시켜 봅시다. 전 절인 cp 와 mv 의 동작 방식 으로 다시 이동해서 잘못된 컨텍스트를 가진 컨텐츠를 웹 서버에 생성하고 curl 로 요청하여 "403
Forbidden"
에러를 발생시켜 봅시다.
이제 audit2why 를 활용하여 에러 로그를 조회해 볼 수 있습니다.
$ sudo audit2why < /var/log/audit/audit.log type=AVC msg=audit(1511362993.402:6272): avc: denied { read } for pid=879 comm="nginx" name="hello.html" dev="xvda3" ino=69230554 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:admin_home_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.
TODO 진하게 type=AVC msg=audit(1511362993.402:6272): avc: denied { read } for pid=879 comm="nginx" name="hello.html" dev="xvda3" ino=69230554 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file
이제 진하게 표시한 중요 부분을 보고 SELinux의 에러 로그을 해석해 봅시다.
- avc: denied { read } for pid=87 : 해당 컨텐츠에 대해 read 동작이 거부되었고 해당 프로세스의 id 는 87 번이라는 의미입니다. read가 거부되었으므로 웹 서버가 읽을 수 있는 컨텐스트가 아닌 것을 짐작할 수 있습니다.
- comm="nginx" name="hello.html" : 읽기를 시도한 프로세스는 nginx 이며 컨텐츠는 hello.html 임을 표시하고 있습니다.
- scontext=system_u:system_r:httpd_t: 읽기를 시도한 프로세스에 부여된 컨텍스트는 httpd_t 입니다.
- tcontext=unconfined_u:object_r:admin_home_t : 컨텐츠에 부여된 컨텍스트는 admin_home_t 입니다.
에러 메시지를 분석한 내용을 취합해 보면 nginx 가 hello.html을 읽으려고 했는데 admin_home_t가 부여되어 있으므로 읽기를 실패한 것을 알수 있으며 이는 SELinux 의 컨텍스트(context) 다루기 에서 설명한 restorecon이나 chcon 명령어를 사용해 정확한 컨텍스트를 부여해주면 해결됩니다.
그 아래 로그를 보면 audit2allow 로 로딩 가능한 모듈을 만들어서 해결하라는 메시지를 볼 수 있습니다.
위 에러는 잘못된 보안 컨텍스트때문이므로 간단하게 해결 가능했지만 드물게 보안 정책을 새로 만들어 주어야 하는 경우가 있으며 audit2allow 는 이럴 때 사용하는 유틸리티로 간단한 사용법은 다음 절 "SELinux 문제 해결(TroubleShooting)" 절에서 다룹니다.
또 자주 하는 실수는 웹 서버와 WAS 를 연결할 경우 WAS 가 사용하는 포트에 웹 서버 연결을 허용하지 않아서이며 이는 보안 정책 관리 에서 semanage 를 설명했는데 권한없는 포트(8080)에 WAS를 구동하고 웹 서버가 연결을 시도할 경우 에러 로그를 확인해 봅시다.
$ sudo audit2why < /var/log/audit/audit.log type=AVC msg=audit(1511363946.708:7324): avc: denied { name_connect } for pid=5125 comm="nginx" dest=8080 scontext=system_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 can network connect Allow access by executing: # setsebool -P httpd_can_network_connect 1 Description: Allow httpd to can network relay Allow access by executing: # setsebool -P httpd_can_network_relay 1
- avc: denied { name_connect } : 해당 포트에 대해 connect 동작이 거부되었고 해당 프로세스의 id 는 5125 번이라는 의미입니다.
- comm="nginx"dest=8080 : 연결을 시도한 프로세스는 nginx 이며 포트는 8080 임을 표시하고 있습니다.
- scontext=system_u:system_r:httpd_t: 연결을 시도한 프로세스에 부여된 컨텍스트는 httpd_t 입니다.
- tcontext=unconfined_u:object_r:http_cache_port_t: 8080 포트에 부여된 컨텍스트는 http_cache_port_t 입니다.
audit2why 는 웹 서버가 모든 네트워크에 연결 가능하게 하도록 해당 불린을 설정하라고 권고하고 있습니다.
$ sudo setsebool -P httpd_can_network_connect 1 $ sudo setsebool -P httpd_can_network_relay 1
하지만 이는 웹 서버의 네트워크 접근을 허용하므로 웹 서버가 해킹당했을 경우 ssh등의 포트에 연결 가능해져서 2차 피해가 확산될 수 있으므로 권장하는 방법이 아니며 가장 좋은 방법은 아래처럼 명시적으로 연결할 포트만 허용해 주는 것입니다.
$ sudo semanage port -m -p tcp -t http_port_t 8080
위와 같이 audit2why 가 제시했다고 정답은 아니며 에러가 발생할 경우 원인을 분석하고 가장 영향도가 적은 방향으로 SELinux 정책을 수정하며 운영의 모를 발휘해 가면 됩니다.
setroubleshoot-server 패키지
SELinux 에 익숙하지 않다면 audit2why 유틸리티도 처음 접하면 난해하기는 마찬가지이고 이런 어려움때문에 SELinux 문제 발생시 대처가 어려워 사용을 망설이게 만들고는 합니다.
setroubleshoot-server 는 python 으로 만들어진 유틸리티로 어려운 SELinux AVC 메시지를 알기 쉽게 번역해 주고 처리 방안도 제시해 주는 유틸리티입니다.
SELinux를 적극적으로 사용하고 싶지만 에러가 두렵고 audit2why 가 익숙하지 않다면 꼭 설치해서 사용하기를 권장하는 유틸리티입니다.
먼저 사용을 위해 다음 명령어로 패키지를 설치합니다.
$ sudo yum install setroubleshoot-server -y
설정
setroubleshoot-server 는 dbus 패키지에 포함된 message bus 라는 데몬을 필요로 하므로 설치후 dbus를 재구동 해주는게 좋습니다.
$ sudo service messagebus restart
$ sudo systemctl restart messagebus
사용
이제 setroubleshoot 를 사용할 준비가 되었으니 어떻게 동작하는지 확인해 보기 위해 cp 와 mv 의 동작 방식 절에서 설명한 잘못된 컨텍스트를 가진 파일을 curl 명령어로 다시 요청해 봅시다.
결과로 403 응답이 발생했다면 에러 로그가 남았을테니 로그를 조회해 봅시다.
setroubleshoot 패키지에 포함된 sealert 유틸리티는 setroubleshoot 패키지의 두 가지 컴포넌트중 하나로(다른 하나는 setroubleshootd 로그 수집 데몬) X-Windows 에서 GUI 또는 커맨드에서 동작합니다.
setroubleshoot 서버가 수집한 SELinux 에러 로그를 사용자 단에서 조회할 수 있게 해주며 audit.log 에 남은 SELinux 로그도 분석해주므로 audit2why 대용으로도 쓸 수 있는 유용한 유틸리티입니다.
sealert 은 여러 가지 옵션이 있는데 가장 많이 사용되는 옵션은 특정 이벤트 id 를 조회하는 -l 옵션과 audit.log 파일내에서 SELinux 관련 에러 메시지만 뽑아서 사용자 친화적으로 표시해 주는 -a 옵션입니다.
$ sudo sealert -a /var/log/audit/audit.log -------------------------------------------------------------------------------- SELinux is preventing /usr/sbin/nginx from read access on the file /usr/share/nginx/html/hello.html. ***** Plugin restorecon (99.5 confidence) suggests ************************ If you want to fix the label. /usr/share/nginx/html/hello.html default label should be httpd_sys_content_t. Then you can run restorecon. Do # /sbin/restorecon -v /usr/share/nginx/html/hello.html ***** Plugin catchall (1.49 confidence) suggests ************************** If you believe that nginx should be allowed read access on the hello.html file by default. Then you should report this as a bug. You can generate a local policy module to allow this access. Do allow this access for now by executing: # ausearch -c 'nginx' --raw | audit2allow -M my-nginx # semodule -i my-nginx.pp
sealert -a 옵션을 사용하면 위와 같이 audit2why 처럼 전체 감사 로그를 스캔해서 화면에 처리 방법을 제시해 줍니다.
실제 운영 환경에서는 모든 로그를 출력하므로 기존에 이미 에러 로그가 있을 경우 많은 로그 정보로 인해 원인 파악이 어려워지는 문제가 있습니다.
이런 문제를 해결하려면 setroubleshoot 서버의 에러 로그만 조회할 필요가 있으며 관련 로그는 /var/log/audit/audit.log 이 아닌 /var/log/messages 에 남게 되므로 grep 명령어로 필터링할 수 있습니다.
$ sudo grep -E 'setroubleshoot|preventing' /var/log/messages /var/log/messages:Nov 23 12:38:22 websecu setroubleshoot: failed to retrieve rpm info for /usr/share/nginx/html/hello.html /var/log/messages:Nov 23 12:38:22 websecu setroubleshoot: SELinux is preventing /usr/sbin/nginx from read access on the file /usr/share/nginx/html/hello.html. For complete SELinux messages run: sealert -l 14330311-5f30-4292-bc6c-75d9a30a8720 /var/log/messages:Nov 23 12:38:22 websecu python: SELinux is preventing /usr/sbin/nginx from read access on the file /usr/share/nginx/html/hello.html.#012#012***** Plugin catchall (100. confidence) suggests **************************#012#012If you believe that nginx should be allowed read access on the hello.html file by default.#012Then you should report this as a bug.#012You can generate a local policy module to allow this access.#012Do#012allow this access for now by executing:#012# ausearch -c 'nginx' --raw | audit2allow -M my-nginx#012# semodule -i my-nginx.pp#012
TODO /var/log/messages:Nov 23 12:38:22 websecu setroubleshoot: SELinux is preventing /usr/sbin/nginx from read access on the file /usr/share/nginx/html/hello.html. For complete SELinux messages run: sealert -l 14330311-5f30-4292-bc6c-75d9a30a8720 진하게
위와 같이 setroubleshoot 과 preventing 을 키워드로 해서 필터링해서 정보량을 줄일 수 있습니다.
출력 결과를 보면 첫 번째로 로그가 발생한 날자와 시간이 표시되며(Nov 23 12:38:22) 그 후에 SELinux 가 /usr/sbin/nginx 에 대해 /usr/share/nginx/html/hello.html 에 대한 접근을 차단했다는 내용이 표시되었습니다.
가장 중요한 부분은 자세한 SELinux 메시지를 보려면 sealert -l 14330311-5f30-4292-bc6c-75d9a30a8720 를 실행하라는 부분으로 안내대로 쉘에서 다음 명령어를 실행해 봅시다.
$ sudo sealert -l 14330311-5f30-4292-bc6c-75d9a30a8720 ***** Plugin catchall (100. confidence) suggests ************************** If you believe that nginx should be allowed read access on the hello.html file by default. Then you should report this as a bug. You can generate a local policy module to allow this access. Do allow this access for now by executing: # ausearch -c 'nginx' --raw | audit2allow -M my-nginx # semodule -i my-nginx.pp Additional Information: Source Context system_u:system_r:httpd_t:s0 Target Context unconfined_u:object_r:admin_home_t:s0 Target Objects /usr/share/nginx/html/hello.html [ file ] Source nginx Source Path /usr/sbin/nginx Port <Unknown> Host websecu Source RPM Packages nginx-1.12.2-1.el7_4.ngx.x86_64 Target RPM Packages Policy RPM selinux-policy-3.13.1-166.el7_4.5.noarch Selinux Enabled True Policy Type targeted Enforcing Mode Enforcing Host Name websecu Platform Linux websecu 3.10.0-693.5.2.el7.x86_64 #1 SMP Fri Oct 20 20:32:50 UTC 2017 x86_64 x86_64 Alert Count 2 First Seen 2017-11-23 08:57:30 KST Last Seen 2017-11-23 12:38:21 KST Local ID 14330311-5f30-4292-bc6c-75d9a30a8720
TODO ausearch, semodule 과 Target Objects /usr/share/nginx/html/hello.html [ file ] 진하게
위와 같이 sealert -l 을 실행하면 에러 로그 별로 자세한 원인과 분석을 알려주고 처리 방법까지 제안하고 있으므로 차단된 원인을 손쉽게 찾고 문제를 해결할 수 있습니다.
여기까지 SELinux 의 개념과 보안 컨텍스트, SELinux 를 잘 사용하기 위한 여러 가지 유틸리티와 setroubleshoot 패키지를 살펴 보았으며 독자들은 이제 SELinux 의 구조와 기본 개념, 동작 방식에 대해서 이해 했으리라 생각됩니다.