Nginx HTTPS 보안 설정: 단계별 가이드

이 포괄적인 단계별 가이드에서는 Certbot을 사용하여 Let's Encrypt에서 무료 SSL/TLS 인증서를 얻고, Nginx를 암호화된 연결로 구성하며, HSTS와 같은 필수 보안 조치를 구현하는 방법을 알아봅니다. 적절히 구성된 HTTPS 설정으로 데이터를 보호하고, 사용자 신뢰를 구축하며, SEO를 개선하세요.

Nginx HTTPS 보안 설정: 단계별 가이드

Nginx를 HTTPS로 보호하는 것은 일반적으로 작은 작업이지만, 작은 실수가 큰 문제를 일으키는 작업 중 하나입니다. 인증서 파일이 없으면 Nginx가 다시 로드되지 않습니다. 방화벽 규칙을 잊어버리면 사이트가 다운된 것처럼 보입니다. 성급한 HSTS 헤더는 사용자를 의도한 것보다 더 오래 손상된 HTTPS 설정에 가둘 수 있습니다.

좋은 소식은 일반적인 경로가 간단하다는 것입니다. DNS를 서버로 지정하고, Nginx가 포트 80에서 응답하는지 확인하고, Certbot을 사용하여 Let's Encrypt 인증서를 요청하고, 생성된 Nginx 구성을 테스트하고, 다시 로드한 다음 갱신을 확인합니다. 이 가이드는 이 경로를 따릅니다.

아래에서는 example.comwww.example.com을 사용합니다. 실제 도메인 이름으로 바꾸고, Nginx를 다시 로드하기 전에 확인 단계를 건너뛰지 마십시오.

인증서 요청 전 확인 사항

Certbot을 사용하기 전에 기본적인 사항을 확인하십시오. 대부분의 인증서 문제는 DNS, 방화벽 또는 요청된 도메인에 응답하지 않는 서버 블록에서 발생합니다.

서버 외부에서 DNS를 확인합니다:

dig +short example.com
dig +short www.example.com

두 이름 모두 Nginx 호스트 또는 로드 밸런서에 도달하는 공용 주소로 확인되어야 합니다.

클라우드 보안 그룹, 호스트 방화벽, 네트워크 방화벽 및 호스트 앞의 모든 로드 밸런서 등 모든 계층에서 포트 80과 443이 열려 있는지 확인합니다.

sudo ss -tulnp | grep nginx
sudo ufw status

클라우드 VM에서는 공급자 콘솔도 확인하십시오. 인스턴스 방화벽이 443을 허용했지만 클라우드 보안 그룹이 여전히 차단하여 깨끗한 Nginx 구성이 실패하는 경우를 많이 보았습니다.

마지막으로 Nginx가 일반 HTTP를 통해 도메인을 이미 제공하고 있는지 확인합니다:

curl -I http://example.com
curl -I http://www.example.com

응답이 예쁠 필요는 없습니다. 플레이스홀더 페이지여도 됩니다. 해당 호스트 이름에 대한 요청이 이 Nginx 서버에 도착한다는 것만 증명하면 됩니다.

Certbot 설치

Debian/Ubuntu의 경우:

sudo apt update
sudo apt install certbot python3-certbot-nginx

RHEL 호환 시스템의 경우:

sudo dnf install certbot python3-certbot-nginx

이전 CentOS 시스템은 yum을 사용할 수 있으며 먼저 EPEL을 활성화해야 할 수 있습니다. 패키지 이름도 배포판 버전에 따라 다르므로 해당 명령이 플러그인을 찾지 못하면 배포판의 패키지 문서를 사용하십시오.

인증서 요청

Nginx 플러그인이 인증서를 요청하고 일치하는 서버 블록을 편집할 수 있습니다:

sudo certbot --nginx -d example.com -d www.example.com

Certbot은 이메일 주소, 약관 동의, 때로는 HTTP를 HTTPS로 리디렉션할지 여부를 묻습니다. 일반적인 공개 웹사이트의 경우 리디렉션을 선택하십시오. API 또는 내부 서비스의 경우 먼저 클라이언트를 확인하십시오. 일부 이전 클라이언트 또는 상태 확인은 여전히 http://를 호출하고 특정 응답을 기대할 수 있습니다.

Certbot이 일치하는 서버 블록을 찾을 수 없다고 말하면 server_name을 확인하십시오. 다음과 같은 블록은 Certbot이 작업할 명확한 대상을 제공합니다:

