[kubernetes] Secret으로 민감 데이터 관리
설정 파일에는 비밀번호 같은 민감 정보가 있을 수 있다.
이런 민감 데이터를 관리하기 위한 오브젝트인 Secret을 사용하면 Base64로 인코딩하여 저장하고 불러올때는 디코딩해준다!
이번 실습은 nginx 인증서와 키 파일을 secret을 통해 저장하고 파드 볼륨에서 해당 secret에 있는 데이터를 불러오도록 하여 안전하게 관리 하도록 해보자!
인증서는 cert 디렉토리에 생성하도록 한다.
$ mkdir cert
nginx에 사용할 인증서와 키파일을 생성한다.
# 키 생성
$ openssl genrsa -aes256 -out ./cert/corin.com.key 2048
# 개인키 자체 암호화 제거
$ cp ./corin.com.key ./cert/corin.com.key.enc
$ openssl rsa -in ./cert/corin.com.key.enc -out ./cert/corin.com.key
# 개인키 group other 권한 제거
$ chmod 600 ./cert/corin.com.key*
# csr 생성 (Common Name에 도메인 입력)
$ openssl req -new -key ./cert/corin.com.key -out ./cert/corin.com.csr
# 확인
$ openssl req -text -in ./cert/corin.com.csr
# csr > crt`
$ openssl x509 -req -days 365 -in ./cert/corin.com.csr -signkey ./cert/corin.com.key -out ./cert/corin.com.crt
# 확인
$ openssl x509 -in ./cert/corin.com.crt -noout -text
이제 생성한 인증서를 저장할 secret을 생성하도록 한다.
$ kubectl create secret generic tls-config --from-file=cert
secret/tls-config created
secret의 데이터를 확인해보면 실제 데이터와 다른값이 저장 되어 있는데 이는 Base64로 인코딩 된 값임을 알 수 있다.
$ kubectl get secret tls-config -o yaml
apiVersion: v1
data:
corin.com.crt: LS0tLS...
corin.com.key: LS0tLS1CRU
kind: Secret
metadata:
creationTimestamp: "2023-06-04T13:17:36Z"
name: tls-config
namespace: default
resourceVersion: "22093594"
uid: fb03a2b6-0cfe-4cf6-8f6c-bffa0a127084
type: Opaque
이는 아래와 같이 Base64로 디코딩 하면 실제 저장한 값이 나오며 이를 통해 Base64로 인코딩 된 값임을 알 수 있다.
$ kubectl get secret tls-config -o jsonpath='{.data.corin\.com\.crt}' | base64 --decode
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
이제 인증서 관련 설정을 아래와 같이 진행한다.
ssl_certificate 부분의 인증서와 key 파일은 secret 을 통해 읽어올 예정이며 이는 뒤에서 yaml 파일에서 정의하도록 한다.
# server.conf
server {
listen 80;
listen [::]:80;
listen 443 ssl;
server_name corin.com;
ssl_certificate tls/corin.com.crt; # 인증서 위치 (상대 경로이기에 /etc/nginx/tls/ 에서 읽음)
ssl_certificate_key tls/corin.com.key; # 키 위치
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location /myapp {
proxy_pass http://my-app:8080/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
이제 해당 server.conf 파일을 읽을 수 있도록 configmap 을 생성한다.
$ kubectl create configmap nginx-config --from-file=conf
configmap/nginx-config created
$ kubectl get configmap nginx-config -o yaml
이제 파드, 서비스를 생성하며 nginx 컨테이너에서 생성한 configmap, secret 을 읽을 수 있도록 volumes, volumeMounts를 정의한다.
secret 의 경우 secret.secretName 으로 secret을 파드 볼륨에서 읽어 올 수 있다. 사용은 컨테이너에서 볼륨 마운트 하면 된다.
# web-server.yaml
apiVersion: v1
kind: Service
metadata:
name: my-app
labels:
app: my-app
spec:
selector:
app: my-app
ports:
- port: 8080
targetPort: 8080
---
apiVersion: v1
kind: Pod
metadata:
name: my-app
labels:
app: my-app
spec:
containers:
- name: my-app
image: yoonjeong/my-app:1.0
ports:
- containerPort: 8080
resources:
limits:
memory: "64Mi"
cpu: "50m"
---
apiVersion: v1
kind: Pod
metadata:
name: web-server
labels:
name: nginx
spec:
volumes:
- name: tls-config
secret:
secretName: tls-config
- name: app-config
configMap:
name: nginx-config
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: app-config
mountPath: /etc/nginx/conf.d
- name: tls-config
mountPath: /etc/nginx/tls
resources:
limits:
memory: "64Mi"
cpu: "50m"
- name: my-app
image: yoonjeong/my-app:1.0
ports:
- containerPort: 8080
resources:
limits:
memory: "64Mi"
cpu: "50m"
클러스터에 적용한다.
$ kubectl apply -f web-server.yaml
service/my-app created
pod/my-app created
pod/web-server created
파드가 정상적으로 생성 되었다.
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
my-app 1/1 Running 0 116s
web-server 2/2 Running 0 115s
이제 테스트를 위해 위에 생성한 도메인명으로 host 설정을 로컬에 한다.
$ sudo vi /etc/hosts
127.0.0.1 localhost corin.com
이제 cacert 옵션을 통해 클라이언트가 인증서를 신뢰할 수 있도록 하여 생성한 도메인의 myapp URL로 호출 하면 http status code 200을 받으며 정상적으로 통신 된다.
$ curl --cacert cert/corin.com.crt -sv https://corin.com:8443/myapp
* Trying ::1...
* TCP_NODELAY set
* Connected to corin.com (::1) port 8443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: cert/corin.com.crt
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=corin.com
* start date: Jun 4 13:03:18 2023 GMT
* expire date: Jun 3 13:03:18 2024 GMT
* common name: corin.com (matched)
* issuer: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=corin.com
* SSL certificate verify ok.
> GET /myapp HTTP/1.1
> Host: corin.com:8443
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.25.0
< Date: Sun, 04 Jun 2023 14:21:10 GMT
< Transfer-Encoding: chunked
< Connection: keep-alive
< X-Powered-By: Express
<
Welcome to Version 1!
===== Host Info =====
HostIP: 10.100.3.101
* Connection #0 to host corin.com left intact
HostName: my-app* Closing connection 0
실제 컨테이너의 인증서 확인시 정상적으로 디코딩 되어 저장 되어 있음을 알 수 있다.
$ kubectl exec web-server -c nginx -- cat /etc/nginx/tls/corin.com.crt
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
끝!