|
|
@ -17,15 +17,27 @@ limitations under the License.
|
|
|
|
package tiller
|
|
|
|
package tiller
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
import (
|
|
|
|
|
|
|
|
"crypto/x509"
|
|
|
|
|
|
|
|
"encoding/base64"
|
|
|
|
|
|
|
|
"encoding/pem"
|
|
|
|
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"log"
|
|
|
|
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/spf13/pflag"
|
|
|
|
"golang.org/x/net/context"
|
|
|
|
"golang.org/x/net/context"
|
|
|
|
"google.golang.org/grpc"
|
|
|
|
"google.golang.org/grpc"
|
|
|
|
"google.golang.org/grpc/metadata"
|
|
|
|
"google.golang.org/grpc/metadata"
|
|
|
|
|
|
|
|
"k8s.io/helm/pkg/helm"
|
|
|
|
"k8s.io/helm/pkg/version"
|
|
|
|
"k8s.io/helm/pkg/version"
|
|
|
|
|
|
|
|
authenticationapi "k8s.io/kubernetes/pkg/apis/authentication"
|
|
|
|
|
|
|
|
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
|
|
|
|
|
|
|
rest "k8s.io/kubernetes/pkg/client/restclient"
|
|
|
|
|
|
|
|
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
|
|
|
|
|
|
|
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
|
|
|
|
|
|
|
utilflag "k8s.io/kubernetes/pkg/util/flag"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// maxMsgSize use 10MB as the default message size limit.
|
|
|
|
// maxMsgSize use 10MB as the default message size limit.
|
|
|
@ -41,29 +53,97 @@ func NewServer() *grpc.Server {
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func authenticate(ctx context.Context) (context.Context, error) {
|
|
|
|
|
|
|
|
md, ok := metadata.FromContext(ctx)
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
|
|
return nil, errors.New("Missing metadata in context.")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var user *authenticationapi.UserInfo
|
|
|
|
|
|
|
|
var kubeConfig *rest.Config
|
|
|
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
authHeader, ok := md[helm.Authorization]
|
|
|
|
|
|
|
|
if !ok || authHeader[0] == "" {
|
|
|
|
|
|
|
|
user, kubeConfig, err = checkClientCert(ctx)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if strings.HasPrefix(authHeader[0], "Bearer ") {
|
|
|
|
|
|
|
|
user, kubeConfig, err = checkBearerAuth(ctx)
|
|
|
|
|
|
|
|
} else if strings.HasPrefix(authHeader[0], "Basic ") {
|
|
|
|
|
|
|
|
user, kubeConfig, err = checkBasicAuth(ctx)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return nil, errors.New("Unknown authorization scheme.")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx = context.WithValue(ctx, helm.K8sUser, user)
|
|
|
|
|
|
|
|
ctx = context.WithValue(ctx, helm.K8sConfig, kubeConfig)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: Remove
|
|
|
|
|
|
|
|
if user == nil {
|
|
|
|
|
|
|
|
log.Println("user not found in context")
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
log.Println("authenticated user:", user)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return ctx, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func newUnaryInterceptor() grpc.UnaryServerInterceptor {
|
|
|
|
func newUnaryInterceptor() grpc.UnaryServerInterceptor {
|
|
|
|
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
|
|
|
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
|
|
|
if err := checkClientVersion(ctx); err != nil {
|
|
|
|
err = checkClientVersion(ctx)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
// whitelist GetVersion() from the version check
|
|
|
|
// whitelist GetVersion() from the version check
|
|
|
|
if _, m := splitMethod(info.FullMethod); m != "GetVersion" {
|
|
|
|
if _, m := splitMethod(info.FullMethod); m != "GetVersion" {
|
|
|
|
log.Println(err)
|
|
|
|
log.Println(err)
|
|
|
|
return nil, err
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx, err = authenticate(ctx)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
log.Println(err)
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
return handler(ctx, req)
|
|
|
|
return handler(ctx, req)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func newStreamInterceptor() grpc.StreamServerInterceptor {
|
|
|
|
func newStreamInterceptor() grpc.StreamServerInterceptor {
|
|
|
|
return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
|
|
|
return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
|
|
|
if err := checkClientVersion(ss.Context()); err != nil {
|
|
|
|
ctx := ss.Context()
|
|
|
|
|
|
|
|
err := checkClientVersion(ctx)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
log.Println(err)
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx, err = authenticate(ctx)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
log.Println(err)
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return handler(srv, ss)
|
|
|
|
|
|
|
|
|
|
|
|
newStream := serverStreamWrapper{
|
|
|
|
|
|
|
|
ss: ss,
|
|
|
|
|
|
|
|
ctx: ctx,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return handler(srv, newStream)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// serverStreamWrapper wraps original ServerStream but uses modified context.
|
|
|
|
|
|
|
|
// this modified context will be available inside handler()
|
|
|
|
|
|
|
|
type serverStreamWrapper struct {
|
|
|
|
|
|
|
|
ss grpc.ServerStream
|
|
|
|
|
|
|
|
ctx context.Context
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (w serverStreamWrapper) Context() context.Context { return w.ctx }
|
|
|
|
|
|
|
|
func (w serverStreamWrapper) RecvMsg(msg interface{}) error { return w.ss.RecvMsg(msg) }
|
|
|
|
|
|
|
|
func (w serverStreamWrapper) SendMsg(msg interface{}) error { return w.ss.SendMsg(msg) }
|
|
|
|
|
|
|
|
func (w serverStreamWrapper) SendHeader(md metadata.MD) error { return w.ss.SendHeader(md) }
|
|
|
|
|
|
|
|
func (w serverStreamWrapper) SetHeader(md metadata.MD) error { return w.ss.SetHeader(md) }
|
|
|
|
|
|
|
|
func (w serverStreamWrapper) SetTrailer(md metadata.MD) { w.ss.SetTrailer(md) }
|
|
|
|
|
|
|
|
|
|
|
|
func splitMethod(fullMethod string) (string, string) {
|
|
|
|
func splitMethod(fullMethod string) (string, string) {
|
|
|
|
if frags := strings.Split(fullMethod, "/"); len(frags) == 3 {
|
|
|
|
if frags := strings.Split(fullMethod, "/"); len(frags) == 3 {
|
|
|
|
return frags[1], frags[2]
|
|
|
|
return frags[1], frags[2]
|
|
|
@ -87,3 +167,213 @@ func checkClientVersion(ctx context.Context) error {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func checkBearerAuth(ctx context.Context) (*authenticationapi.UserInfo, *rest.Config, error) {
|
|
|
|
|
|
|
|
md, _ := metadata.FromContext(ctx)
|
|
|
|
|
|
|
|
token := md[helm.Authorization][0][len("Bearer "):]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apiServer, err := getServerURL(md)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
caCert, _ := getCertificateAuthority(md)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ref: k8s.io/helm/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/util#NewFactory()
|
|
|
|
|
|
|
|
flags := pflag.NewFlagSet("", pflag.ContinueOnError)
|
|
|
|
|
|
|
|
flags.SetNormalizeFunc(utilflag.WarnWordSepNormalizeFunc) // Warn for "_" flags
|
|
|
|
|
|
|
|
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
|
|
|
|
|
|
|
|
// use the standard defaults for this client command
|
|
|
|
|
|
|
|
// DEPRECATED: remove and replace with something more accurate
|
|
|
|
|
|
|
|
loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
flags.StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
overrides := &clientcmd.ConfigOverrides{
|
|
|
|
|
|
|
|
ClusterDefaults: clientcmd.ClusterDefaults,
|
|
|
|
|
|
|
|
ClusterInfo: clientcmdapi.Cluster{
|
|
|
|
|
|
|
|
Server: apiServer,
|
|
|
|
|
|
|
|
CertificateAuthorityData: caCert,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
flagNames := clientcmd.RecommendedConfigOverrideFlags("")
|
|
|
|
|
|
|
|
// short flagnames are disabled by default. These are here for compatibility with existing scripts
|
|
|
|
|
|
|
|
flagNames.ClusterOverrideFlags.APIServer.ShortName = "s"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
clientcmd.BindOverrideFlags(overrides, flags, flagNames)
|
|
|
|
|
|
|
|
tokenConfig, err := clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, os.Stdin).ClientConfig()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
client, err := clientset.NewForConfig(tokenConfig)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// verify token
|
|
|
|
|
|
|
|
tokenReq := &authenticationapi.TokenReview{
|
|
|
|
|
|
|
|
Spec: authenticationapi.TokenReviewSpec{
|
|
|
|
|
|
|
|
Token: token,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
result, err := client.AuthenticationClient.TokenReviews().Create(tokenReq)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if !result.Status.Authenticated {
|
|
|
|
|
|
|
|
return nil, nil, errors.New("Not authenticated")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
kubeConfig := &rest.Config{
|
|
|
|
|
|
|
|
Host: apiServer,
|
|
|
|
|
|
|
|
BearerToken: token,
|
|
|
|
|
|
|
|
TLSClientConfig: rest.TLSClientConfig{
|
|
|
|
|
|
|
|
CAData: caCert,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return &result.Status.User, kubeConfig, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func checkBasicAuth(ctx context.Context) (*authenticationapi.UserInfo, *rest.Config, error) {
|
|
|
|
|
|
|
|
md, _ := metadata.FromContext(ctx)
|
|
|
|
|
|
|
|
authz := md[helm.Authorization][0]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apiServer, err := getServerURL(md)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
basicAuth, err := base64.StdEncoding.DecodeString(authz[len("Basic "):])
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
username, password := getUserPasswordFromBasicAuth(string(basicAuth))
|
|
|
|
|
|
|
|
if len(username) == 0 || len(password) == 0 {
|
|
|
|
|
|
|
|
return nil, nil, errors.New("Missing username or password.")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
kubeConfig := &rest.Config{
|
|
|
|
|
|
|
|
Host: apiServer,
|
|
|
|
|
|
|
|
Username: username,
|
|
|
|
|
|
|
|
Password: password,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
caCert, err := getCertificateAuthority(md)
|
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
|
|
kubeConfig.TLSClientConfig = rest.TLSClientConfig{
|
|
|
|
|
|
|
|
CAData: caCert,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
client, err := clientset.NewForConfig(kubeConfig)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// verify credentials
|
|
|
|
|
|
|
|
_, err = client.DiscoveryClient.ServerVersion()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return &authenticationapi.UserInfo{
|
|
|
|
|
|
|
|
Username: username,
|
|
|
|
|
|
|
|
}, kubeConfig, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func getUserPasswordFromBasicAuth(token string) (string, string) {
|
|
|
|
|
|
|
|
st := strings.SplitN(token, ":", 2)
|
|
|
|
|
|
|
|
if len(st) == 2 {
|
|
|
|
|
|
|
|
return st[0], st[1]
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", ""
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func checkClientCert(ctx context.Context) (*authenticationapi.UserInfo, *rest.Config, error) {
|
|
|
|
|
|
|
|
md, _ := metadata.FromContext(ctx)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
apiServer, err := getServerURL(md)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
kubeConfig := &rest.Config{
|
|
|
|
|
|
|
|
Host: apiServer,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
crt, err := getClientCert(md)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
key, err := getClientKey(md)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
kubeConfig.TLSClientConfig = rest.TLSClientConfig{
|
|
|
|
|
|
|
|
KeyData: key,
|
|
|
|
|
|
|
|
CertData: crt,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
caCert, err := getCertificateAuthority(md)
|
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
|
|
kubeConfig.TLSClientConfig.CAData = caCert
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
client, err := clientset.NewForConfig(kubeConfig)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// verify credentials
|
|
|
|
|
|
|
|
_, err = client.DiscoveryClient.ServerVersion()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pem, _ := pem.Decode([]byte(crt))
|
|
|
|
|
|
|
|
c, err := x509.ParseCertificate(pem.Bytes)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return &authenticationapi.UserInfo{
|
|
|
|
|
|
|
|
Username: c.Subject.CommonName,
|
|
|
|
|
|
|
|
}, kubeConfig, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func getClientCert(md metadata.MD) ([]byte, error) {
|
|
|
|
|
|
|
|
cert, ok := md[helm.K8sClientCertificate]
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
|
|
return nil, errors.New("Client certificate not found")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
certData, err := base64.StdEncoding.DecodeString(cert[0])
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return certData, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func getClientKey(md metadata.MD) ([]byte, error) {
|
|
|
|
|
|
|
|
key, ok := md[helm.K8sClientKey]
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
|
|
return nil, errors.New("Client key not found")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
keyData, err := base64.StdEncoding.DecodeString(key[0])
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return keyData, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func getCertificateAuthority(md metadata.MD) ([]byte, error) {
|
|
|
|
|
|
|
|
caData, ok := md[helm.K8sCertificateAuthority]
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
|
|
return nil, errors.New("CAcert not found")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
caCert, err := base64.StdEncoding.DecodeString(caData[0])
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return caCert, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func getServerURL(md metadata.MD) (string, error) {
|
|
|
|
|
|
|
|
apiserver, ok := md[helm.K8sServer]
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
|
|
return "", errors.New("API server url not found")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return apiserver[0], nil
|
|
|
|
|
|
|
|
}
|
|
|
|