server {
    listen 80;
    server_name example.com www.example.com;

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

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

Certbot이 아무것도 편집하도록 요청하기 전에 구문 검사를 실행하십시오:

sudo nginx -t

현재 구성이 이미 손상된 경우 먼저 수정하십시오.

Nginx 구성의 모습

성공적인 실행 후에는 일반적으로 HTTP를 리디렉션하는 하나의 HTTP 블록과 사이트를 제공하는 하나의 HTTPS 블록이 표시됩니다:

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name example.com www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

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

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

프로덕션 구성에 이것을 맹목적으로 복사하지 마십시오. 기존 location 규칙, 프록시 설정, 로그 및 업로드 제한을 유지하십시오. 중요한 부분은 listen 443 ssl, 인증서 경로 및 포트 80의 리디렉션 동작입니다.

테스트, 다시 로드 및 확인

다시 로드하기 전에 항상 테스트하십시오:

sudo nginx -t

그런 다음 다시 로드하십시오:

sudo systemctl reload nginx

명령줄에서 확인하십시오:

curl -I http://example.com
curl -I https://example.com
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates

해당 옵션을 선택한 경우 HTTP 요청이 리디렉션되어야 합니다. HTTPS 요청은 예상된 상태를 반환해야 합니다. openssl 명령은 도메인을 포함하는 주체 또는 주체 대체 이름과 현재 날짜가 있는 인증서를 표시해야 합니다.

브라우저 테스트도 여전히 중요합니다. 시크릿 창에서 사이트를 열고 자물쇠 아이콘을 클릭하여 인증서를 검사하십시오. 사이트가 이전 http:// URL에서 자산을 로드하는 경우 기본 인증서는 괜찮더라도 페이지에 혼합 콘텐츠 경고가 표시될 수 있습니다. 앱, CMS 또는 템플릿 계층에서 해당 자산 URL을 수정하십시오.

갱신

Let's Encrypt 인증서는 수명이 짧으므로 사용자가 기억하지 않아도 갱신이 작동해야 합니다. Certbot은 일반적으로 systemd 타이머 또는 cron 작업을 설치합니다. 확인하십시오:

systemctl list-timers | grep certbot
sudo certbot renew --dry-run

드라이 런이 유용한 부분입니다. 갱신 시뮬레이션을 수행하고 손상된 HTTP 검증, DNS 변경, 누락된 플러그인 또는 다시 로드할 수 없는 구성과 같은 일반적인 문제를 포착합니다.

Nginx 호스트에서 직접 TLS를 종료하지 않고 로드 밸런서 또는 CDN에서 종료하는 경우 갱신에 DNS 챌린지 또는 다른 배포 경로가 필요할 수 있습니다. 공용 트래픽이 이 서버에 도달하지 않는 경우 기본 HTTP 챌린지가 작동한다고 가정하지 마십시오.

확인할 가치가 있는 TLS 설정

대부분의 최신 공개 사이트의 경우 TLS 1.2 및 TLS 1.3이 실용적인 기준입니다:

ssl_protocols TLSv1.2 TLSv1.3;

이유를 알지 못하면 암호 제품군을 수동으로 조정하지 마십시오. Certbot에 포함된 options-ssl-nginx.conf와 Mozilla SSL 구성 생성기가 오래된 블로그 게시물에서 복사한 암호 문자열보다 더 나은 시작점입니다. 암호 지침은 시간이 지남에 따라 변경되며 호환성 요구 사항은 공개 마케팅 사이트와 내부 레거시 API 간에 다릅니다.

HSTS는 유용하지만 주의가 필요합니다:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

테스트 중에는 짧은 값으로 시작하십시오:

add_header Strict-Transport-Security "max-age=300" always;

모든 하위 도메인이 유효한 HTTPS를 제공할 수 있는 경우에만 includeSubDomains를 사용하십시오. old.example.com, mail.example.com 또는 고객별 하위 도메인이 준비되지 않은 경우 해당 옵션은 실제 사용자를 차단할 수 있습니다.

일반적인 실패

Certbot이 도메인을 확인할 수 없는 경우 먼저 DNS를 확인한 다음 포트 80, 그 다음 Nginx 서버 블록을 확인하십시오. HTTP-01 챌린지는 Let's Encrypt가 /.well-known/acme-challenge/ 아래의 토큰에 도달해야 합니다. 올바르게 구성된 경우 리디렉션은 괜찮지만 프록시, CDN 또는 포괄 블록이 실수로 챌린지를 다른 곳으로 보낼 수 있습니다.

Certbot 변경 후 Nginx가 다시 로드되지 않으면 다음을 실행하십시오:

sudo nginx -t
sudo journalctl -u nginx -n 80 --no-pager

구문 테스트는 일반적으로 정확한 파일과 줄을 알려줍니다. 일반적인 원인은 중복된 listen 443 ssl 블록, 제거된 인증서 경로 또는 이 서버에 존재하지 않는 경로에서 포함된 스니펫입니다.

HTTPS가 로컬에서는 작동하지만 사용자에게는 작동하지 않는 경우 공용 경로를 확인하십시오. 로드 밸런서가 여전히 이전 대상 그룹을 가리킬 수 있습니다. CDN이 리디렉션을 캐시했을 수 있습니다. IPv6 DNS가 IPv4와 다른 호스트를 가리킬 수 있습니다. 둘 다 테스트하십시오:

curl -4 -I https://example.com
curl -6 -I https://example.com

가장 깔끔한 HTTPS 설정은 지루합니다. DNS가 올바른 위치를 가리키고, Nginx에 도메인에 대한 하나의 명확한 서버 블록이 있고, Certbot 갱신이 드라이 런에서 통과하고, 기본 사항이 작동한 후에만 헤더가 추가됩니다.