Development/썬카(SunCar) - 개인 프로젝트

[썬카/백엔드] AWS 없이 무료로 SSL 인증서 등록하여 HTTPS 통신하기 - HTTP -> HTTPS 리다이렉션, HSTS, Let's Encrypt, snap, certbot

양선규 2025. 4. 24. 19:02
728x90
반응형

HTTP에 SSL 인증서(암호화라고 보면 됨)를 적용한 것이 HTTPS 이다.

 

웹을 서비스하기 위해서 SSL 인증서는 필수라고 할 수 있다. SSL 인증서가 없다면 1차적으로 브라우저에 안전하지 않다는 경고가 뜨며, 데이터가 평문으로 노출되어 탈취당할 가능성이 있고, 최신 웹 프로토콜이나 기술들과 호환되지 않을 수 있다. 심지어 개인정보를 취급하는 웹 사이트의 경우 암호화 등의 보안 조치를 취하는 것이 법적으로 의무화 되어 있고, HTTPS는 암호화를 적용하는 가장 쉽고 표준적인 방법이기 때문에 적용하지 않을 이유가 없다.

 

SSL 인증서를 적용하는 대표적인 방법은 크게 두 가지가 있다.

1. AWS의 ACM, Route53, 로드 밸런서를 이용한 SSL 인증서 적용

2. Let's Encrypt, 가비아(또는 다른 도메인 등록 서비스), Nginx

 

1번 ACM을 통한 SSL 인증서 발급은 무료이다. 그러나 Route53 도메인 등록과 로드 밸런서는 약간의 비용이 발생하게 된다.

2번 방식도 도메인 등록은 유료이지만, 가비아의 값싼 도메인은 1년에 500원 같은 식으로 거의 무료나 다름이 없으므로 공부하는 내 입장에서는 2번 방식을 선택했다. 물론 Let's Encrypt와 Nginx도 당연히 무료이다.

 

그리고 생각보다 어렵지 않다. 오히려 AWS에 익숙하지 않으면 2번이 더 쉬울 것이다. 그러나 1번은 AWS의 다른 서비스들과 아주 잘 호환되어 상호작용이 가능하다는 이점이 있으므로, 본인이 금액은 상관이 없고 이미 AWS의 다양한 서비스를 이용하고 있다면 1번을 선택하는 것이 좋겠다.

 

0. 사전 준비

배포는 이미 되어 있다고 가정한다. 서버는 AWS EC2 인스턴스(Ubuntu)에, DB는 AWS RDS에 존재한다. Nginx도 이미 설치되어 Blue/Green 서버로 로드 밸런서 역할을 하고 있다. 

 

SSL 인증서 등록에 사용되는 것은 아래와 같다.

Let's Encrypt - 무료 SSL 인증서를 제공하는 비영리 인증 기관(CA)

Certbot - Let's Encrypt 인증서를 자동으로 발급받고 갱신하는 오픈소스 클라이언트 툴

snap - Ubuntu에서 사용되는 패키지 관리자. macOS의 Homebrew와 친구이며 apt, yum의 최신버전 같은 느낌이다.

 

Let's Encrypt는 인증서를 발급하는 기관이고,

Certbot은 인증서 발급 요청, 검증, 인증서 자동 갱신 등의 역할을 수행하며,

snap은 그런 Certbot을 설치하고 업데이트 하는, 현재 권장되는 방법이다.

 

1. snap 설치

# snap 설치
sudo apt update
sudo apt install snapd

# snap 업데이트
sudo snap install core
sudo snap refresh core

 

우선 snap을 설치한다. 여기서 core가 뭔데 설치하고 refresh 까지 하냐 라고 할 수 있는데, core는 snap이 동작하는 런타임 환경이라고 보면 된다.

 

그런데 core를 설치하는데 시간이 조금 걸릴 수 있다. 난 EC2 프리티어 인스턴스 사용 중이라 이거 너무 크면 서버 리소스 다 먹어버리는 거 아냐? 하고 걱정했는데, snap과 core 합쳐도 200MB 도 안 되며, 처음 설치할 때만 오래 걸리지 한번 설치 후엔 딱히 리소스를 먹지 않으니 걱정하지 않아도 된다. 프리티어 인스턴스는 일반적으로 8GB ~ 30GB의 디스크를 가지니 크게 부담되는 용량도 아니다.

 

2. Certbot 설치

# certbot 설치
sudo snap install --classic certbot

# certbot 명령어 링크 생성
sudo ln -s /snap/bin/certbot /usr/bin/certbot

 

snap 을 설치했으니 Certbot을 설치한다.

 

다만 --classic 이라는 인자가 눈에 띈다. snap에 의해 설치된 패키지는 기본적으로 confined모드(제한된 권한)로 실행되는데, --classic 인자를 붙일 경우 이러한 제한을 해제한다. Certbot은 SSL 인증서 설정을 위해 Nginx 설정 파일 등 시스템 레벨 작업을 수행해야 하므로 제한을 해제할 필요가 있다.

 

