// Copied from https://github.com/kubernetes/kubernetes/blob/master/pkg/apis/certificates/helpers.go // to avoid importing k8s.io/kubernetes package approver import ( "crypto/x509" "encoding/pem" "errors" "fmt" "reflect" "strings" "k8s.io/apimachinery/pkg/util/sets" ) // ParseCSR extracts the CSR from the bytes and decodes it. func ParseCSR(pemBytes []byte) (*x509.CertificateRequest, error) { block, _ := pem.Decode(pemBytes) if block == nil || block.Type != "CERTIFICATE REQUEST" { return nil, errors.New("PEM block type must be CERTIFICATE REQUEST") } csr, err := x509.ParseCertificateRequest(block.Bytes) if err != nil { return nil, err } return csr, nil } var ( organizationNotSystemNodesErr = fmt.Errorf("subject organization is not system:nodes") commonNameNotSystemNode = fmt.Errorf("subject common name does not begin with system:node:") dnsOrIPSANRequiredErr = fmt.Errorf("DNS or IP subjectAltName is required") emailSANNotAllowedErr = fmt.Errorf("Email subjectAltNames are not allowed") uriSANNotAllowedErr = fmt.Errorf("URI subjectAltNames are not allowed") ) type KeyUsage string const ( UsageDigitalSignature KeyUsage = "digital signature" UsageKeyEncipherment KeyUsage = "key encipherment" UsageServerAuth KeyUsage = "server auth" ) var kubeletServingRequiredUsages = sets.NewString( string(UsageDigitalSignature), string(UsageKeyEncipherment), string(UsageServerAuth), ) func IsKubeletServingCSR(req *x509.CertificateRequest, usages sets.String) bool { return ValidateKubeletServingCSR(req, usages) == nil } func ValidateKubeletServingCSR(req *x509.CertificateRequest, usages sets.String) error { if !reflect.DeepEqual([]string{"system:nodes"}, req.Subject.Organization) { return organizationNotSystemNodesErr } // at least one of dnsNames or ipAddresses must be specified if len(req.DNSNames) == 0 && len(req.IPAddresses) == 0 { return dnsOrIPSANRequiredErr } if len(req.EmailAddresses) > 0 { return emailSANNotAllowedErr } if len(req.URIs) > 0 { return uriSANNotAllowedErr } if !kubeletServingRequiredUsages.Equal(usages) { return fmt.Errorf("usages did not match %v", kubeletServingRequiredUsages.List()) } if !strings.HasPrefix(req.Subject.CommonName, "system:node:") { return commonNameNotSystemNode } return nil }