Profile picture

[Linux] 리눅스에서 SSL/TLS (HTTPS) 인증서 처리하는 방법 - Let's Encrypt / Nginx

JaehyoJJAng2025년 01월 01일

개요

오픈소스 인증서인 Let's Encrypt를 이용해 SSL/TLS를 적용해보고,

특히 여러 서브도메인을 Nginx 리버스 프록시로 운영하며 각각의 서브도메인에 대해 HTTPS를 적용하는 방법에 대해서 기록해보려고 합니다.


SSL/TLS 인증서, 대체 어떻게 작동하나요? 🧐

image
웹사이트에 접속할 때 주소창에 뜨는 자물쇠(🔒) 아이콘, 바로 그게 SSL/TLS 인증서가 적용되었다는 신호입니다!


쉽게 말해 "우리 웹사이트는 당신의 브라우저와 안전하게 암호화된 통신을 합니다" 라는 것을 공인된 기관(CA)이 보증해주는 신분증 같은 것이죠.


발급 구조를 간단하게 알아볼까요?

  • 1. 서버 운영자: "내 도메인(your-domain.com)에 대한 인증서를 발급해주세요!" 라고 인증 기관(CA)에 요청합니다.
  • 2. 인증 기관 (CA, Certificate Authority): "정말 당신이 그 도메인의 주인이 맞소? 증명해보시오!" 라고 답하며 미션을 줍니다.
  • 3. 소유권 증명 (Domain Validation): 서버 운영자는 CA가 제시한 방법으로 도메인 소유권을 증명합니다. Let's Encrypt가 사용하는 방법은 다음과 같아요.
    • HTTP-01 방식: Let's Encrypt의 자동화 봇(Certbot)이 우리 서버의 특정 경로에 임의의 파일을 생성합니다.
    • CA는 해당 도메인으로 접속해 그 파일이 있는지 확인하죠. 만약 파일이 있다면 "아, 이 도메인을 제어할 수 있는 사람이 맞구나!"라고 인정 합니다.
  • 4. 인증서 발급: 소유권 증명이 완료되면, CA는 해당 도메인만을 위한 고유한 암호화 키가 담긴 SSL/TLS 인증서를 발급해줍니다.
  • 5. 서버에 설치: 발급 받은 인증서를 웹서버(nginx,apache 등)에 설치하면, 모든 방문자는 HTTPS 프로토콜을 통해 안전하게 접속할 수 있게 됩니다.

1. Let's Encrypt 설치 및 자동 갱신 (feat. Certbot) ⚙️

이제 실전으로 바로 넘어가볼까요?

Ubuntu 환경에서 웹서버는 Nginx를 사용한다는 가정하에, Certbot을 설치하고 인증서를 발급받아 보겠습니다.


사전 준비물

  • 리눅스 서버
  • your-domain.com 이라는 도메인 보유
  • your-domain.com, test1.your-domain.com, test2.your-domain.com, test3.your-domain.com 의 DNS A 레코드가 모두 서버의 공인 IP 주소를 가리키고 있어야 합니다.

1단계: Certbot 설치

최신 버전의 Certbot은 snap을 통해 설치하는 것이 공식 권장 사항으로 나와있습니다.

# 기존에 설치된 certbot이 있다면 제거합니다.
sudo apt-get remove certbot

# snapd를 최신 버전으로 업데이트하고 certbot을 설치합니다.
sudo snap install --classic certbot

# certbot 명령을 바로 사용할 수 있도록 심볼릭 링크를 생성합니다.
sudo ln -s /snap/bin/certbot /usr/bin/certbot

2단계: Nginx용 Certbot 플러그인 설치 및 인증서 발급

Certbot은 Nginx 설정을 자동으로 읽고 HTTPS 설정을 추가해주는 편리한 플러그인을 제공합니다.

아래 명령을 실행하여 메인 도메인과 모든 서브도메인에 대한 인증서를 한 번에 발급받겠습니다.

# --nginx 플러그인을 사용하고, -d 옵션으로 필요한 모든 도메인을 나열합니다.
sudo certbot --nginx -d your-domain.com -d test1.your-domain.com -d test2.your-domain.com -d test3.your-domain.com

명령어를 실행하면 몇 가지 질문을 할겁니다.

  • 이메일 주소 입력 (인증서 만료 시 알림을 받기 위해 중요함)
  • 서비스 약관 동의
  • EFF 뉴스레터 구독 여부

모든 과정을 마치면 Certbot이 성공적으로 인증서를 발급하고 Nginx 설정에 HTTPS를 적용해줍니다.

또한, HTTP로 들어온 요청을 HTTPS로 자동 리디렉션하는 설정까지 추가할 거냐고 물어보는데, 2번 (Redirect) 을 선택하는 것을 추천드려요!


3단계: 자동 갱신 테스트

Let's Encrypt 인증서는 90일마다 갱신해야 하지만, Certbot 패키지는 설치 시 systemd timercron job을 통해 자동 갱신 스크립트를 등록해줍니다.

따라서 우리는 신경 쓸 필요가 없는거죠!


자동 갱신이 잘 작동하는지 테스트해보려면 아래 명령어를 실행해보세요.

# 실제 갱신을 하지는 않고, 갱신 프로세스가 정상적으로 동작하는지 시뮬레이션합니다.
sudo certbot renew --dry-run

"Congratulations, all simulated renewals succeeded" 라는 메시지가 보인다면 성공입니다! 🎉


2. Nginx 서브도메인별 리버스 프록시 설정 (HTTPS 적용) 📜

이제 마지막 단계입니다.

test1, test2, test3 서브도메인으로 들어온 요청을 각각 다른 내부 서비스(예: localhost:3000, localhost:3001, localhost:3002)로 연결하는 리버스 프록시를 설정해보겠습니다.


Certbot이 Nginx 설정을 일부 수정하기는 했지만, 리버스 프록시 설정은 우리가 직접 해줘야 해요.


Nginx 설정은 보통 /etc/nginx/sites-available 경로에 있습니다.

default 파일을 수정하거나 새로운 설정 파일을 만들어 관리할 수 있어요.


여기서는 default 파일을 수정하는 것을 예로 들겠습니다.


sudo nano /etc/nginx/sites-available/default 명령어로 파일을 열고,

아래와 같이 내용을 작성하거나 수정합니다. Certbot이 이미 생성한 내용이 있다면, 그 내용을 기반으로 location 블록의 proxy_pass 부분만 추가/수정해주면 됩니다.


Nginx 설정 파일 예시 (/etc/nginx/sites-available/default)

# 각 서브도메인이 사용할 내부 애플리케이션의 포트를 미리 정의해두면 관리가 편합니다.
# test1.your-domain.com -> localhost:3001
# test2.your-domain.com -> localhost:3002
# test3.your-domain.com -> localhost:3003