또한 Certbot 명령어를 간단하게 입력하기 위해 심볼릭 링크를 생성한다. 링크를 생성하지 않으면 /snap/bin/certbot {인자} 이렇게 입력해야 한다.

 

3. Nginx 설정 확인

Let's Encrypt는 SSL 인증서를 발급할 도메인을 확인하기 위해 Nginx 설정을 확인한다. 따라서 Nginx 설정 파일에 정확한 도메인을 입력해 두어야 한다.

# Nginx 설정 파일 확인
sudo vi /etc/nginx/sites-available/suncar

 

썬카 설정 파일을 확인해 보자.

 

# /etc/nginx/sites-available/suncar

upstream suncar {
    server 127.0.0.1:8081;
}

server {
    listen 80;
    server_name your-domain.com www.your-domain.com;  # 정확한 도메인 이름 입력(서브도메인까지 여러개 가능)
    
    # 생략...

 

server_name 쪽에 정확한 도메인 이름을 입력해 두어야 한다. 서브도메인까지 여러 도메인을 입력 가능하다.

썬카는 아직 보안 설정이 취약하기 때문에 도메인은 공개하지 않겠다.

 

sudo nginx -t
sudo systemctl restart nginx

 

설정을 저장했으면 검사를 수행한 후 Nginx를 재시작하자.

 

4. 인증서 발급

sudo certbot --nginx -d your-domain.com -d www.your-domain.com

 

이 명령어 하나면 알아서 인증서가 발급되고, certbot이 알아서 시스템 내 Nginx 설정 파일을 찾아 인증서 등록 설정까지 마쳐준다. 이 과정에서 HTTP 접속 시 HTTPS로 리다이렉트하는 설정 등, 이런저런 선택지를 주는데 그냥 다 yes 해도 된다.

-d 는 도메인을 의미하는데 Nginx 설정 파일에 기재한 내 도메인을 똑같이 입력하면 된다.

 

이제 수정된 Nginx 설정 파일을 확인해 보자.

 

# /etc/nginx/sites-available/suncar

upstream suncar {
    server 127.0.0.1:8082; # 현재 프록시 방향
}

server { # Nginx가 80포트 요청 수신하도록 설정
    server_name your-domain.com www.your-domain.com;  # 모든 도메인 이름에 대해 이 server 블록 적용

    location / { # / (루트) 경로로 들어오는 모든 요청에 대한 처리 방법 정의
        proxy_pass http://suncar; # 들어오는 요청을 upstream suncar 그룹으로 전달
        proxy_set_header Host $host; # 원래 요청의 호스트명 ( host 및 아래 설정들은 클라이언트 정보를 담는 설정)
        proxy_set_header X-Real-IP $remote_addr; # 클라이언트 실제 IP 주소
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 클라이언트와 중간 프록시의 ip 주소들
        proxy_set_header X-Forwarded-Proto $scheme; # 원래 요청의 프로토콜 (http/https)
    }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    
    # HSTS 설정 ( 누락되어 있어서 추가했다 )
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}

server {
    if ($host = www.your-domain.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    if ($host = your-domain.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    server_name your-domain.com www.your-domain.com;
    return 404; # managed by Certbot
}

 

수정된 Nginx 설정 파일이다. # managed by Certbot 주석이 붙은 부분들이 Certbot이 알아서 작성해 준 내용들이다

이제 SSL 인증서 적용이 완료되었고 HTTPS로 접속이 가능해졌다.

 

이 설정은 내 도메인으로 HTTP 접속 시 HTTPS로 자동 리다이렉트 시키는 기능을 포함하고 있다. 그러나 HSTS 설정은 누락되어 있었다. SSL 인증서를 적용했더라도 사용자 측에서 HTTP로 접속한다면 말짱 도루묵이다. HSTS는 그것을 막기 위한 효과적인 방법이다. 따라서 나는 HSTS 설정을 추가해 주었다. 한 줄만 추가하면 되기 때문에, 설정 파일을 확인해 보고 없다면 추가하는 것을 추천한다. 그런데.. HTTPS 리다이렉트와 HSTS는 정확히 무엇인가?

 

HTTP -> HTTPS 리다이렉트

- 사용자의 HTTP 요청이 "서버에 도착한 후" 서버가 브라우저에게 HTTPS로 다시 요청하라고 지시하는 것

HSTS (HTTP Strict Transport Security)

- 사용자가 HTTP로 접속하려 할 때, 브라우저 자체적으로 요청을 HTTPS로 바꿔서 보내는 방식. 즉 서버에 HTTP 요청이 도착하지 않음

 

HSTS의 동작 방식

1. 사용자가 HTTP로 접속 시도를 하면, 서버는 브라우저에게 HTTPS로 다시 요청하라고 지시한다(HTTP->HTTPS 리다이렉트)

2. 브라우저는 HTTPS로 접속 시도를 하고, 서버는 응답에 HSTS 헤더를 포함하여 보낸다. 이 헤더엔 유효기간이 있다.

3. 브라우저는 이 헤더를 보고, "이 사이트에는 무조건 HTTPS로만 연결해야 해"라고 인식하게 된다.

4. 이후 헤더 유효기간 동안의 사이트에 대한 모든 요청은, 사용자가 HTTP로 요청하더라도 브라우저에 의해 자동으로 HTTPS로 변경되어 전달된다.

 

즉, HSTS가 동작하기 위해선 HTTP -> HTTPS 리다이렉트 방식을 한번 거쳐야 한다.

 

어째서 HTTP -> HTTPS 리다이렉트에 더해, HSTS를 함께 사용해야 하는가?

그것은 최초 요청의 취약성 때문이다. 사용자가 HTTP 요청을 보낸다면, 최초의 요청은 여전히 암호화되지 않은 HTTP로 서버에 전송된다. 서버는 이 HTTP 요청을 받고 나서야 "HTTPS로 다시 오세요" 라는 리다이렉트 응답을 브라우저에게 보낸다. 이 최초의 요청과 응답이 오가는 짧은 시간 동안, 공격자가 이 요청을 가로챌 수 있는 기회가 발생한다. 따라서 최초의 요청도 HTTPS로 보내게 하는 HSTS 방식은 필수적이다. 하지만.. 완전히 최초의 요청, 즉 HSTS 헤더가 브라우저에 아직 없는 상태의 요청은 HTTP일 것이다. 그것을 해결하기 위한 방법으로 HSTS Preload List 라는 게 있는데, ... 이건 다음에 알아보도록 하자.

 

5. 자동 갱신 확인

Certbot은 자동으로 SSL 인증서 갱신 타이머를 지정한다. 자동 갱신이 제대로 동작하는지, 타이머는 잘 설정되어 있는지 확인할 필요가 있다.

# 자동 갱신 테스트
sudo certbot renew --dry-run

# 갱신 타이머 확인
systemctl list-timers | grep snap.certbot

 

먼저 인증서 자동 갱신이 제대로 동작하는지 테스트해 보자. --dry-run 옵션은 실제로 실행하지 말고 테스트만 한다는 뜻이다.

 

자동 갱신 테스트 성공

 

우선, 위와 같이 자동 갱신 테스트는 성공했다.

 

갱신 타이머 확인

 

갱신 타이머도 잘 설정되어 있다.

 

Thu 2025-04-24 21:54:00 UTC -> 다음 실행 예정 시간(UTC)

11h left -> 다음 실행까지 남은 시간 (11시간 남음)

n/a -> 마지막 실행 시간 (아직 실행된 적 없음)

n/a -> 마지막 실행 이후 경과 시간 (마지막 실행이 없음)

snap.certbot.renew.timer -> 타이머 이름

snap.certbot.renew.service -> 타이머가 실행할 서비스 이름

 

 

하지만 여기서 의문이 생긴다. 이 타이머는 내가 직접 등록하지 않았다.

그렇다면 Certbot이 시스템 레벨 타이머를 등록했다는 것인데.. 무슨 권한으로?

 

sudo snap install --classic certbot

 

위에서 내가 sudo 권한으로 certbot을 설치하긴 했었다. 타이머는 이 명령어에 의해 생성된다.

타이머는 root 권한이 있거나 sudo 권한이 있는 계정만 등록할 수 있다. 내가 sudo 명령어도 Certbot을 설치했기 때문에 타이머 생성도 가능했다.

 

그런데 이거 위험하지 않나? 라는 의문이 들었다. 결국 root 권한을 Certbot에게 준다는 건데... Certbot에 악성 스크립트라도 포함되어 있다면? 그럼 내 서버 순식간에 털리는 거 아닌가. 그래서 좀 알아보았다.

 

Certbot에게 sudo 권한을 주는 것이 "일반적으로" 보안 우려가 없는 이유

- Ubuntu 제작사인 Canonical은, 공식 snap 저장소에 등록되는 패키지를 검토하고 검증한다.

- Certbot과 같은 주요 패키지는 Electronic FrontierFoundation(EFF)와 같은 신뢰할 수 있는 기관에서 관리한다.

- Certbot은 오픈소스 이기 때문에 누구나 소스코드를 검토할 수 있다.

 

...등의 이유로 "일반적으로" 안전하다고 할 수 있다. 하지만 그렇다고 해도 비공식적인 경로로 설치한다면 위험할 수 있기 때문에 반드시 snap을 통한 공식 저장소에서만 Certbot을 설치하는 게 좋겠다. 물론 Certbot 뿐만 아니라 다른 모든 패키지도 포함이다. 일반적으로 안전하다고 한 이유는 어떤 소프트웨어든 100% 안전하다고 단언할 수는 없기 때문이다.

6. 테스트

자, 이제 모든 설정이 끝났다. HTTPS 접속이 되는지 확인해 보자.

 

HTTPS 접속 성공

 

HTTPS 접속에 성공했다.

 

www 서브 도메인도 접속 성공

 

www가 붙은 서브 도메인도 HTTPS 접속에 성공했다.

 

 

=============

 

728x90
반응형