본문 바로가기

생활코딩

라즈베리파이 - nginx - ModSecurity 설치

요즘 간단한 웹앱을 하나 만들어서 라즈베리파이 5에서 홈서버로 운영하려고 하고 있습니다. 앱은 간단한 것이어서 금방 만들었고, duckdns를 이용해 공인 도메인도 설정하고, Let's Encrypt를 사용하여 인증서 설정으로 HTTPS 연결까지 마쳤습니다.

당연히, 방문자는 없겠지만 그래도 방문 통계는 필요할 거 같아 로그 분석 툴을 찾아 보다 goaccess를 알게 되어 간단히 설치하고 access.log를 분석했는데, 진짜 인터넷 세계에 연결된지 얼마 되지도 않았고, 아무도 모르는 앱인데 벌써 취약점 공격이 들어왔더군요.

 

예상은 했던 일이고 전체적인 설정 막바지에 보안 관련 작업을 하려고 생각하고 있었는데, 예상 보다 훨씬 빨리 공격이 들어 왔습니다. 한 이틀 정도 연결해 둔 거 같은데 바로 공격이 들어 왔네요. 본래의 앱 개발이나 서버 운영 보다는 이런 부분들 때문에 가능하면 홈서버는 운영하지 않으려고 했던 건데, 이 번에는 꼭 운영을 해야 하는 상황이라 부랴부랴 조금 더 보안 장치를 추가하기로 했습니다.

 

ModSecurity

ModSecurity는 HTTP로 들어오는 요청을 분석하여 다향한 보안 위협을 차단하는 웹 애플리케이션 방화벽(web application firewall, WAF) 모듈입니다. 홈페이지에 보면 WAF계의 맥가이버 칼(swiss army knife)이라고 말하고 있습니다.

ModSecurity가 2.0까지는 nginx 모듈을 패키지로 제공하여 쉽게 설치할 수 있었습니다만, 3.0 부터는 독립적인 라이브러리로 분리되고 nginx 같은 웹 서버 모듈은 별도의 프로젝트로 관리를 시작했다고합니다. 이게 시작이 Apache 모듈로 시작한 것인지 2.0까지는 Apache 모듈과 결합성이 강하던 것을 3.x대로 오면서 모두 분리하고 보다 효율적으로 빨라졌다고 얘기하네요. 그래서, Apache 모듈로는 2.x대를 계속 사용하면 되지만 nginx에서는 최신 버전을 쓰려면 3.x대를 써야 합니다. 상세 내용이 궁금하신 분들은 여기를 한 번 읽어 보시면 좋을 거 같습니다.

 

Nginx에 ModSecurity를 동적 모듈로 설치하는 절차

라즈베리파이에 Nginx는 apt로 설치를 해 놓은 상황에서 ModSecurity를 동적 모듈로 추가하는 방법을 설명해 보겠습니다.

nginx는 다음과 같은 명령어로 간단히 설치해 놓은 상황입니다.
sudo apt install nginx fcgiwrap nginx-doc 

1. ModSecurity 설치

소스 컴파일을 위해서 기본적으로 필요한 것들을 설치합니다.

sudo apt-get install git g++ apt-utils autoconf automake build-essential libcurl4-openssl-dev libgeoip-dev liblmdb-dev libpcre2-dev libtool libxml2-dev libyajl-dev pkgconf zlib1g-dev

 

ModSecurity를 소스를 가지고와서 빌드합니다.

git clone https://github.com/owasp-modsecurity/ModSecurity
cd ModSecurity/
git submodule init
git submodule update
sh build.sh
./configure --with-pcre2
make
make install

 

이렇게 하면 ModSecurity가 /usr/local/modsecurity 에 설치됩니다.

ModSecurity 디렉토리에 보면 modsecurity.conf-recommended 라는 파일과 unicode.mapping 이라는 파일이 있습니다. 이 파일을 잘 챙겨둡니다.

2. ModSecurity-nginx connector 설치

