AOF Frontend HTTPS 설정 가이드
이 문서는 AOF 프로젝트에서 Nginx frontend 컨테이너에 HTTPS를 적용하기 위해 수행한 작업을 정리한 참고 자료입니다.
인증서 발급부터 배포, CI/CD, 트러블슈팅까지 한 번에 파악할 수 있도록 구성했습니다.
1. 전체 아키텍처 한눈에 보기

핵심 원칙
원칙설명
| SSL 종료 지점 | frontend Nginx 컨테이너 (443) |
| Backend SSL | 불필요 — Nginx 뒤에서 HTTP(8001/8002)로 프록시 |
| 인증서 저장 | Git ❌ → 호스트 고정 경로 /data001/config/dev/ssl/ |
| 자동 전환 | entrypoint.sh가 인증서 유무로 HTTP/HTTPS 설정 선택 |
2. 프로젝트에서 SSL이 처리되는 위치
2.1 관련 파일 맵
파일역할
| cicd/container/entrypoint.sh | server.crt + server.key 존재 시 HTTPS 템플릿 적용 |
| cicd/container/default.conf.ssl.template | 80(HTTP) + 443(HTTPS) Nginx 설정 |
| cicd/container/default.conf.template | 인증서 없을 때 HTTP만 |
| cicd/container/docker-compose-prd.yml | 포트 80/443 노출, SSL 볼륨 마운트 |
| cicd/container/ssl-setup/setup-ssl.sh | 보안팀 원본 → server.crt/server.key 변환·배포 |
| cicd/container/Dockerfile | Nginx 이미지 + 템플릿/entrypoint 포함 |
2.2 entrypoint 자동 전환
# cicd/container/entrypoint.sh (요약)
if [ -f /etc/nginx/ssl/server.crt ] && [ -f /etc/nginx/ssl/server.key ]; then
# → default.conf.ssl.template 사용 (HTTP + HTTPS)
else
# → default.conf.template 사용 (HTTP만)
fi
컨테이너 시작 시점에 판단하므로, 인증서만 배치하고 frontend를 재시작하면 HTTPS가 활성화됩니다. 이미지 재빌드는 필요 없습니다.
예외: default.conf.ssl.template 내용을 바꾼 경우(리다이렉트, HSTS 등)에는 frontend 이미지 재빌드가 필요합니다.
2.3 docker-compose 볼륨 마운트
# cicd/container/docker-compose-prd.yml
frontend:
ports:
- "80:80"
- "443:443"
volumes:
- /data001/config/dev/ssl:/etc/nginx/ssl:ro
- 컨테이너 내부 경로: /etc/nginx/ssl/server.crt, /etc/nginx/ssl/server.key
- 호스트 경로: /data001/config/dev/ssl/ (GitLab clone 경로와 무관)
Backend .env와 동일한 패턴으로, 비밀/인증서는 repo 밖 고정 경로에 둡니다.
3. 보안팀 인증서 파일 이해하기
고객사 등에서 받는 파일 구성 예시:
파일의미Nginx 배포에 사용
| File_Wildcard.xxx.com_crt.crt | 서버(리프) 인증서 (*.xxx.com) | ✅ server.crt의 첫 번째 블록 |
| KeyFile_Wildcard.xxx.com_crt.key | 개인키 (암호화된 경우 password.txt 필요) | ✅ server.key |
| ChainFile_ChainBundle.crt | 중간 CA 체인 | ✅ server.crt에 이어 붙임 |
| CA_GLOBALSIGN.crt | 루트 CA | ❌ 배포 불필요 (검증·참고용) |
| password.txt | 개인키 복호화 비밀번호 | ❌ 배포 불필요 (작업 시에만 사용) |
3.1 PEM 파일에 BEGIN CERTIFICATE가 여러 개?
정상입니다. 하나의 .crt에 서버 인증서 + 중간 CA가 함께 들어 있는 경우가 많습니다.
-----BEGIN CERTIFICATE----- ← 1번: 서버(리프) 인증서
...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE----- ← 2번: 중간 CA
...
-----END CERTIFICATE-----
- openssl x509 -in file.crt → 첫 번째 인증서만 읽음
- Nginx ssl_certificate → 모든 PEM 블록 사용 (fullchain)
3.2 최종 산출물
Nginx가 기대하는 형태는 딱 2개입니다.
파일내용
| server.crt | 서버 인증서 + 중간 CA 체인 (fullchain PEM) |
| server.key | 복호화된 개인키 (평문 PEM) |
CA_GLOBALSIGN.crt(루트)는 Nginx 설정에 넣지 않습니다. 브라우저/OS에 내장된 루트 CA로 검증합니다.
4. setup-ssl.sh 워크플로우
4.1 디렉토리 구조
cicd/container/ssl-setup/
├── setup-ssl.sh ← 실행 스크립트 (Git 포함)
├── .gitignore ← *.crt, *.key, password.txt 제외
├── File_Wildcard.xxx.com_crt.crt ← 보안팀 파일 (Git 제외)
├── ChainFile_ChainBundle.crt
├── CA_GLOBALSIGN.crt
├── KeyFile_Wildcard.xxx.com_crt.key
├── password.txt
└── .work/ ← 중간 산출물 (실패 시 디버깅용, 성공 시 삭제)
4.2 실행
# 1. 보안팀 파일 5개를 ssl-setup/ 에 복사
# 2. 스크립트 실행
./cicd/container/ssl-setup/setup-ssl.sh
# 3. frontend 재시작 (인증서 반영)
docker compose -f cicd/container/docker-compose-prd.yml restart frontend
4.3 스크립트가 하는 일
- 원본 파일 존재·크기(0바이트) 검사
- password.txt로 암호화된 개인키 복호화 → server.key
- 서버 인증서 + 체인 번들 결합 → server.crt
- 인증서·키 쌍 일치(modulus) 및 체인 검증
- /data001/config/dev/ssl/에 배포 (기존 파일 백업 후 교체)
4.4 상단 변수 (재활용 시 수정)
SERVER_CERT_FILE="File_Wildcard.xxx.com_crt.crt"
CHAIN_BUNDLE_FILE="ChainFile_ChainBundle.crt"
ROOT_CA_FILE="CA_GLOBALSIGN.crt"
PRIVATE_KEY_FILE="KeyFile_Wildcard.xxx.com_crt.key"
PASSWORD_FILE="password.txt"
SSL_DEPLOY_DIR="/data001/config/dev/ssl"
5. 트러블슈팅: 반드시 알아둘 PEM 이슈
5.1 파일 끝 줄바꿈 누락 (cat 합칠 때 PEM 깨짐)
보안팀에서 받은 .crt 끝에 \n(개행)이 없는 경우가 있습니다.
증상:
- 원본 File_Wildcard...crt 단독 → openssl x509 성공 ✅
- 합본 .work/server.crt → Could not read certificate ❌
원인: cat으로 체인을 붙일 때 마지막 base64 줄과 -----BEGIN CERTIFICATE-----가 한 줄에 붙음
...abc123-----BEGIN CERTIFICATE-----
해결: setup-ssl.sh의 append_pem_file이 합칠 때 끝 개행을 자동 보정합니다.
직접 확인:
tail -c 5 File_Wildcard.lgdisplay.com_crt.crt | xxd
# 끝이 0a (LF)가 아니면 개행 없음
5.2 빈 server.crt
원본 파일이 0바이트이거나 placeholder인 경우. 스크립트가 사전에 ! -s 검사로 차단합니다.
5.3 적용 확인
# 로그
docker logs frontend 2>&1 | head -5
# "SSL 인증서가 발견되었습니다. HTTPS 설정을 적용합니다."
# 컨테이너 내부
docker exec frontend ls -la /etc/nginx/ssl/
docker exec frontend nginx -t
# 연결 테스트
curl -vk https://<도메인>/
curl -I http://<도메인>/
6. GitLab Runner / CI/CD와의 관계
6.1 왜 Git에 인증서를 넣지 않는가
- 개인키 유출 위험
- 보안 정책 위반
- GitLab Runner는 매 파이프라인마다 새 clone → clone 경로에 인증서를 두면 불안정
6.2 현재 CI/CD 구조 (요약)
prd 브랜치 push
→ GitLab Runner (shell executor)
→ docker compose build/up (호스트 Docker)
→ frontend 컨테이너 교체
.gitlab-ci.yml의 HOST_PROJECT_DIR 변환은 소스 clone 경로용이며, SSL은 /data001/config/dev/ssl 고정 경로를 사용합니다.
6.3 Backend와 동일한 패턴
종류호스트 경로Git
| Backend .env | /data001/config/dev/.env.* | ❌ |
| SSL 인증서 | /data001/config/dev/ssl/ | ❌ |
| 애플리케이션 코드 | GitLab clone | ✅ |
6.4 인증서 갱신 시
# 1. ssl-setup/ 에 새 원본 파일 배치
# 2. setup-ssl.sh 실행
# 3. frontend만 재시작 (CI/CD 불필요)
docker compose -f cicd/container/docker-compose-prd.yml restart frontend
7. HTTP / HTTPS 병행 정책
현재 default.conf.ssl.template은 HTTP(80)와 HTTPS(443)를 동시에 제공하고, 강제 리다이렉트는 하지 않습니다.
7.1 변경 내용
항목이전현재
| 80 포트 | 301 → https:// 리다이렉트 | HTTP 서비스 제공 (template과 동일) |
| HSTS | 활성화 | 주석 처리 (HTTP 병행과 충돌 방지) |
| 443 포트 | HTTPS 서비스 | 동일 |
리다이렉트/HSTS 설정은 템플릿에 주석으로 보존되어 있어, 나중에 HTTPS 강제 전환 시 주석 해제만 하면 됩니다.
7.2 HTTP vs HTTPS 블록 관계
default.conf.ssl.template
├── server { listen 80; ... } ← default.conf.template 과 동일
└── server { listen 443 ssl; ... + SSL 설정 } ← 추가
인증서 없을 때(default.conf.template)의 HTTP 동작과, 인증서 있을 때의 HTTP 동작은 동일합니다. HTTPS가 추가되는 것뿐입니다.
7.3 애플리케이션 영향
항목영향
| Frontend API (/management) | 상대 경로 → 프로토콜 자동 맞춤, 재빌드 불필요 |
| X-Forwarded-Proto | Nginx가 $scheme으로 전달 → backend가 HTTP/HTTPS 구분 가능 |
| 인증 쿠키 | 현재 secure=False → HTTP/HTTPS 모두 동작 |
| 보안 | HTTP 구간은 평문 통신 — 사내 정책·노출 범위 확인 필요 |
8. 운영 체크리스트
최초 HTTPS 적용
-
보안팀 인증서 5개 파일을 ssl-setup/에 배치
-
./cicd/container/ssl-setup/setup-ssl.sh 실행·검증 통과
-
/data001/config/dev/ssl/server.crt, server.key 존재 확인
-
default.conf.ssl.template 변경 사항 반영 시 frontend 재빌드
-
docker compose restart frontend 또는 CI/CD 배포
-
docker logs frontend에서 HTTPS 적용 메시지 확인
-
curl -vk https://<도메인>/ 및 curl -I http://<도메인>/ 테스트
-
방화벽/LB에서 80, 443 포트 개방 확인
인증서 갱신
-
ssl-setup/에 새 원본 파일 교체
-
setup-ssl.sh 재실행 (기존 인증서 자동 백업)
-
frontend 재시작
HTTPS 강제 전환 시 (향후)
-
default.conf.ssl.template에서 80 포트 리다이렉트 블록 주석 해제
-
HTTP 서비스 블록 주석 처리 (선택)
-
HSTS 주석 해제 검토
-
backend 쿠키 secure=True 검토
-
frontend 재빌드·배포
9. 자주 묻는 질문 (FAQ)
Q. ssl/ (프로젝트 루트)와 cicd/container/ssl-setup/ 차이는?
A. ssl/은 xxx 사내 CA 등 빌드용 참고 자료. 운영 HTTPS 배포는 ssl-setup/ + /data001/config/dev/ssl/을 사용합니다.
Q. 와일드카드 *.xxx.com이면 어떤 도메인까지 되나?
A. 1단계 서브도메인 (aof.xxx.com) ✅. 2단계 (x.aof.xxx.com)는 와일드카드로 커버되지 않을 수 있음.
Q. 인증서 변경만 했는데 frontend 재빌드가 필요한가?
A. 아니요. 볼륨 마운트이므로 재시작만 하면 됩니다. Nginx 템플릿을 수정한 경우에만 재빌드가 필요합니다.
Q. server_name localhost는 문제 없나?
A. 실제 도메인 접속 시 리다이렉트 URL에 영향을 줄 수 있습니다. HTTPS 강제 리다이렉트를 켤 때는 운영 도메인으로 server_name 수정을 검토하세요. 현재는 리다이렉트를 사용하지 않으므로 영향이 적습니다.
Q. .gitignore를 폴더마다 둬도 되나?
A. 네. ssl-setup/.gitignore는 해당 폴더의 인증서만 제외하고, 다른 경로의 .crt(예: ssl/xxx_CA_v3.crt)에는 영향을 주지 않습니다.
10. 참고: OpenSSL 명령어 모음
# 인증서 정보
openssl x509 -in server.crt -noout -subject -dates
# 개인키 검증
openssl rsa -in server.key -check -noout
# 인증서·키 쌍 일치 (MD5 해시가 같아야 함)
openssl x509 -noout -modulus -in server.crt | openssl md5
openssl rsa -noout -modulus -in server.key | openssl md5
# 체인 검증
openssl verify -CAfile CA_GLOBALSIGN.crt \
-untrusted ChainFile_ChainBundle.crt \
File_Wildcard.xxx.com_crt.crt
# 암호화된 키 복호화
openssl rsa -in KeyFile_Wildcard.xxx.com_crt.key \
-out server.key -passin file:password.txt
# fullchain 수동 생성 (스크립트 없이)
cat File_Wildcard.xxx.com_crt.crt ChainFile_ChainBundle.crt > server.crt
# ⚠️ 파일 끝 개행 없으면 빈 줄 삽입 필요'웹 개발 > 백엔드 일반' 카테고리의 다른 글
| [Node.js 라이브러리] 개발 참고자료 목록 (0) | 2022.10.23 |
|---|---|
| [Node.js] nvm이란? Node Version Manager (0) | 2021.07.05 |
| [vue.js/react] HTTP 통신 라이브러리 : axios (0) | 2020.12.02 |
| [npm] fastify란? (0) | 2020.11.24 |
| [npm] NestCloud란? (0) | 2020.11.23 |





최근댓글