홈 서버 만들기 08. 웹 서버: Nginx + https 보안 연결

May 10, 2018

홈 서버에서 웹사이트를 운영하려면 웹 서버를 설치해야 한다. 꼭 웹사이트를 운영하지 않더라도 클라우드 스토리지나 온라인 오피스를 작동시키려면 웹 서버를 설치해야 한다. 웹 서버의 프록시 기능을 이용하면 홈 서버의 다른 여러 기능을 웹에서 확인하는데도 사용할 수 있다. 설치할 웹 서버로 가볍고 간결하고 성능이 좋은 Nginx를 선택했다.

도메인 연결 및 포트 포워딩

보유하고 있는 도메인의 DNS 또는 DDNS 설정을 통해서 홈 서버의 공인 IP 주소를 도메인과 연결하고, 인터넷 공유기를 사용하고 있다면 포트 포워딩 기능을 이용해서 웹 서버가 사용하는 TCP 80번 포트와 TCP 443번 포트가 홈 서버 컴퓨터로 향하도록 포워딩한다.

방화벽 설정

sudo iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT

iptables 방화벽에 웹 서버가 사용하는 TCP 80번, TCP 443번 포트를 허용하는 규칙을 생성한다. (기본적인 방화벽 세팅은 03. 우분투 서버 18.04 LTS 기본 설정에서 완료했다.)

sudo netfilter-persistent save
sudo netfilter-persistent reload

iptables 방화벽의 설정 내용을 저장하고 리로드한다. (iptables-persistent는 03. 우분투 서버 18.04 LTS 기본 설정에서 설치했다.)

Nginx 설치

sudo apt-get update
sudo apt-get install nginx

패키지 저장소 정보를 갱신하고 nginx를 설치한다.

example.com 웹사이트의 웹 서버 공간 설정

example.com 도메인을 사용한다고 가정한다. 아래의 내용에서 example.com 을 자신의 도메인으로 변경해서 설정해야 한다.

sudo mkdir -p /var/www/example.com/html

example.com 웹사이트의 root 디렉토리를 생성한다. /var/www/ 하위의 디렉토리 이름은 원하는 대로 생성하면 되는데 여기서는 구분하기 쉽도록 example.com 도메인 이름을 그대로 사용했다.

sudo chown -R varins:varins /var/www/example.com/html

생성한 root 디렉토리 및 그 하위 디렉토리(-R 옵션)의 소유자와 소유그룹을 자신의 사용자 계정으로 설정한다. varins:varins를 자신의 사용자 이름으로 바꿔서 입력한다.

sudo nano /var/www/example.com/html/index.html

example.com 웹사이트의 작동 확인용 index.html 파일을 nano 편집기로 생성한다.

<html>
	<head>
		<title>example.com</title>
	</head>
	<body>
		example.com
	</body>
</html>

파일 내용은 위와 같이 적당히 작성하고, Ctrl키와 x키를 동시에 눌러서 nano 편집기를 빠져나오면서 저장한다.

example.com 웹사이트의 서버 블록 설정

sudo nano /etc/nginx/sites-available/example.com

Nginx는 Nginx가 운영하는 각각의 웹사이트를 각각의 설정 파일로 관리하며, 이를 서버 블록이라고 부른다. example.com 웹사이트의 설정을 담을 서버 블록 파일을 nano 편집기로 새로 생성한다. 구분하기 쉽도록 서버 블록 파일의 이름을 example.com 도메인 이름 그대로 사용했다.

server {
	listen 80;

	root /var/www/example.com/html;

	index index.html index.htm;

	server_name example.com www.example.com;

	location / {
		try_files $uri $uri/ =404;
	}
}

위의 내용을 입력한다. 80번 포트의 접속을 받아들이고, 웹사이트의 내용이 있는 root 디렉토리를 지정하고, 웹사이트에 개시될 index 파일의 종류와 순서를 지정하고, 접속할 수 있는 도메인을 지정하고, URI 접속 요청을 파일 → 디렉토리 → 404오류 순서로 응답하라는 내용이다. 3곳의 example.com을 자신의 것으로 바꿔서 입력한다. 작성했으면 Ctrl키와 x키를 동시에 눌러서 nano 편집기를 빠져나오면서 저장한다.

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

작성한 서버 블록을 작동시키기 위해 /etc/nginx/sites-enabled/ 경로에 심볼릭 링크를 만들어준다. 서버 블록 파일이 /etc/nginx/sites-available/ 경로에 있으면 웹사이트가 준비된 상태이고 아직 작동은 하지 않는 상태이다. 서버 블록 파일이 /etc/nginx/sites-enabled/ 경로에 있으면 비로소 웹사이트가 작동한다. 이때 서버 블록 파일을 sites-enabled 경로에 복사해 넣는 대신에 심볼릭 링크를 만들어주면 sites-available 경로의 서버 블록 파일만 관리하면 되므로 편리하다.

