์ด ๊ธ์์๋ Windows IIS๋ CA์์ ๋ฐ๊ธํ PFX(PKCS#12) ์ธ์ฆ์ ํ์ผ์ OpenSSL ๋ช
๋ น์ผ๋ก ๊ฐ์ธํค(.key)์ ์ธ์ฆ์ ์ฒด์ธ(.crt) PEM ํ์ผ๋ก ๋ถ๋ฆฌํ๊ณ , ์ด๋ฅผ kubectl create secret tls๋ก Kubernetes TLS Secret ๋งค๋ํ์คํธ๋ก ๋ณํํ๋ ์ํฌํ๋ก์ฐ๋ฅผ ๋จ๊ณ๋ณ๋ก ๋ค๋ฃน๋๋ค. IngressยทArgo CD ๋ฑ์์ ์ฌ์ฉํ TLS Secret์ ๋ง๋ค ๋ ๊ทธ๋๋ก ํ์ฉํ ์ ์์ต๋๋ค.
๐ฆ PFX(PKCS#12) ํ์ผ์ด๋? #
PFX๋ PKCS#12 ํ์ค์ ๋ฐ๋ฅด๋ ๋ฐ์ด๋๋ฆฌ ํ์์ ์ธ์ฆ์ ์ปจํ ์ด๋์ ๋๋ค. ํ๋์ ํ์ผ ์์ ๊ฐ์ธํค + ์ธ์ฆ์ + ์ค๊ฐ CA ์ฒด์ธ์ด ๋ชจ๋ ํจ์ค์๋๋ก ์ํธํ๋์ด ๋ค์ด ์์ด, Windows ํ๊ฒฝ์์ ์ธ์ฆ์๋ฅผ ๋ฐฑ์ ํ๊ฑฐ๋ ์ด๊ดํ ๋ ์ฃผ๋ก ์ฌ์ฉ๋ฉ๋๋ค.
๋ฐ๋ฉด Linux ์ง์(Nginx, HAProxy, Kubernetes Ingress ๋ฑ)์ ์ผ๋ฐ์ ์ผ๋ก PEM ํ์์ ๋ณ๋ ํ์ผ(.key, .crt)์ ์๊ตฌํฉ๋๋ค. ๋ฐ๋ผ์ PFX๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํ ์ ์๊ณ OpenSSL๋ก ๋ณํํด์ผ ํฉ๋๋ค.
| ํญ๋ชฉ | PFX (PKCS#12) | PEM |
|---|---|---|
| ํ์ | ๋ฐ์ด๋๋ฆฌ | Base64 ํ ์คํธ |
| ํ์ฅ์ | .pfx, .p12 | .key, .crt, .pem |
| ๊ตฌ์ฑ | ํค + ์ธ์ฆ์ + ์ฒด์ธ ํตํฉ | ํ์ผ๋ณ ๋ถ๋ฆฌ |
| ์ํธํ | ํญ์ ํจ์ค์๋ ๋ณดํธ | ์ ํ์ (ํค๋ง ์ํธํ ๊ฐ๋ฅ) |
| ์ฃผ ์ฌ์ฉ์ฒ | Windows, IIS | Linux, Nginx, Kubernetes |
๐ ๏ธ ์ฌ์ ์ค๋น #
OpenSSL๊ณผ kubectl์ด ์ค์น๋์ด ์์ด์ผ ํฉ๋๋ค.
1openssl version
2# OpenSSL 3.0.x ์ด์ ๊ถ์ฅ
3
4kubectl version --client์์ ํ PFX ํ์ผ๊ณผ ๊ทธ ํ์ผ์ ํจ์ค์๋๋ฅผ ๋ฏธ๋ฆฌ ์ค๋นํด ๋ก๋๋ค. ์ด ๊ธ์์๋ ๋ค์๊ณผ ๊ฐ์ ๋ณ์๋ก ์งํํ๋ค๊ณ ๊ฐ์ ํ๊ฒ ์ต๋๋ค.
1PFX_FILE="example.com.pfx"
2PFX_PASS="ChangeMeStrongPass" # PFX ์๋ณธ ํจ์ค์๋
3TMP_PASS="ChangeMeTempPass" # ์ค๊ฐ ์์
์ฉ ์์ ํจ์ค์๋
4KEY_OUT="tls.key"
5CRT_OUT="fullchain.crt"
6NAMESPACE="argocd"
7SECRET_NAME="tls-example-com"โ ๏ธ ์ค์ ์ด์ ํ๊ฒฝ์์๋ ํจ์ค์๋๋ฅผ ์ ธ ํ์คํ ๋ฆฌ์ ๋จ๊ธฐ์ง ์๋๋ก
-passin file:...๋๋ ํ๊ฒฝ๋ณ์(-passin env:VAR)๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์์ ํฉ๋๋ค.
1๏ธโฃ PFX์์ ๊ฐ์ธํค ์ถ์ถํ๊ธฐ #
๋จผ์ PFX์์ ๊ฐ์ธํค๋ง ๋ถ๋ฆฌํฉ๋๋ค. ์ด ์์ ์์๋ ํค๊ฐ ์ฌ์ ํ ์์ ํจ์ค์๋(TMP_PASS)๋ก ์ํธํ๋ PEM ํ์์ผ๋ก ์ถ๋ ฅ๋ฉ๋๋ค.
1openssl pkcs12 \
2 -passin pass:${PFX_PASS} \
3 -passout pass:${TMP_PASS} \
4 -in ${PFX_FILE} \
5 -nocerts \
6 -out tmp.key| ์ต์ | ์ค๋ช |
|---|---|
pkcs12 | PKCS#12 ํ์ ์ฒ๋ฆฌ ์๋ธ์ปค๋งจ๋ |
-passin pass:... | ์ ๋ ฅ PFX ํ์ผ์ ํจ์ค์๋ |
-passout pass:... | ์ถ๋ ฅ PEM ํค์ ์ ์ฉํ ํจ์ค์๋ |
-in | ์ ๋ ฅ PFX ํ์ผ ๊ฒฝ๋ก |
-nocerts | ์ธ์ฆ์๋ ์ ์ธํ๊ณ ํค๋ง ์ถ์ถ |
-out | ์ถ๋ ฅ ํ์ผ ๊ฒฝ๋ก |
Tip:
-passout์ ๋นผ๋ฉด OpenSSL์ด ๋ํํ์ผ๋ก ํจ์ค์๋๋ฅผ ๋ฌผ์ด๋ด ๋๋ค. ์๋ํ ์คํฌ๋ฆฝํธ์์๋ ๋ช ์์ ์ผ๋ก ์ง์ ํ๋ ํธ์ด ์์ ์ ์ ๋๋ค.
2๏ธโฃ ๊ฐ์ธํค ํจ์ค์๋ ์ ๊ฑฐ (๋ณตํธํ) #
Kubernetes TLS Secret๊ณผ ๋๋ถ๋ถ์ Ingress ์ปจํธ๋กค๋ฌ๋ ํจ์ค์๋๊ฐ ์๋ PEM ํค๋ฅผ ์๊ตฌํฉ๋๋ค. ๋ฐ๋ผ์ ์์ ํจ์ค์๋๋ก ์ํธํ๋ ํค๋ฅผ ํ๋ฌธ RSA ํค๋ก ๋ณํํฉ๋๋ค.
1openssl rsa \
2 -passin pass:${TMP_PASS} \
3 -in tmp.key \
4 -out ${KEY_OUT}๋ช ๋ น์ด ์ฑ๊ณตํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ๋ฉ์์ง๊ฐ ์ถ๋ ฅ๋ฉ๋๋ค.
1writing RSA key์์ฑ๋ ${KEY_OUT} ํ์ผ์ ๋ค์๊ณผ ๊ฐ์ด ์์ํฉ๋๋ค.
1-----BEGIN RSA PRIVATE KEY-----
2MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQ...
3-----END RSA PRIVATE KEY-----โ ๏ธ ํ๋ฌธ ๊ฐ์ธํค๋ ๋ ธ์ถ ์ ์ฆ์ ์ธ์ฆ์ ํ๊ธฐ ์ฌ์ ๊ฐ ๋ฉ๋๋ค. ์์ ์ด ๋๋๋ฉด ์์ ํ์ผ(
tmp.key)์ ๋ฐ๋์ ์ญ์ ํ์ธ์.
3๏ธโฃ ์ธ์ฆ์ ์ฒด์ธ ์ถ์ถํ๊ธฐ #
์ด๋ฒ์๋ ํค๋ฅผ ์ ์ธํ ํด๋ผ์ด์ธํธ ์ธ์ฆ์์ ์ค๊ฐ CA ์ธ์ฆ์๋ฅผ ํ ํ์ผ๋ก ๋ฌถ์ด ์ถ์ถํฉ๋๋ค.
1openssl pkcs12 \
2 -passin pass:${PFX_PASS} \
3 -in ${PFX_FILE} \
4 -clcerts \
5 -nokeys \
6 -out ${CRT_OUT}| ์ต์ | ์ค๋ช |
|---|---|
-clcerts | ํด๋ผ์ด์ธํธ(์๋ฒ) ์ธ์ฆ์๋ง ์ถ๋ ฅ |
-nokeys | ๊ฐ์ธํค๋ ์ ์ธ |
์ฒด์ธ์ด ๋ถ๋ฆฌ๋์ด ์์ด ํ์ฒด์ธ(fullchain)์ผ๋ก ๋ฌถ๊ณ ์ถ๋ค๋ฉด -clcerts ์ต์
์ ๋นผ๊ณ , ์ถ๋ ฅ๋ ์ธ์ฆ์ ๋ธ๋ก ์ค ํ์ํ ๊ฒ์ ํ์ธํด ์ ๋ ฌํฉ๋๋ค.
1openssl pkcs12 \
2 -passin pass:${PFX_PASS} \
3 -in ${PFX_FILE} \
4 -nokeys \
5 -out fullchain.crtTip: Ingress์์ SSL Labs A+ ๋ฑ๊ธ์ ๋ฐ์ผ๋ ค๋ฉด ์๋ฒ ์ธ์ฆ์ + ์ค๊ฐ CA ์์๋ก ์ ๋ ฌ๋ ํ์ฒด์ธ์ด ํ์ํฉ๋๋ค.
4๏ธโฃ ์ถ์ถ ๊ฒฐ๊ณผ ๊ฒ์ฆํ๊ธฐ #
๋ณํ์ด ์ ์์ ์ผ๋ก ๋๋ฌ๋์ง ๋ฐ๋์ ํ์ธํฉ๋๋ค.
์ธ์ฆ์ ์ ๋ณด ํ์ธ
1openssl x509 -in ${CRT_OUT} -noout -subject -issuer -dates1subject=CN=example.com
2issuer=CN=Internal Issuing CA
3notBefore=Jan 1 00:00:00 2026 GMT
4notAfter=Dec 31 23:59:59 2026 GMT๊ฐ์ธํค์ ์ธ์ฆ์๊ฐ ํ ์์ธ์ง ํ์ธ (๋ชจ๋๋ฌ์ค ๋น๊ต)
1diff \
2 <(openssl rsa -in ${KEY_OUT} -modulus -noout) \
3 <(openssl x509 -in ${CRT_OUT} -modulus -noout)์ถ๋ ฅ์ด ์์ผ๋ฉด ๊ฐ์ ์์ ๋๋ค. ๋ค๋ฅด๋ฉด ์๋ชป๋ ํค/์ธ์ฆ์ ์กฐํฉ์ด๋ฏ๋ก Secret์ ๋ง๋ค์ด๋ TLS ํธ๋์ ฐ์ดํฌ๊ฐ ์คํจํฉ๋๋ค.
5๏ธโฃ Kubernetes TLS Secret YAML ๋ง๋ค๊ธฐ #
kubectl create secret tls์ --dry-run=client -o yaml์ ๋ถ์ด๋ฉด ํด๋ฌ์คํฐ์ ๋ฐ์ํ์ง ์๊ณ ๋งค๋ํ์คํธ๋ง ์์ฑํ ์ ์์ต๋๋ค.
1kubectl create secret tls ${SECRET_NAME} \
2 -n ${NAMESPACE} \
3 --key ${KEY_OUT} \
4 --cert ${CRT_OUT} \
5 --dry-run=client -o yaml > tls-example-com.yaml์์ฑ๋ ๋งค๋ํ์คํธ๋ ๋ค์๊ณผ ๊ฐ์ ํํ์ ๋๋ค.
1apiVersion: v1
2kind: Secret
3metadata:
4 name: tls-example-com
5 namespace: argocd
6type: kubernetes.io/tls
7data:
8 tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZ...
9 tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpN...Tip: Git์ ์ปค๋ฐํ๋ค๋ฉด ํ๋ฌธ Secret ๋์ Sealed Secrets๋ External Secrets Operator๋ก ์ํธํํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
6๏ธโฃ ํด๋ฌ์คํฐ์ ์ ์ฉ ๋ฐ Ingress ์ฐ๊ฒฐ #
์์ฑํ ๋งค๋ํ์คํธ๋ฅผ ์ ์ฉํฉ๋๋ค.
1kubectl apply -f tls-example-com.yaml
2
3kubectl get secret ${SECRET_NAME} -n ${NAMESPACE}
4# NAME TYPE DATA AGE
5# tls-example-com kubernetes.io/tls 2 5s์ดํ Ingress ๋ฆฌ์์ค์์ tls.secretName์ผ๋ก ์ฐธ์กฐํฉ๋๋ค.
1apiVersion: networking.k8s.io/v1
2kind: Ingress
3metadata:
4 name: argocd-server-ingress
5 namespace: argocd
6spec:
7 ingressClassName: nginx
8 tls:
9 - hosts:
10 - example.com
11 secretName: tls-example-com
12 rules:
13 - host: example.com
14 http:
15 paths:
16 - path: /
17 pathType: Prefix
18 backend:
19 service:
20 name: argocd-server
21 port:
22 number: 443๐งน ๋ง๋ฌด๋ฆฌ ์ ๋ฆฌ #
์์ ์ด ๋๋๋ฉด ๋ฏผ๊ฐ ํ์ผ์ ์์ ํ๊ฒ ์ ๋ฆฌํฉ๋๋ค.
1shred -u tmp.key ${KEY_OUT} 2>/dev/null || rm -f tmp.key ${KEY_OUT}โ ๏ธ
shred๋ ext4 ๋ฑ ์ผ๋ถ ํ์ผ์์คํ ์์๋ง ํจ๊ณผ์ ์ ๋๋ค. SSDยท๋ณต์ฌ๋ณธ ์บ์ยทtmpfs์์๋ ์์ ์ญ์ ๊ฐ ๋ณด์ฅ๋์ง ์์ผ๋ฏ๋ก ์์ ๋๋ ํฐ๋ฆฌ ์์ฒด๋ฅผ ์์ ํ ์์น(์:tmpfs๋ง์ดํธ)๋ก ์ก๋ ํธ์ด ์ข์ต๋๋ค.
๐ ํ ๋ฒ์ ์ฒ๋ฆฌํ๋ ์คํฌ๋ฆฝํธ #
์ ๋จ๊ณ๋ฅผ ์๋ํํ ์์ ์คํฌ๋ฆฝํธ์ ๋๋ค.
1#!/usr/bin/env bash
2set -euo pipefail
3
4PFX_FILE="${1:?usage: $0 <pfx> <pfx-pass> <namespace> <secret-name>}"
5PFX_PASS="${2}"
6NAMESPACE="${3}"
7SECRET_NAME="${4}"
8TMP_PASS="$(openssl rand -hex 8)"
9
10WORKDIR="$(mktemp -d)"
11trap 'rm -rf "${WORKDIR}"' EXIT
12
13openssl pkcs12 -passin "pass:${PFX_PASS}" -passout "pass:${TMP_PASS}" \
14 -in "${PFX_FILE}" -nocerts -out "${WORKDIR}/tmp.key"
15
16openssl rsa -passin "pass:${TMP_PASS}" \
17 -in "${WORKDIR}/tmp.key" -out "${WORKDIR}/tls.key"
18
19openssl pkcs12 -passin "pass:${PFX_PASS}" \
20 -in "${PFX_FILE}" -nokeys -out "${WORKDIR}/tls.crt"
21
22kubectl create secret tls "${SECRET_NAME}" \
23 -n "${NAMESPACE}" \
24 --key "${WORKDIR}/tls.key" \
25 --cert "${WORKDIR}/tls.crt" \
26 --dry-run=client -o yamlโ ์์ฃผ ๋ฌป๋ ์ง๋ฌธ #
Q. -nodes ์ต์
์ ํ ๋ฒ์ ์ฐ๋ฉด ๋์ง ์๋์?
#
๊ฐ๋ฅํฉ๋๋ค. openssl pkcs12 -in file.pfx -nodes -out all.pem ํ ์ค๋ก ํค์ ์ธ์ฆ์๋ฅผ ํ๋ฌธ์ผ๋ก ํจ๊ป ์ถ์ถํ ์ ์์ต๋๋ค. ๋ค๋ง ํค/์ธ์ฆ์๋ฅผ ํ ํ์ผ์ ์์ผ๋ฉด kubectl create secret tls์ฒ๋ผ ๋ ํ์ผ์ ์๊ตฌํ๋ ๋๊ตฌ์์ ๋ค์ ๋ถ๋ฆฌํด์ผ ํ๋ฏ๋ก, ๋จ๊ณ๋ณ๋ก ์ถ์ถํ๋ ํธ์ด ์๋ํ์ ์ ๋ฆฌํฉ๋๋ค.
Q. “Mac verify error: invalid password” ์ค๋ฅ๊ฐ ๋ฉ๋๋ค. #
PFX ํ์ผ์ ํจ์ค์๋๊ฐ ํ๋ฆฐ ๊ฒฝ์ฐ์
๋๋ค. ์ผ๋ถ ํ๊ฒฝ์์๋ OpenSSL 3.x์์ ๊ตฌ๋ฒ์ PFX์ ํด์ ์๊ณ ๋ฆฌ์ฆ์ ๊ฑฐ๋ถํ๊ธฐ๋ ํฉ๋๋ค. ์ด ๊ฒฝ์ฐ -legacy ์ต์
์ ์ถ๊ฐํด ๋ณด์ธ์.
1openssl pkcs12 -legacy -passin pass:... -in file.pfx -out out.pemQ. ํ๋ฌธ RSA ํค์ PKCS#8 ํค ์ค ์ด๋ ์ชฝ์ ์จ์ผ ํ๋์? #
๋๋ถ๋ถ์ Kubernetes Ingress ์ปจํธ๋กค๋ฌ๋ ๋ ํ์์ ๋ชจ๋ ์ง์ํฉ๋๋ค. ๋ค๋ง ์ผ๋ถ ์ ๊ท ๋๊ตฌ(์: Envoy ์ผ๋ถ ๋น๋)๋ PKCS#8์ ์ ํธํฉ๋๋ค. ๋ณํ์ ๋ค์๊ณผ ๊ฐ์ด ํฉ๋๋ค.
1openssl pkcs8 -topk8 -nocrypt -in tls.key -out tls.pkcs8.keyQ. --dry-run=client์ --dry-run=server์ ์ฐจ์ด๋?
#
client๋ ํด๋ผ์ด์ธํธ(kubectl) ์ธก์์๋ง ๋งค๋ํ์คํธ๋ฅผ ์์ฑํ๊ณ API ์๋ฒ๋ก ๋ณด๋ด์ง ์์ต๋๋ค. server๋ API ์๋ฒ๊น์ง ๊ฐ์ ์ด๋๋ฏธ์
๊ฒ์ฆ์ ์ํํ์ง๋ง ์ค์ ๋ก ์ ์ฅํ์ง ์์ต๋๋ค. Secret YAML์ ๋ง๋ค ๋๋ client๋ก ์ถฉ๋ถํฉ๋๋ค.
Q. Secret์ ๋ง๋ ๋ค ์ธ์ฆ์๊ฐ ๊ฐฑ์ ๋๋ฉด ์ด๋ป๊ฒ ํ๋์? #
๊ฐ์ ์ด๋ฆ์ผ๋ก kubectl create secret tls ... --dry-run=client -o yaml | kubectl apply -f -๋ฅผ ๋ค์ ์คํํ๋ฉด ๋ฉ๋๋ค. Ingress ์ปจํธ๋กค๋ฌ๋ Secret ๋ณ๊ฒฝ์ ๊ฐ์งํด ์๋์ผ๋ก ์ ์ธ์ฆ์๋ฅผ ๋ก๋ํฉ๋๋ค.