in cmd/gcp-controller-manager/node_csr_approver.go [438:519]
func validateTPMAttestation(ctx *controllerContext, csr *capi.CertificateSigningRequest, x509cr *x509.CertificateRequest) (bool, error) {
blocks, err := parsePEMBlocks(csr.Spec.Request)
if err != nil {
klog.Infof("deny CSR %q: parsing csr.Spec.Request: %v", csr.Name, err)
return false, nil
}
attestDataRaw := blocks["ATTESTATION DATA"].Bytes
attestSig := blocks["ATTESTATION SIGNATURE"].Bytes
// TODO(awly): call ekPubAndIDFromCert instead of ekPubAndIDFromAPI when
// ATTESTATION CERTIFICATE is reliably present in CSRs.
aikPub, nodeID, err := ekPubAndIDFromAPI(ctx, blocks)
if err != nil {
if _, ok := err.(temporaryError); ok {
return false, fmt.Errorf("fetching EK public key from API: %v", err)
}
klog.Infof("deny CSR %q: fetching EK public key from API: %v", csr.Name, err)
return false, nil
}
hostname := strings.TrimPrefix(x509cr.Subject.CommonName, "system:node:")
if nodeID.Name != hostname {
klog.Infof("deny CSR %q: VM name in ATTESTATION CERTIFICATE (%q) doesn't match CommonName in x509 CSR (%q)", csr.Name, nodeID.Name, x509cr.Subject.CommonName)
return false, nil
}
if fmt.Sprint(nodeID.ProjectName) != ctx.gcpCfg.ProjectID {
klog.Infof("deny CSR %q: received CSR for a different project Name (%q)", csr.Name, nodeID.ProjectName)
return false, nil
}
recordMetric := csrmetrics.OutboundRPCStartRecorder("compute.InstancesService.Get")
srv := compute.NewInstancesService(ctx.gcpCfg.Compute)
inst, err := srv.Get(fmt.Sprint(nodeID.ProjectID), nodeID.Zone, nodeID.Name).Do()
if err != nil {
if isNotFound(err) {
klog.Infof("deny CSR %q: VM doesn't exist in GCE API: %v", csr.Name, err)
recordMetric(csrmetrics.OutboundRPCStatusNotFound)
return false, nil
}
recordMetric(csrmetrics.OutboundRPCStatusError)
return false, fmt.Errorf("fetching VM data from GCE API: %v", err)
}
recordMetric(csrmetrics.OutboundRPCStatusOK)
if ctx.csrApproverVerifyClusterMembership {
ok, err := clusterHasInstance(ctx, inst.Zone, inst.Id)
if err != nil {
return false, fmt.Errorf("checking VM membership in cluster: %v", err)
}
if !ok {
klog.Infof("deny CSR %q: VM %q doesn't belong to cluster %q", csr.Name, inst.Name, ctx.gcpCfg.ClusterName)
return false, nil
}
}
attestHash := sha256.Sum256(attestDataRaw)
if err := rsa.VerifyPKCS1v15(aikPub, crypto.SHA256, attestHash[:], attestSig); err != nil {
klog.Infof("deny CSR %q: verifying certification signature with AIK public key: %v", csr.Name, err)
return false, nil
}
// Verify that attestDataRaw matches certificate.
pub, err := tpmattest.MakePublic(x509cr.PublicKey)
if err != nil {
klog.Infof("deny CSR %q: converting public key in CSR to TPM Public structure: %v", csr.Name, err)
return false, nil
}
attestData, err := tpm2.DecodeAttestationData(attestDataRaw)
if err != nil {
klog.Infof("deny CSR %q: parsing attestation data in CSR: %v", csr.Name, err)
return false, nil
}
ok, err := attestData.AttestedCertifyInfo.Name.MatchesPublic(pub)
if err != nil {
klog.Infof("deny CSR %q: comparing ATTESTATION DATA to CSR public key: %v", csr.Name, err)
return false, nil
}
if !ok {
klog.Infof("deny CSR %q: ATTESTATION DATA doesn't match CSR public key", csr.Name)
return false, nil
}
return true, nil
}