package admin import ( "errors" "fmt" "io" "io/ioutil" "os" "path/filepath" "github.com/golang/glog" "github.com/spf13/cobra" kapi "k8s.io/kubernetes/pkg/api" kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" kcrypto "k8s.io/kubernetes/pkg/util/crypto" cliconfig "github.com/openshift/origin/pkg/cmd/cli/config" "github.com/openshift/origin/pkg/cmd/server/crypto" "github.com/openshift/origin/pkg/cmd/templates" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" ) const CreateKubeConfigCommandName = "create-kubeconfig" var createKubeConfigLongDesc = templates.LongDesc(` Create's a .kubeconfig file at <--kubeconfig> that looks like this: clusters: - cluster: certificate-authority-data: server: <--master> name: <--cluster> - cluster: certificate-authority-data: server: <--public-master> name: public-<--cluster> contexts: - context: cluster: <--cluster> user: <--user> namespace: <--namespace> name: <--context> - context: cluster: public-<--cluster> user: <--user> namespace: <--namespace> name: public-<--context> current-context: <--context> kind: Config users: - name: <--user> user: client-certificate-data: client-key-data: `) type CreateKubeConfigOptions struct { APIServerURL string PublicAPIServerURL string APIServerCAFiles []string CertFile string KeyFile string ContextNamespace string KubeConfigFile string Output io.Writer } func NewCommandCreateKubeConfig(commandName string, fullName string, out io.Writer) *cobra.Command { options := &CreateKubeConfigOptions{Output: out} cmd := &cobra.Command{ Use: commandName, Short: "Create a basic .kubeconfig file from client certs", Long: createKubeConfigLongDesc, Run: func(cmd *cobra.Command, args []string) { if err := options.Validate(args); err != nil { kcmdutil.CheckErr(kcmdutil.UsageError(cmd, err.Error())) } if _, err := options.CreateKubeConfig(); err != nil { kcmdutil.CheckErr(err) } }, } flags := cmd.Flags() flags.StringVar(&options.APIServerURL, "master", "https://localhost:8443", "The API server's URL.") flags.StringVar(&options.PublicAPIServerURL, "public-master", "", "The API public facing server's URL (if applicable).") flags.StringSliceVar(&options.APIServerCAFiles, "certificate-authority", []string{"openshift.local.config/master/ca.crt"}, "Files containing signing authorities to use to verify the API server's serving certificate.") flags.StringVar(&options.CertFile, "client-certificate", "", "The client cert file.") flags.StringVar(&options.KeyFile, "client-key", "", "The client key file.") flags.StringVar(&options.ContextNamespace, "namespace", kapi.NamespaceDefault, "Namespace for this context in .kubeconfig.") flags.StringVar(&options.KubeConfigFile, "kubeconfig", ".kubeconfig", "Path for the resulting .kubeconfig file.") // autocompletion hints cmd.MarkFlagFilename("certificate-authority") cmd.MarkFlagFilename("client-certificate") cmd.MarkFlagFilename("client-key") cmd.MarkFlagFilename("kubeconfig") return cmd } func (o CreateKubeConfigOptions) Validate(args []string) error { if len(args) != 0 { return errors.New("no arguments are supported") } if len(o.KubeConfigFile) == 0 { return errors.New("kubeconfig must be provided") } if len(o.CertFile) == 0 { return errors.New("client-certificate must be provided") } if len(o.KeyFile) == 0 { return errors.New("client-key must be provided") } if len(o.APIServerCAFiles) == 0 { return errors.New("certificate-authority must be provided") } else { for _, caFile := range o.APIServerCAFiles { if _, err := kcrypto.CertPoolFromFile(caFile); err != nil { return fmt.Errorf("certificate-authority must be a valid certificate file: %v", err) } } } if len(o.ContextNamespace) == 0 { return errors.New("namespace must be provided") } if len(o.APIServerURL) == 0 { return errors.New("master must be provided") } return nil } func (o CreateKubeConfigOptions) CreateKubeConfig() (*clientcmdapi.Config, error) { glog.V(4).Infof("creating a .kubeconfig with: %#v", o) // read all the referenced filenames caData, err := readFiles(o.APIServerCAFiles, []byte("\n")) if err != nil { return nil, err } certData, err := ioutil.ReadFile(o.CertFile) if err != nil { return nil, err } keyData, err := ioutil.ReadFile(o.KeyFile) if err != nil { return nil, err } certConfig, err := crypto.GetTLSCertificateConfig(o.CertFile, o.KeyFile) if err != nil { return nil, err } // determine all the nicknames clusterNick, err := cliconfig.GetClusterNicknameFromURL(o.APIServerURL) if err != nil { return nil, err } userNick, err := cliconfig.GetUserNicknameFromCert(clusterNick, certConfig.Certs...) if err != nil { return nil, err } contextNick := cliconfig.GetContextNickname(o.ContextNamespace, clusterNick, userNick) credentials := make(map[string]*clientcmdapi.AuthInfo) credentials[userNick] = &clientcmdapi.AuthInfo{ ClientCertificateData: certData, ClientKeyData: keyData, } clusters := make(map[string]*clientcmdapi.Cluster) clusters[clusterNick] = &clientcmdapi.Cluster{ Server: o.APIServerURL, CertificateAuthorityData: caData, } contexts := make(map[string]*clientcmdapi.Context) contexts[contextNick] = &clientcmdapi.Context{Cluster: clusterNick, AuthInfo: userNick, Namespace: o.ContextNamespace} createPublic := (len(o.PublicAPIServerURL) > 0) && o.APIServerURL != o.PublicAPIServerURL if createPublic { publicClusterNick, err := cliconfig.GetClusterNicknameFromURL(o.PublicAPIServerURL) if err != nil { return nil, err } publicContextNick := cliconfig.GetContextNickname(o.ContextNamespace, publicClusterNick, userNick) clusters[publicClusterNick] = &clientcmdapi.Cluster{ Server: o.PublicAPIServerURL, CertificateAuthorityData: caData, } contexts[publicContextNick] = &clientcmdapi.Context{Cluster: publicClusterNick, AuthInfo: userNick, Namespace: o.ContextNamespace} } kubeConfig := &clientcmdapi.Config{ Clusters: clusters, AuthInfos: credentials, Contexts: contexts, CurrentContext: contextNick, } glog.V(3).Infof("Generating '%s' API client config as %s\n", userNick, o.KubeConfigFile) // Ensure the parent dir exists if err := os.MkdirAll(filepath.Dir(o.KubeConfigFile), os.FileMode(0755)); err != nil { return nil, err } if err := clientcmd.WriteToFile(*kubeConfig, o.KubeConfigFile); err != nil { return nil, err } return kubeConfig, nil }