From 42879dc91553c4cbd563dfe6338c4093eaa49d48 Mon Sep 17 00:00:00 2001 From: Hidetake Iwata Date: Wed, 12 Feb 2020 21:27:08 +0900 Subject: [PATCH] Revise setup instruction (#235) --- acceptance_test/Makefile | 32 ++++++++++++- acceptance_test/kubeconfig_oidc.yaml | 25 ---------- docs/setup.md | 66 ++++++++++++++------------- pkg/adaptors/cmd/setup.go | 9 ++-- pkg/usecases/setup/stage2.go | 65 ++++++++++++++++---------- pkg/usecases/standalone/standalone.go | 30 +++++------- 6 files changed, 125 insertions(+), 102 deletions(-) delete mode 100644 acceptance_test/kubeconfig_oidc.yaml diff --git a/acceptance_test/Makefile b/acceptance_test/Makefile index ebe4bfc..2e35353 100644 --- a/acceptance_test/Makefile +++ b/acceptance_test/Makefile @@ -1,13 +1,41 @@ CLUSTER_NAME := kubelogin-acceptance-test OUTPUT_DIR := $(CURDIR)/output +PATH := $(PATH):$(OUTPUT_DIR)/bin +export PATH KUBECONFIG := $(OUTPUT_DIR)/kubeconfig.yaml export KUBECONFIG +# run the login script instead of opening chrome +BROWSER := $(OUTPUT_DIR)/bin/chromelogin +export BROWSER + .PHONY: test test: build - PATH=$(PATH):$(OUTPUT_DIR)/bin BROWSER=chromelogin KUBECONFIG=$(KUBECONFIG):kubeconfig_oidc.yaml \ - kubectl --user=oidc cluster-info + # see the setup instruction + kubectl oidc-login setup \ + --oidc-issuer-url=https://dex-server:10443/dex \ + --oidc-client-id=YOUR_CLIENT_ID \ + --oidc-client-secret=YOUR_CLIENT_SECRET \ + --oidc-extra-scope=email \ + --certificate-authority=$(OUTPUT_DIR)/ca.crt + # set up the kubeconfig + kubectl config set-credentials oidc \ + --exec-api-version=client.authentication.k8s.io/v1beta1 \ + --exec-command=kubectl \ + --exec-arg=oidc-login \ + --exec-arg=get-token \ + --exec-arg=--oidc-issuer-url=https://dex-server:10443/dex \ + --exec-arg=--oidc-client-id=YOUR_CLIENT_ID \ + --exec-arg=--oidc-client-secret=YOUR_CLIENT_SECRET \ + --exec-arg=--oidc-extra-scope=email \ + --exec-arg=--certificate-authority=$(OUTPUT_DIR)/ca.crt + # make sure we can access the cluster + kubectl --user=oidc cluster-info + # switch the current context + kubectl config set-context --current --user=oidc + # make sure we can access the cluster + kubectl cluster-info .PHONY: setup setup: build dex cluster setup-chrome diff --git a/acceptance_test/kubeconfig_oidc.yaml b/acceptance_test/kubeconfig_oidc.yaml deleted file mode 100644 index d8822a9..0000000 --- a/acceptance_test/kubeconfig_oidc.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: v1 -kind: Config -users: -- name: oidc - user: - exec: - apiVersion: client.authentication.k8s.io/v1beta1 - args: - - oidc-login - - get-token - - --oidc-issuer-url=https://dex-server:10443/dex - - --oidc-client-id=YOUR_CLIENT_ID - - --oidc-client-secret=YOUR_CLIENT_SECRET - - --oidc-extra-scope=email - - --certificate-authority=output/ca.crt - - --token-cache-dir=output/token-cache - - --listen-address=127.0.0.1:8000 - - -v1 - command: kubectl -contexts: -- context: - cluster: kind-kubelogin-acceptance-test - user: oidc - name: oidc -current-context: oidc diff --git a/docs/setup.md b/docs/setup.md index 58baabb..6358ed8 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -1,6 +1,6 @@ # Kubernetes OpenID Connection authentication -This document guides how to set up the Kubernetes OpenID Connect (OIDC) authentication. +This document guides how to set up Kubernetes OpenID Connect (OIDC) authentication. Let's see the following steps: 1. Set up the OIDC provider @@ -35,7 +35,7 @@ Variable | Value You can log in with a user of Keycloak. Make sure you have an administrator role of the Keycloak realm. -Open the Keycloak and create an OIDC client as follows: +Open Keycloak and create an OIDC client as follows: - Client ID: `YOUR_CLIENT_ID` - Valid Redirect URLs: @@ -52,7 +52,7 @@ You can associate client roles by adding the following mapper: - Token Claim Name: `groups` - Add to ID token: on -For example, if you have the `admin` role of the client, you will get a JWT with the claim `{"groups": ["kubernetes:admin"]}`. +For example, if you have `admin` role of the client, you will get a JWT with the claim `{"groups": ["kubernetes:admin"]}`. Replace the following variables in the later sections. @@ -72,7 +72,7 @@ Open [GitHub OAuth Apps](https://github.com/settings/developers) and create an a - Homepage URL: `https://dex.example.com` - Authorization callback URL: `https://dex.example.com/callback` -Deploy the [dex](https://github.com/dexidp/dex) with the following config: +Deploy [Dex](https://github.com/dexidp/dex) with the following config: ```yaml issuer: https://dex.example.com @@ -138,13 +138,20 @@ kubectl oidc-login setup \ --oidc-client-secret=YOUR_CLIENT_SECRET ``` -It will open the browser and you can log in to the provider. -Then it will show the instruction. +It launches the browser and navigates to `http://localhost:8000`. +Please log in to the provider. + +You can set extra options, for example, extra scope or CA certificate. +See also the full options. + +```sh +kubectl oidc-login setup --help +``` ## 3. Bind a cluster role -In this tutorial, bind the `cluster-admin` role to you. +Here bind `cluster-admin` role to you. Apply the following manifest: ```yaml @@ -170,14 +177,14 @@ As well as you can create a custom cluster role and bind it. ## 4. Set up the Kubernetes API server -Add the following options to the kube-apiserver: +Add the following flags to kube-apiserver: ``` --oidc-issuer-url=ISSUER_URL --oidc-client-id=YOUR_CLIENT_ID ``` -See [OpenID Connect Tokens](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens) for details. +See [Kubernetes Authenticating: OpenID Connect Tokens](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens) for the all flags. If you are using [kops](https://github.com/kubernetes/kops), run `kops edit cluster` and append the following settings: @@ -200,35 +207,32 @@ If you are using [kube-aws](https://github.com/kubernetes-incubator/kube-aws), a ## 5. Set up the kubeconfig -Add the following user to the kubeconfig: +Add `oidc` user to the kubeconfig. -```yaml -users: -- name: oidc - user: - exec: - apiVersion: client.authentication.k8s.io/v1beta1 - command: kubectl - args: - - oidc-login - - get-token - - --oidc-issuer-url=ISSUER_URL - - --oidc-client-id=YOUR_CLIENT_ID - - --oidc-client-secret=YOUR_CLIENT_SECRET +```sh +kubectl config set-credentials oidc \ + --exec-api-version=client.authentication.k8s.io/v1beta1 \ + --exec-command=kubectl \ + --exec-arg=oidc-login \ + --exec-arg=get-token \ + --exec-arg=--oidc-issuer-url=ISSUER_URL \ + --exec-arg=--oidc-client-id=YOUR_CLIENT_ID \ + --exec-arg=--oidc-client-secret=YOUR_CLIENT_SECRET ``` -You can share the kubeconfig to your team members for on-boarding. - ## 6. Verify cluster access Make sure you can access the Kubernetes cluster. +```sh +kubectl --user=oidc cluster-info ``` -% kubectl get nodes -Open http://localhost:8000 for authentication -You got a valid token until 2019-05-16 22:03:13 +0900 JST -NAME STATUS ROLES AGE VERSION -ip-1-2-3-4.us-west-2.compute.internal Ready node 21d v1.9.6 -ip-1-2-3-5.us-west-2.compute.internal Ready node 20d v1.9.6 + +You can switch the current context to oidc. + +```sh +kubectl config set-context --current --user=oidc ``` + +You can share the kubeconfig to your team members for on-boarding. diff --git a/pkg/adaptors/cmd/setup.go b/pkg/adaptors/cmd/setup.go index e07fa1a..c6102da 100644 --- a/pkg/adaptors/cmd/setup.go +++ b/pkg/adaptors/cmd/setup.go @@ -15,7 +15,8 @@ type setupOptions struct { ClientID string ClientSecret string ExtraScopes []string - CertificateAuthority string + CACertFilename string + CACertData string SkipTLSVerify bool authenticationOptions authenticationOptions } @@ -26,7 +27,8 @@ func (o *setupOptions) register(f *pflag.FlagSet) { f.StringVar(&o.ClientID, "oidc-client-id", "", "Client ID of the provider") f.StringVar(&o.ClientSecret, "oidc-client-secret", "", "Client secret of the provider") f.StringSliceVar(&o.ExtraScopes, "oidc-extra-scope", nil, "Scopes to request to the provider") - f.StringVar(&o.CertificateAuthority, "certificate-authority", "", "Path to a cert file for the certificate authority") + f.StringVar(&o.CACertFilename, "certificate-authority", "", "Path to a cert file for the certificate authority") + f.StringVar(&o.CACertData, "certificate-authority-data", "", "Base64 encoded data for the certificate authority") f.BoolVar(&o.SkipTLSVerify, "insecure-skip-tls-verify", false, "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure") o.authenticationOptions.register(f) } @@ -51,7 +53,8 @@ func (cmd *Setup) New(ctx context.Context) *cobra.Command { ClientID: o.ClientID, ClientSecret: o.ClientSecret, ExtraScopes: o.ExtraScopes, - CACertFilename: o.CertificateAuthority, + CACertFilename: o.CACertFilename, + CACertData: o.CACertData, SkipTLSVerify: o.SkipTLSVerify, GrantOptionSet: grantOptionSet, } diff --git a/pkg/usecases/setup/stage2.go b/pkg/usecases/setup/stage2.go index 99260ce..6441f6a 100644 --- a/pkg/usecases/setup/stage2.go +++ b/pkg/usecases/setup/stage2.go @@ -10,11 +10,18 @@ import ( ) var stage2Tpl = template.Must(template.New("").Parse(` -## 3. Bind a role +## 2. Verify authentication -Run the following command: +You got a token with the following claims: + +{{- range $k, $v := .Claims }} + {{ $k }}={{ $v }} +{{- end }} + +## 3. Bind a cluster role + +Apply the following manifest: - kubectl apply -f - <<-EOF kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: @@ -26,7 +33,6 @@ Run the following command: subjects: - kind: User name: {{ .IssuerURL }}#{{ .Subject }} - EOF ## 4. Set up the Kubernetes API server @@ -37,25 +43,32 @@ Add the following options to the kube-apiserver: ## 5. Set up the kubeconfig -Add the following user to the kubeconfig: +Run the following command: - users: - - name: google - user: - exec: - apiVersion: client.authentication.k8s.io/v1beta1 - command: kubectl - args: - - oidc-login - - get-token + kubectl config set-credentials oidc \ + --exec-api-version=client.authentication.k8s.io/v1beta1 \ + --exec-command=kubectl \ + --exec-arg=oidc-login \ + --exec-arg=get-token \ {{- range .Args }} - - {{ . }} + --exec-arg={{ . }} {{- end }} -Run kubectl and verify cluster access. +## 6. Verify cluster access + +Make sure you can access the Kubernetes cluster. + + kubectl --user=oidc get nodes + +You can switch the default context to oidc. + + kubectl config set-context --current --user=oidc + +You can share the kubeconfig to your team members for on-boarding. `)) type stage2Vars struct { + Claims map[string]string IssuerURL string ClientID string Args []string @@ -68,18 +81,24 @@ type Stage2Input struct { ClientID string ClientSecret string ExtraScopes []string // optional - CACertFilename string // If set, use the CA cert + CACertFilename string // optional + CACertData string // optional SkipTLSVerify bool ListenAddressArgs []string // non-nil if set by the command arg GrantOptionSet authentication.GrantOptionSet } func (u *Setup) DoStage2(ctx context.Context, in Stage2Input) error { - u.Logger.Printf(`## 2. Verify authentication`) + u.Logger.Printf("authentication in progress...") certPool := u.NewCertPool() if in.CACertFilename != "" { if err := certPool.AddFile(in.CACertFilename); err != nil { - return xerrors.Errorf("could not load the certificate: %w", err) + return xerrors.Errorf("could not load the certificate file: %w", err) + } + } + if in.CACertData != "" { + if err := certPool.AddBase64Encoded(in.CACertData); err != nil { + return xerrors.Errorf("could not load the certificate data: %w", err) } } out, err := u.Authentication.Do(ctx, authentication.Input{ @@ -94,12 +113,9 @@ func (u *Setup) DoStage2(ctx context.Context, in Stage2Input) error { if err != nil { return xerrors.Errorf("error while authentication: %w", err) } - u.Logger.Printf("You got the following claims in the token:") - for k, v := range out.IDTokenClaims.Pretty { - u.Logger.Printf("\t%s=%s", k, v) - } v := stage2Vars{ + Claims: out.IDTokenClaims.Pretty, IssuerURL: in.IssuerURL, ClientID: in.ClientID, Args: makeCredentialPluginArgs(in), @@ -126,6 +142,9 @@ func makeCredentialPluginArgs(in Stage2Input) []string { if in.CACertFilename != "" { args = append(args, "--certificate-authority="+in.CACertFilename) } + if in.CACertData != "" { + args = append(args, "--certificate-authority-data="+in.CACertData) + } if in.SkipTLSVerify { args = append(args, "--insecure-skip-tls-verify") } diff --git a/pkg/usecases/standalone/standalone.go b/pkg/usecases/standalone/standalone.go index e5767df..6367481 100644 --- a/pkg/usecases/standalone/standalone.go +++ b/pkg/usecases/standalone/standalone.go @@ -118,29 +118,24 @@ var deprecationTpl = template.Must(template.New("").Parse( The credential plugin mode is available since v1.14.0. Kubectl will automatically run kubelogin and you do not need to run kubelogin explicitly. -You can switch to the credential plugin mode by setting the following user to -{{ .Kubeconfig }}. ---- -users: -- name: oidc - user: - exec: - apiVersion: client.authentication.k8s.io/v1beta1 - command: kubectl - args: - - oidc-login - - get-token +You can switch to the credential plugin mode by the following command: + + kubectl config set-credentials oidc \ + --exec-api-version=client.authentication.k8s.io/v1beta1 \ + --exec-command=kubectl \ + --exec-arg=oidc-login \ + --exec-arg=get-token \ {{- range .Args }} - - {{ . }} + --exec-arg={{ . }} {{- end }} ---- + kubectl config set-context --current --user=oidc + See https://github.com/int128/kubelogin for more. `)) type deprecationVars struct { - Kubeconfig string - Args []string + Args []string } func (u *Standalone) showDeprecation(in Input, p *kubeconfig.AuthProvider) error { @@ -169,8 +164,7 @@ func (u *Standalone) showDeprecation(in Input, p *kubeconfig.AuthProvider) error } v := deprecationVars{ - Kubeconfig: p.LocationOfOrigin, - Args: args, + Args: args, } var b strings.Builder if err := deprecationTpl.Execute(&b, &v); err != nil {