Linux 서버에서 SSL 사설 인증서 만들고 적용·검증하는 방법

반응형
Linux 서버에서 SSL 사설 인증서 만들고 적용·검증하는 방법

Linux 서버에서 SSL 사설 인증서 만들고 적용·검증하는 방법

내부망(사내 서비스, 개발/스테이징, 프라이빗 API)에서는 공인 인증서 대신 사설 CA로 서버 인증서를 발급해 쓰는 경우가 많습니다.
이 글은 사설 CA 생성 → 서버 인증서 발급 → Nginx/Apache 적용 → 클라이언트 신뢰 등록 → 검증까지를 한 흐름으로 정리합니다.

전체 흐름 한눈에 보기

1) 사설 CA(루트 또는 중간 CA) 생성
2) 서버 키 생성 + CSR 생성(SAN 포함)
3) CA로 서버 인증서 서명(체인 구성)
4) 웹서버(Nginx/Apache)에 인증서/키/체인 적용
5) 클라이언트(서버/PC/컨테이너)에서 CA 신뢰 등록
6) openssl·curl·브라우저로 검증(체인/호스트명/SNI 포함)

사전 준비

필요 도구

  • OpenSSL (대부분 기본 설치)
  • 웹서버: Nginx 또는 Apache(httpd)
  • (권장) 인증서/키 관리용 디렉터리 정책

권장 디렉터리 구조

실제 사용 시에는 키 유출이 가장 큰 사고 포인트입니다.
관리자 입장에서 보안 사고를 줄이려면 “어디에 무엇을 두는지”부터 표준화하는 게 좋습니다.
# 예시: CA 관련 파일(민감)
sudo mkdir -p /root/pki/ca

# 예시: 서비스용 인증서/키(서비스 계정이 읽을 수 있어야 함)
sudo mkdir -p /etc/ssl/private
sudo mkdir -p /etc/ssl/certs

# 권한(키는 반드시 제한)
sudo chmod 700 /etc/ssl/private

용어 정리

  • CA: 인증서를 서명하는 기관(사설 CA면 내부에서 직접 운영)
  • CSR: 인증서 서명 요청서(서버 정보/도메인/SAN 포함)
  • SAN: Subject Alternative Name. 요즘 TLS에서 사실상 필수(도메인/IP를 여기에 넣음)
  • 체인(Chain): 서버 인증서 + (중간 CA들). 클라이언트는 최종적으로 루트 CA를 신뢰함

1단계: 사설 CA 만들기

1-1. 루트 CA 키 생성

운영 환경에서는 루트 CA 키를 가능한 한 오프라인(접근 통제된 시스템)로 두고,
실무 기준으로 보면 중간 CA를 만들어 일상 서명에 쓰는 구성이 더 안전합니다.
다만 여기서는 이해를 위해 “루트 CA로 바로 서명”하는 흐름을 먼저 구성합니다.
# (권장) 암호화된 개인키 생성 (비밀번호 입력 필요)
sudo openssl genrsa -aes256 -out /root/pki/ca/rootCA.key 4096

# 권한 강화
sudo chmod 600 /root/pki/ca/rootCA.key

1-2. 루트 CA 인증서(자체 서명) 생성

# 유효기간(예: 10년 = 3650일)
sudo openssl req -x509 -new -nodes \
  -key /root/pki/ca/rootCA.key \
  -sha256 -days 3650 \
  -out /root/pki/ca/rootCA.crt

# 질문 입력 예시:
# Country Name (2 letter code) [AU]: KR
# Organization Name: MyCompany
# Common Name: MyCompany Root CA

1-3. CA 인증서 확인

openssl x509 -in /root/pki/ca/rootCA.crt -noout -text | head -n 40
openssl x509 -in /root/pki/ca/rootCA.crt -noout -subject -issuer -dates
여기서 issuer와 subject가 동일하면 자체 서명 루트 인증서입니다.
루트 CA는 클라이언트가 “신뢰 저장소”에 직접 넣어야 경고가 사라집니다.

2단계: 서버 인증서 발급 준비(키 + CSR)

2-1. 서버 개인키 생성

# 비밀번호 없는 키(서버 자동 기동을 위해 종종 사용)
# 대신 파일 권한/접근 통제가 더 중요해집니다.
sudo openssl genrsa -out /etc/ssl/private/example.internal.key 2048
sudo chmod 600 /etc/ssl/private/example.internal.key

2-2. SAN 포함 CSR 설정 파일 작성

요즘 브라우저/클라이언트는 CN(Common Name)만으로 도메인 검증을 하지 않고 SAN을 요구합니다.
따라서 CSR 생성 시 SAN을 반드시 넣어야 합니다.
# 파일 생성
sudo tee /root/pki/ca/example.internal.csr.cnf > /dev/null <<'EOF'
[ req ]
default_bits       = 2048
prompt             = no
default_md         = sha256
req_extensions     = req_ext
distinguished_name = dn

[ dn ]
C  = KR
ST = Seoul
L  = Seoul
O  = MyCompany
OU = Platform
CN = example.internal

