PPAK

[Spring/Spring Boot] 서버 https 적용 (Certbot, Let's Encrypt) 본문

spring

[Spring/Spring Boot] 서버 https 적용 (Certbot, Let's Encrypt)

PPakSang 2022. 8. 27. 05:57

이전 게시글 에서 로컬 환경에서 SSL 인증서를 발급하고 https 를 적용해 보았습니다.

 

하지만 로컬에서 발급한 인증서는 공식적인 CA(Certificate Authority) 기관에 등록되어있지 않기 때문에 원격 서버에서 사용 시 브라우저에서 경고메세지와 함께 접근을 차단합니다. 따라서 CA 기관에 인증서 발급 요청을 하고 등록을 하는 과정이 필요합니다.

 

보통의 CA 기관은 일정 가격을 지불하고 복잡한 절차를 거쳐 인증서를 발급하지만 개인적인 토이 프로젝트를 진행하면서 비용을 지불하기란 쉽지 않습니다.

 

여기서 Let's Encrypt 라는 비영리 CA 기관은 https 의 확산에 기여하기 위해 무료로 SSL 인증서를 발급해주고 있습니다.

 

이번 프로젝트 에서는 Let's Encrypt SSL 인증서 발급을 도와주는 Certbot 툴을 이용해서 실제로 저희 서버 도메인 용 SSL 인증서를 발급받고 https 를 적용해보도록 하겠습니다.

 

Certbot 공식 홈페이지 에 방문하여 본인의 OS 및 소프트웨어 스펙에 맞게 선택을 해줍니다.

 

저의 경우에는 데비안 베이스 서버 위에서 바로 spring 에서 적용 하기 위해 other on Debian 을 선택하였고 다음 인증서 발급 절차를 밟았습니다.

 

쭉쭉 발급을 하다가 두 가지 선택지를 만날 수 있습니다.

sudo certbot certonly --standalone
sudo certbot certonly --webroot

위 두 가지 명령어는 본인이 도메인의 주인이라는 것을 인증하기 위한 방법들 입니다.

 

standalone: 해당 명령어를 입력한 머신의 80번 포트로 가상 웹서버를 구동시켜 인증하는 방식입니다. CA 기관에 등록하고자 하는 도메인에 대응되는 머신에서 구동시키면 정상적으로 인증됩니다.

 

webroot: 직접 사용해보진 않았지만 현재 구동중인 웹서버의 특정 경로에 디렉토리를 생성하여 검증하는 방식을 의미합니다. 역시나 도메인에 대응되는 머신의 수정 권한을 확인하여 인증하는 방식을 가지는 것 같습니다.

 

따라서 80번 웹서버 포트를 사용하고 있다면 webroot 방식을 그렇지 않다면 standalone 방식을 사용하면 될 것 같습니다.

그 다음 Let's Encrypt 에서 요구하는 동의서와 항목들을 입력하고 나면 정상적으로 인증서가 발급됩니다.

 

발급된 인증서와 개인키는 /etc/letsencrypt/live/[도메인 이름]/ 의 위치에 fullchain.pem(인증서), privkey.pem(개인키) 가 저장된 것을 확인할 수 있습니다.

 

Spring Applicaiton 에 적용하기

자바에서는 pem 형태의 파일을 p12(pkcs12), jks 형태로 바꾸어 주어야 사용 가능합니다.

sudo openssl pkcs12 -export -in fullchain.pem -inkey privkey.pem -out AN-keystore.p12 -name aliasName

in: 공개 인증서

inkey: 개인키

name: keystore alias

out: .p12 결과물 이름

 

위와같이 .p12 파일로 바꾸어준 후 이전 글과 같은 방식으로 https 를 적용하면 됩니다.

 

원격 서버에 포함하기

현재 제가 구동하고 있는 서버는 jenkins 를 통한 자동 빌드/배포 방식을 사용하고 있기 때문에 빌드 시점에 .p12 파일을 지정한 위치에 포함시킬 방법이 필요했습니다.

 

jenkins 컨테이너에 마운팅된 폴더에 .p12 파일을 포함시키고 jenkins 빌드 구성에서 해당 경로의 파일을 빌드 전 workspace resource 하위 경로에 복사하는 방식으로 .p12 파일을 프로젝트에 추가를 했습니다.

 

 

Chrome80 버전 이후

api 서버는 대부분의 호출이 Cross Domain 에서 이루어진다고 봐도 무방할 것입니다.

실제로 Chrome80 버전 이후 Cross Domain 상황에서의 Set-Cookie 정책이 다소 변경되었습니다.

 

우선적으로 Chrome 은 기본적으로 Croos Domain 일 때 제약적인 환경(Get Method 한정) 에서 쿠키 교환이 가능한 SameSite=Lax 라는 옵션을 디폴트로 설정하였습니다.

또한, SameSite=None 을 통해서 제약없이 쿠키 교환이 가능하나 해당 옵션을 사용하기 위해서는 Secure Cookie + https 통신을 통해서만 교환이 가능하다는 것을 확인할 수 있었습니다.

 

public ResponseCookie createRefreshTokenCookie(String name, String value) {
        return ResponseCookie.from(name, value)
                .httpOnly(true)
                .path("/")
                .secure(true)
                .sameSite("None")
                .maxAge(COOKIE_VALIDATION_SECOND)
                .build();
    }

 

자바 기본 스펙에서 제공하는 Cookie 클래스는 SameSite 옵션을 적용할 수 없기 때문에 위와같이 Spring 에서 제공하는 RespinseCookie 를 사용하여 Cookie 를 생성해 줍니다. 물론 직접 Set-Cookie Header 값을 작성해도 무방합니다.

 

실제 도메인에 접속하였을 때 해당 문구와 함께 쿠키 교환이 성공적으로 이루어 진다면 https 적용이 완료된 것을 확인할 수 있습니다!!

 

잘못된 정보가 있다면 댓글로 알려주시면 감사하겠습니다!!

Comments