Nginx 설정 옵션 정리
설치는 Nginx source를 이용한 compile 설치를 참고 하셔요.
/usr/local/nginx 750 권한으로 오류 발생
이부분은 nginx에서 work process가 nobody(기본값)으로 동작하면서
/usr/local/nginx
디렉토리(rwxr_x___)에 접근 권한이 없어서 발생한 문제입니다. 쉽게는 해당 디렉토리의 권한을 755(rwxr_xr_x)변경하면 되지만, 보안(?)이슈로 변경이 안되어 아래와 같이 처리 했습니다.
ERR_CONTENT_LENGTH_MISMATCH 오류
문제를 해결하기 위해서 버퍼링을 비활성화
하였습니다.
proxy_buffering 문제로 해당 설정을 사용하면, Nginx는 프록시 서버로 부터 응답을 받아 버퍼에 저장합니다. 다만 전체 응답이 메모리에 맞지 않으면 일부를 디스크에 임시 저장합니다.
버퍼링을 비활성화 되면 응답이 수신되는 즉시 클라이언트에 동기적으로 전달됩니다.
proxy_buffering off;
/var/lib/nginx/client_body/0000000011 failed (13: Permission denied) 오류
Nobody
로 실행되어서 권한을 최대한 풀어주거나 client_body_buffer_size의 크기를 늘려서 임시파일로 저장 되지 않도록 변경해야 합니다.
client_body_buffer_size는 클라이언트 요청 본문을 읽기 위한 버퍼 크기를 설정합니다. 요청 본문이 버퍼보다 큰 경우 본문 전체 또는 일부만 임시 파일 에 기록됩니다. 기본적으로 버퍼 크기는 2개의 메모리 페이지와 같습니다. 이것은 x86, 기타 32비트 플랫폼 및 x86-64에서 8K입니다. 다른 64비트 플랫폼에서는 일반적으로 16K입니다.
client_body_buffer_size 10M
보안 점검 이행 처리
진행 배경을 간략하게 설명하면, 보안점검 결과처리를 Nginx 설정만으로 해결하였습니다.
파일 업로드
XWIKI 첨부파일 확장자 확인 후 업로드 처리 를 이용해서 client 단에서 처리를 하였고,
서버의 경우 inotify-tools 설치해서 shell-script
로 처리 하였습니다.
inotify-tools는 inotify에 대한 간단한 인터페이스를 제공하는 Linux용 명령줄 프로그램 세트입니다. 이러한 프로그램을 사용하여 파일 시스템 이벤트를 모니터링하고 조치를 취할 수 있습니다. inotify-tools는 두 가지 유틸리티로 구성됩니다.
- inotifywait는 단순히 inotify 이벤트를 차단하므로 셸 스크립트에서 사용하기에 적합합니다.
- inotifywatch는 파일 시스템 사용 통계를 수집하고 각 inotify 이벤트의 개수를 출력합니다.
#!/bin/bash
REGEX="\(.txt|.png|.gif|.jpg|.pdf|.csv|.xml|.doc|.docx|.xls|.xlsx|.ppt|.pptx|.jpeg|.json|.svg|.webp|.gif|.apng|.avif)"
inotifywait -m -r /applications/xwiki/data/store/file/xwiki -e moved_to |
while read dir action file; do
if [[ $file =~ $REGEX ]]; then
echo "matched! the file '$file' appeared in directory '$dir' via '$action'"
else
echo "the file '$file' appeared in directory '$dir' via '$action'"
rm $dir/$file
fi
done
데이터 평문 전송
자체 서명 사설 SSL 인증서 만들기를 참조해서 사설 인증서를 만들었습니다.
인증서를 생성하기 위해서 openssl
이 설치 되어있어야 합니다.
명령어만 정리 하면 아래와 같습니다.
# 루트 인증서(CA) 만들기
$ openssl ecparam -out rootca.key -name prime256v1 -genkey
$ openssl req -new -sha256 -key rootca.key -out rootca.csr
...
-----
Country Name (2 letter code) [XX]:KR
State or Province Name (full name) []:Gyeonggi-do
Locality Name (eg, city) [Default City]:Inchon
Organization Name (eg, company) [Default Company Ltd]:lahuman
Organizational Unit Name (eg, section) []: nginx
Common Name (eg, your name or your server's hostname) []: CA Root Certificate
Email Address []:lahuman@daum.net
...
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:Enter 누르기
An optional company name []:Enter 누르기
$ openssl x509 -req -sha256 -days 999999 -in rootca.csr -signkey rootca.key -out rootca.crt
#서버 인증서 만들기
$ openssl ecparam -out server.key -name prime256v1 -genkey
$ openssl req -new -sha256 -key server.key -out server.csr
...
Country Name (2 letter code) [XX]:KR
State or Province Name (full name) []:Gyeonggi-do
Locality Name (eg, city) [Default City]:Inchon
Organization Name (eg, company) [Default Company Ltd]:lahuman
Organizational Unit Name (eg, section) []: nginx
Common Name (eg, your name or your server's hostname) []:*.lahuman.nginx
Email Address []:lahuman@daum.net
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:Enter 누르기
An optional company name []:Enter 누르기
...
$ openssl x509 -req -sha256 -days 999999 -in server.csr -CA rootca.crt -CAkey rootca.key -CAcreateserial -out server.crt
$ cat server.crt rootca.crt > server.pem
# Nginx ssl 설정
$ vi /usr/local/nginx/nginx.conf
...
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name _;
root /usr/local/nginx/html;
ssl_certificate "/usr/local/nginx/ssl/server.pem";
ssl_certificate_key "/usr/local/nginx/ssl/server.key";
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
...
}
...
#Nginx 재기동
$ /usr/local/nginx/sbin/nginx -s reload
설정이 완료 후 사이트로 이동하여 인증서를 설치합니다.
중요 정보 노출
서버의 정보를 노출하는 문제로, 오류 페이지
나 resepons에서 서버의 정보를 표기하는 헤더를 삭제 해야 하였습니다.
오류 페이지는 /usr/local/nginx/html/50x.html
파일을 먼저 생성하고 아래 내용을 nginx.conf
에 추가 하여 처리 하였습니다.
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
nginx의 reponse에서 Server 헤더를 삭제하는 방식은 추가 모듈을 설치하거나, 소스코드를 다시 컴파일 해야 합니다.
nginx.conf
파일에 아래 서버 토큰을 off 합니다.
server_tokens off;
이렇게 처리해도 Server 헤더에 nginx가 계속 표기 되는데, 이는 소스를 다시 컴파일 하여 처리 합니다.
Nginx source를 이용한 compile 설치에서 nginx 소스에서 아래 명령어를 실행하고 다시 컴파일 하면 됩니다.
sed -i 's@"nginx/"@"-/"@g' src/core/nginx.h
sed -i 's@r->headers_out.server == NULL@0@g' src/http/ngx_http_header_filter_module.c
sed -i 's@r->headers_out.server == NULL@0@g' src/http/v2/ngx_http_v2_filter_module.c
sed -i 's@<hr><center>nginx</center>@@g' src/http/ngx_http_special_response.c
로그인 보안 강화
XWIKI는 username/password를 쿠키에 암호화 하여 저장합니다. 또한 로그인 화면에 연결되었던 세션을 재활용 하는데, 이 두가지를 처리 하였습니다.
request 쿠키 값과 response 쿠키 값을 로그인 시 변조 하는 처리
로그인시 쿠키값을 모두 삭제하여서 로그인 성공 페이지로 전환되며
url
에jsessionid
를 포함해서 전달됩니다.
...
location = / {
return 301 https://$host/xwiki;
}
location /xwiki/bin/loginsubmit/XWiki/XWikiLogin {
proxy_set_header Host $host;
proxy_hide_header Cookie;
proxy_set_header Cookie "";
proxy_pass http://127.0.0.1:8080/xwiki/bin/loginsubmit/XWiki/XWikiLogin;
proxy_hide_header Set-Cookie;
proxy_ignore_headers Set-Cookie;
}
location /xwiki {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
# nginx에서 uri를 디코딩하여 넘기기 때문에 $request_uri를 사용하여 넘어온 uri를 그대로 넘김
proxy_pass http://127.0.0.1:8080$request_uri;
# xwiki에서 redirect를 http로 하기 때문에 http 부분을 뜯어내고 uri만 전달하도록 변경 처리
proxy_redirect ~^http://[^/]+(/.+)$ $1;
}
...
jsessionid
를 포함하는 URL일 경우 JSESSIONID
쿠키를 생성하여 로그인 처리 마무리를 한다.
location ~* "/xwiki/bin/view/(.*)/;jsessionid=(.*)" {
proxy_set_header Host $host;
proxy_set_header Cookie "JSESSIONID=$2; Path=/xwiki; HttpOnly;";
add_header Set-Cookie "JSESSIONID=$2; Path=/xwiki; HttpOnly;";
proxy_pass http://127.0.0.1:8080/xwiki/bin/view/$1;
}
Visual Realtime Collaborative Editor SSL 적용하기
XWIKI에 SSL을 적용하고 나면, websocket 연결 또한 SSL처리를 해야 합니다. 이는 [Deprecated] WebSocket Integration를 참고하여 아래와 같이 처리 하였습니다.
# /applications/ssl 에 인증서가 있다고 가정하고 진행
# cert.pem, cert.key가 있을 경우 cert.key 파일을 pkcs8 형식으로 변환
$ openssl pkcs8 -topk8 -nocrypt -out /applications/ssl/cert_pkcs8.key -in /applications/ssl/cert.key
# xwiki.properties 파일의 가장 아래에 아래 항목 추가
$ vi /appliations/xwiki/webapps/xwiki/WEB-INF/xwiki.properties
...
websocket.ssl.enable=true
websocket.ssl.certChainFile= /applications/ssl/cert.pem
websocket.ssl.pkcs8PrivateKeyFile= /applications/ssl/cert_pkcs8.key
마치며
보안 점검 사항을 진행하면서 이해하기 어려운 부분도 꽤 있었는데, 융통성이 없이 모두 처리 해야했습니다.
특히 오픈소스의 결과물만 사용하는 입장으로서는 소스코드를 수정하지 못하여 더 어려웠던거 같네요.
참고 자료
- remove specific cookie in nginx reverse proxy
- How to set Secure attribute to Set-cookie in Nginx through nginx.conf file
- ERR_CONTENT_LENGTH_MISMATCH on nginx and proxy on Chrome when loading large files
- /var/lib/nginx/client_body/0000000011 failed (13: Permission denied) with node.js
- 자체 서명 사설 SSL 인증서 만들기
- How to remove the Server header in NGINX