109 lines
2.6 KiB
Go
109 lines
2.6 KiB
Go
![]() |
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
|
||
|
}
|