抱えていた課題
普段運用しているクラスタでは、クラスタ内に Vault を設置して、その Vault で Secret を管理しています。(↓ 参考)
ExternalSecrets を使って、Vault にある Secret を Kubernetes 内で使えるようにしているのですが、Vault との通信には HTTP を使っていました。↓
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: vault-secret-store
spec:
provider:
vault:
server: "http://vault.vault.svc.cluster.local:8200"
このままだと、クラスタ内に悪意のあるワークロードが入り込んだ場合、Secret が盗まれる可能性があり危険です。
そこで今回は、Vault との通信を HTTPS に変更し、さらに Vault に証明書を設定して、通信を暗号化することにしました。
実装
証明書の作成
基本的には、下記のドキュメントに従って設定を行いました。
環境変数を設定
export VAULT_K8S_NAMESPACE="vault"
export VAULT_HELM_RELEASE_NAME="vault"
export VAULT_SERVICE_NAME="vault"
export K8S_CLUSTER_NAME="cluster.local"
export WORKDIR="/tmp/vault"
mkdir -p ${WORKDIR}
秘密鍵を生成
openssl genrsa -out ${WORKDIR}/vault.key 2048
CSR(Certificate Signing Request)を作成
cat > ${WORKDIR}/vault-csr.conf <<EOF
[req]
default_bits = 2048
prompt = no
encrypt_key = yes
default_md = sha256
distinguished_name = kubelet_serving
req_extensions = v3_req
[ kubelet_serving ]
O = system:nodes
CN = system:node:*.${VAULT_K8S_NAMESPACE}.svc.${K8S_CLUSTER_NAME}
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = *.${VAULT_SERVICE_NAME}
DNS.2 = *.${VAULT_SERVICE_NAME}.${VAULT_K8S_NAMESPACE}
DNS.3 = *.${VAULT_SERVICE_NAME}.${VAULT_K8S_NAMESPACE}.svc
DNS.4 = *.${VAULT_SERVICE_NAME}.${VAULT_K8S_NAMESPACE}.svc.cluster.local
DNS.5 = ${VAULT_SERVICE_NAME}
DNS.6 = ${VAULT_SERVICE_NAME}.${VAULT_K8S_NAMESPACE}
DNS.7 = ${VAULT_SERVICE_NAME}.${VAULT_K8S_NAMESPACE}.svc
DNS.8 = ${VAULT_SERVICE_NAME}.${VAULT_K8S_NAMESPACE}.svc.cluster.local
IP.1 = 127.0.0.1
EOF
openssl req -new -key ${WORKDIR}/vault.key -out ${WORKDIR}/vault.csr -config ${WORKDIR}/vault-csr.conf
証明書を発行
cat | kubectl create -f - <<EOF
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: vault.svc
spec:
signerName: kubernetes.io/kubelet-serving
expirationSeconds: 8640000
request: $(cat ${WORKDIR}/vault.csr|base64|tr -d '\n')
usages:
- digital signature
- key encipherment
- server auth
EOF
Approve
kubectl certificate approve vault.svc
証明書から secret を作成
kubectl get csr vault.svc -o jsonpath='{.status.certificate}' | openssl base64 -d -A -out ${WORKDIR}/vault.crt
kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.certificate-authority-data}' | base64 -d > ${WORKDIR}/vault.ca
kubectl create secret generic vault-tls -n $VAULT_K8S_NAMESPACE --from-file=vault.key=${WORKDIR}/vault.key --from-file=vault.crt=${WORKDIR}/vault.crt --from-file=vault.ca=${WORKDIR}/vault.ca
Vault の設定
Helm の values.yaml
に証明書の設定を追加します。
global:
enabled: true
tlsDisable: false
extraEnvironmentVars:
VAULT_CACERT: /vault/userconfig/vault-tls/vault.ca
VAULT_TLSCERT: /vault/userconfig/vault-tls/vault.crt
VAULT_TLSKEY: /vault/userconfig/vault-tls/vault.key
volumes:
- name: userconfig-vault-tls
secret:
defaultMode: 420
secretName: vault-tls
volumeMounts:
- mountPath: /vault/userconfig/vault-tls
name: userconfig-vault-tls
readOnly: true
standalone:
enabled: true
config: |
ui = true
listener "tcp" {
address = "[::]:8200"
cluster_address = "[::]:8201"
tls_cert_file = "/vault/userconfig/vault-tls/vault.crt"
tls_key_file = "/vault/userconfig/vault-tls/vault.key"
tls_client_ca_file = "/vault/userconfig/vault-tls/vault.ca"
}
storage "file" {
path = "/vault/data"
}
ingress:
enabled: true
ingressClassName: nginx
tls:
- hosts:
- vault.example.com
secretName: cluster-tls
pathType: Prefix
hosts:
- host: vault.example.com
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
ドキュメントとは異なる点は以下の 2 点です。
-
standalone.config
にui = true
を記述
これがないと、Vault の UI にアクセスできなくなります(参考:https://github.com/hashicorp/vault/issues/4345) - ingress の
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
を追加
nginx から vault に対して HTTPS で通信するように設定します(参考:https://stackoverflow.com/questions/69169466/ingress-nginx-client-sent-http-request-to-https-server )
ExternalSecrets の設定
ClusterSecretStore の設定を変更して、Vault との通信を HTTPS に変更します。
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: vault-secret-store
spec:
provider:
vault:
server: "https://vault.vault.svc.cluster.local:8200" # HTTPS に変更
path: k8s
version: v2
namespace: vault
auth:
kubernetes:
mountPath: kubernetes
role: k8s-cluster
serviceAccountRef:
name: vault-auth
namespace: vault
caProvider: # 追加
type: Secret
namespace: vault
name: vault-tls
key: vault.ca
これで、Vault との通信が HTTPS に変更されました。
おわりに
今回は、Vault との通信を HTTPS に変更しました。
これにより、クラスタ内での Secret のやりとりがより安全になりました。
次回は cert-manager を使って、証明書の自動更新を行えるようにしたいと思います。
参考