사내 서버에 gitlab을 구축하려다보니, 기존에 구축되어 있는
cert-manager리소스들과 충돌이 날 거 같아, gitlab yaml 을 커스텀하고cert-manager충돌을 막을려고 하다보니,CA/SSL/TLS/KEY/Certificate/Issuer/ClusterIssuer/Secret등 리소스를 정확히 알고 사용하기 위해서, 해당 글을 써보고자한다.
중간 중간 내가 궁금했던 것들이 너무 많아 공부하면서 꽤 자세히 공부해보았다. 해당 공부 내용을 정리하고자 함.
우선 HTTP 와 HTTPS 차이와 동작 방식을 먼저 알아보자.
HTTP/2는 HTTPS 위에서 동작합니다.
HTTPS는 애플리케이션 계층과 전송 계층 사이에 신뢰 계층인SSL/TLS계층을 넣는 신뢰할 수 있는 HTTP 요청을 말합니다.
⇒ 이를 통해 ‘통신을 암호화’ 합니다.
SSL/TLS는 네트워크를 통해 동작하는 서버, 시스템 및 응용프로그램 간에 인증 및 데이터 암호화를 제공하는 암호화 프로토콜입니다.
SSL 인증서는 종종 디지털 인증서로 불림.
⇒ 브라우저(사용자의 컴퓨터) ↔ 서버(웹사이트) 사이의 암호화된 연결을 수립하는 데 사용됨.
서버가 "나는 진짜 이 도메인의 주인이고, CA가 보증했다" 라는 것을 증명하는 신분증
CN : CommonName (DNS)CA(Issuer) : 발급한 기관유효 기간 : 인증서의 유효 기간이 만료되지 않았는지 확인TLS는 SSL의 향상된, 더욱 안전한 버전
개인 정보와 데이터 보안을 용이하게 하기 위해 설계되어 널리 채택된 보안 프로토콜
사실상 SSL인증서와 차이가 없다 = 보안 파일 구조도 똑같고 사용 방식도 100% 동일합니다.
PKI(Public Key Infrastructure)?
인터넷에서 "상대가 진짜 맞는지" 검증하고, "안전하게 암호화"된 통신을 할 수 있도록 만드는 보안 시스템 전체
- 공개키 / 개인키
- 인증서
- CA 서명
- 인증서 검증 절차
이 모든걸 묶어 놓은 완성된 신뢰 시스템
-----BEGIN CERTIFICATE----- MIID... -----END CERTIFICATE-----=> 해당 파일이 X.509 인증서 형태
공개키 인프라(PKI)에서 TLS 인증서는 기본적으로 신뢰체인으로 불리는 모델을 기반으로 이루어진다.
신뢰 체인은 CA에서 발급한Root 인증서(Root Certificate)부터 하나 이상의중간 인증서(Intermediate Certificate)및 서버인증서(Leaf Certificate)로 구성되어 있음.
Root CA (절대 신뢰, Self-signed, 오프라인)
↓ 서명(Sign)
Intermediate CA (온라인 신뢰 관리, 실무 발급 담당)
↓ 서명(Sign)
Leaf Certificate (서버/도메인 인증서)
서명(Sign)이란?
온라인에서 누군가 문서나 인증서에 "내가 보증한다"는 표시를 남기는 방법
우선 위에서 봤듯, 서명이란걸 알기 위해서
[최상위 : Root-CA]가[중간 : Intermediate-CA]에게 서명하고 검증하는 과정을 알아보자
- 폴더 구조
pki/ ├─ root/ │ ├─ root.key # Root private key │ └─ root.crt # Root self-signed cert ├─ intermediate/ │ ├─ intermediate.key │ ├─ intermediate.csr │ └─ intermediate.crt └─ v3.ext # cert extensions for intermediate
디렉토리 만들기
mkdir -p pki/root pki/intermediate
root/ : Root CA 키와 인증서 보관intermediate/ : Intermediate CA키, CSR, 인증서를 보관Root CA key 키 생성
openssl genpkey -algorithm RSA -out pki/root/root.key -pkeyopt rsa_keygen_bits:4096
chmod 400 pki/root/root.key
openssl genpkey : 새로운 비대칭 키를 생성하는 명령어root.crt) => self-sign 인증서Root Self-signed crt 인증서 생성
openssl req -x509 -new -nodes -key pki/root/root.key \
-sha256 -days 3650 \
-subj "/C=KR/ST=Seoul/L=Seoul/O=ExampleOrg/OU=PKI/CN=Example Root CA" \
-out pki/root/root.crt
openssl req : OpenSSL에서 CSR(Certificate Singing Request) 생성 또는 Self-Signed 인증서 생성-x509 : 인증서 자체를 바로 생성-new : 새 요청 또는 새 인증서-nodes : 생성된 private key를 암호화하지 않고 저장subj : 인증서 Subject 정보(국가, 조직, CN)→ 결과물 : Root CA 인증서 (root.crt)
보안 포인트
Root 키는 절대 온라인에 연결되지 않은 상태로 보관해야 함.
why?
Root Key가 유출되면?
1. 마음대로 인증서를 발급할 수 있음.
2. 브라우저는 Root CA를 절대적으로 신뢰함.
3. 이렇게 만든 위조 인증서를 정상 인증서처럼 100% 신뢰함.
=> 전 세계 모든 TLS 웹사이트가 신뢰를 잃음.
Intermediate Private Key 생성
openssl genpkey -algorithm RSA -out pki/intermediate/intermediate.key -pkeyopt rsa_keygen_bits:4096
chmod 400 pki/intermediate/intermediate.key
Root와 동일하게 RSA 4096비트 키 생성
이 키로 실제 서버 인증서를 발급할 계획
→ 결과물 : Intermediate CA Key (intermediate.key)
intermediate.key 안에는 private / public key가 존재함.
CSR (Certificate Signing Request) 생성
openssl req -new -key pki/intermediate/intermediate.key \
-subj "/C=KR/ST=Seoul/O=ExampleOrg/OU=Issuing CA/CN=Example Intermediate CA" \
-out pki/intermediate/intermediate.csr
→ 결과물 : Intermediate CA CSR (intermediate.csr)
intermediate.key(내부의 공개키)를 CA 인증서로 만들어주세요.publickey + subject 정보만 포함basicConstraints = critical, CA:TRUE, pathlen:0
keyUsage = critical, cRLSign, keyCertSign
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
# crlDistributionPoints = URI:http://crl.example.com/intermediate.crl
# authorityInfoAccess = OCSP;URI:http://ocsp.example.combasicConstraints : CA 여부, pathlen 제한keyUsage : 서명 가능 권한 설정subjectKeyIdentifier / authorityKeyIdentifier : 체인 검증용openssl x509 -req -in pki/intermediate/intermediate.csr \
-CA pki/root/root.crt \
-CAkey pki/root/root.key \
-CAcreateserial \
-out pki/intermediate/intermediate.crt \
-days 1825 \
-sha256 \
-extfile pki/v3.ext \→ 결과물 : Intermediate CA 인증서(intermediate.crt)
아무래도
root.key를 통해intermediate.crt인증서를 만들어낼텐데 오프라인에 있는root.key를 어떻게 이용해야하는가?정답 : 파일을 전달해줘야함.
- Intermediate file(
intermediate.csr/intermediate.crt) 전달 방식
- USB
- 오프라인 저장 매체
- 안전한 네트워크(SFTP, SCP)
- 이메일은 권장 X
openssl verify -CAfile pki/root/root.crt pki/intermediate/intermediate.crt
OK → Intermediate가 Root에 의해 정상 서명됨
이후,
Leaf Key도 생성한 후,CSR을 생성하고intermediate에게Leaf Certificate+ext(CA : False)를 만들어서 같이 주면, 더 이상 인증서를 발급할 수 없는 Leaf 인증서(Leaf Certificate)가 발급이 된다.
현재 날짜가 범위 내인지 확인
CN or SAN 필드가 접속 URL과 일치하는지
인증서가 폐기되지 않았는지
Leaf Certificate의 Issuer가 Intermediate CA인지 확인
Intermediate CA의 Issuer가 Root CA인지 확인
Issuer?
이 인증서를 발급한 CA의 이름
https 연결시, 서버는 TLS 핸드셰이크 중, client에게
Leaf Certificate와Intermediate Certificate를 반드시 보냄.
- Root는 이미 브라우저/OS 안에 내장되어 있기 때문
- Root는 온라인 노출 최소화해야 해서 서버가 보내지 않음
Leaf 인증서 서명 검증
(Leaf ⇄ Intermediate)
Intermediate 인증서 서명 검증
(Intermediate ⇄ Root(브라우저/OS 내장 신뢰소))
✔ 같으면 → 해당 CA가 실제로 서명한 것이 맞다.
✘ 다르면 → 위조된 인증서 → "안전하지 않은 연결" 경고
지금까지 내용을 보면 해당 개념들은 다음과 같이 이해할 수 있을 것이다.
가장 위에 존재하는 최상위 인증 기관
실제 발급 담당
Leaf Certificate 발급하는 기관
실제 서비스 도메인에 사용하는 인증서

