如何用Go从Azure Vault证书创建Kubernetes TLS Secret?
自定义从Azure Key Vault同步证书到Kubernetes Secret的实现方案
原本我可以直接用AKS的密钥存储CSI驱动对接Azure Key Vault,但因为需要通过自研的Kubernetes管理API实现更精细的证书管控,过程中遇到了不少问题,目前摸索出一套可行的实现方案,在这里分享出来,希望有相关经验的朋友能给些意见。
实现步骤
- 在Azure Vault中创建或上传证书时,证书会以Secret形式存储,我通过Azure SDK for Go获取这些证书;
- 使用base64包解码Azure返回的、包含公私钥的PFX文件;
- 调用pkcs12包的
pkcs12.ToPEM函数,将PFX文件解析为pem.Block数组; - 解析得到的每个Block中会包含额外的头部信息;
- 在将证书内容存入Kubernetes Secret之前,需要先去除这些头部信息。
实现代码
package secretcreator import ( "context" "encoding/base64" "encoding/pem" "log" "time" "github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates" "github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets" "golang.org/x/crypto/pkcs12" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" ) type MyStruct struct { kubeClientSet *kubernetes.Clientset azureVaultSecrets *azsecrets.Client azureVaultCertificates *azcertificates.Client beNamespace *v1.Namespace identitySecrests *v1.Secret } func (t *MyStruct) createSecrets() error { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() // tlsSecret.Value是经过base64编码的PEM/PFX内容,包含私钥和证书块 tlsSecret, err := t.azureVaultSecrets.GetSecret(ctx, `myCertName`, ``, nil) if err != nil { log.Printf(`获取证书失败:%s`, err.Error()) return err } // 将tlsSecret.Value中的字符串解码为字节数组,得到PFX文件的原始内容 pfxBytes, err := base64.StdEncoding.Strict().DecodeString(*tlsSecret.Value) if err != nil { log.Printf(`解码PFX证书失败:%s`, err.Error()) return err } // pemBlocks是*pem.Block类型的数组,在我的场景里包含1个私钥块和3个证书块 // 这些块会附带头部信息,比如私钥块编码后会是这样的格式: // -----BEGIN PRIVATE KEY----- // Microsoft CSP Name: Microsoft Enhanced Cryptographic Provider v1.0 // friendlyName: {D06B3D5A-98D1-45F1-A8C2-DC091232D6A7} // localKeyId: 01000000 // // MIIEAI.......ymfjsQ== // -----END PRIVATE KEY----- pemBlocks, err := pkcs12.ToPEM(pfxBytes, "") if err != nil { log.Printf(`从PEM块中解析密钥或证书失败:%s`, err.Error()) return err } // Kubernetes要求TLS Secret的data字段包含tls.key和tls.crt两个键值对 // 这里遍历所有块,根据类型拆分出私钥和证书内容 // 因为K8s的Ingress-Nginx不兼容这些头部信息,所以需要去掉它们: // 先将Block内容重新编码为base64字符串,再手动添加BEGIN/END标识 var tlsKey []byte var tlsCert []byte for _, b := range pemBlocks { if b.Type == "PRIVATE KEY" { block, _ := pem.Decode(pem.EncodeToMemory(b)) tlsKey = append(tlsKey, "-----BEGIN PRIVATE KEY-----\n"...) tlsKey = append(tlsKey, base64.StdEncoding.EncodeToString(block.Bytes)...) tlsKey = append(tlsKey, "-----END PRIVATE KEY-----\n"...) } if b.Type == "CERTIFICATE" { block, _ := pem.Decode(pem.EncodeToMemory(b)) tlsCert = append(tlsCert, "-----BEGIN CERTIFICATE-----\n"...) tlsCert = append(tlsCert, base64.StdEncoding.EncodeToString(block.Bytes)...) tlsCert = append(tlsCert, "-----END CERTIFICATE-----\n"...) } } // 使用client-go在Kubernetes中创建TLS Secret tlsData := make(map[string][]byte) tlsData[`tls.key`] = tlsKey tlsData[`tls.crt`] = tlsCert beTls := &v1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: `mycertname`, Namespace: t.beNamespace.Name, }, Data: tlsData, Type: v1.SecretTypeTLS, } _, err = t.kubeClientSet.CoreV1().Secrets(t.beNamespace.Name).Create(ctx, beTls, metav1.CreateOptions{}) if err != nil { log.Printf(`创建TLS Secret失败:%s`, err.Error()) return err } return nil }
内容的提问来源于stack exchange,提问作者williamohara
相关产品推荐
相关产品推荐