만일 이런 서버 블록을 여러 개 생성해서 다수의 웹사이트를 운영한다면 도메인 처리를 원활하게 하기 위해서 Nginx 설정 파일 nginx.conf에 아래의 설정을 추가한다.

sudo nano /etc/nginx/nginx.conf

Nginx 설정 파일인 nginx.conf 파일을 nano 편집기로 연다.

server_names_hash_bucket_size 64;

server_names_hash_bucket_size 64;를 찾아서 앞에 주석 #을 제거하고, Ctrl키와 x키를 동시에 눌러서 nano 편집기를 빠져나오면서 저장한다.

이상으로 서버 블록 설정이 끝났으면, 설정한 서버 블록이 Nginx에 반영되도록 아래와 같이 Nginx를 재시작한다.

sudo nginx -t

Nginx 설정에 기본적인 문법 오류가 없는지 점검한다. 오류가 없다면
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
이런 메시지가 출력된다. 만일 다른 메시지가 나온다면 메시지를 참고해서 설정 사항을 점검한다.

sudo systemctl restart nginx

오류가 없으면 Nginx를 재시작한다.

이제 웹브라우저로 example.com에 접속하면 앞서 작성한 index.html의 내용이 보일 것이다.

https 보안 연결 설정

도메인에 SSL/TLS 인증서를 발급받으면 신뢰할 수 있는 도메인이라는 효력이 생기고, 이를 바탕으로 웹 서버와 클라이언트 사이의 연결을 암호화하는 https 프로토콜을 적용할 수 있다. 무료로 SSL/TLS 인증서를 발급받으려면 Let’s Encrypt 서비스를 이용하면 되는데, 자세한 인증서 발급 방법은 앞선 글 07. SSL/TLS 인증서를 참고한다. 이 글에서 설명하는 방식으로 인증서를 발급받으면 *.example.com 형태의 와일드카드 서브 도메인에 대한 인증서를 받을 수 있어서 하나의 인증서로 모든 서브 도메인에 사용할 수 있다.

인증서를 발급받았으면 Nginx 웹 서버에 인증서를 적용해서 https 보안 연결을 구축한다.

sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096

먼저, 운영하는 모든 웹사이트의 https 보안 연결에 공통으로 사용하는 DH Param 키를 생성한다. DH Param 키는 일부 암호화 방식의 성능을 높이기 위해서 사용하는 난수인데, 위 명령어는 4096 비트 (512 바이트) 로 생성하므로 시간이 오래 걸린다. 서버 컴퓨터의 성능에 따라 다르지만 길게는 수십 분 가량 소요되기도 한다. 2048 비트 (256 바이트) 로 생성하려면 위의 명령에서 끝에 4096을 2048로 바꿔서 입력한다. 키가 생성되는 위치는 /etc/ssl/certs/dhparam.pem 으로 지정했다.

sudo nano /etc/nginx/sites-available/example.com

example.com의 Nginx 서버 블록 파일을 nano 편집기로 열어서 아래와 같이 편집한다.
참고로, 편집할 내용이 많기 때문에 echo '' | sudo tee /etc/nginx/sites-available/example.com 명령을 먼저 사용해서 서버 블록 파일의 내용을 모두 삭제한 다음에 새롭게 작성하는 것도 하나의 방법이다.

server {
	listen 80;

	server_name example.com www.example.com;

	return 301 https://$server_name$request_uri;
}

server {
	listen 443 ssl http2;

	server_name example.com www.example.com;

	root /var/www/example.com/html;

	ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
	ssl_dhparam /etc/ssl/certs/dhparam.pem;

	ssl_protocols TLSv1.2;
	ssl_prefer_server_ciphers on;
	ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256;
	ssl_ecdh_curve secp384r1;
	ssl_session_timeout 10m;
	ssl_session_cache shared:SSL:10m;
	ssl_session_tickets off;
	ssl_stapling on;
	ssl_stapling_verify on;
	resolver 8.8.8.8 8.8.4.4 valid=300s;
	resolver_timeout 5s;

	add_header Strict-Transport-Security max-age=31536000;
	add_header X-Frame-Options SAMEORIGIN;
	add_header X-Content-Type-Options nosniff;
	add_header X-XSS-Protection "1; mode=block";

	index index.html index.htm;

	location / {
		try_files $uri $uri/ =404;
	}
}

7군데의 example.com을 자신의 것으로 바꾸는 것에 주의하면서, 위의 내용으로 서버 블록 파일을 바꾼다.

위의 내용을 살펴보면, 80번 포트로 비보안 http 연결이 들어오면 https 주소로 301 Moved Permanently 이동시키고, 443번 포트로 보안 https 연결이 들어오면 인증서와 DH Param 키를 바탕으로 강력한 암호 조합을 사용해서 TLSv1.2 보안 연결을 구축하고, http2 연결도 활성화한다. add_header의 내용은 처음 한 번 방문한 다음부터는 https로만 접속하게 하고, iframe 등으로 웹사이트를 불러가지 못하게 하고, MIMETYPE 변조를 막고, XSS 공격을 막는 내용이다.

이 중에서 return 301add_header Strict-Transport-Security를 사용하면 방문자는 한 번 접속한 다음부터는 자신의 웹 브라우저의 캐시가 삭제되지 않는 한 항상 https 주소로 접속하게 되고 http 주소로는 접속하지 못한다. 만약에 테스트 등을 위해서 http 주소로도 접속해야 한다면 301 (영구 리턴) 을 302 (임시 리턴) 로 바꾸거나 return 줄 자체를 삭제하고, add_header Strict-Transport-Security 줄도 삭제한다. 삭제 대신에 각 줄 앞에 #을 붙여서 주석 처리해도 된다.

그리고 ssl_protocols에는 TLSv1.2만 사용하도록 설정했는데, TLSv1.1 이하는 보안성이 약해져서 사용하지 않을 것이 권장되기 때문이다. TLSv1.2를 지원하는 웹 브라우저는 Chrome 30, Firefox 27, Edge 12, Internet Explorer 11, Opera 17, Safari 7, Android 4.4.2 이상의 버전이다. 그밖의 ssl_ 옵션들에 대한 설명은 Nginx 웹사이트의 설명을 참고한다.

편집을 끝냈으면 Ctrl키와 x키를 동시에 눌러서 nano 편집기를 빠져나오면서 저장한다.

sudo nginx -t
sudo systemctl restart nginx

Nginx 설정의 기본적인 문법 오류를 검사하고, Nginx를 재시작한다.

이제 웹브라우저에서 example.com 주소로 접속하면 https 프로토콜로 접속될 것이다. https로 제대로 접속되었다면 웹 브라우저의 주소창 주변에 자물쇠 모양이 나타날 것이고 이 자물쇠를 눌러보면 인증서의 내용을 확인할 수 있다. 설정된 보안 상태가 어느 정도 수준인지는 SSL Labs에서 점검해볼 수 있다. 위에 설명한 대로 설정했다면 A+ 등급을 받을 것이다.

Let’s Encrypt 인증서 갱신 후 Nginx 리로드

Let’s Encrypt 인증서의 유효기간은 90일이고 Certbot으로 인증서를 발급받으면 유효기간을 자동으로 갱신하는 설정이 적용된다는 것을 앞선 글 07. SSL/TLS 인증서에서 설명했다. 인증서가 갱신된 후에는 Nginx를 한 번 리로드 해줘야 갱신된 인증서가 Nginx에 적용되는데, 역시 앞선 글에서 설명한 대로 .sh 파일을 만들어서 후크로 연결해주면 이 역시도 자동으로 진행된다. 자세한 내용은 앞선 글을 참고하면 되고, .sh 파일에 기록할 Nginx 리로드 명령은 다음과 같다.

#! /bin/bash
/bin/systemctl reload nginx

 

 

홈 서버 만들기 목차 
https://varins.com/category/server

01. 하드웨어 구성과 전기 요금
02. 우분투 서버 18.04 LTS 설치
03. 우분투 서버 18.04 LTS 기본 설정
04. 파일 복원: Btrfs 스냅샷
05. 네트워크 파일 공유: Samba (SMB)
06. Dynamic DNS: 구글 도메인 + ddclient
07. SSL/TLS 인증서: Let's Encrypt (Wildcard)
08. 웹 서버: Nginx, 서버 블록, https 보안연결
09. 웹사이트: php + MariaDB + WordPress
10. 서버 모니터링: Monitorix + Nginx Proxy
11. FTPS: vsftpd
12. VPN: IKEv2 (strongSwan)
13. Torrent: Transmission
14. 클라우드 스토리지: Nextcloud (Nginx, MariaDB)
15. 온라인 오피스: 온리오피스 (Docker, Nginx)
16. 트랜스코딩: Plex
17. VNC RDP 클라이언트: Guacamole (Docker, Nginx)
18. 가상 머신: QEMU-KVM + libvirt + virt-install
19. 시스템 업그레이드: i3 8300T, 970 EVO 전력 사용량
20. 서버 모니터링: netdata + Nginx Proxy

Leave a comment

Your email address will not be published. Required fields are marked *

Prev Post Next Post