mirror of
https://github.com/int128/kubelogin.git
synced 2026-02-19 19:09:50 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b776bac764 | ||
|
|
4bf77886a8 | ||
|
|
ea711f91b4 | ||
|
|
cfc6376f69 |
188
README.md
188
README.md
@@ -3,136 +3,208 @@
|
||||
`kubelogin` is a command to get an OpenID Connect (OIDC) token for `kubectl` authentication.
|
||||
|
||||
|
||||
## Getting Started
|
||||
## TL;DR
|
||||
|
||||
Download [the latest release](https://github.com/int128/kubelogin/releases) and save it as `/usr/local/bin/kubelogin`.
|
||||
1. Setup your OpenID Connect provider, e.g. Google Identity Platform or Keycloak.
|
||||
1. Setup your Kubernetes cluster.
|
||||
1. Setup your `kubectl`.
|
||||
|
||||
You have to configure `kubectl` to authenticate with OIDC.
|
||||
See the later section for details.
|
||||
```
|
||||
% kubelogin --help
|
||||
2018/08/15 19:08:58 Usage:
|
||||
kubelogin [OPTIONS]
|
||||
|
||||
Application Options:
|
||||
--kubeconfig= Path to the kubeconfig file (default: ~/.kube/config) [$KUBECONFIG]
|
||||
--insecure-skip-tls-verify If set, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
|
||||
[$KUBELOGIN_INSECURE_SKIP_TLS_VERIFY]
|
||||
|
||||
Help Options:
|
||||
-h, --help Show this help message
|
||||
```
|
||||
|
||||
|
||||
## Getting Started with Google Account
|
||||
|
||||
### 1. Setup Google API
|
||||
|
||||
Open [Google APIs Console](https://console.developers.google.com/apis/credentials) and create an OAuth client as follows:
|
||||
|
||||
- Application Type: Web application
|
||||
- Redirect URL: `http://localhost:8000/`
|
||||
|
||||
### 2. Setup Kubernetes cluster
|
||||
|
||||
Configure your Kubernetes API Server accepts [OpenID Connect Tokens](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens).
|
||||
If you are using [kops](https://github.com/kubernetes/kops), run `kops edit cluster` and append the following settings:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
kubeAPIServer:
|
||||
oidcIssuerURL: https://accounts.google.com
|
||||
oidcClientID: YOUR_CLIENT_ID.apps.googleusercontent.com
|
||||
```
|
||||
|
||||
Here assign the `cluster-admin` role to your user.
|
||||
|
||||
```yaml
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: oidc-admin-group
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: cluster-admin
|
||||
subjects:
|
||||
- kind: User
|
||||
name: https://accounts.google.com#1234567890
|
||||
```
|
||||
|
||||
### 3. Setup kubectl and kubelogin
|
||||
|
||||
Setup `kubectl` to authenticate with your identity provider.
|
||||
|
||||
```sh
|
||||
kubectl config set-credentials CLUSTER_NAME \
|
||||
--auth-provider oidc \
|
||||
--auth-provider-arg idp-issuer-url=https://keycloak.example.com/auth/realms/hello \
|
||||
--auth-provider-arg client-id=kubernetes \
|
||||
--auth-provider-arg idp-issuer-url=https://accounts.google.com \
|
||||
--auth-provider-arg client-id=YOUR_CLIENT_ID.apps.googleusercontent.com \
|
||||
--auth-provider-arg client-secret=YOUR_CLIENT_SECRET
|
||||
```
|
||||
|
||||
Run `kubelogin`.
|
||||
Download [the latest release](https://github.com/int128/kubelogin/releases) and save it.
|
||||
|
||||
Run `kubelogin` and open http://localhost:8000 in your browser.
|
||||
|
||||
```
|
||||
% kubelogin
|
||||
2018/08/10 10:36:38 Reading .kubeconfig
|
||||
2018/08/10 10:36:38 Using current context: devops.hidetake.org
|
||||
2018/08/10 10:36:38 Using current context: hello.k8s.local
|
||||
2018/08/10 10:36:41 Open http://localhost:8000 for authorization
|
||||
2018/08/10 10:36:45 GET /
|
||||
2018/08/10 10:37:07 GET /?state=...&session_state=...&code=ey...
|
||||
2018/08/10 10:37:08 Updated .kubeconfig
|
||||
```
|
||||
|
||||
Now your `~/.kube/config` looks like:
|
||||
Now your `~/.kube/config` should be like:
|
||||
|
||||
```yaml
|
||||
# ~/.kube/config (snip)
|
||||
users:
|
||||
- name: hello.k8s.local
|
||||
user:
|
||||
auth-provider:
|
||||
config:
|
||||
idp-issuer-url: https://keycloak.example.com/auth/realms/hello
|
||||
client-id: kubernetes
|
||||
idp-issuer-url: https://accounts.google.com
|
||||
client-id: YOUR_CLIENT_ID.apps.googleusercontent.com
|
||||
client-secret: YOUR_SECRET
|
||||
id-token: ey... # kubelogin will update ID token here
|
||||
refresh-token: ey... # kubelogin will update refresh token here
|
||||
name: oidc
|
||||
```
|
||||
|
||||
Make sure you can access to the Kubernetes cluster:
|
||||
Make sure you can access to the Kubernetes cluster.
|
||||
|
||||
```
|
||||
% kubectl version
|
||||
Client Version: version.Info{...}
|
||||
Server Version: version.Info{...}
|
||||
% kubectl get nodes
|
||||
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
|
||||
```
|
||||
|
||||
|
||||
## Configuration
|
||||
## Getting Started with Keycloak
|
||||
|
||||
You can set the following environment variable:
|
||||
### 1. Setup Keycloak
|
||||
|
||||
- `KUBECONFIG` - Path to the config. Defaults to `~/.kube/config`.
|
||||
Create an OIDC client as follows:
|
||||
|
||||
|
||||
## Prerequisite
|
||||
|
||||
You have to setup your OIDC identity provider and Kubernetes cluster.
|
||||
|
||||
### 1. Setup OIDC Identity Provider
|
||||
|
||||
This tutorial assumes you have created an OIDC client with the following:
|
||||
|
||||
- Issuer URL: `https://keycloak.example.com/auth/realms/hello`
|
||||
- Redirect URL: `http://localhost:8000/`
|
||||
- Issuer URL: `https://keycloak.example.com/auth/realms/YOUR_REALM`
|
||||
- Client ID: `kubernetes`
|
||||
- Client Secret: `YOUR_CLIENT_SECRET`
|
||||
- Allowed redirect URLs: `http://localhost:8000/`
|
||||
- Groups claim: `groups` (optional for group based access controll)
|
||||
- Groups claim: `groups`
|
||||
|
||||
### 2. Setup Kubernetes API Server
|
||||
Then create a group `kubernetes:admin` and join to it.
|
||||
|
||||
Configure the Kubernetes API server allows your identity provider.
|
||||
### 2. Setup Kubernetes cluster
|
||||
|
||||
If you are using [kops](https://github.com/kubernetes/kops), `kops edit cluster` and append the following settings:
|
||||
Configure your Kubernetes API Server accepts [OpenID Connect Tokens](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens).
|
||||
If you are using [kops](https://github.com/kubernetes/kops), run `kops edit cluster` and append the following settings:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
kubeAPIServer:
|
||||
oidcIssuerURL: https://keycloak.example.com/auth/realms/YOUR_REALM
|
||||
oidcClientID: kubernetes
|
||||
oidcGroupsClaim: groups
|
||||
oidcIssuerURL: https://keycloak.example.com/auth/realms/hello
|
||||
```
|
||||
|
||||
### 3. Setup kubectl
|
||||
Here assign the `cluster-admin` role to the `kubernetes:admin` group.
|
||||
|
||||
Run the following command to configure `kubectl` to authenticate by your identity provider.
|
||||
```yaml
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: keycloak-admin-group
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: cluster-admin
|
||||
subjects:
|
||||
- kind: Group
|
||||
name: /kubernetes:admin
|
||||
```
|
||||
|
||||
### 3. Setup kubectl and kubelogin
|
||||
|
||||
Setup `kubectl` to authenticate with your identity provider.
|
||||
|
||||
```sh
|
||||
kubectl config set-credentials CLUSTER_NAME \
|
||||
--auth-provider oidc \
|
||||
--auth-provider-arg idp-issuer-url=https://keycloak.example.com/auth/realms/hello \
|
||||
--auth-provider-arg idp-issuer-url=https://keycloak.example.com/auth/realms/YOUR_REALM \
|
||||
--auth-provider-arg client-id=kubernetes \
|
||||
--auth-provider-arg client-secret=YOUR_CLIENT_SECRET
|
||||
```
|
||||
|
||||
In actual team operation, you can share the following config to your team members for easy setup.
|
||||
Download [the latest release](https://github.com/int128/kubelogin/releases) and save it.
|
||||
|
||||
```yaml
|
||||
#!/bin/sh
|
||||
Run `kubelogin` and make sure you can access to the cluster.
|
||||
See the previous section for details.
|
||||
|
||||
|
||||
## Tips
|
||||
|
||||
### Config file
|
||||
|
||||
You can set the environment variable `KUBECONFIG` to point the config file.
|
||||
Default to `~/.kube/config`.
|
||||
|
||||
```sh
|
||||
export KUBECONFIG="$PWD/.kubeconfig"
|
||||
```
|
||||
|
||||
### Setup script
|
||||
|
||||
In actual team operation, you can share the following script to your team members for easy setup.
|
||||
|
||||
```sh
|
||||
#!/bin/sh -xe
|
||||
CLUSTER_NAME="hello.k8s.local"
|
||||
|
||||
# Set the certificate
|
||||
mkdir -p "$HOME/.kube"
|
||||
cat > "$HOME/.kube/$CLUSTER_NAME.crt" <<EOF
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MII...
|
||||
-----END CERTIFICATE-----
|
||||
EOF
|
||||
export KUBECONFIG="$PWD/.kubeconfig"
|
||||
|
||||
# Set the cluster
|
||||
kubectl config set-cluster "$CLUSTER_NAME" \
|
||||
--server https://api-xxx.xxx.elb.amazonaws.com \
|
||||
--certificate-authority "$HOME/.kube/$CLUSTER_NAME.crt"
|
||||
--certificate-authority "$PWD/cluster.crt"
|
||||
|
||||
# Set the credentials
|
||||
kubectl config set-credentials "$CLUSTER_NAME" \
|
||||
--auth-provider oidc \
|
||||
--auth-provider-arg idp-issuer-url=https://keycloak.example.com/auth/realms/hello \
|
||||
--auth-provider-arg client-id=kubernetes \
|
||||
--auth-provider-arg client-secret=YOUR_SECRET
|
||||
--auth-provider-arg idp-issuer-url=https://accounts.google.com \
|
||||
--auth-provider-arg client-id=YOUR_CLIENT_ID.apps.googleusercontent.com \
|
||||
--auth-provider-arg client-secret=YOUR_CLIENT_SECRET
|
||||
|
||||
# Set the context
|
||||
kubectl config set-context "$CLUSTER_NAME" --cluster "$CLUSTER_NAME" --user "$CLUSTER_NAME"
|
||||
|
||||
# Set the current context
|
||||
kubectl config use-context "$CLUSTER_NAME"
|
||||
```
|
||||
|
||||
|
||||
@@ -2,37 +2,11 @@ package kubeconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
const userKubeConfig = "~/.kube/config"
|
||||
|
||||
// Find returns path to the kubeconfig file,
|
||||
// that is given by env:KUBECONFIG or ~/.kube/config.
|
||||
// This returns an error if it is not found or I/O error occurred.
|
||||
func Find() (string, error) {
|
||||
path := os.Getenv("KUBECONFIG")
|
||||
if path == "" {
|
||||
var err error
|
||||
path, err = homedir.Expand(userKubeConfig)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Could not expand %s: %s", userKubeConfig, err)
|
||||
}
|
||||
}
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Could not stat %s: %s", userKubeConfig, err)
|
||||
}
|
||||
if info.IsDir() {
|
||||
return "", fmt.Errorf("%s should be a file", userKubeConfig)
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
// Load loads the file and returns the Config.
|
||||
func Load(path string) (*api.Config, error) {
|
||||
config, err := clientcmd.LoadFromFile(path)
|
||||
|
||||
47
main.go
47
main.go
@@ -2,16 +2,53 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/int128/kubelogin/authn"
|
||||
"github.com/int128/kubelogin/kubeconfig"
|
||||
flags "github.com/jessevdk/go-flags"
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
path, err := kubeconfig.Find()
|
||||
type options struct {
|
||||
KubeConfig string `long:"kubeconfig" default:"~/.kube/config" env:"KUBECONFIG" description:"Path to the kubeconfig file"`
|
||||
SkipTLSVerify bool `long:"insecure-skip-tls-verify" env:"KUBELOGIN_INSECURE_SKIP_TLS_VERIFY" description:"If set, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure"`
|
||||
// CertificateAuthority string `long:"certificate-authority" env:"KUBELOGIN_CERTIFICATE_AUTHORITY" description:"Path to a cert file for the certificate authority"`
|
||||
}
|
||||
|
||||
func (o *options) ExpandKubeConfig() (string, error) {
|
||||
d, err := homedir.Expand(o.KubeConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not find kubeconfig: %s", err)
|
||||
return "", fmt.Errorf("Could not expand %s", o.KubeConfig)
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func parseOptions() (*options, error) {
|
||||
var o options
|
||||
parser := flags.NewParser(&o, flags.HelpFlag)
|
||||
args, err := parser.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(args) > 0 {
|
||||
return nil, fmt.Errorf("Too many argument")
|
||||
}
|
||||
return &o, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
opts, err := parseOptions()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
path, err := opts.ExpandKubeConfig()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Printf("Reading %s", path)
|
||||
cfg, err := kubeconfig.Load(path)
|
||||
@@ -28,7 +65,11 @@ func main() {
|
||||
log.Fatalf("Could not find auth-provider: %s", err)
|
||||
}
|
||||
|
||||
client := &http.Client{Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: opts.SkipTLSVerify},
|
||||
}}
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, oauth2.HTTPClient, client)
|
||||
token, err := authn.GetTokenSet(ctx, authProvider.IDPIssuerURL(), authProvider.ClientID(), authProvider.ClientSecret())
|
||||
if err != nil {
|
||||
log.Fatalf("Authentication error: %s", err)
|
||||
|
||||
Reference in New Issue
Block a user