[ req_ext ]
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = example.internal
DNS.2 = api.example.internal
# 내부망 IP로 접근하는 경우가 있다면 IP도 추가(가능하면 DNS 권장)
IP.1  = 10.0.0.10
EOF

2-3. CSR 생성

sudo openssl req -new \
  -key /etc/ssl/private/example.internal.key \
  -out /root/pki/ca/example.internal.csr \
  -config /root/pki/ca/example.internal.csr.cnf

2-4. CSR에 SAN이 들어갔는지 확인

openssl req -in /root/pki/ca/example.internal.csr -noout -text | sed -n '1,120p'
# 출력에서 "Subject Alternative Name" 항목을 확인

3단계: CA로 서버 인증서 서명(발급)

3-1. 서버 인증서용 확장(Extensions) 파일 작성

서버 인증서에는 용도 확장을 명확히 넣는 게 좋습니다.
TLS 서버 인증서라면 serverAuth가 포함되어야 합니다.
sudo tee /root/pki/ca/example.internal.v3.ext > /dev/null <<'EOF'
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = example.internal
DNS.2 = api.example.internal
IP.1  = 10.0.0.10
EOF

3-2. CA로 CSR 서명해서 서버 인증서 생성

# 유효기간 예: 825일(정책에 맞춰 조정)
sudo openssl x509 -req \
  -in /root/pki/ca/example.internal.csr \
  -CA /root/pki/ca/rootCA.crt \
  -CAkey /root/pki/ca/rootCA.key \
  -CAcreateserial \
  -out /etc/ssl/certs/example.internal.crt \
  -days 825 -sha256 \
  -extfile /root/pki/ca/example.internal.v3.ext

3-3. 발급된 서버 인증서 점검

openssl x509 -in /etc/ssl/certs/example.internal.crt -noout -text | sed -n '1,180p'
openssl x509 -in /etc/ssl/certs/example.internal.crt -noout -subject -issuer -dates
체크 포인트
- issuer가 rootCA로 표시되는지
- SAN에 DNS/IP가 원하는 대로 들어갔는지
- Extended Key Usage에 TLS Web Server Authentication(serverAuth)가 있는지

4단계: Nginx에 적용

4-1. 인증서/키 경로 확정

  • 인증서: /etc/ssl/certs/example.internal.crt
  • 키: /etc/ssl/private/example.internal.key
  • 루트 CA(클라이언트 배포용): /root/pki/ca/rootCA.crt

4-2. (중간 CA가 없는 경우) 기본 설정 예시

# /etc/nginx/conf.d/example-ssl.conf (예시)
server {
  listen 443 ssl http2;
  server_name example.internal api.example.internal;

  ssl_certificate     /etc/ssl/certs/example.internal.crt;
  ssl_certificate_key /etc/ssl/private/example.internal.key;

  # (권장) 최신 배포판 기본값이 충분히 좋은 경우가 많아 과도한 튜닝은 지양
  # 필요 시 운영 정책에 맞춰 조정

  location / {
    proxy_pass http://127.0.0.1:8080;
  }
}

4-3. 설정 검증 및 재시작

sudo nginx -t
sudo systemctl reload nginx
만약 개인키에 비밀번호가 걸려 있으면 nginx reload 시 입력을 요구할 수 있습니다.
자동 기동이 필요하면 키 암호 정책을 별도로 설계(예: HSM, 키 관리, 운영 절차)하는 편이 안전합니다.

5단계: Apache(httpd)에 적용

5-1. SSL 모듈 및 가상호스트 예시

# (배포판에 따라 명령이 다를 수 있음)
# Debian/Ubuntu 계열:
# sudo a2enmod ssl
# sudo systemctl restart apache2

# /etc/httpd/conf.d/ssl.conf 또는 vhost 파일에 추가(예시)
<VirtualHost *:443>
  ServerName example.internal
  ServerAlias api.example.internal

  SSLEngine on
  SSLCertificateFile /etc/ssl/certs/example.internal.crt
  SSLCertificateKeyFile /etc/ssl/private/example.internal.key

  ProxyPass / http://127.0.0.1:8080/
  ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>

5-2. 설정 검증 및 반영

# RHEL/CentOS 계열 예시
sudo apachectl configtest
sudo systemctl reload httpd

# Debian/Ubuntu 계열 예시
# sudo apachectl configtest
# sudo systemctl reload apache2

6단계: 클라이언트가 사설 CA를 신뢰하게 만들기

서버에 인증서를 걸었다고 끝이 아닙니다.
사설 CA는 기본적으로 어느 OS/브라우저도 신뢰하지 않기 때문에, 내부 사용자/서버/컨테이너에 “루트 CA 인증서(rootCA.crt)”를 배포해 신뢰 저장소에 넣어야 합니다.

6-1. Debian/Ubuntu: update-ca-certificates

# rootCA.crt를 복사
sudo cp /root/pki/ca/rootCA.crt /usr/local/share/ca-certificates/mycompany-rootca.crt

# 신뢰 저장소 반영
sudo update-ca-certificates

6-2. RHEL/CentOS/Rocky/Alma: update-ca-trust

