Unverified Commit ad5e504c authored by Brad Davidson's avatar Brad Davidson Committed by GitHub

Allow joining clusters when the server CA is trusted by the OS CA bundle (#2743)

* Add tests to clientaccess/token * Fix issues in clientaccess/token identified by tests * Update tests to close coverage gaps * Remove redundant check turned up by code coverage reports * Add warnings if CA hash will not be validated Signed-off-by: 's avatarBrad Davidson <brad.davidson@rancher.com>
parent 6c472b59
......@@ -13,10 +13,11 @@ import (
"time"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
var (
defaultClientTimeout = 20 * time.Second
defaultClientTimeout = 10 * time.Second
defaultClient = &http.Client{
Timeout: defaultClientTimeout,
......@@ -32,8 +33,9 @@ var (
)
const (
tokenPrefix = "K10"
tokenFormat = "%s%s::%s:%s"
tokenPrefix = "K10"
tokenFormat = "%s%s::%s:%s"
caHashLength = sha256.Size * 2
)
type OverrideURLCallback func(config []byte) (*url.URL, error)
......@@ -59,16 +61,10 @@ func ParseAndValidateToken(server string, token string) (*Info, error) {
return nil, err
}
if err := info.setServer(server); err != nil {
if err := info.setAndValidateServer(server); err != nil {
return nil, err
}
if info.caHash != "" {
if err := info.validateCAHash(); err != nil {
return nil, err
}
}
return info, nil
}
......@@ -82,26 +78,24 @@ func ParseAndValidateTokenForUser(server string, token string, username string)
info.Username = username
if err := info.setServer(server); err != nil {
if err := info.setAndValidateServer(server); err != nil {
return nil, err
}
if info.caHash != "" {
if err := info.validateCAHash(); err != nil {
return nil, err
}
}
return info, nil
}
// setAndValidateServer updates the remote server's cert info, and validates it against the provided hash
func (info *Info) setAndValidateServer(server string) error {
if err := info.setServer(server); err != nil {
return err
}
return info.validateCAHash()
}
// validateCACerts returns a boolean indicating whether or not a CA bundle matches the provided hash,
// and a string containing the hash of the CA bundle.
func validateCACerts(cacerts []byte, hash string) (bool, string) {
if len(cacerts) == 0 && hash == "" {
return true, ""
}
newHash := hashCA(cacerts)
return hash == newHash, newHash
}
......@@ -126,6 +120,10 @@ func ParseUsernamePassword(token string) (string, string, bool) {
func parseToken(token string) (*Info, error) {
var info = &Info{}
if len(token) == 0 {
return nil, errors.New("token must not be empty")
}
if !strings.HasPrefix(token, tokenPrefix) {
token = fmt.Sprintf(tokenFormat, tokenPrefix, "", "", token)
}
......@@ -136,13 +134,17 @@ func parseToken(token string) (*Info, error) {
parts := strings.SplitN(token, "::", 2)
token = parts[0]
if len(parts) > 1 {
hashLen := len(parts[0])
if hashLen > 0 && hashLen != caHashLength {
return nil, errors.New("invalid token CA hash length")
}
info.caHash = parts[0]
token = parts[1]
}
parts = strings.SplitN(token, ":", 2)
if len(parts) != 2 {
return nil, fmt.Errorf("invalid token format")
if len(parts) != 2 || len(parts[1]) == 0 {
return nil, errors.New("invalid token format")
}
info.Username = parts[0]
......@@ -212,10 +214,20 @@ func (info *Info) setServer(server string) error {
// ValidateCAHash validates that info's caHash matches the CACerts hash.
func (info *Info) validateCAHash() error {
if ok, serverHash := validateCACerts(info.CACerts, info.caHash); !ok {
return fmt.Errorf("token CA hash does not match the server CA hash: %s != %s", info.caHash, serverHash)
if len(info.caHash) > 0 && len(info.CACerts) == 0 {
// Warn if the user provided a CA hash but we're not going to validate because it's already trusted
logrus.Warn("Cluster CA certificate is trusted by the host CA bundle. " +
"Token CA hash will not be validated.")
} else if len(info.caHash) == 0 && len(info.CACerts) > 0 {
// Warn if the CA is self-signed but the user didn't provide a hash to validate it against
logrus.Warn("Cluster CA certificate is not trusted by the host CA bundle, but the token does not include a CA hash. " +
"Use the full token from the server's node-token file to enable Cluster CA validation.")
} else if len(info.CACerts) > 0 && len(info.caHash) > 0 {
// only verify CA hash if the server cert is not trusted by the OS CA bundle
if ok, serverHash := validateCACerts(info.CACerts, info.caHash); !ok {
return fmt.Errorf("token CA hash does not match the Cluster CA certificate hash: %s != %s", info.caHash, serverHash)
}
}
return nil
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment