semanage는 SELinux 의 보안 정책을 조회하고 추가/변경/삭제할 수 있는 명령행 기반의 유틸리티입니다.


SELinux 는 파일, 네트워크 포트, 네트워크 인터페이스등을 별도의 보안 컨텍스트로 분리하여 다루고 있으므로 서비스 데몬을 SELinux 에서 문제없이 사용하려면 네트워크 포트와 파일 컨텍스트를 이해하고 semanage 로 컨텍스트를 다룰수 있어야 합니다.


semanage 는 일반적으로 추가로 설치해야 하는 패키지로 Red Hat의 다른 시스템 유틸리티처럼 python 으로 작성되어서 python 인터프리터를 필요로 하고 있으며 다음 yum 명령어를 사용하여 패키지를 설치할 수 있습니다.


포트 컨텍스트

SELinux 는 네트워크 데몬마다 명시적으로 접근할 수 있는 포트 번호를 관리하고 있으며 설령 root 로 구동한 데몬 프로세스라도 허용된 포트 이외에는 차단해 버립니다.

이는 공격자가 웹 서버나 메일 서버등 인프라 프로세스의 취약점이나 잘못된 설정을 해킹하여 쉘을 얻은 후에 ssh 등을 사용하여 내부 망으로 다시 침투하는 2차 피해를 방지해 주는 강력한 기능이지만 well known 이 아닌 포트 번호를 사용하거나 새로운 포트 번호를 할당하는 등의 변경이 있을 경우 제대로 동작하지 않게 됩니다.


이런 내용을 모른다면 SELinux 가 서버의 포트 접근을 차단하여 SELinux 를 사용하면 서비스 운영이 힘들다는 생각을 갖게 될 수 있으므로 포트 컨텍스트에 대해 반드시 이해해야 합니다.


포트를 다루려면 semange 명령어 뒤에 port 명령을 사용하고 뒤에 옵션을 주면 되며 조회를 할 경우 -l 옵션을 사용하면 됩니다.

아래 명령은 SELinux 에 등록된 전체 포트 컨텍스트를 출력하며 첫 번째 행은 포트 유형, 두 번째는 프로토콜, 세 번째는 포트 번호이며 첫번째 포트 유형은 프로토콜:포트번호에 접근을 허용한다는 의미입니다.

즉 마지막 줄의 zope_port_t tcp 8021zope_port_t 라는 포트 유형은 tcp:8021 포트를 사용할 수 있습니다.


우리의 관심사는 웹 서버이므로 http 에 관한 포트 번호를 검색해야 하며 semanage 명령에서 타입별로 분류할 수 있는 기능은 제공하지 않으므로 특정 타입만 확인할 경우 grep 으로 분류를 해주면 됩니다.

아래는 http 가 들어간 전체 포트 컨텍스트 목록을 출력합니다.


http_cache_port_t 는 squid 같은 웹 캐시 서버가 접근할 수 있는 포트를 의미하며 웹 서버가 접근할 수 있는 포트 컨텍스트의 이름은 http_port_t  타입으로 지정되어 있습니다. 


결과를 보면 웹 서버는 http_port_t 에 할당된 4개의 포트에 접근할 수 있다는 의미입니다. 보통 웹 서버는 80, 443을 리슨하는데 8009 나 8080 이 있는 것이 의아한 독자들이 있을 수 있습니다.


http_port_t 에 지정하는 포트는 웹 서버가 리슨하는 포트만이 아니라 커넥트하는 포트까지 지정해 주어야 하며 8009, 8080 는 아파치 톰캣이 리슨하는 포트이므로 웹 서버가 톰캣에 연결할 때 필요하므로 설정해 주어야 합니다.

예로 웹 서버를 PHP 의 WAS 라고 볼 수 있는 php-fpm 과 네트워크 소켓 방식으로 연동한다면 php-fpm 이 사용하는 포트(기본: 9000) 를 http_port_t 에 등록해 주지 않으면 SELinux 는 웹 서버의 9000 번 포트 연결을 차단해 버립니다.

또 하나의 사례로 아파치 웹 서버를 900 포트를 통해 서비스를 해야 할 일이 생겼다고 가정해 봅시다. 새로운 포트 추가는 아파치의 설정 파일(CentOS의 경우 /etc/httpd/conf/httpd.conf)에서 사용하는 포트를 지정하는 Listen 항목에서 900 을 추가하고 아파치 웹서버를 재시작해 주면 됩니다.


nginx 사용자는 가상 호스트 설정의 server 블록에 listen 900; 를 추가해 주면 됩니다.

server {
	listen 80;
	listen 900;
CODE



에러 로그를 보면 다음과 같이 900 번 포트에 바인드할 수 없다는 에러가 발생하고 웹 서버가 정상적으로 구동되지 않는 것을 볼 수 있습니다.


이는 아파치 웹 서버가 루트로 구동되지만 부여된 컨텍스트인 http_port_t 는 위에서 본 8개 포트이외에는 연결할 수 없으므로 발생하는 현상이며 이 문제를 해결하려면 http_port_t 컨텍스트가 연결할 수 있는 포트를 추가해 주면 됩니다.

포트를 추가할 경우 추가 옵션인 -a 와 사용할 프로토콜, 보안 컨텍스트, 포트 번호를 지정해 주면 됩니다. 


HTTP 는 tcp 를 사용하므로 -p tcp 옵션과  보안 컨텍스트를 지정하는 -t http_port_t 그리고 허용할 포트 번호 900 을 지정하여 최종 실행할 semanage 명령어는 아래와 같습니다.

만약 "Port tcp/900 already defined" 같이 이미 포트가 지정되어 있다는 에러가 발생한다면 추가 옵션인 -a 대신 변경 옵션인 -m 을 사용하면 됩니다.


$ sudo semanage port -m -p tcp -t http_port_t 900 
CODE


정상적으로 설정이 끝났다면 포트 목록을 다시 확인해 봅시다.


웹 서버는 DMZ 구간에 위치하고 WAS 와는 리버스 프락시로 연결하면 서비스가 커질 경우 WAS 를 수평/수직 적으로 확장할 수 있는 장점과 더불어 보안도 견고해 지는 장점이 있습니다.

만약 WAS 를 한 서버에 별도의 포트로 추가 구성하는 수직 확장의 경우 포트 번호가 추가된다면 꼭 semanage port 명령으로 새로 만들어진 WAS 의 포트를 등록해 주어야 합니다.


추가된 포트 컨텍스트는 -d 옵션으로 삭제할 수 있으며 해당 포트를 추가할 때와 마찬가지로 -p 로 tcp 인지 udp 인지 지정해 주고 삭제할 포트 번호를 지정해 주면 되며 아래 명령어는 http_port_t 에 추가한 900 번 포트를 삭제합니다.


명령어가 정상적으로 종료된 후에 포트 목록을 조회해 보면 900 번이 사라진 것을 확인할 수 있습니다.



파일 컨텍스트 

/var/www/html 폴더 밑에 cp 로 파일을 복사하거나 또는 에디터등으로 파일을 새로 만들면 자동으로 httpd_sys_content_t  컨텍스트가 부여되는 것을 확인했습니다. 또 restorecon 을 사용하면 위치에 맞는 보안 컨텍스트를 복구해 준다는 것도 알게 되었는데 어떻게 이게 가능한 것일까요? 


SELinux 는 사용자들이 일일이 보안 컨텍스트를 지정하지 않아도 되도록 주요 디렉터리 경로마다 자동으로 특정 컨텍스트를 부여하도록 사전 탑재 정책에 설정되어 있습니다. semange 의  파일 컨텍스트 기능은 바로 이런 SELinux 의 설정을 확인하고 추가/변경할 수 있는 해주는 기능으로 semanage 명령어 뒤에 fcontext 옵션을 주고 실행하면 됩니다.


먼저 설정된 파일 컨텍스트의 목록을 표시하는 옵션인 -l 을 사용해 봅시다. 많은 결과가 나오므로 어떤 경로에 파일을 작성하면 자동으로 httpd_sys_content_t 컨텍스트가 부여되는지 grep 을 사용하여 필터링을 합니다.



경로는 정규식을 사용할 수 있으며 /var/www(/.*)? 는 /var/www 하위의 모든 파일과 폴더를 의미하므로 /var/www/html 이나 /var/www/mywebapp 모두 httpd_sys_content_t가 붙게 됩니다.


서비스를 운영하다 보면 MySQL 데이타가 늘어나서 기본 경로인 /var/lib/mysql 에 있는 데이타 베이스 파일을 다른 파티션(예: /opt/mysql)으로 이동해야 하는 일이 생길 수 있습니다.


MySQL 도 중요한 데몬 프로세스이므로 SELinux 에 사전 탑재 정책이 있고 MySQL 의 데이타베이스 파일도 대상이므로 MySQL 설정 파일에 datadir=/opt/mysql 로 설정하고 재부팅하면 기본 설정에서 어긋나므로 SELinux 가 차단하여 정상적으로 구동되지 않습니다.


먼저 MySQL 이 데이타베이스 파일을 저장할 수 있도록 허용된 타입인 mysqld_db_t 를 파일 컨텍스트를 사용하여 확인해 봅시다.

/var/lib/mysql/var/lib/squeezeboxserver 하위의 경로가 아니라면 자동으로 mysql_db_t 가 붙지 않아서 문제가 될수 있으며 이를 해결하기 위해서는 두 가지 방법이 있습니다.


1.chcon 으로 타입 부여

MySQL 데이타를 새로운 폴더로 이동한 후에 아래 명령으로 타입을 부여해 주면 됩니다. 

$ sudo chcon -R -t mysqld_db_t /opt/mysql
CODE

이 방법의 단점은 새로운 database 를 추가할 경우 mysqld_db_t 가 자동으로 부여되지 않아서 오작동 한다는 점입니다.


2. 새로운 경로에 컨텍스트 설정


port 를 추가했을때 처럼 경로에 자동으로 적절한 보안 컨텍스트가 부여될 수 있도록 설정할 수 있습니다. 기존에 없는 위치이므로 -a 옵션으로 추가를 하고 -t 옵션에 부여할 컨텍스트를 적고 정규식으로 설정할 경로를 적어 주면 됩니다.다.


다음은 /opt/mysql 하단에 생기는 모든 파일과 폴더에 mysqld_db_t 를 자동으로 부여하는 semanage 명령어입니다.

성공적으로 완료되었다면 새로 추가한 경로에 정책이 반영되도록 restorecon 을 실행해 줍니다.

정책에 추가되었으므로 이제 새로운 MySQL 데이타베이스 파일이 생겨도 정상적으로 동작합니다.


설정이 변경되어 파일 컨텍스트를 삭제할 경우 포트의 경우와 마찬가지로 -d 옵션으로 추가한 경로를 삭제 할 수 있으며 등록할 때와 마찬가지로 삭제할 경로를 입력해야 합니다.


이제 SELinux 의 보안 컨텍스트에서 두 가지 중요한 요소인 파일과 보안 컨텍스트를 이해하면 큰 문제없이 SELinux 를 사용할 수 있을 것입니다.