Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions cmd/cluster-version.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func runClusterVersion(cmd *cobra.Command, args []string) error {
if err != nil {
// 2) Fallback to authenticated
if !hasAuth(cfg) {
return fmt.Errorf("no authentication methods found in your kubeconfig. please authenticate (`kubelogin`, etc.) and try again.")
return fmt.Errorf("no authentication methods found in your kubeconfig. please authenticate (`kubelogin`, etc.) and try again")
}

clientset, cerr := kubernetes.NewForConfig(cfg)
Expand Down Expand Up @@ -73,7 +73,7 @@ func hasAuth(cfg *rest.Config) bool {
if cfg.Username != "" && cfg.Password != "" {
return true
}
if len(cfg.TLSClientConfig.CertData) > 0 || cfg.TLSClientConfig.CertFile != "" {
if len(cfg.CertData) > 0 || cfg.CertFile != "" {
return true
}
if cfg.ExecProvider != nil {
Expand All @@ -94,19 +94,19 @@ func getUnauthenticatedVersion(cfg *rest.Config) (*version.Info, error) {

// build TLS config
tlsCfg := &tls.Config{}
if cfg.TLSClientConfig.Insecure {
if cfg.Insecure {
tlsCfg.InsecureSkipVerify = true
}

// trust the same CA if provided
if len(cfg.TLSClientConfig.CAData) > 0 {
if len(cfg.CAData) > 0 {
pool := x509.NewCertPool()
if ok := pool.AppendCertsFromPEM(cfg.TLSClientConfig.CAData); !ok {
if ok := pool.AppendCertsFromPEM(cfg.CAData); !ok {
return nil, fmt.Errorf("failed to append CA data")
}
tlsCfg.RootCAs = pool
} else if cfg.TLSClientConfig.CAFile != "" {
pem, err := os.ReadFile(cfg.TLSClientConfig.CAFile)
} else if cfg.CAFile != "" {
pem, err := os.ReadFile(cfg.CAFile)
if err != nil {
return nil, err
}
Expand All @@ -122,7 +122,7 @@ func getUnauthenticatedVersion(cfg *rest.Config) (*version.Info, error) {
if err != nil {
return nil, err
}
defer resp.Body.Close()
defer func() { _ = resp.Body.Close() }()

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected HTTP status: %s", resp.Status)
Expand Down
23 changes: 20 additions & 3 deletions cmd/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,16 @@ var (
authType string
kubeloginPath string
kubeloginExtraArgs []string
kubeloginTokenCacheDir string
)

func init() {
syncCmd.Flags().StringVarP(&greenhouseClusterKubeconfig, "greenhouse-cluster-kubeconfig", "k", clientcmd.RecommendedHomeFile, "kubeconfig file path for Greenhouse cluster")
syncCmd.Flags().StringVarP(&greenhouseClusterContext, "greenhouse-cluster-context", "c", "", "context in greenhouse-cluster-kubeconfig, the context in the file is used if this flag is not set")
syncCmd.Flags().StringVarP(&greenhouseClusterNamespace, "greenhouse-cluster-namespace", "n", "", "namespace for greenhouse-cluster-kubeconfig, it is the same value as Greenhouse organization")
syncCmd.MarkFlagRequired("greenhouse-cluster-namespace")
if err := syncCmd.MarkFlagRequired("greenhouse-cluster-namespace"); err != nil {
panic(err)
}
syncCmd.Flags().StringVarP(&remoteClusterKubeconfig, "remote-cluster-kubeconfig", "r", clientcmd.RecommendedHomeFile, "kubeconfig file path for remote clusters")
syncCmd.Flags().StringVar(&remoteClusterName, "remote-cluster-name", "", "name of the remote cluster, if not set all clusters are retrieved")
syncCmd.Flags().StringVar(&prefix, "prefix", "cloudctl", "prefix for kubeconfig entries. it is used to separate and manage the entries of this tool only")
Expand All @@ -50,6 +53,7 @@ func init() {
syncCmd.Flags().StringVar(&authType, "auth-type", "auth-provider", "authentication config style to write for users: auth-provider or exec-plugin")
syncCmd.Flags().StringVar(&kubeloginPath, "kubelogin-path", "kubelogin", "path to kubelogin command when using exec-plugin auth-type")
syncCmd.Flags().StringSliceVar(&kubeloginExtraArgs, "kubelogin-extra-args", nil, "extra arguments to pass to kubelogin exec plugin")
syncCmd.Flags().StringVar(&kubeloginTokenCacheDir, "kubelogin-token-cache-dir", "$(HOME)/.kube/cache/oidc-login", "token cache directory for kubelogin")
}

var syncCmd = &cobra.Command{
Expand Down Expand Up @@ -177,7 +181,7 @@ func buildIncomingKubeconfig(items []v1alpha1.ClusterKubeconfig) (*clientcmdapi.
Exec: &clientcmdapi.ExecConfig{
APIVersion: "client.authentication.k8s.io/v1",
Command: kubeloginPath,
Args: buildKubeloginArgs(authItem.AuthInfo.AuthProvider.Config, kubeloginExtraArgs),
Args: buildKubeloginArgs(authItem.AuthInfo.AuthProvider.Config, kubeloginExtraArgs, kubeloginTokenCacheDir),
InteractiveMode: clientcmdapi.IfAvailableExecInteractiveMode,
},
}
Expand Down Expand Up @@ -355,7 +359,7 @@ func generateAuthInfoKey(authInfo *clientcmdapi.AuthInfo) string {
}

// buildKubeloginArgs constructs kubelogin arguments from an oidc auth-provider config and extra args
func buildKubeloginArgs(cfg map[string]string, extra []string) []string {
func buildKubeloginArgs(cfg map[string]string, extra []string, tokenCacheDir string) []string {
args := []string{"get-token"}
if v := cfg["idp-issuer-url"]; v != "" {
args = append(args, "--oidc-issuer-url="+v)
Expand All @@ -376,6 +380,19 @@ func buildKubeloginArgs(cfg map[string]string, extra []string) []string {
}
if v := cfg["auth-request-extra-params"]; v != "" {
args = append(args, "--oidc-auth-request-extra-params="+v)
// If connector_id is used, use a separate token cache directory to avoid collisions
// between multiple users on the same machine.
// See https://github.com/int128/kubelogin/issues/29
for _, param := range strings.Split(v, ",") {
kv := strings.SplitN(param, "=", 2)
if len(kv) == 2 && strings.TrimSpace(kv[0]) == "connector_id" {
connectorID := strings.TrimSpace(kv[1])
if connectorID != "" {
args = append(args, fmt.Sprintf("--token-cache-dir=%s/%s", tokenCacheDir, connectorID))
}
break
}
}
}
// allow caller to inject additional flags
if len(extra) > 0 {
Expand Down
37 changes: 36 additions & 1 deletion cmd/sync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,10 @@ func TestSyncFlags_AuthTypeAndKubeloginDefaults(t *testing.T) {
g.Expect(fExtra).ToNot(BeNil())
// StringSliceVar defaults to [] if nil; DefValue is representation of default (empty)
g.Expect(fExtra.DefValue).To(Or(Equal("[]"), Equal("")))

fCache := syncCmd.Flags().Lookup("kubelogin-token-cache-dir")
g.Expect(fCache).ToNot(BeNil())
g.Expect(fCache.DefValue).To(Equal("$(HOME)/.kube/cache/oidc-login"))
}

func TestBuildKubeloginArgs_MappingAndExtras(t *testing.T) {
Expand All @@ -240,7 +244,7 @@ func TestBuildKubeloginArgs_MappingAndExtras(t *testing.T) {
}
extra := []string{"--v=4", "--token-cache-dir=/tmp/k"}

args := buildKubeloginArgs(cfg, extra)
args := buildKubeloginArgs(cfg, extra, "$(HOME)/.kube/cache/oidc-login")

// Starts with subcommand
g.Expect(args[0]).To(Equal("get-token"))
Expand All @@ -260,6 +264,37 @@ func TestBuildKubeloginArgs_MappingAndExtras(t *testing.T) {
g.Expect(args[len(args)-2:]).To(Equal(extra))
}

func TestBuildKubeloginArgs_ConnectorID(t *testing.T) {
g := NewWithT(t)

cfg := map[string]string{
"idp-issuer-url": "https://issuer.example.com",
"client-id": "cid",
"auth-request-extra-params": "connector_id=my-id",
}

args := buildKubeloginArgs(cfg, nil, "$(HOME)/.kube/cache/oidc-login")

g.Expect(args).To(ContainElement("--oidc-auth-request-extra-params=connector_id=my-id"))
g.Expect(args).To(ContainElement(HavePrefix("--token-cache-dir=")))
g.Expect(args).To(ContainElement(ContainSubstring("my-id")))
}

func TestBuildKubeloginArgs_ConnectorIDInMultiParams(t *testing.T) {
g := NewWithT(t)

cfg := map[string]string{
"idp-issuer-url": "https://issuer.example.com",
"client-id": "cid",
"auth-request-extra-params": "foo=bar,connector_id=another-id,baz=qux",
}

args := buildKubeloginArgs(cfg, nil, "/custom/cache")

g.Expect(args).To(ContainElement("--oidc-auth-request-extra-params=foo=bar,connector_id=another-id,baz=qux"))
g.Expect(args).To(ContainElement("--token-cache-dir=/custom/cache/another-id"))
}

func TestAuthInfoEqual_ExecBased(t *testing.T) {
g := NewWithT(t)

Expand Down
Loading