create_authinfo.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  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/spf13/cobra"
  22. "k8s.io/client-go/tools/clientcmd"
  23. clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
  24. cliflag "k8s.io/component-base/cli/flag"
  25. cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
  26. "k8s.io/kubernetes/pkg/kubectl/util/i18n"
  27. "k8s.io/kubernetes/pkg/kubectl/util/templates"
  28. )
  29. type createAuthInfoOptions struct {
  30. configAccess clientcmd.ConfigAccess
  31. name string
  32. authPath cliflag.StringFlag
  33. clientCertificate cliflag.StringFlag
  34. clientKey cliflag.StringFlag
  35. token cliflag.StringFlag
  36. username cliflag.StringFlag
  37. password cliflag.StringFlag
  38. embedCertData cliflag.Tristate
  39. authProvider cliflag.StringFlag
  40. authProviderArgs map[string]string
  41. authProviderArgsToRemove []string
  42. execCommand cliflag.StringFlag
  43. execAPIVersion cliflag.StringFlag
  44. execArgs []string
  45. execEnv map[string]string
  46. execEnvToRemove []string
  47. }
  48. const (
  49. flagAuthProvider = "auth-provider"
  50. flagAuthProviderArg = "auth-provider-arg"
  51. flagExecCommand = "exec-command"
  52. flagExecAPIVersion = "exec-api-version"
  53. flagExecArg = "exec-arg"
  54. flagExecEnv = "exec-env"
  55. )
  56. var (
  57. createAuthInfoLong = fmt.Sprintf(templates.LongDesc(`
  58. Sets a user entry in kubeconfig
  59. Specifying a name that already exists will merge new fields on top of existing values.
  60. Client-certificate flags:
  61. --%v=certfile --%v=keyfile
  62. Bearer token flags:
  63. --%v=bearer_token
  64. Basic auth flags:
  65. --%v=basic_user --%v=basic_password
  66. Bearer token and basic auth are mutually exclusive.`), clientcmd.FlagCertFile, clientcmd.FlagKeyFile, clientcmd.FlagBearerToken, clientcmd.FlagUsername, clientcmd.FlagPassword)
  67. createAuthInfoExample = templates.Examples(`
  68. # Set only the "client-key" field on the "cluster-admin"
  69. # entry, without touching other values:
  70. kubectl config set-credentials cluster-admin --client-key=~/.kube/admin.key
  71. # Set basic auth for the "cluster-admin" entry
  72. kubectl config set-credentials cluster-admin --username=admin --password=uXFGweU9l35qcif
  73. # Embed client certificate data in the "cluster-admin" entry
  74. kubectl config set-credentials cluster-admin --client-certificate=~/.kube/admin.crt --embed-certs=true
  75. # Enable the Google Compute Platform auth provider for the "cluster-admin" entry
  76. kubectl config set-credentials cluster-admin --auth-provider=gcp
  77. # Enable the OpenID Connect auth provider for the "cluster-admin" entry with additional args
  78. kubectl config set-credentials cluster-admin --auth-provider=oidc --auth-provider-arg=client-id=foo --auth-provider-arg=client-secret=bar
  79. # Remove the "client-secret" config value for the OpenID Connect auth provider for the "cluster-admin" entry
  80. kubectl config set-credentials cluster-admin --auth-provider=oidc --auth-provider-arg=client-secret-
  81. # Enable new exec auth plugin for the "cluster-admin" entry
  82. kubectl config set-credentials cluster-admin --exec-command=/path/to/the/executable --exec-api-version=client.authentication.k8s.io/v1beta
  83. # Define new exec auth plugin args for the "cluster-admin" entry
  84. kubectl config set-credentials cluster-admin --exec-arg=arg1 --exec-arg=arg2
  85. # Create or update exec auth plugin environment variables for the "cluster-admin" entry
  86. kubectl config set-credentials cluster-admin --exec-env=key1=val1 --exec-env=key2=val2
  87. # Remove exec auth plugin environment variables for the "cluster-admin" entry
  88. kubectl config set-credentials cluster-admin --exec-env=var-to-remove-`)
  89. )
  90. // NewCmdConfigSetAuthInfo returns an Command option instance for 'config set-credentials' sub command
  91. func NewCmdConfigSetAuthInfo(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
  92. options := &createAuthInfoOptions{configAccess: configAccess}
  93. return newCmdConfigSetAuthInfo(out, options)
  94. }
  95. func newCmdConfigSetAuthInfo(out io.Writer, options *createAuthInfoOptions) *cobra.Command {
  96. cmd := &cobra.Command{
  97. Use: fmt.Sprintf(
  98. "set-credentials NAME [--%v=path/to/certfile] "+
  99. "[--%v=path/to/keyfile] "+
  100. "[--%v=bearer_token] "+
  101. "[--%v=basic_user] "+
  102. "[--%v=basic_password] "+
  103. "[--%v=provider_name] "+
  104. "[--%v=key=value] "+
  105. "[--%v=exec_command] "+
  106. "[--%v=exec_api_version] "+
  107. "[--%v=arg] "+
  108. "[--%v=key=value]",
  109. clientcmd.FlagCertFile,
  110. clientcmd.FlagKeyFile,
  111. clientcmd.FlagBearerToken,
  112. clientcmd.FlagUsername,
  113. clientcmd.FlagPassword,
  114. flagAuthProvider,
  115. flagAuthProviderArg,
  116. flagExecCommand,
  117. flagExecAPIVersion,
  118. flagExecArg,
  119. flagExecEnv,
  120. ),
  121. DisableFlagsInUseLine: true,
  122. Short: i18n.T("Sets a user entry in kubeconfig"),
  123. Long: createAuthInfoLong,
  124. Example: createAuthInfoExample,
  125. Run: func(cmd *cobra.Command, args []string) {
  126. err := options.complete(cmd, out)
  127. if err != nil {
  128. cmd.Help()
  129. cmdutil.CheckErr(err)
  130. }
  131. cmdutil.CheckErr(options.run())
  132. fmt.Fprintf(out, "User %q set.\n", options.name)
  133. },
  134. }
  135. cmd.Flags().Var(&options.clientCertificate, clientcmd.FlagCertFile, "Path to "+clientcmd.FlagCertFile+" file for the user entry in kubeconfig")
  136. cmd.MarkFlagFilename(clientcmd.FlagCertFile)
  137. cmd.Flags().Var(&options.clientKey, clientcmd.FlagKeyFile, "Path to "+clientcmd.FlagKeyFile+" file for the user entry in kubeconfig")
  138. cmd.MarkFlagFilename(clientcmd.FlagKeyFile)
  139. cmd.Flags().Var(&options.token, clientcmd.FlagBearerToken, clientcmd.FlagBearerToken+" for the user entry in kubeconfig")
  140. cmd.Flags().Var(&options.username, clientcmd.FlagUsername, clientcmd.FlagUsername+" for the user entry in kubeconfig")
  141. cmd.Flags().Var(&options.password, clientcmd.FlagPassword, clientcmd.FlagPassword+" for the user entry in kubeconfig")
  142. cmd.Flags().Var(&options.authProvider, flagAuthProvider, "Auth provider for the user entry in kubeconfig")
  143. cmd.Flags().StringSlice(flagAuthProviderArg, nil, "'key=value' arguments for the auth provider")
  144. cmd.Flags().Var(&options.execCommand, flagExecCommand, "Command for the exec credential plugin for the user entry in kubeconfig")
  145. cmd.Flags().Var(&options.execAPIVersion, flagExecAPIVersion, "API version of the exec credential plugin for the user entry in kubeconfig")
  146. cmd.Flags().StringSlice(flagExecArg, nil, "New arguments for the exec credential plugin command for the user entry in kubeconfig")
  147. cmd.Flags().StringArray(flagExecEnv, nil, "'key=value' environment values for the exec credential plugin")
  148. f := cmd.Flags().VarPF(&options.embedCertData, clientcmd.FlagEmbedCerts, "", "Embed client cert/key for the user entry in kubeconfig")
  149. f.NoOptDefVal = "true"
  150. return cmd
  151. }
  152. func (o createAuthInfoOptions) run() error {
  153. err := o.validate()
  154. if err != nil {
  155. return err
  156. }
  157. config, err := o.configAccess.GetStartingConfig()
  158. if err != nil {
  159. return err
  160. }
  161. startingStanza, exists := config.AuthInfos[o.name]
  162. if !exists {
  163. startingStanza = clientcmdapi.NewAuthInfo()
  164. }
  165. authInfo := o.modifyAuthInfo(*startingStanza)
  166. config.AuthInfos[o.name] = &authInfo
  167. if err := clientcmd.ModifyConfig(o.configAccess, *config, true); err != nil {
  168. return err
  169. }
  170. return nil
  171. }
  172. // authInfo builds an AuthInfo object from the options
  173. func (o *createAuthInfoOptions) modifyAuthInfo(existingAuthInfo clientcmdapi.AuthInfo) clientcmdapi.AuthInfo {
  174. modifiedAuthInfo := existingAuthInfo
  175. var setToken, setBasic bool
  176. if o.clientCertificate.Provided() {
  177. certPath := o.clientCertificate.Value()
  178. if o.embedCertData.Value() {
  179. modifiedAuthInfo.ClientCertificateData, _ = ioutil.ReadFile(certPath)
  180. modifiedAuthInfo.ClientCertificate = ""
  181. } else {
  182. certPath, _ = filepath.Abs(certPath)
  183. modifiedAuthInfo.ClientCertificate = certPath
  184. if len(modifiedAuthInfo.ClientCertificate) > 0 {
  185. modifiedAuthInfo.ClientCertificateData = nil
  186. }
  187. }
  188. }
  189. if o.clientKey.Provided() {
  190. keyPath := o.clientKey.Value()
  191. if o.embedCertData.Value() {
  192. modifiedAuthInfo.ClientKeyData, _ = ioutil.ReadFile(keyPath)
  193. modifiedAuthInfo.ClientKey = ""
  194. } else {
  195. keyPath, _ = filepath.Abs(keyPath)
  196. modifiedAuthInfo.ClientKey = keyPath
  197. if len(modifiedAuthInfo.ClientKey) > 0 {
  198. modifiedAuthInfo.ClientKeyData = nil
  199. }
  200. }
  201. }
  202. if o.token.Provided() {
  203. modifiedAuthInfo.Token = o.token.Value()
  204. setToken = len(modifiedAuthInfo.Token) > 0
  205. }
  206. if o.username.Provided() {
  207. modifiedAuthInfo.Username = o.username.Value()
  208. setBasic = setBasic || len(modifiedAuthInfo.Username) > 0
  209. }
  210. if o.password.Provided() {
  211. modifiedAuthInfo.Password = o.password.Value()
  212. setBasic = setBasic || len(modifiedAuthInfo.Password) > 0
  213. }
  214. if o.authProvider.Provided() {
  215. newName := o.authProvider.Value()
  216. // Only overwrite if the existing auth-provider is nil, or different than the newly specified one.
  217. if modifiedAuthInfo.AuthProvider == nil || modifiedAuthInfo.AuthProvider.Name != newName {
  218. modifiedAuthInfo.AuthProvider = &clientcmdapi.AuthProviderConfig{
  219. Name: newName,
  220. }
  221. }
  222. }
  223. if modifiedAuthInfo.AuthProvider != nil {
  224. if modifiedAuthInfo.AuthProvider.Config == nil {
  225. modifiedAuthInfo.AuthProvider.Config = make(map[string]string)
  226. }
  227. for _, toRemove := range o.authProviderArgsToRemove {
  228. delete(modifiedAuthInfo.AuthProvider.Config, toRemove)
  229. }
  230. for key, value := range o.authProviderArgs {
  231. modifiedAuthInfo.AuthProvider.Config[key] = value
  232. }
  233. }
  234. if o.execCommand.Provided() {
  235. newExecCommand := o.execCommand.Value()
  236. // create new Exec if doesn't exist, otherwise just modify the command
  237. if modifiedAuthInfo.Exec == nil {
  238. modifiedAuthInfo.Exec = &clientcmdapi.ExecConfig{
  239. Command: newExecCommand,
  240. }
  241. } else {
  242. modifiedAuthInfo.Exec.Command = newExecCommand
  243. // explicitly reset exec arguments
  244. modifiedAuthInfo.Exec.Args = nil
  245. }
  246. }
  247. // modify next values only if Exec exists, ignore these changes otherwise
  248. if modifiedAuthInfo.Exec != nil {
  249. if o.execAPIVersion.Provided() {
  250. modifiedAuthInfo.Exec.APIVersion = o.execAPIVersion.Value()
  251. }
  252. // rewrite exec arguments list with new values
  253. if o.execArgs != nil {
  254. modifiedAuthInfo.Exec.Args = o.execArgs
  255. }
  256. // iterate over the existing exec env values and remove the specified
  257. if o.execEnvToRemove != nil {
  258. newExecEnv := []clientcmdapi.ExecEnvVar{}
  259. for _, value := range modifiedAuthInfo.Exec.Env {
  260. needToRemove := false
  261. for _, elemToRemove := range o.execEnvToRemove {
  262. if value.Name == elemToRemove {
  263. needToRemove = true
  264. break
  265. }
  266. }
  267. if !needToRemove {
  268. newExecEnv = append(newExecEnv, value)
  269. }
  270. }
  271. modifiedAuthInfo.Exec.Env = newExecEnv
  272. }
  273. // update or create specified environment variables for the exec plugin
  274. if o.execEnv != nil {
  275. newEnv := []clientcmdapi.ExecEnvVar{}
  276. for newEnvName, newEnvValue := range o.execEnv {
  277. needToCreate := true
  278. for i := 0; i < len(modifiedAuthInfo.Exec.Env); i++ {
  279. if modifiedAuthInfo.Exec.Env[i].Name == newEnvName {
  280. // update the existing value
  281. needToCreate = false
  282. modifiedAuthInfo.Exec.Env[i].Value = newEnvValue
  283. break
  284. }
  285. }
  286. if needToCreate {
  287. // create a new env value
  288. newEnv = append(newEnv, clientcmdapi.ExecEnvVar{Name: newEnvName, Value: newEnvValue})
  289. }
  290. }
  291. modifiedAuthInfo.Exec.Env = append(modifiedAuthInfo.Exec.Env, newEnv...)
  292. }
  293. }
  294. // If any auth info was set, make sure any other existing auth types are cleared
  295. if setToken || setBasic {
  296. if !setToken {
  297. modifiedAuthInfo.Token = ""
  298. }
  299. if !setBasic {
  300. modifiedAuthInfo.Username = ""
  301. modifiedAuthInfo.Password = ""
  302. }
  303. }
  304. return modifiedAuthInfo
  305. }
  306. func (o *createAuthInfoOptions) complete(cmd *cobra.Command, out io.Writer) error {
  307. args := cmd.Flags().Args()
  308. if len(args) != 1 {
  309. return fmt.Errorf("Unexpected args: %v", args)
  310. }
  311. authProviderArgs, err := cmd.Flags().GetStringSlice(flagAuthProviderArg)
  312. if err != nil {
  313. return fmt.Errorf("Error: %s", err)
  314. }
  315. if len(authProviderArgs) > 0 {
  316. newPairs, removePairs, err := cmdutil.ParsePairs(authProviderArgs, flagAuthProviderArg, true)
  317. if err != nil {
  318. return fmt.Errorf("Error: %s", err)
  319. }
  320. o.authProviderArgs = newPairs
  321. o.authProviderArgsToRemove = removePairs
  322. }
  323. execArgs, err := cmd.Flags().GetStringSlice(flagExecArg)
  324. if err != nil {
  325. return fmt.Errorf("Error: %s", err)
  326. }
  327. if len(execArgs) > 0 {
  328. o.execArgs = execArgs
  329. }
  330. execEnv, err := cmd.Flags().GetStringArray(flagExecEnv)
  331. if err != nil {
  332. return fmt.Errorf("Error: %s", err)
  333. }
  334. if len(execEnv) > 0 {
  335. newPairs, removePairs, err := cmdutil.ParsePairs(execEnv, flagExecEnv, true)
  336. if err != nil {
  337. return fmt.Errorf("Error: %s", err)
  338. }
  339. o.execEnv = newPairs
  340. o.execEnvToRemove = removePairs
  341. }
  342. o.name = args[0]
  343. return nil
  344. }
  345. func (o createAuthInfoOptions) validate() error {
  346. if len(o.name) == 0 {
  347. return errors.New("you must specify a non-empty user name")
  348. }
  349. methods := []string{}
  350. if len(o.token.Value()) > 0 {
  351. methods = append(methods, fmt.Sprintf("--%v", clientcmd.FlagBearerToken))
  352. }
  353. if len(o.username.Value()) > 0 || len(o.password.Value()) > 0 {
  354. methods = append(methods, fmt.Sprintf("--%v/--%v", clientcmd.FlagUsername, clientcmd.FlagPassword))
  355. }
  356. if len(methods) > 1 {
  357. return fmt.Errorf("you cannot specify more than one authentication method at the same time: %v", strings.Join(methods, ", "))
  358. }
  359. if o.embedCertData.Value() {
  360. certPath := o.clientCertificate.Value()
  361. keyPath := o.clientKey.Value()
  362. if certPath == "" && keyPath == "" {
  363. return fmt.Errorf("you must specify a --%s or --%s to embed", clientcmd.FlagCertFile, clientcmd.FlagKeyFile)
  364. }
  365. if certPath != "" {
  366. if _, err := ioutil.ReadFile(certPath); err != nil {
  367. return fmt.Errorf("error reading %s data from %s: %v", clientcmd.FlagCertFile, certPath, err)
  368. }
  369. }
  370. if keyPath != "" {
  371. if _, err := ioutil.ReadFile(keyPath); err != nil {
  372. return fmt.Errorf("error reading %s data from %s: %v", clientcmd.FlagKeyFile, keyPath, err)
  373. }
  374. }
  375. }
  376. return nil
  377. }