이제 nginx의 동적 모듈을 빌드하고 설치할 차례입니다. 동적 모듈을 빌드하려면 nginx를 빌드하면서 부수적으로 동적 모듈이 만들어져야합니다.

먼저 패키지로 설치해 놓은 nginx의 버전을 확인합니다.

nginx -v

 

라즈베리파이 5에 설치된 버전은 1.22.1 입니다.

다음의 과정으로 nginx를 빌드하면서 ModSecurity-nginx connector 동적 모듈을 빌드합니다.

# 현재 디렉토리는 /home/pi 입니다.
git clone https://github.com/nginx/nginx.git
git clone https://github.com/owasp-modsecurity/ModSecurity-nginx.git
cd nginx
# 패키지로 설치된 버전과 일치시키기 위해 git checkout
git checkout release-1.22.1
./auto/configure --add-dynamic-module=/home/pi/ModSecurity-nginx --with-compat
make

 

기존에 패키지로 설치한 nginx와 똑같이 빌드하려면 nginx -V (대문자 V에 주의)로 확인하여 설정 인수를 모두 맞추어 주어야 합니다. 즉, ./auto/configure 를 실행할 때 설정 인수를 모두 같게 해 줘야 합니다. 하지만, 지금은 단순히 ModSecurity-nginx 모듈만 잘 빌드되면 되므로 그와 관련된 인수만 설정합니다.

이렇게하고 나면 ./obj 디렉토리에 ngx_http_modsecurity_module.so 파일이 만들어집니다. 이 파일을 niginx 모듈 디렉토리로 복사합니다.

sudo mkdir -p /usr/share/nginx/modules
sudo cp ./obj/ngx_http_modsecurity_module.so /usr/share/nginx/modules

 

기존 패키지로 설치할 때 모듈을 추가한 것이 없는 경우 모듈 디렉토리가 존재하지 않을 수 있습니다. 그래서, /usr/share/nginx/modules를 만드는 명령을 먼저 실행한 후에 모듈을 복사합니다.

 

3. nginx 모듈 활성화

ModSecurity-nginx connector 활성화를 위한 설정 파일을 만듭니다.

sudo vi /etc/nginx/modules-available/modsecurity.conf

# 아래 내용을 modsecurity.conf 파일에 추가합니다.
load_module modules/ngx_http_modsecurity_module.so;

 

모듈 활성화

sudo ln -s /etc/nginx/modules-available/modsecurity.conf /etc/nginx/mdules-enabled

 

4. nginx 설정

3번까지 한 상황에서 다음과 같은 내용을 nginx 설정 파일에 추가하고 나서 재시작을 하면 ModSecurity가 동작을 시작합니다.

server {
    modsecurity on;
    location / {
        modsecurity_rules '
	      SecRuleEngine On
	      SecAuditEngine On
	      SecAuditLog /var/log/modsec_audit.log
	      SecDebugLog /tmp/modsec_debug.log
	      SecDebugLogLevel 9
	      SecRuleRemoveById 10
	    ';
    }
}

 

재시작 후에 HTTP 요청을 하고 tail -F /tmp/modsec_debug.log 로 확인을 하면 ModSecurity가 동작하는 것을 확인할 수 있습니다.

실질적으로는 좀 더 많은 설절을 해 줘야 하는데 기본적으로는 아까 1번에 챙겨둔 modsecurity.conf-recommended 파일을 modsecurity.conf로 바꾼 후에 이 파일과 unicode.mappig 이라는 파일을 적절한 위치에 복사해 넣고 다음과 같이 설정해야 합니다.

server {
   modsecurity on;
   
   location / {
       modsecurity_rules_file /디렉토리경로/modsecurity.conf;
   }
}

 

ModSecurity 규칙 적용

이렇게하면 설치는 온전히 끝난 거지만 여기서 끝이 아닙니다. 이제 ModSecurity가 잘 동작하도록 규칙을 지정해 줘야 합니다. modsecurity.conf-recommended에도 어느 정도 규칙이 정의돼 있지만 보다 다양한 보안 위협들에 대한 규칙이 필요합니다. 이를 위해  OWASP CRS(Core Rule Set) 프로젝트라는 것이 있습니다. CRS 프로젝트에는 다양한 웹 애플리케이션 위협에 대응하는 규칙들이 정의돼 있습니다. 이 규칙셋까지 적용해야 ModSecurity 설치가 끝났다고 볼 수 있습니다.

https://coreruleset.org/

 

OWASP CRS Project

The 1st Line Of Defense

coreruleset.org

 

다음 단계를 통해 규칙 셋 파일을 다운로드 및 적절한 위치에 설치합니다.

wget https://github.com/coreruleset/coreruleset/releases/download/v4.7.0/coreruleset-4.7.0-minimal.tar.gz
sudo mkdir /etc/crs4
sudo tar xvfz coreruleset-4.7.0-minimal.tar.gz --strip-components 1 -C /etc/crs4

 

그리고, 다음과 같이 기본 설정 sample 파일 이름을 바꿔줍니다.

cd /etc/crs4
mv crs-setup.conf.example crs-setup.conf

 

저 같은 경우는 이 곳에서 ModSecurity 설정을 모두 관리하려고 여기에 modsecurity.conf 파일과 unicode.mapping 파일로 모두 복사해 넣었습니다.

cd ModSecurity
sudo cp modsecurity.conf-recommended /etc/crs4/modsecurity.conf
sudo cp unicode.mapping /etc/crs4/unicode.mapping

 

modsecurity.conf 파일을 열어서 SecRuleEngine과 SetAuditLog  디렉티브들을 찾아 다음과 같이 설정해 줍니다.

sudo vi /etc/crs4/modsecurity.conf

SecRuleEngine On
SecAuditLog /var/log/nginx/modsec_audit.log

 

그리고, IP 주소 단위로 차단을 하기 위한 파일도 하나 만들어 두면 좋습니다. 저는 ip_rules.conf 라는 이름으로 다음과 같은 내용의 파일을 하나 만들었습니다.

sudo vi /etc/crs4/ip_rules.conf

SecRule REMOTE_ADDR "@ipMatch 차단할IP주소" "id:1002,phase:1,deny,status:403,msg:'Access denied for your IP range'"

 

차단할 IP 주소는 192.168.0.0/24 처럼 CIDR 형식도 사용 가능합니다. id 값은 임의로 유일한 값을 적어 주면 됩니다. CRS 규칙 번호들을 훨씬 벗어나게 크게 지정합니다. 

 

다음은 세부 룰셋을 모두 포함하는 파일을 하나 만들어야 합니다.

sudo vi /etc/crs4/modisecurity_includes.conf

Include /etc/crs4/modsecurity.conf
Include /etc/crs4/crs-setup.conf
Include /etc/crs4/plugins/*-config.conf
Include /etc/crs4/plugins/*-before.conf
Include /etc/crs4/rules/*.conf
Include /etc/crs4/plugins/*-after.conf
Include /etc/crs4/ip_rules.conf

 

위의 내용을 입력하고 저장합니다. 규칙셋은 ip_rules.conf는 직접 만들었으니 상관 없고, 나머지 파일들은 꼭 위의 순서대로 포함돼야 한다고 가이드에서 얘기하고 있습니다.

이제 nginx 설정을 다음과 같이 합니다.

# ModSecurity 설정에 필요한 부분만 적어 놨습니다.
server {
    modsecurity on;
    location / {
	    modsecurity_rules_file /etc/crs4/modsecurity_includes.conf;
    }
}

 

nginx를 재시작하면 ModSecurity가 제대로 동작을 시작합니다.

sudo systemctl restart nginx

 

테스트

curl 'http://자기주소/?foo=/etc/passwd&bar=/bin/sh'

 

이런 식으로 요청했을 때 403 (Forbidden) 응답이 오면 제대로 동작을 하는 것입니다. /var/log/nginx/modsec_audit.log 에 보면 이런 식의 로그가 남게 됩니다.

 

참고