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 스크립트가 하는 일

  1. 원본 파일 존재·크기(0바이트) 검사
  2. password.txt로 암호화된 개인키 복호화 → server.key
  3. 서버 인증서 + 체인 번들 결합 → server.crt
  4. 인증서·키 쌍 일치(modulus) 및 체인 검증
  5. /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
# ⚠️ 파일 끝 개행 없으면 빈 줄 삽입 필요
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기