# ==============================================================
# 메인 도메인(your-domain.com) 서버 블록
# ==============================================================
server {
    server_name your-domain.com; # 이 서버 블록이 처리할 도메인 이름입니다.

    # 여기에 메인 도메인에 대한 설정을 추가합니다.
    # 예를 들어, 정적 웹사이트를 제공한다면 root 와 index 지시자를 사용합니다.
    root /var/www/main; # 메인 웹사이트 파일이 위치한 경로
    index index.html index.htm;

    listen 443 ssl; # 443 포트에서 SSL 통신을 리스닝합니다.
    # 아래 두 줄은 Certbot이 자동으로 추가해주는 SSL 인증서 경로입니다. 건드리지 마세요.
    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; # 발급받은 인증서 전체 체인
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; # 개인 키

    # Certbot이 권장하는 SSL 관련 보안 설정을 포함합니다.
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

# ==============================================================
# 서브도메인 1 (test1.your-domain.com) 리버스 프록시 서버 블록
# ==============================================================
server {
    server_name test1.your-domain.com; # 이 서버 블록은 test1 서브도메인을 처리합니다.

    location / {
        # 이 경로로 들어온 모든 요청을 아래 주소로 전달(프록시)합니다.
        # http://localhost:3001 은 내부에서 실행 중인 첫 번째 애플리케이션을 의미합니다.
        proxy_pass http://localhost:3001;

        # 프록시 관련 중요 헤더 설정입니다.
        # 실제 클라이언트의 IP 주소를 백엔드 서버로 전달합니다.
        proxy_set_header X-Real-IP $remote_addr;
        # 프록시 서버를 거쳤다는 것을 알려주고, 거쳐온 프록시 서버들의 IP를 기록합니다.
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        # 웹소켓(WebSocket) 같은 연결을 위해 Upgrade 와 Connection 헤더를 설정합니다.
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        # 호스트 헤더를 설정하여 백엔드 서버가 올바른 도메인 요청으로 인지하게 합니다.
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    listen 443 ssl; # 443 포트에서 SSL 통신을 리스닝합니다.
    # 메인 도메인과 동일한 인증서를 사용합니다. Certbot이 모든 도메인을 묶어 하나의 인증서를 발급했기 때문입니다.
    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

# ==============================================================
# 서브도메인 2 (test2.your-domain.com) 리버스 프록시 서버 블록
# ==============================================================
server {
    server_name test2.your-domain.com; # 이 서버 블록은 test2 서브도메인을 처리합니다.

    location / {
        # 요청을 내부 포트 3002번으로 전달합니다.
        proxy_pass http://localhost:3002;
        # 헤더 설정은 위와 동일하게 적용합니다.
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    # SSL 설정도 동일하게 적용합니다.
    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

# ==============================================================
# 서브도메인 3 (test3.your-domain.com) 리버스 프록시 서버 블록
# ==============================================================
server {
    server_name test3.your-domain.com; # 이 서버 블록은 test3 서브도메인을 처리합니다.

    location / {
        # 요청을 내부 포트 3003번으로 전달합니다.
        proxy_pass http://localhost:3003;
        # 헤더 설정은 위와 동일하게 적용합니다.
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    # SSL 설정도 동일하게 적용합니다.
    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

# ==============================================================
# HTTP(80) 요청을 HTTPS로 리디렉션하는 서버 블록
# Certbot이 보통 자동으로 생성해줍니다.
# ==============================================================
server {
    listen 80;
    # 여기에 명시된 모든 도메인에 대한 HTTP(80) 요청을 처리합니다.
    server_name your-domain.com test1.your-domain.com test2.your-domain.com test3.your-domain.com;
    # $host 변수는 요청된 도메인 이름을 그대로 사용하며, $request_uri는 도메인 뒤의 경로를 의미합니다.
    # 301 리디렉션(영구 이동)을 통해 모든 HTTP 요청을 HTTPS로 보냅니다.
    return 301 https://$host$request_uri;
}

설정 적용 및 테스트

설정 파일 작성이 완료되었다면, 아래 명령어를 순서대로 실행해 Nginx에 적용합시다.

# Nginx 설정 파일에 문법 오류가 없는지 확인합니다.
sudo nginx -t

# 오류가 없다면 Nginx 서비스를 재시작하여 설정을 적용합니다.
sudo systemctl restart nginx

이제 웹 브라우저에서 https://test1.your-domain.com, https://test2.your-domain.com, https://test3.your-domain.com 으로 각각 접속해보세요!

자물쇠 아이콘과 함께 각기 다른 내부 애플리케이션 화면이 정상적으로 보인다면 모든 작업이 성공적으로 완료된 겁니다 ~~~


[번외] 와일드카드가 뭔가요?

와일드카드(*.your-domain.com)가 뭘까요?

와일드카드는 DNS와 SSL 인증서 양쪽에서 모두 사용되는 매우 강력한 기능이에요!


와일드카드 DNS 레코드

이 위에 *.your-domain.com에서 *는 "어떤 문자열이든" 이라는 의미의 특수 기호입니다.

DNS 설정에서 *.your-domain.com에 대해 CNAME 또는 A 레코드를 설정하면,

별도로 레코드가 지정되지 않은 모든 서브도메인(blog.your-domain.com, shop.your-domain.com, anything.your-domain.com 등)이 해당 레코드를 따라가게 됩니다!


즉, 와일드카드는 정의되지 않은 모든 서브도메인을 하나의 IP로 보내고 싶을 때 매우 유용한거죠!


와일드카드 SSL 인증서

이게 아마 핵심적인 내용일 겁니다.

*.your-domain.com에 대한 와일드카드 인증서를 하나 발급받으면,

your-domain.com과 그 아래의 모든 1단계 서브도메인(test1, test2, shop 등)이 전부 이 인증서 하나로 커버됩니다.


장점으로는 뭐가 있을까요?

서브도메인을 추가할 때마다 인증서를 다시 발급받을 필요가 없어 매우 편리합니다.


단점도 있겠죠?

와일드카드 인증서를 Let's Encrypt로 발급받으려면 인증 방식이 더 복잡해집니다.

이전 가이드에서 사용한 certbot --nginx 방식은 서버에 파일을 올리는 HTTP-01 인증 방식입니다.


이 방식은 와일드카드 인증을 지원하지 않습니다.

와일드카드 인증은 DNS-01 인증 방식을 사용해야 합니다.

이것은 Certbot이 도메인의 DNS 설정에 직접 접근해서, 특정 TXT 레코드를 생성했다가 삭제하는 방식으로 소유권을 증명합니다.


즉, 사용 중인 DNS 서비스(Cloudflare, AWS Route 53 등)의 API 키를 Certbot에 제공하고, 해당 DNS 서비스에 맞는 플러그인을 설치하는 등 초기 설정이 훨씬 복잡합니다.


[번외] Cloudflare에서 와일드카드 인증서 발급받는 방법!

위에서 언급했듯이 와일드카드(*.your-domain.com) 인증서는 서브도메인을 자주 사용하는 환경에서 매우 유용합니다.


다만 이전 설명처럼, 발급 과정이 조금 더 복잡한 DNS 인증(DNS-01 Challenge) 을 사용해야 하죠!


그래도 궁금하시죠?

그럼 가장 많이 사용하는 Cloudflare에서 와일드카드를 인증서를 어떻게 발급받는지 알아보도록 하겠습니다.


참고로 Cloudflare는 Certbot 공식 플러그인을 지원하여 API를 통한 DNS 인증 과정이 매우 깔끔하고 자동 갱신도 완벽하게 지원합니다.


1. Cloudflare에서 API 토큰 발급받기

Certbot이 내 도메인의 DNS 설정을 변경할 수 있도록 전용 비밀번호(API 토큰)를 만들어야 합니다.

Cloudflare 대시보드에 로그인 후, 오른쪽 위 '내 프로필' > 'API 토큰' 으로 이동합니다.


  • 1. '토큰 생성' 버튼을 클릭합니다.
  • 2. '사용자 정의 토큰' 섹션에서 **'시작하기'**를 클릭합니다.
  • 3. 아래와 같이 권한을 설정합니다. 이것이 가장 중요합니다.
    • 토큰 이름: certbot-token 처럼 알아보기 쉽게 입력
    • 권한:
      • 영역(Zone) | DNS | 편집(Edit)
    • 영역 리소스:
      • 포함(Include) | 특정 영역(Specific zone) | your-domain.com (인증받을 도메인 선택)
  • 4. '계속해서 요약으로' > '토큰 생성' 버튼을 클릭합니다.
  • 5. 생성된 API 토큰을 안전한 곳에 즉시 복사해두세요. 이 창을 벗어나면 다시 볼 수 없습니다.

2. 서버에 API 토큰 정보 저장하기

서버에 API 토큰을 담을 설정 파일을 만듭니다.

# 자격 증명 파일을 저장할 디렉터리를 만들고 권한을 설정합니다.
sudo mkdir -p /root/.secrets/certbot
sudo chmod 0700 /root/.secrets/certbot

# 설정 파일을 만들고 내용을 작성합니다.
sudo nano /root/.secrets/certbot/cloudflare.ini

파일 안에 아래 내용 붙여넣고, 복사해둔 API 토큰으로 교체해주세요.

# Cloudflare API token used by Certbot
dns_cloudflare_api_token = "여기에_복사한_API_토큰을_붙여넣으세요"

작성 후 저장하고 파일 권한을 소유자만 읽고 쓸 수 있도록 변경합시다.

# 파일 권한을 엄격하게 설정하여 보안을 강화합니다.
sudo chmod 0600 /root/.secrets/certbot/cloudflare.ini

3. Certbot 플러그인 설치 및 인증서 발급

이제 모든 준비가 끝났습니다!

Certbot을 실행해 인증서를 발급받읍시다.


3-1. Cloudflare DNS 플러그인 설치하기

sudo apt update
sudo apt install python3-certbot-dns-cloudflare -y

만약 snap으로 Certbot 설치했다면 플러그인이 이미 포함되어 있어 또 설치할 필요는 없습니다.


3-2. 인증서를 발급합니다.

sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /root/.secrets/certbot/cloudflare.ini \
  -d your-domain.com \
  -d '*.your-domain.com'
  • certonly: 인증서만 발급받고 Nginx 설정은 직접 하겠다는 옵션입니다.
  • --dns-cloudflare: Cloudflare 플러그인을 사용합니다.
  • --dns-cloudflare-credentials: API 토큰이 담긴 파일 경로를 지정합니다.
  • -d your-domain.com -d '*.your-domain.com': 반드시 기본 도메인과 와일드카드 도메인을 함께 적어줘야 합니다.

이 명령을 실행하면 Certbot이 Cloudflare API를 이용해 DNS TXT 레코드를 자동으로 추가했다가 삭제하면서 인증을 완료합니다.

자동 갱신 시에도 이 과정이 동일하게 자동으로 수행됩니다.

    Tag -

Loading script...