create_authinfo.go 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. /*
  2. Copyright 2014 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package config
  14. import (
  15. "errors"
  16. "fmt"
  17. "io"
  18. "io/ioutil"
  19. "path/filepath"
  20. "strings"
  21. "github.com/renstrom/dedent"
  22. "github.com/spf13/cobra"
  23. "k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
  24. clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
  25. cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
  26. "k8s.io/kubernetes/pkg/util"
  27. "k8s.io/kubernetes/pkg/util/flag"
  28. )
  29. type createAuthInfoOptions struct {
  30. configAccess clientcmd.ConfigAccess
  31. name string
  32. authPath util.StringFlag
  33. clientCertificate util.StringFlag
  34. clientKey util.StringFlag
  35. token util.StringFlag
  36. username util.StringFlag
  37. password util.StringFlag
  38. embedCertData flag.Tristate
  39. authProvider util.StringFlag
  40. authProviderArgs map[string]string
  41. authProviderArgsToRemove []string
  42. }
  43. const (
  44. flagAuthProvider = "auth-provider"
  45. flagAuthProviderArg = "auth-provider-arg"
  46. )
  47. var (
  48. create_authinfo_long = fmt.Sprintf(`
  49. Sets a user entry in kubeconfig
  50. Specifying a name that already exists will merge new fields on top of existing values.
  51. Client-certificate flags:
  52. --%v=certfile --%v=keyfile
  53. Bearer token flags:
  54. --%v=bearer_token
  55. Basic auth flags:
  56. --%v=basic_user --%v=basic_password
  57. Bearer token and basic auth are mutually exclusive.
  58. `, clientcmd.FlagCertFile, clientcmd.FlagKeyFile, clientcmd.FlagBearerToken, clientcmd.FlagUsername, clientcmd.FlagPassword)
  59. create_authinfo_example = dedent.Dedent(`
  60. # Set only the "client-key" field on the "cluster-admin"
  61. # entry, without touching other values:
  62. kubectl config set-credentials cluster-admin --client-key=~/.kube/admin.key
  63. # Set basic auth for the "cluster-admin" entry
  64. kubectl config set-credentials cluster-admin --username=admin --password=uXFGweU9l35qcif
  65. # Embed client certificate data in the "cluster-admin" entry
  66. kubectl config set-credentials cluster-admin --client-certificate=~/.kube/admin.crt --embed-certs=true
  67. # Enable the Google Compute Platform auth provider for the "cluster-admin" entry
  68. kubectl config set-credentials cluster-admin --auth-provider=gcp
  69. # Enable the OpenID Connect auth provider for the "cluster-admin" entry with additional args
  70. kubectl config set-credentials cluster-admin --auth-provider=oidc --auth-provider-arg=client-id=foo --auth-provider-arg=client-secret=bar
  71. # Remove the "client-secret" config value for the OpenID Connect auth provider for the "cluster-admin" entry
  72. kubectl config set-credentials cluster-admin --auth-provider=oidc --auth-provider-arg=client-secret-`)
  73. )
  74. func NewCmdConfigSetAuthInfo(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
  75. options := &createAuthInfoOptions{configAccess: configAccess}
  76. return newCmdConfigSetAuthInfo(out, options)
  77. }
  78. func newCmdConfigSetAuthInfo(out io.Writer, options *createAuthInfoOptions) *cobra.Command {
  79. cmd := &cobra.Command{
  80. Use: fmt.Sprintf("set-credentials NAME [--%v=path/to/certfile] [--%v=path/to/keyfile] [--%v=bearer_token] [--%v=basic_user] [--%v=basic_password] [--%v=provider_name] [--%v=key=value]", clientcmd.FlagCertFile, clientcmd.FlagKeyFile, clientcmd.FlagBearerToken, clientcmd.FlagUsername, clientcmd.FlagPassword, flagAuthProvider, flagAuthProviderArg),
  81. Short: "Sets a user entry in kubeconfig",
  82. Long: create_authinfo_long,
  83. Example: create_authinfo_example,
  84. Run: func(cmd *cobra.Command, args []string) {
  85. if !options.complete(cmd, out) {
  86. cmd.Help()
  87. return
  88. }
  89. err := options.run()
  90. if err != nil {
  91. fmt.Fprintf(out, "%v\n", err)
  92. } else {
  93. fmt.Fprintf(out, "user %q set.\n", options.name)
  94. }
  95. },
  96. }
  97. cmd.Flags().Var(&options.clientCertificate, clientcmd.FlagCertFile, "path to "+clientcmd.FlagCertFile+" file for the user entry in kubeconfig")
  98. cmd.MarkFlagFilename(clientcmd.FlagCertFile)
  99. cmd.Flags().Var(&options.clientKey, clientcmd.FlagKeyFile, "path to "+clientcmd.FlagKeyFile+" file for the user entry in kubeconfig")
  100. cmd.MarkFlagFilename(clientcmd.FlagKeyFile)
  101. cmd.Flags().Var(&options.token, clientcmd.FlagBearerToken, clientcmd.FlagBearerToken+" for the user entry in kubeconfig")
  102. cmd.Flags().Var(&options.username, clientcmd.FlagUsername, clientcmd.FlagUsername+" for the user entry in kubeconfig")
  103. cmd.Flags().Var(&options.password, clientcmd.FlagPassword, clientcmd.FlagPassword+" for the user entry in kubeconfig")
  104. cmd.Flags().Var(&options.authProvider, flagAuthProvider, "auth provider for the user entry in kubeconfig")
  105. cmd.Flags().StringSlice(flagAuthProviderArg, nil, "'key=value' arugments for the auth provider")
  106. f := cmd.Flags().VarPF(&options.embedCertData, clientcmd.FlagEmbedCerts, "", "embed client cert/key for the user entry in kubeconfig")
  107. f.NoOptDefVal = "true"
  108. return cmd
  109. }
  110. func (o createAuthInfoOptions) run() error {
  111. err := o.validate()
  112. if err != nil {
  113. return err
  114. }
  115. config, err := o.configAccess.GetStartingConfig()
  116. if err != nil {
  117. return err
  118. }
  119. startingStanza, exists := config.AuthInfos[o.name]
  120. if !exists {
  121. startingStanza = clientcmdapi.NewAuthInfo()
  122. }
  123. authInfo := o.modifyAuthInfo(*startingStanza)
  124. config.AuthInfos[o.name] = &authInfo
  125. if err := clientcmd.ModifyConfig(o.configAccess, *config, true); err != nil {
  126. return err
  127. }
  128. return nil
  129. }
  130. // authInfo builds an AuthInfo object from the options
  131. func (o *createAuthInfoOptions) modifyAuthInfo(existingAuthInfo clientcmdapi.AuthInfo) clientcmdapi.AuthInfo {
  132. modifiedAuthInfo := existingAuthInfo
  133. var setToken, setBasic bool
  134. if o.clientCertificate.Provided() {
  135. certPath := o.clientCertificate.Value()
  136. if o.embedCertData.Value() {
  137. modifiedAuthInfo.ClientCertificateData, _ = ioutil.ReadFile(certPath)
  138. modifiedAuthInfo.ClientCertificate = ""
  139. } else {
  140. certPath, _ = filepath.Abs(certPath)
  141. modifiedAuthInfo.ClientCertificate = certPath
  142. if len(modifiedAuthInfo.ClientCertificate) > 0 {
  143. modifiedAuthInfo.ClientCertificateData = nil
  144. }
  145. }
  146. }
  147. if o.clientKey.Provided() {
  148. keyPath := o.clientKey.Value()
  149. if o.embedCertData.Value() {
  150. modifiedAuthInfo.ClientKeyData, _ = ioutil.ReadFile(keyPath)
  151. modifiedAuthInfo.ClientKey = ""
  152. } else {
  153. keyPath, _ = filepath.Abs(keyPath)
  154. modifiedAuthInfo.ClientKey = keyPath
  155. if len(modifiedAuthInfo.ClientKey) > 0 {
  156. modifiedAuthInfo.ClientKeyData = nil
  157. }
  158. }
  159. }
  160. if o.token.Provided() {
  161. modifiedAuthInfo.Token = o.token.Value()
  162. setToken = len(modifiedAuthInfo.Token) > 0
  163. }
  164. if o.username.Provided() {
  165. modifiedAuthInfo.Username = o.username.Value()
  166. setBasic = setBasic || len(modifiedAuthInfo.Username) > 0
  167. }
  168. if o.password.Provided() {
  169. modifiedAuthInfo.Password = o.password.Value()
  170. setBasic = setBasic || len(modifiedAuthInfo.Password) > 0
  171. }
  172. if o.authProvider.Provided() {
  173. newName := o.authProvider.Value()
  174. // Only overwrite if the existing auth-provider is nil, or different than the newly specified one.
  175. if modifiedAuthInfo.AuthProvider == nil || modifiedAuthInfo.AuthProvider.Name != newName {
  176. modifiedAuthInfo.AuthProvider = &clientcmdapi.AuthProviderConfig{
  177. Name: newName,
  178. }
  179. }
  180. }
  181. if modifiedAuthInfo.AuthProvider != nil {
  182. if modifiedAuthInfo.AuthProvider.Config == nil {
  183. modifiedAuthInfo.AuthProvider.Config = make(map[string]string)
  184. }
  185. for _, toRemove := range o.authProviderArgsToRemove {
  186. delete(modifiedAuthInfo.AuthProvider.Config, toRemove)
  187. }
  188. for key, value := range o.authProviderArgs {
  189. modifiedAuthInfo.AuthProvider.Config[key] = value
  190. }
  191. }
  192. // If any auth info was set, make sure any other existing auth types are cleared
  193. if setToken || setBasic {
  194. if !setToken {
  195. modifiedAuthInfo.Token = ""
  196. }
  197. if !setBasic {
  198. modifiedAuthInfo.Username = ""
  199. modifiedAuthInfo.Password = ""
  200. }
  201. }
  202. return modifiedAuthInfo
  203. }
  204. func (o *createAuthInfoOptions) complete(cmd *cobra.Command, out io.Writer) bool {
  205. args := cmd.Flags().Args()
  206. if len(args) != 1 {
  207. return false
  208. }
  209. authProviderArgs, err := cmd.Flags().GetStringSlice(flagAuthProviderArg)
  210. if err != nil {
  211. fmt.Fprintf(out, "Error: %s\n", err)
  212. return false
  213. }
  214. if len(authProviderArgs) > 0 {
  215. newPairs, removePairs, err := cmdutil.ParsePairs(authProviderArgs, flagAuthProviderArg, true)
  216. if err != nil {
  217. fmt.Fprintf(out, "Error: %s\n", err)
  218. return false
  219. }
  220. o.authProviderArgs = newPairs
  221. o.authProviderArgsToRemove = removePairs
  222. }
  223. o.name = args[0]
  224. return true
  225. }
  226. func (o createAuthInfoOptions) validate() error {
  227. if len(o.name) == 0 {
  228. return errors.New("you must specify a non-empty user name")
  229. }
  230. methods := []string{}
  231. if len(o.token.Value()) > 0 {
  232. methods = append(methods, fmt.Sprintf("--%v", clientcmd.FlagBearerToken))
  233. }
  234. if len(o.username.Value()) > 0 || len(o.password.Value()) > 0 {
  235. methods = append(methods, fmt.Sprintf("--%v/--%v", clientcmd.FlagUsername, clientcmd.FlagPassword))
  236. }
  237. if len(methods) > 1 {
  238. return fmt.Errorf("you cannot specify more than one authentication method at the same time: %v", strings.Join(methods, ", "))
  239. }
  240. if o.embedCertData.Value() {
  241. certPath := o.clientCertificate.Value()
  242. keyPath := o.clientKey.Value()
  243. if certPath == "" && keyPath == "" {
  244. return fmt.Errorf("you must specify a --%s or --%s to embed", clientcmd.FlagCertFile, clientcmd.FlagKeyFile)
  245. }
  246. if certPath != "" {
  247. if _, err := ioutil.ReadFile(certPath); err != nil {
  248. return fmt.Errorf("error reading %s data from %s: %v", clientcmd.FlagCertFile, certPath, err)
  249. }
  250. }
  251. if keyPath != "" {
  252. if _, err := ioutil.ReadFile(keyPath); err != nil {
  253. return fmt.Errorf("error reading %s data from %s: %v", clientcmd.FlagKeyFile, keyPath, err)
  254. }
  255. }
  256. }
  257. return nil
  258. }