Cert-manager?
Kubernetes 내부에서 HTTPS 통신을 위한
X.509 인증서를 생성하고, 또X.509 인증서의 만료 기간이 되면 자동으로X.509 인증서를 갱신해주는 역할을 하는Certificate-manager Controller입니다.
Certificate)에 따라 인증서 발급 및 Secret (kubernetes.io/tls : tls.crt/tls.key) 생성issuers) 지원Ingress, Gateway, 서비스와 연계되어 TLS 제공 자동화cert-manager : 컨트롤러, 실제로직webhook : CRD 유효성 검사 및 변환cainjector : 자동으로 Secret/CA 주입 등CRD : Certificate / Issuer / ClusterIssuer / Order / Challenge / CertificateReqeust 등Let's Encrypt를 통해 외부 도메인에 자동 TLS 발급/갱신"누가 인증서를 발급할 것인지?"를 정의하는 CA 프로파일
Issuer : 특정 namespace 전용
ClusterIssuer : Cluster 전체에서 사용 가능
즉, Certificate 객체가 어떤 Issuer로부터 인증서를 발급 받을지를 결정한다.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginx
ACME(
AutomaticCertificate Managment Environment)?
- HTTPS 인증서를
자동으로 발급/갱신하기 위한 표준 프로토콜ACME Server: 인증서 발급 기관(CA)ACME Client: Cert-Manger, Certbot 같은 도구
"이 도메인의 TLS 인증서를 만들어 주세요." 라는 요청 객체
Certificate : 발급 요청서이며, 직접 인증서를 가진 것이 아님.Secret : 발급된 인증서apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: my-app-cert
spec:
secretName: my-app-tls # 여기에 발급된 인증서가 저장됨
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames: # 어떤 도메인을 위한 cert인지 지정
- example.com
- www.example.com
실제 인증서 파일(
tls.crt+tls.key)을 저장하는 Kubernetes Secret
Certificate 객체가 발급되면? cert-manager는 다음 Secret을 생성apiVersion: v1
kind: Secret
type: kubernetes.io/tls
metadata:
name: my-app-tls
data:
tls.crt: BASE64
tls.key: BASE64
tls.crt : 공개키 + 서버 인증서 체인tls.key: 개인키(private key)ACME(예: Let's Encrypt)를 사용할 때만 자동 생성되는 내부 리소스
“이 도메인이 정말 네 것이 맞냐?” 검증 단계
apiVersion: acme.cert-manager.io/v1
kind: Challenge
spec:
authorizationURL: https://acme-v02.api.letsencrypt.org/acme/authz/1234
solver:
http01:
ingress:
class: nginx
cert-manager가 인증서를 받아와 Secret에 저장Certificate 생성
↓
Issuer 선택 (ACME / CA / SelfSigned)
↓
(ACME일 경우만)
Order 생성 → Let’s Encrypt 요청
↓
Challenge 생성 → 도메인 소유권 검증
↓
검증 성공 → LE가 인증서 발급
↓
cert-manager가 Secret 생성 (tls.crt + tls.key)
↓
Certificate Ready=True
사용자는 HTTPS로 서비스에 접속한다.
Ingress Controller(Nginx/Istio 등)가 TLS Termination을 처리한다.
TLS에 필요한 인증서를 cert-manager가 자동 발급한다.
cert-manager는 Issuer/ClusterIssuer 구조로 CA 또는 Let’s Encrypt와 통신한다.
발급된 인증서는 Kubernetes Secret에 저장된다.
Ingress는 그 Secret을 참조해 HTTPS 제공한다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls: # tls 요구
- hosts:
- app.example.com
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /
backend:
service:
name: app-svc
port:
number: 80