웹을 배포해본 적이 있다면
SSL
에 대해 들어본 적이 있을 것이다. SSL이 적용된 URL의 프로토콜은https://
로 시작하며,http://
와 달리 보안을 의미하는s
가 붙는다. 현재 velog도 ssl이 적용된 걸 확인할 수 있다.
SSL은 Secure Socket Layer의 약자로, 직역하면 암호화 소켓층이다.
SSL은 웹 서버와 클라이언트의 통신 암호화 프로토콜
이다.
SSL이 적용되지 않은 통신의 경우, 위 그림과 같이 평문(Plain Text)이 그대로 전송된다.
만약 제 3자가 어떠한 방식으로든 통신 패킷을 탈취할 경우, 그 내용을 쉽게 확인할 수 있다.
SSL을 적용한다면 요청을 암호화해서 보내므로 통신 패킷이 탈취돼도 복호화 키가 없으면 원래 내용을 알 수 없기 때문이다.
SSL을 접하다보면 TLS
라는 개념도 접하게 된다.
TLS는 SSL 3.0의 보안취약점을 보완한 통신 보안 프로토콜로, SSL을 보완한 것이 TLS지만, 인터넷에 적용되는 통신 보안 프로토콜을 전부 통틀어서 SSL이라고 부른다.
SSL은 마지막 버전인 3.0마저 금지됐으므로, 현재 사용 중인 통신 보안 프로토콜은 모두 TLS이다.
- URL 프로토콜은
https
이며, 기본 포트는443
이다.- 통신 데이터가 암호화되어, 패킷이 탈취되는 사고가 발생해도 데이터를 지킬 수 있다.
- SSL 인증서를 통해 도메인의 신뢰성을 검증할 수 있다.
- 데이터 송/수신 과정에서 암/복호화가 발생하므로 속도가 느리다.
SSL을 적용시키기 위해선 단일 Nginx 방식이 있고, 이중 Nginx 방식이 있다.
단일 Nginx 방식:
Docker 컨테이너 안에 React 앱과 Nginx를 함께 배치하고, 이 Nginx를 사용하여 HTTPS를 설정한다. 이렇게 하려면, Let's Encrypt와 같은 툴로 인증서를 획득한 후, 해당 인증서를 컨테이너 안의 Nginx에 연결해야 한다.
이 방식의 단점은 컨테이너를 재시작할 때마다 인증서를 다시 설정해야 할 가능성이 있다는 것이다.
이중 Nginx 방식:
호스트 PC에 Nginx를 설치하고, 이를 리버스 프록시로 사용하여 Docker 컨테이너 내의 앱으로 트래픽을 전달한다. 호스트 PC의 Nginx에서 HTTPS를 설정한다.
Docker 컨테이너 내에도 Nginx를 두어 React 앱을 서빙한다. (이 Nginx는 HTTPS 설정 없이 그냥 사용한다.)
이 방식의 장점은 호스트 PC의 Nginx가 HTTPS를 관리하므로, 컨테이너를 자유롭게 재시작하거나 변경하는 것이 더 쉽다는 것이다.
필자는 이중 Nginx 방식으로 진행하겠다.
이전에 진행하였던 프로젝트를 ssl적용시켜 보겠다.
기본적으로 ssl은 443을 사용하기 때문에 ec2 보안그룹에 접속해서 인바운드 규칙을 생성한다.
호스트 pc로 접속하고 Nginx를 설치한다.
sudo apt update
sudo apt install nginx
sudo apt install certbot python3-certbot-nginx
Certbot을 사용하여 인증서를 획득한다. 웹 서버(Nginx)를 직접 제어하지 않으므로, 일반적으로 standalone
모드를 사용하여 인증서를 받아야 한다. 80번 포트를 잠시 사용 가능해야 하므로 중지하고 실행시키도록 하자
sudo certbot certonly --standalone -d [yourdomain.com]
위 명령을 실행하면, Certbot은 도메인의 소유권을 확인하기 위해 잠시 HTTP 서버를 실행한다. 인증서가 성공적으로 발급되면 /etc/letsencrypt/live/[yourdomain.com]/
디렉터리에 저장된다.
호스트 PC의 Nginx에서 리버스 프록시 설정을 통해 Docker 컨테이너 내의 앱으로 트래픽을 전달하도록 설정해야 한다.
/etc/nginx/sites-available/default
파일을 수정하여 아래의 리버스 프록시 설정을 추가한다.
server {
listen 80;
server_name [your_domain.com];
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name [your_domain.com];
ssl_certificate /etc/letsencrypt/live/[your_domain.com]/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/[your_domain.com]/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://localhost:[8081]; # 맞게 변경한다.
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
docker-compose.yml
파일에 인증서의 경로를 볼륨으로 연결해야 한다.
또한 기존의 서비스가 80포트를 사용한다면, 충돌하기 때문에 포트번호를 변경해야 한다.
react-app:
...
ports:
- "8081:80"
volumes:
- /etc/letsencrypt:/etc/letsencrypt
docker-compose를 통해 인증서를 연결 시켜줬기 때문에 기존의 docker pull
, docker run
방식을 변화 시켜야한다.
최신 이미지 pull 및 실행
docker pull bangjinseong/my-react-app:${BUILD_NUMBER}
docker run -d -p 80:80 bangjinseong/my-react-app:${BUILD_NUMBER}
를
react-app 서비스 이미지를 최신으로 업데이트
docker-compose pull react-app
react-app 서비스만 재시작
docker-compose up -d react-app
로 변화시킨다.
docker-compose up -d react-app을 한다면, docker-compose.yml파일 중에 react-app파일만 실행시킨다.
하지만, 이렇게 되면 jenkins 컨테이너 안에서 docker-compose 명령어를 실행시킬 수 있어야 하기 때문에, 컨테이너 안에 docker-compose를 설치해준다.
docker exec -it [JENKINS_CONTAINER_NAME] /bin/bash
로 컨테이너 안에 접속해준 뒤
curl -L "https://github.com/docker/compose/releases/download/1.28.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
를 통해 docker-compose를 설치해준다.
이렇게 하면 jenkins shell script로 docker-compose 명령어를 실행시킬 수 있게 된다.
필자는
sudo systemctl restart nginx
를 통해 해결하였다.
sudo nginx -t
를 통해 Nginx 설정을 테스트할 수 있고,
/var/log/nginx/error.log
파일을 확인하여 관련 오류 메시지나 경고를 찾아볼 수 있다.