1- # kuberos /kubelogin poc
1+ # int128 /kubelogin POC
22
3- <!-- markdownlint-disable MD013 -->
4-
5- ## Kubelogin, or kubectl-oidc_login
6-
7- Docs:
3+ Upstream documentation:
84
95- < https://github.com/int128/kubelogin >
106- < https://github.com/int128/kubelogin/blob/master/docs/usage.md >
7+ - < https://dexidp.io/docs/connectors/oidc/ >
8+ - < https://openid.net/specs/openid-connect-core-1_0.html#Claims >
9+ - < https://dexidp.io/docs/custom-scopes-claims-clients/ >
1110
12- ### Kubelogin installation
11+ ## Kubelogin installation
1312
1413Get the binary from
1514[ releases] ( https://github.com/int128/kubelogin/releases/tag/v1.28.0 ) , and
1615install as ` kubectl-oidc_login ` in ` /usr/local/bin ` or elsewhere in ` $PATH ` .
1716
18- ## Grant types
17+ ## Cluster POC
1918
20- Kubelogin and Dex support following grant types:
21-
22- - auto (automatic selection)
23- - authcode (token passthru via web browser callback)
24- - authcode-keyboard (generates code you need to paste yourself)
25- - device-code (like authcode, but for a device of certain code)
26- - password (supply username and password on the command line, not enabled by default)
27-
28- Grant type is specified with ` --grant-type=<type> ` on command line during setup.
29-
30- ### Password
31-
32- Password flow is enabled only, if Dex config is set to allow it by setting one
33- of the connectors as ` passwordConnector ` :
34-
35- ``` yaml
36- oauth2 :
37- passwordConnector : ldap
38- ` ` `
39-
40- If this config is not present, password grant type is rejected as
41- ` not supported`. Most of the connectors do not support username/password.
42- ` local` and `ldap` do.
43-
44- # # Putting it all together
45-
46- Now that we know we can do `kubelogin` -> `Dex` -> `openLDAP`, we need to
47- setup the final chain, where Dex is integrated with k8s apiserver, and it
48- actually authenticates `kubectl` commands via Dex.
19+ POC sets up ` kubelogin ` -> ` Dex ` -> ` openLDAP ` auth chain, where Dex is
20+ integrated with k8s apiserver as OIDC provider, and it actually authenticates
21+ ` kubectl ` commands via Dex.
4922
5023TL;DR: ` ./run.sh ` creates Kind cluster and will setup everything for you.
5124Read [ the script] ( ./run.sh ) before running it, or do the steps one by one
5225following the steps below.
5326
27+ - ` ./run.sh password ` to create cluster with password grant flow
28+ - ` ./run.sh device-code ` to create cluster with device-code flow
29+
5430### Kind setup
5531
5632In case you don't have a k8s cluster you want to test this in, you can setup
@@ -64,14 +40,14 @@ users won't have any rights.
6440
6541For Kind setup, we also need to mount a directory holding a Dex generated CA
6642cert, so apiserver will trust the Dex when connecting. Using [ Dex's certificate
67- generation script](gencert.sh), you get a CA cert, which should be mounted via
43+ generation script] ( ./ gencert.sh) , you get a CA cert, which should be mounted via
6844directory ` /etc/ssl/certs/dex-test ` in the apiserver.
6945
7046### Dex setup
7147
7248We setup Dex to run in nodeport ` 32000 ` so it is reachable by the apiserver and
73- the user's kubectl client. Alternatively, Dex could be configued with service
74- andpoint , and apiserver could be configured to connect to `dex.dex` for example.
49+ the user's kubectl client. Alternatively, Dex could be configured with service
50+ endpoint , and apiserver could be configured to connect to ` dex.dex ` for example.
7551OIDC provider is often external to the cluster, and hence the nodeport simulates
7652reality better than in-cluster service.
7753
@@ -84,13 +60,150 @@ the generated CA certificate in previous step. Using expired or non-trusted
8460certificates causes apiserver not trust Dex, and login attempts will fail even
8561if the Dex token kubectl gained is valid.
8662
87- See [dex.yaml](k8s/dex.yaml).
88-
8963We also need to add a line to ` /etc/hosts ` to create fake Dex domain:
90- ` 127.0.0.1 dex.example.com` or simply change all occurences of
64+ ` 127.0.0.1 dex.example.com ` or simply change all occurrences of
9165` dex.example.com ` with ` 127.0.0.1 ` . Here we use the ` dex.example.com ` in order
9266to make a difference between Dex host and other services.
9367
68+ Depending on the connector and grant type you want to configure, choose the
69+ appropriate configs below.
70+
71+ #### Password connector
72+
73+ Password flow is enabled only if Dex config is set to allow it by setting one
74+ of the connectors as ` passwordConnector ` :
75+
76+ ``` yaml
77+ oauth2 :
78+ passwordConnector : ldap
79+ ` ` `
80+
81+ If this config is not present, password grant type is rejected as
82+ ` not supported`. Most of the connectors do not support username/password.
83+ ` local` and `ldap` do.
84+
85+ # ### Grant types
86+
87+ Kubelogin and Dex support the following grant types :
88+
89+ - auto (automatic selection)
90+ - authcode (token passthrough via web browser callback)
91+ - authcode-keyboard (generates code you need to paste yourself)
92+ - device-code (like authcode, but can be completed on remote machine, for a session
93+ identified by a device-code)
94+ - password (supply username and password on the command line, not enabled by default)
95+
96+ Grant type is specified with `--grant-type=<type>` on command line during setup.
97+
98+ # #### Grant type: password
99+
100+ <https://oauth.net/2/grant-types/password/>
101+
102+ If we want to achieve headless CLI login, we need to use `password` grant type.
103+ It should be noted that password grants are removed in OAuth 2.1 spec, and kind
104+ of exist outside the OAuth2 spec. Password grants skip the auth code flow, and
105+ exchange credentials directly to tokens. For this reason, a client secret must
106+ be passed to user to be included in their kubeconfig since PKCE is not available.
107+
108+ We configure the client as a `staticClient` in
109+ [Dex configuration](./k8s/dex-password.yaml) :
110+
111+ ` ` ` yaml
112+ staticClients:
113+ - id: kubelogin-test
114+ name: Kubelogin
115+ secret: kubelogin-test-secret
116+ ` ` `
117+
118+ and the [kubelogin config](./kubeconfig.password.example) in kubeconfig needs to
119+ look like :
120+
121+ ` ` ` yaml
122+ - name: oidc
123+ user:
124+ exec:
125+ apiVersion: client.authentication.k8s.io/v1beta1
126+ args:
127+ - oidc-login
128+ - get-token
129+ - --oidc-issuer-url=https://dex.example.com:32000
130+ - --oidc-client-id=kubelogin-test
131+ - --oidc-client-secret=kubelogin-test-secret
132+ - --oidc-extra-scope=email
133+ - --oidc-extra-scope=profile
134+ - --oidc-extra-scope=groups
135+ - --grant-type=password
136+ - --insecure-skip-tls-verify
137+ ` ` `
138+
139+ NOTE : password grant is legacy, and often
140+ [recommended](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth-ropc)
141+ not to be used outside high-trust environments. LDAP auth does not support MFA
142+ and involves giving out your username and password to service admins (versus
143+ identity provider only).
144+
145+ Read more :
146+
147+ - <https://datatracker.ietf.org/doc/html/rfc6749>
148+
149+ # #### Grant type: device-code
150+
151+ If we do not want to pass client-secret to the user (it has security
152+ implications), we lose the full headless CLI login experience, as we cannot use
153+ password grant anymore. Instead, we need to use `device-code` flow. This allows
154+ the user to login on another machine that is browser capable, and tie that login to
155+ one's own kubectl via the device-code.
156+
157+ We need to adjust the `oauth2` configuration to have specific response types :
158+
159+ ` ` ` yaml
160+ oauth2:
161+ passwordConnector: ldap
162+ skipApprovalScreen: true
163+ responseTypes: ["code", "token", "id_token"]
164+ ` ` `
165+
166+ and the `staticClients` part will look like :
167+
168+ ` ` ` yaml
169+ staticClients:
170+ - id: kubelogin-test
171+ name: Kubelogin
172+ # public client must be true or secret must be given to user
173+ public: true
174+ # redirect uris are needed for device code flow
175+ redirectURIs:
176+ - 'urn:ietf:wg:oauth:2.0:oob'
177+ - '/device/callback'
178+ ` ` `
179+
180+ and the [kubelogin config](./kubeconfig.device-code.example) in kubeconfig needs
181+ to look like :
182+
183+ ` ` ` yaml
184+ - name: oidc
185+ user:
186+ exec:
187+ apiVersion: client.authentication.k8s.io/v1beta1
188+ args:
189+ - oidc-login
190+ - get-token
191+ - --oidc-issuer-url=https://dex.example.com:32000
192+ - --oidc-client-id=kubelogin-test
193+ - --oidc-pkce-method=S256
194+ - --oidc-extra-scope=email
195+ - --oidc-extra-scope=profile
196+ - --oidc-extra-scope=groups
197+ - --insecure-skip-tls-verify
198+ - --use-device-code
199+ ` ` `
200+
201+ Read more about PKCE :
202+
203+ - <https://datatracker.ietf.org/doc/html/rfc7636>
204+ - <https://oauth.net/2/pkce/>
205+ - <https://github.com/dexidp/dex/issues/2244>
206+
94207# ## OpenLDAP k8s setup
95208
96209We need to set up OpenLDAP so it is reachable by Dex. In this setup, we run it
@@ -121,58 +234,22 @@ Kubernetes Apiserver. Client can be configured to skip
121234 --exec-arg=get-token \
122235 --exec-arg=--oidc-issuer-url=https://dex.example.com:32000 \
123236 --exec-arg=--oidc-client-id=kubelogin-test \
124- --exec-arg=--oidc-client-secret=kubelogin-test-secret \
125237 --exec-arg=--oidc-extra-scope=email \
126238 --exec-arg=--oidc-extra-scope=profile \
127239 --exec-arg=--oidc-extra-scope=groups \
240+ --exec-arg=--insecure-skip-tls-verify \
241+ # for password grant you need these
242+ --exec-arg=--oidc-client-secret=kubelogin-test-secret \
128243 --exec-arg=--grant-type=password \
129- --exec-arg=--insecure-skip-tls-verify
130- ` ` `
131-
132- and kubeconfig ends up looking like [this](kubeconfig.example).
244+ # for device-code you need these
245+ --exec-arg=--oidc-pkce-method=S256 \
246+ --exec-arg=--grant-type=device-code \
247+ # if you need to debug
248+ --exec-arg=-v1
133249
134- ` ` ` yaml
135- apiVersion: v1
136- # regular cluster configuration here
137- clusters:
138- - cluster:
139- certificate-authority-data: <cluster ca redacted>
140- server: https://<apiserver ip>:<port>
141- name: kind
142- contexts:
143- - context:
144- cluster: kind
145- user: oidc
146- name: kind-oidc
147- current-context: kind-oidc
148- kind: Config
149- preferences: {}
150- users:
151- # in user section, use whatever user name you want, but the oidc-login setup
152- # needs to match the OIDC config passed to the apiserver
153- # insecure-skip-tls-verify is only here because of the test setup
154- # issuer url needs to be accessible and match the Dex issuer config
155- - name: oidc
156- user:
157- exec:
158- apiVersion: client.authentication.k8s.io/v1beta1
159- args:
160- - oidc-login
161- - get-token
162- - --oidc-issuer-url=https://dex.example.com:32000
163- - --oidc-client-id=kubelogin-test
164- - --oidc-client-secret=kubelogin-test-secret
165- - --oidc-extra-scope=email
166- - --oidc-extra-scope=profile
167- - --oidc-extra-scope=groups
168- - --grant-type=password
169- - --insecure-skip-tls-verify
170- command: kubectl
171- env: null
172- provideClusterInfo: false
173250` ` `
174251
175- # ### Cluster roles
252+ # ## Cluster roles
176253
177254Cluster role needs to be assigned based on the username or the groups passed via
178255the `groups` scope. These roles need to be configured by the pre-existing
@@ -195,8 +272,4 @@ kubectl create clusterrolebinding oidc-dex-group \
195272
196273LDAP configuration would be different per organization regardless.
197274
198- Some references :
199-
200- - <https://dexidp.io/docs/connectors/oidc/>
201- - <https://openid.net/specs/openid-connect-core-1_0.html#Claims>
202- - <https://dexidp.io/docs/custom-scopes-claims-clients/>
275+ <!-- cSpell:ignore apiserver,kubelogin,endpoint,nodeport,pkce -->
0 commit comments