Skip to content

Commit

Permalink
Merge pull request kubernetes#32 from awly/vm-id-from-cert
Browse files Browse the repository at this point in the history
Read VM identity from AIK cert
  • Loading branch information
k8s-ci-robot authored Jul 18, 2018
2 parents 93d2533 + ed50c40 commit 231f082
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 24 deletions.
46 changes: 24 additions & 22 deletions cmd/gcp-controller-manager/app/gke_approver.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
Expand Down Expand Up @@ -452,7 +451,6 @@ var tpmAttestationBlocks = []string{
"ATTESTATION CERTIFICATE",
"ATTESTATION DATA",
"ATTESTATION SIGNATURE",
"VM IDENTITY",
}

func isNodeClientCertWithAttestation(opts approverOptions, csr *capi.CertificateSigningRequest, x509cr *x509.CertificateRequest) bool {
Expand Down Expand Up @@ -481,14 +479,34 @@ func validateTPMAttestation(opts approverOptions, csr *capi.CertificateSigningRe
glog.Errorf("Parsing csr.Spec.Request: %v", err)
return false
}
nodeIDRaw := blocks["VM IDENTITY"].Bytes
attestDataRaw := blocks["ATTESTATION DATA"].Bytes
attestSig := blocks["ATTESTATION SIGNATURE"].Bytes
attestCert := blocks["ATTESTATION CERTIFICATE"].Bytes

var nodeID nodeidentity.Identity
if err := json.Unmarshal(nodeIDRaw, &nodeID); err != nil {
glog.Errorf("Unmarshaling VM identity JSON: %v", err)
// TODO(awly): get AIK public key from GCE API.
aikCert, err := x509.ParseCertificate(attestCert)
if err != nil {
glog.Errorf("Parsing ATTESTATION_CERTIFICATE: %v", err)
return false
}
if err := opts.tpmCACache.verify(aikCert); err != nil {
glog.Errorf("Verifying EK certificate validity: %v", err)
return false
}
aikPub, ok := aikCert.PublicKey.(*rsa.PublicKey)
if !ok {
glog.Errorf("Public key in ATTESTATION CERTIFICATE is %T, want *rsa.PublicKey", aikCert.PublicKey)
return false
}

nodeID, err := nodeidentity.FromAIKCert(aikCert)
if err != nil {
glog.Errorf("Failed extracting VM identity from EK certificate: %v", err)
return false
}
hostname := strings.TrimPrefix("system:node:", x509cr.Subject.CommonName)
if nodeID.Name != hostname {
glog.Errorf("VM name in ATTESTATION CERTIFICATE (%q) doesn't match CommonName in x509 CSR (%q)", nodeID.Name, x509cr.Subject.CommonName)
return false
}
if fmt.Sprint(nodeID.ProjectName) != opts.projectID {
Expand Down Expand Up @@ -520,22 +538,6 @@ func validateTPMAttestation(opts approverOptions, csr *capi.CertificateSigningRe
}
}

// TODO(awly): get AIK public key from GCE API.
aikCert, err := x509.ParseCertificate(attestCert)
if err != nil {
glog.Errorf("Parsing ATTESTATION_CERTIFICATE: %v", err)
return false
}
if err := opts.tpmCACache.verify(aikCert); err != nil {
glog.Errorf("Verifying EK certificate validity: %v", err)
return false
}
aikPub, ok := aikCert.PublicKey.(*rsa.PublicKey)
if !ok {
glog.Errorf("Public key in ATTESTATION CERTIFICATE is %T, want *rsa.PublicKey", aikCert.PublicKey)
return false
}

attestHash := sha256.Sum256(attestDataRaw)
if err := rsa.VerifyPKCS1v15(aikPub, crypto.SHA256, attestHash[:], attestSig); err != nil {
glog.Errorf("Verifying certification signature with AIK public key: %v", err)
Expand Down
25 changes: 23 additions & 2 deletions pkg/nodeidentity/id.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,25 @@
package nodeidentity

import (
"crypto/x509"
"encoding/asn1"
"fmt"
"strconv"

"cloud.google.com/go/compute/metadata"
)

var cloudComputeInstanceIdentifierOID = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 1, 21}

// Identity uniquely identifies a GCE VM.
//
// Note: field order matters, this struct is used to unmarshal an asn1 object.
type Identity struct {
ProjectID uint64 `json:"project_id"`
ProjectName string `json:"project_name"`
Zone string `json:"zone"`
ID uint64 `json:"id"`
Name string `json:"name"`
ProjectID uint64 `json:"project_id"`
ProjectName string `json:"project_name"`
}

// FromMetadata builds VM Identity from GCE Metadata using default client.
Expand Down Expand Up @@ -52,3 +59,17 @@ func FromMetadata() (Identity, error) {

return id, nil
}

// FromAIKCert extracts VM Identity from cloudComputeInstanceIdentifier
// extension in cert.
func FromAIKCert(cert *x509.Certificate) (Identity, error) {
var id Identity
for _, ext := range cert.Extensions {
if !ext.Id.Equal(cloudComputeInstanceIdentifierOID) {
continue
}
_, err := asn1.Unmarshal(ext.Value, &id)
return id, err
}
return id, fmt.Errorf("certificate does not have cloudComputeInstanceIdentifier extension (OID %s)", cloudComputeInstanceIdentifierOID)
}

0 comments on commit 231f082

Please sign in to comment.