diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index f46d09fe..200c2d49 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -348,11 +348,11 @@ }, { "ImportPath": "k8s.io/api", - "Rev": "37fc98278a4b" + "Rev": "40f4bbc2b486" }, { "ImportPath": "k8s.io/apimachinery", - "Rev": "d496f1036126" + "Rev": "3db02fd2eea7" }, { "ImportPath": "k8s.io/gengo", diff --git a/go.mod b/go.mod index d75e0ad6..61e6724b 100644 --- a/go.mod +++ b/go.mod @@ -28,8 +28,8 @@ require ( golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 google.golang.org/appengine v1.5.0 // indirect - k8s.io/api v0.0.0-20191114100032-37fc98278a4b - k8s.io/apimachinery v0.0.0-20191114095527-d496f1036126 + k8s.io/api v0.0.0-20191114100036-40f4bbc2b486 + k8s.io/apimachinery v0.0.0-20191114095528-3db02fd2eea7 k8s.io/klog v1.0.0 k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d sigs.k8s.io/yaml v1.1.0 @@ -38,6 +38,6 @@ require ( replace ( golang.org/x/sys => golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a golang.org/x/tools => golang.org/x/tools v0.0.0-20190821162956-65e3620a7ae7 - k8s.io/api => k8s.io/api v0.0.0-20191114100032-37fc98278a4b - k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20191114095527-d496f1036126 + k8s.io/api => k8s.io/api v0.0.0-20191114100036-40f4bbc2b486 + k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20191114095528-3db02fd2eea7 ) diff --git a/go.sum b/go.sum index 8c0034f1..0ebd9da1 100644 --- a/go.sum +++ b/go.sum @@ -191,8 +191,8 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.0.0-20191114100032-37fc98278a4b/go.mod h1:8svLRMiLwQReMTycutfjsaQ0ackWIf8HCT4UcixYLjI= -k8s.io/apimachinery v0.0.0-20191114095527-d496f1036126/go.mod h1:+6CX7hP4aLfX2sb91JYDMIp0VqDSog2kZu0BHe+lP+s= +k8s.io/api v0.0.0-20191114100036-40f4bbc2b486/go.mod h1:IM5ceavki8HjRhUlaRYP3oGw0J/hbXKiKiSqS5AR728= +k8s.io/apimachinery v0.0.0-20191114095528-3db02fd2eea7/go.mod h1:+6CX7hP4aLfX2sb91JYDMIp0VqDSog2kZu0BHe+lP+s= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= diff --git a/util/cert/server_inspection.go b/util/cert/server_inspection.go new file mode 100644 index 00000000..0fef8811 --- /dev/null +++ b/util/cert/server_inspection.go @@ -0,0 +1,97 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cert + +import ( + "crypto/tls" + "crypto/x509" + "net/url" + "strings" +) + +// GetClientCANames gets the CA names for client certs that a server accepts. This is useful when inspecting the +// state of particular servers. apiHost is "host:port" +func GetClientCANames(apiHost string) ([]string, error) { + // when we run this the second time, we know which one we are expecting + acceptableCAs := []string{} + tlsConfig := &tls.Config{ + InsecureSkipVerify: true, // this is insecure to always get to the GetClientCertificate + GetClientCertificate: func(hello *tls.CertificateRequestInfo) (*tls.Certificate, error) { + acceptableCAs = []string{} + for _, curr := range hello.AcceptableCAs { + acceptableCAs = append(acceptableCAs, string(curr)) + } + return &tls.Certificate{}, nil + }, + } + + conn, err := tls.Dial("tcp", apiHost, tlsConfig) + if err != nil { + return nil, err + } + defer conn.Close() + + return acceptableCAs, nil +} + +// GetClientCANamesForURL is GetClientCANames against a URL string like we use in kubeconfigs +func GetClientCANamesForURL(kubeConfigURL string) ([]string, error) { + apiserverURL, err := url.Parse(kubeConfigURL) + if err != nil { + return nil, err + } + return GetClientCANames(apiserverURL.Host) +} + +// GetServingCertificates returns the x509 certs used by a server as certificates and pem encoded bytes. +// The serverName is optional for specifying a different name to get SNI certificates. apiHost is "host:port" +func GetServingCertificates(apiHost, serverName string) ([]*x509.Certificate, [][]byte, error) { + tlsConfig := &tls.Config{ + InsecureSkipVerify: true, // this is insecure so that we always get connected + } + // if a name is specified for SNI, set it. + if len(serverName) > 0 { + tlsConfig.ServerName = serverName + } + + conn, err := tls.Dial("tcp", apiHost, tlsConfig) + if err != nil { + return nil, nil, err + } + conn.Close() + + peerCerts := conn.ConnectionState().PeerCertificates + peerCertBytes := [][]byte{} + for _, a := range peerCerts { + actualCert, err := EncodeCertificates(a) + if err != nil { + return nil, nil, err + } + peerCertBytes = append(peerCertBytes, []byte(strings.TrimSpace(string(actualCert)))) + } + + return peerCerts, peerCertBytes, err +} + +// GetServingCertificatesForURL is GetServingCertificates against a URL string like we use in kubeconfigs +func GetServingCertificatesForURL(kubeConfigURL, serverName string) ([]*x509.Certificate, [][]byte, error) { + apiserverURL, err := url.Parse(kubeConfigURL) + if err != nil { + return nil, nil, err + } + return GetServingCertificates(apiserverURL.Host, serverName) +}