웹 서버: Nginx

July 1, 2019
    본문 업데이트
    2019년 7월 7일: OpenSSL 업그레이드 및 Nginx 서버 블록 TLS 1.3 설정 추가
    

    Nginx

    웹사이트나 클라우드 스토리지, 온라인 오피스 등을 운영하려면 웹 서버가 필요하다. 또한 웹 서버의 리버스 프록시 기능을 이용하면 홈 서버의 여러 기능을 웹에서 확인하는 데에도 사용할 수 있다. 여러 가지 웹 서버 중에서 가볍고 성능이 좋은 Nginx를 설치한다.

    도메인 연결

    보유하고 있는 도메인의 DNS에서 도메인과 홈 서버의 공인 IP 주소를 연결한다. DNS에 관한 전반적인 내용은 앞선 글 도메인과 DNS를 참고한다.

    포트 포워딩

    라우터 (인터넷 공유기) 를 사용하고 있다면 포트 포워딩 기능을 이용해서 웹 서버가 사용하는 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번 포트를 허용하는 규칙을 생성한다. (기본적인 방화벽 세팅은 앞선 글 우분투 서버 기본 설정에서 완료했다.)

    sudo netfilter-persistent save
    sudo netfilter-persistent reload
    

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

    Nginx 설치

    sudo apt update
    sudo apt install nginx
    

    패키지 저장소 정보를 업데이트하고 nginx를 설치한다.

    서버 블록 이해

    Nginx에서 각 웹사이트의 설정 내용은 server { } 형태의 문법으로 구성되는데, 이를 서버 블록이라고 부른다.

    우분투 서버에서 서버 블록 파일은 /etc/nginx/sites-available/ 경로와 /etc/nginx/sites-enabled/경로에 위치하는데, available 경로에 있으면 웹사이트가 준비되었지만 아직 작동은 하지 않는 상태이고, enabled 경로에 있으면 비로소 웹사이트가 작동한다.

    즉, 웹사이트를 작동시키기 위해서는 available 경로에서 준비한 서버 블록 파일을 enabled 경로에 복사해야 하는데, 이때 복사 대신 available 경로의 파일에 연결되는 심볼릭 링크만 만들어주면 available 경로의 파일 하나만 관리하면 되므로 편리하다.

    이를 직접 확인해보면, ls -ls /etc/nginx/sites-available/ 명령으로 available 경로의 파일을 확인하면 Nginx가 예시로 제공하는 서버 블록 파일 default를 볼 수 있다. 또 ls -ls /etc/nginx/sites-enabled/ 명령으로 enabled 경로의 파일을 확인하면 default -> /etc/nginx/sites-available/default 이렇게 available 경로의 default 파일에 연결되는 심볼릭 링크만 있는 것을 확인할 수 있다.

    예시 서버 블록 중지

    sudo rm /etc/nginx/sites-enabled/default

    Nginx가 예시로 제공하는 서버 블록에는 웹 서버의 기본 서버 블록으로 지정하는 default_server 옵션이 적용되어 있기 때문에 내가 원하는 대로 웹 서버를 구성하기 위해서는 이 예시 서버 블록의 작동을 중지해야 한다. 위 명령으로 enabled 경로의 default 심볼릭 링크를 삭제하면 작동 중지 되고, 심볼릭 링크만 삭제 할 뿐 실제 서버 블록 파일 default는 available 경로에 여전히 남아 있으므로 필요한 경우 그 내용을 계속 참고할 수 있다.

    웹사이트 공간 생성

    example.com 도메인을 사용한다고 가정하고, example.com 웹사이트가 사용할 공간을 생성한다. 아래의 내용에서 example.com 을 자신의 도메인으로 변경해서 설정한다.

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

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

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

    생성한 root 디렉토리 및 그 하위 디렉토리(-R 옵션)의 소유자와 소유그룹을 www-data로 설정한다. www-data는 우분투 서버의 웹 서버 프로세스를 뜻한다.

    테스트 페이지 생성

    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 편집기를 빠져나오면서 저장한다.

    서버 블록 생성

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

    example.com 웹사이트의 설정을 담을 서버 블록 파일을 nano 편집기로 새로 생성한다. 구분하기 쉽도록 서버 블록 파일의 이름을 example.com 도메인 이름 그대로 사용했다.

    server {
            listen 80 default_server;
    
            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오류 순서로 응답하라는 내용이다.

    default_server는 각 포트에 대한 기본 서버 블록을 지정하는 옵션이다. Nginx는 서버 블록 상에 listen 80, listen 443 등으로 설정된 각 포트에서 접속을 대기하는데, 서버 블록을 여러 개 생성해서 같은 포트에서 대기하는 서버 블록이 여러 개가 될 경우에, 이 포트로 들어오는 접속에 대한 기본 서버 블록을 명시하는 옵션이다. DNS에는 도메인이 존재하지만 실제 서버의 Nginx 서버 블록 상에 이 도메인이 설정되어 있지 않으면 default_server로 지정된 서버 블록에 연결된다. default_server를 아예 지정하지 않는다면 알파벳 순서로 가장 먼저인 도메인의 서버 블록이 기본 서버 블록이 된다. default_server 옵션은 각 포트당 하나의 서버 블록에만 명시할 수 있다.

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

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

    작성한 서버 블록 파일을 작동시키기 위해 /etc/nginx/sites-enabled/ 경로에 심볼릭 링크를 만들어준다.

    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 보안 연결 설정

    도메인에 TLS 인증서를 발급받으면 신뢰할 수 있는 도메인이라는 효력이 생기고, 이를 바탕으로 웹 서버와 클라이언트 사이의 연결을 암호화하는 https 프로토콜을 적용할 수 있다.

    TLS 인증서 발급

    Let’s Encrypt 서비스를 이용하면 무료로 TLS 인증서를 발급받을 수 있는데, 자세한 방법은 앞선 글 TLS 인증서: Let’s Encrypt를 참고한다. 이 글에서 설명하는 방식으로 인증서를 발급받으면 *.example.com 형태의 와일드카드 서브도메인에 대한 인증서가 발급되어 하나의 인증서로 모든 서브도메인에 사용할 수 있게 된다.

    DH Param 키 생성

    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 으로 지정했다.

    OpenSSL 업그레이드

    보안 연결을 구축하는 프로토콜 TLS는 2018년 8월에 1.3버전으로 업데이트 되었다. 우분투 서버에서는 OpenSSL이 TLS를 담당하는데, 2018년 9월에 출시된 OpenSSL 1.1.1 버전부터 TLS 1.3을 지원한다. 다만 우분투 서버 18.04 ~ 18.04.2 버전에 기본 탑재된 OpenSSL은 1.1.0g 버전이기 때문에, 자신의 시스템에 설치된 OpenSSL의 버전을 확인한 후 1.1.1 미만의 버전이라면 1.1.1 이상의 버전으로 업그레이드해야 TLS 1.3을 이용할 수 있다.

    openssl version

    이 명령으로 현재 시스템의 OpenSSL 버전을 확인한다. 1.1.1 미만의 버전이라면 아래의 절차대로 업그레이드 한다.

    sudo apt update
    
    sudo apt install --only-upgrade openssl
    

    패키지 저장소 정보를 업데이트하고, openssl을 업그레이드 한다. 위의 openssl 개별 업그레이드 명령 대신에 sudo apt upgrade 명령으로 시스템의 모든 패키지 전체를 업그레이드 할 수도 있다. 업그레이드가 완료되면 openssl version 명령으로 업그레이드된 OpenSSL의 버전이 1.1.1 이상인지 확인해본다.

    서버 블록 설정

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

    example.com의 Nginx 서버 블록 파일을 nano 편집기로 열어서 아래와 같이 편집한다.

    참고로, 편집할 내용이 많을 경우에는 echo '' | sudo tee /etc/nginx/sites-available/example.com 명령을 먼저 사용해서 파일의 내용을 모두 삭제한 다음에 새롭게 작성하는 것도 하나의 방법이다.

    server {
            listen 80 default_server;
    
            server_name example.com www.example.com;
    
            return 301 https://$server_name$request_uri;
    }
    
    server {
            listen 443 ssl http2 default_server;
    
            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_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
            ssl_dhparam /etc/ssl/certs/dhparam.pem;
    
            ssl_protocols TLSv1.2 TLSv1.3;
            ssl_prefer_server_ciphers on;
            ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305: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 1.1.1.1 1.0.0.1 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;
            }
    }
    

    8군데의 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 주소로도 접속해야 한다면 add_header Strict-Transport-Security 줄을 삭제하고, 301 (영구 리턴) 을 302 (임시 리턴) 로 바꾸거나 return 301 줄 자체를 삭제한다. 삭제 대신에 각 줄 앞에 #을 붙여서 주석 처리해도 된다.

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

    default_server는 각 포트에 대한 기본 서버 블록을 지정하는 옵션이다. Nginx는 서버 블록 상에 listen 80, listen 443 등으로 설정된 각 포트에서 접속을 대기하는데, 서버 블록을 여러 개 생성해서 같은 포트에서 대기하는 서버 블록이 여러 개가 될 경우에, 이 포트로 들어오는 접속에 대한 기본 서버 블록을 명시하는 옵션이다. DNS에는 도메인이 존재하지만 실제 서버의 Nginx 서버 블록 상에 이 도메인이 설정되어 있지 않으면 default_server로 지정된 서버 블록에 연결된다. default_server를 아예 지정하지 않는다면 알파벳 순서로 가장 먼저인 도메인의 서버 블록이 기본 서버 블록이 된다. default_server 옵션은 각 포트당 하나의 서버 블록에만 명시할 수 있다.

    443 포트에 default_server로 지정된 서버 블록이 있다면 이 서버 블록에 TLSv1.3 옵션을 적용해야 TLS 1.3이 정상 작동한다.

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

    인증서 갱신 이후 작업

    Let’s Encrypt 인증서는 유효기간이 90일이고 유효기간이 30일 남았을 때부터 인증서를 갱신할 수 있는데, 앞선 글 TLS 인증서: Let’s Encrypt에서 설명하는 방법으로 설정하면 별도의 추가 조작 없이 와일드카드 인증서의 유효기간을 자동으로 갱신할 수 있다. 인증서가 갱신된 후에는 Nginx를 한 번 리로드 해줘야 갱신된 인증서가 Nginx에 적용되는데, 역시 앞선 글에서 설명한 대로 .sh 파일을 만들어서 후크로 연결해주면 이 또한 자동으로 진행된다. 자세한 내용은 앞선 글 TLS 인증서: Let’s Encrypt를 참고하면 되고, .sh 파일에 기록할 Nginx 리로드 명령은 다음과 같다.

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

    Nginx 설정 변경

    각 웹사이트의 설정은 서버 블록에서 제어하고, Nginx 자체의 설정은 /etc/nginx/nginx.conf 파일에서 제어할 수 있다.

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

    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 편집기를 빠져나오면서 저장한다.

    sudo nginx -t
    sudo systemctl restart nginx
    

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

     

    본 글의 저작권은 작성자 Varins에게 있습니다.
    Varins의 사전 서면 동의 없이는 본 글의 전부 또는 일부를 무단으로 전재, 게시, 배포하는 것을 금지합니다.
    

Leave a comment

댓글은 관리자의 승인 이후에 게시됩니다.