sudo cp /root/pki/ca/rootCA.crt /etc/pki/ca-trust/source/anchors/mycompany-rootca.crt
sudo update-ca-trust

6-3. 컨테이너(Docker) 팁

운영 환경에서는 “호스트는 신뢰하는데 컨테이너에서만 실패”가 아주 흔합니다.
컨테이너 이미지에도 CA를 넣고 신뢰 저장소를 갱신해야 합니다.
# Debian/Ubuntu 베이스 이미지 예시(Dockerfile 일부)
# COPY mycompany-rootca.crt /usr/local/share/ca-certificates/mycompany-rootca.crt
# RUN update-ca-certificates

# RHEL 계열 베이스 이미지라면 update-ca-trust 흐름 사용

7단계: 적용 후 검증(가장 중요)

7-1. openssl s_client로 체인/호스트명/SNI 확인

# SNI를 반드시 넣어야 올바른 인증서가 나오는 경우가 많습니다.
openssl s_client -connect example.internal:443 -servername example.internal -showcerts
확인 포인트
- 서버가 어떤 인증서를 내보내는지(Subject, SAN)
- 인증서 체인(중간 CA가 있다면 함께 나오는지)
- Verify return code가 0인지(단, 로컬이 rootCA를 신뢰하지 않으면 실패가 정상)

7-2. CA를 지정해서 검증(로컬 신뢰 저장소 반영 전 테스트용)

openssl s_client -connect example.internal:443 -servername example.internal \
  -CAfile /root/pki/ca/rootCA.crt \
  -verify_return_error

7-3. curl로 실제 애플리케이션 레벨 확인

# 신뢰 저장소를 반영한 시스템이라면 그냥 동작해야 합니다.
curl -Iv https://example.internal/

# 신뢰 저장소 반영 전이라면 --cacert로 CA를 직접 지정해 테스트 가능
curl -Iv --cacert /root/pki/ca/rootCA.crt https://example.internal/

7-4. 브라우저 검증 체크리스트

  • 인증서 경고가 사라졌는지(사설 CA를 OS/브라우저가 신뢰해야 함)
  • 주소가 도메인(SAN)과 일치하는지(IP로 접속하면 SAN에 IP가 있어야 함)
  • 리버스 프록시/로드밸런서 사용 시, 실제 종료 지점(LB/Ingress)에 인증서가 적용됐는지

7-5. 흔한 오류와 원인 빠른 진단

- “certificate verify failed”
→ 클라이언트가 rootCA를 신뢰하지 않음, 또는 체인이 누락됨

- “hostname mismatch”
→ SAN에 접속한 도메인/DNS가 없음(CN만 넣은 경우 포함)

- “wrong version number” / “handshake failure”
→ 443에서 TLS가 아닌 서비스가 응답하거나, 프록시/포트 매핑 오류, 프로토콜/암호군 정책 충돌

운영에서 자주 필요한 추가 팁

1) 중간 CA를 두는 구조(권장)

루트 CA를 직접 온라인에 두고 매번 서명하는 방식은 사고 위험이 큽니다.
일반적으로는 루트 CA(오프라인) → 중간 CA(온라인) → 서버 인증서 서명 순으로 운영합니다.
이때 서버는 “서버 인증서 + 중간 CA 체인”을 제공하고, 클라이언트는 루트 CA만 신뢰 저장소에 넣습니다.

2) 인증서 갱신(만료 전 교체)

  • 서버 인증서는 보통 1년~2년 단위로 운영 정책에 맞춰 갱신
  • 만료 임박 알림(모니터링)을 걸어 “갑작스런 장애”를 예방
  • 갱신 시에도 SAN 목록이 변경되었는지 꼭 재점검

3) 권한/소유권 기본 원칙

- 개인키 파일은 600, 디렉터리는 700 권장
- 웹서버 프로세스가 읽어야 하면 그룹/ACL로 최소 권한만 부여
- 키/인증서 배포는 구성관리(Ansible 등)로 감사 가능하게 유지

4) 내부 DNS와 함께 설계

  • 가능하면 IP 직접 접속 대신 내부 DNS를 표준으로
  • 로드밸런서/Ingress 뒤 서비스는 접속 도메인 기준 SAN을 중심으로 설계

마무리 정리

사설 SSL 인증서는 “발급”보다 “배포와 검증”에서 문제가 더 많이 납니다.
실제 사용 시에는 SAN(도메인/IP), 체인(중간 CA), 클라이언트 신뢰 저장소(호스트/컨테이너/PC)를 한 세트로 관리해야 장애를 줄일 수 있습니다.

2026.03.01 - [지식 공유/ETC] - Linux 사설 SSL 인증서 작업에서 자주 발생하는 오류와 해결 방법

 

Linux 사설 SSL 인증서 작업에서 자주 발생하는 오류와 해결 방법

Linux 사설 SSL 인증서 작업에서 자주 발생하는 오류와 해결 방법 사설 CA 기반 SSL은 “만드는 것”보다 적용·배포·검증에서 더 자주 문제가 납니다. 이 글은 사설 인증서를 만들고 적용하는 과정

one-day-growth.com

반응형