package controller import ( "crypto/x509" "net" "strings" "unicode/utf8" ) // Please check https://github.com/golang/go/issues/22922 // // Since Go 1.9 the common name field is not used anymore. // We copy the code to not break existing clusters that doesn't have certificates with SAN yet // TODO: Remove this helpers in the future. // verifyHostname returns nil if c is a valid certificate for the named host. // Otherwise it returns an error describing the mismatch. func verifyHostname(h string, c *x509.Certificate) error { // IP addresses may be written in [ ]. candidateIP := h if len(h) >= 3 && h[0] == '[' && h[len(h)-1] == ']' { candidateIP = h[1 : len(h)-1] } if ip := net.ParseIP(candidateIP); ip != nil { // We only match IP addresses against IP SANs. // https://tools.ietf.org/html/rfc6125#appendix-B.2 for _, candidate := range c.IPAddresses { if ip.Equal(candidate) { return nil } } return x509.HostnameError{Certificate: c, Host: candidateIP} } lowered := toLowerCaseASCII(h) if len(c.DNSNames) > 0 { for _, match := range c.DNSNames { if matchHostnames(toLowerCaseASCII(match), lowered) { return nil } } // If Subject Alt Name is given, we ignore the common name. } else if matchHostnames(toLowerCaseASCII(c.Subject.CommonName), lowered) { return nil } return x509.HostnameError{Certificate: c, Host: h} } // toLowerCaseASCII returns a lower-case version of in. See RFC 6125 6.4.1. We use // an explicitly ASCII function to avoid any sharp corners resulting from // performing Unicode operations on DNS labels. func toLowerCaseASCII(in string) string { // If the string is already lower-case then there's nothing to do. isAlreadyLowerCase := true for _, c := range in { if c == utf8.RuneError { // If we get a UTF-8 error then there might be // upper-case ASCII bytes in the invalid sequence. isAlreadyLowerCase = false break } if 'A' <= c && c <= 'Z' { isAlreadyLowerCase = false break } } if isAlreadyLowerCase { return in } out := []byte(in) for i, c := range out { if 'A' <= c && c <= 'Z' { out[i] += 'a' - 'A' } } return string(out) } func matchHostnames(pattern, host string) bool { host = strings.TrimSuffix(host, ".") pattern = strings.TrimSuffix(pattern, ".") if len(pattern) == 0 || len(host) == 0 { return false } patternParts := strings.Split(pattern, ".") hostParts := strings.Split(host, ".") if len(patternParts) != len(hostParts) { return false } for i, patternPart := range patternParts { if i == 0 && patternPart == "*" { continue } if patternPart != hostParts[i] { return false } } return true }