Compare commits

..

4 Commits
1.1 ... 1.2

Author SHA1 Message Date
Hidetake Iwata
b776bac764 Add insecure-skip-tls-verify option 2018-08-16 09:34:12 +09:00
Hidetake Iwata
4bf77886a8 Introduce flags 2018-08-15 19:12:59 +09:00
Hidetake Iwata
ea711f91b4 Update README.md 2018-08-14 16:33:00 +09:00
Hidetake Iwata
cfc6376f69 Refactor README.md 2018-08-14 10:56:30 +09:00
3 changed files with 174 additions and 87 deletions

188
README.md
View File

@@ -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"
```

View File

@@ -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
View File

@@ -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)