Skip to content

Commit

Permalink
Merge pull request #42 from ngtuna/gke-permission
Browse files Browse the repository at this point in the history
binding active account to the `cluster-admin` role in GKE
  • Loading branch information
ngtuna authored Nov 20, 2017
2 parents 0c04f9a + 4f49e15 commit 24abab1
Show file tree
Hide file tree
Showing 43 changed files with 6,821 additions and 8 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ VERSION = dev-$(shell date +%FT%T%z)
OS = linux
ARCH = amd64
BINARY = kubeapps
GO_PACKAGES = $(IMPORT_PATH)/cmd
GO_PACKAGES = $(IMPORT_PATH)/cmd $(IMPORT_PATH)/pkg/...
GO_FILES := $(shell find $(shell $(GOBIN) list -f '{{.Dir}}' $(GO_PACKAGES)) -name \*.go)
GO_FLAGS = -ldflags="-w -X github.com/kubeapps/installer/cmd.VERSION=${VERSION}"
EMBEDDED_STATIC = generated/statik/statik.go
Expand Down
1 change: 1 addition & 0 deletions cmd/down.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ var downCmd = &cobra.Command{
if err != nil {
return err
}

return c.Run(objs)
},
}
Expand Down
44 changes: 43 additions & 1 deletion cmd/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ package cmd

import (
"os"
"strings"

"github.com/ksonnet/kubecfg/metadata"
"github.com/ksonnet/kubecfg/pkg/kubecfg"
"github.com/kubeapps/installer/pkg/gke"
"github.com/spf13/cobra"
"k8s.io/client-go/discovery"
)

const (
Expand Down Expand Up @@ -73,11 +76,50 @@ List of components that kubeapps up installs:
return err
}

// k8s on GKE
if ok, err := isGKE(c.Discovery); err != nil {
return err
} else if ok {
gcloudPath, err := gke.SdkConfigPath()
if err != nil {
return err
}

user, err := gke.GetActiveUser(gcloudPath)
if err != nil {
return err
}

crb, err := gke.BuildCrbObject(user)
if err != nil {
return err
}

//(tuna): we force the deployment ordering here:
// this clusterrolebinding will be created before others for granting the proper permission.
// when the installation finishes, it will be gc'd immediately.
c.SkipGc = true
c.Run(crb, wd)
c.SkipGc = false
}

return c.Run(objs, wd)
},
}

func init() {
RootCmd.AddCommand(upCmd)
upCmd.Flags().Bool("dry-run", false, "Provides output to be submitted to the server")
upCmd.Flags().Bool("dry-run", false, "Provides output to be submitted to the server.")
}

func isGKE(disco discovery.DiscoveryInterface) (bool, error) {
sv, err := disco.ServerVersion()
if err != nil {
return false, err
}
if strings.Contains(sv.GitVersion, "gke") {
return true, nil
}

return false, nil
}
63 changes: 63 additions & 0 deletions cmd/up_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package cmd

import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"

"k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/discovery"
restclient "k8s.io/client-go/rest"
)

func TestIsGKE(t *testing.T) {
gkeVersion := version.Info{
Major: "foo",
Minor: "bar",
GitVersion: "foobar-gke",
}

nonGkeVersion := version.Info{
Major: "foo",
Minor: "bar",
GitVersion: "baz",
}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
output, err := json.Marshal(gkeVersion)
if err != nil {
t.Errorf("unexpected encoding error: %v", err)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(output)
}))
defer server.Close()
client := discovery.NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})

if ok, err := isGKE(client); err != nil {
t.Error(err)
} else if !ok {
t.Errorf("expect GKE but got non-GKE")
}

server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
output, err := json.Marshal(nonGkeVersion)
if err != nil {
t.Errorf("unexpected encoding error: %v", err)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(output)
}))
defer server.Close()
client = discovery.NewDiscoveryClientForConfigOrDie(&restclient.Config{Host: server.URL})

if ok, err := isGKE(client); err != nil {
t.Error(err)
} else if ok {
t.Errorf("expect non-GKE but got GKE")
}
}
10 changes: 6 additions & 4 deletions glide.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions glide.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import:
- tools/clientcmd
- plugin/pkg/client/auth/gcp
- package: github.com/ksonnet/kubecfg
version: 897a3db8a83ca195a2825b1fabe59ffca103e700
subpackages:
- utils
- metadata
Expand All @@ -23,3 +24,4 @@ import:
version: d4266bc12aae0b4128952c889b769442fce5d2f1
subpackages:
- unix
- package: github.com/go-ini/ini
96 changes: 96 additions & 0 deletions pkg/gke/gke.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package gke

import (
"fmt"
"io/ioutil"
"os"
"os/user"
"path/filepath"
"runtime"

"github.com/go-ini/ini"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

// BuildCrbObject builds the clusterrolebinding for granting
// cluster-admin permission to the active user.
func BuildCrbObject(user string) ([]*unstructured.Unstructured, error) {
crb := &unstructured.Unstructured{
Object: map[string]interface{}{
"kind": "ClusterRoleBinding",
"apiVersion": "rbac.authorization.k8s.io/v1beta1",
"metadata": map[string]interface{}{
"name": "kubeapps-cluster-admin",
},
"roleRef": map[string]interface{}{
"apiGroup": "rbac.authorization.k8s.io",
"kind": "ClusterRole",
"name": "cluster-admin",
},
"subjects": []map[string]interface{}{
{
"apiGroup": "rbac.authorization.k8s.io",
"kind": "User",
"name": user,
},
},
},
}
crbs := []*unstructured.Unstructured{}
crbs = append(crbs, crb)
return crbs, nil
}

// GetActiveUser returns user logging to gcloud
func GetActiveUser(gcloudPath string) (string, error) {
activeConfigPath := filepath.Join(gcloudPath, "active_config")
activeConfig, err := ioutil.ReadFile(activeConfigPath)
if err != nil {
return "", fmt.Errorf("can't read file active_config: %v", err)
}

configPath := filepath.Join(gcloudPath, "configurations")
if _, err := os.Stat(filepath.Join(configPath, "config_"+string(activeConfig))); os.IsNotExist(err) {
return "", fmt.Errorf("the config file for active_config doesn't exist: %v", err)
}

cfg, err := ini.Load(filepath.Join(configPath, "config_"+string(activeConfig)))
if err != nil {
return "", fmt.Errorf("can't load file for the active_config: %v", err)
}

core, err := cfg.GetSection("core")
if err != nil {
return "", fmt.Errorf("can't get section [core]: %v", err)
}
account, err := core.GetKey("account")
if err != nil {
return "", fmt.Errorf("can't get key account: %v", err)
}

return account.Value(), nil
}

// SdkConfigPath returns path to gcloud sdk config
var SdkConfigPath = func() (string, error) {
if runtime.GOOS == "windows" {
return filepath.Join(os.Getenv("APPDATA"), "gcloud"), nil
}
homeDir := guessUnixHomeDir()
if homeDir == "" {
return "", fmt.Errorf("unable to get current user home directory: os/user lookup failed; $HOME is empty")
}
return filepath.Join(homeDir, ".config", "gcloud"), nil
}

func guessUnixHomeDir() string {
// Prefer $HOME over user.Current due to glibc bug: golang.org/issue/13470
if v := os.Getenv("HOME"); v != "" {
return v
}
// Else, fall back to user.Current:
if u, err := user.Current(); err == nil {
return u.HomeDir
}
return ""
}
51 changes: 51 additions & 0 deletions pkg/gke/gke_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package gke

import (
"testing"
)

const (
SUCCESS_PATH = "testsdkconfig/success"
FAILURE_PATH = "testsdkconfig/failure"
)

func TestGetActiveUserSuccess(t *testing.T) {
email, err := GetActiveUser(SUCCESS_PATH)
if err != nil {
t.Error()
}
if email != "foo@example.com" {
t.Errorf("expected email = foo@example.com, got email = %s", email)
}
}

func TestGetActiveUserFailure(t *testing.T) {
email, err := GetActiveUser(FAILURE_PATH)
if err == nil {
t.Error()
}
if email != "" {
t.Errorf("expected email not found, got email = %s", email)
}
}

func TestBuildCrbObject(t *testing.T) {
user := "foo"
crd, err := BuildCrbObject(user)
if err != nil {
t.Errorf("expected crb object can be built successfully, got error %v", err)
}
if len(crd) != 1 {
t.Errorf("expected build a single srb, got %d elements", len(crd))
}

data := crd[0].UnstructuredContent()
sub := data["subjects"].([]map[string]interface{})
if len(sub) != 1 {
t.Errorf("expected to have only one subject, got %v", len(sub))
}

if sub[0]["name"].(string) != user {
t.Errorf("expected user %s should be loaded into crb object, got %s", user, sub[0]["name"].(string))
}
}
1 change: 1 addition & 0 deletions pkg/gke/testsdkconfig/failure/active_config
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test
5 changes: 5 additions & 0 deletions pkg/gke/testsdkconfig/failure/configurations/config_test
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[core]
foo = bar

[compute]
bar = baz
1 change: 1 addition & 0 deletions pkg/gke/testsdkconfig/success/active_config
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test
7 changes: 7 additions & 0 deletions pkg/gke/testsdkconfig/success/configurations/config_test
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[core]
account = foo@example.com
foo = bar

[baz]
bar = baz
foo = bar
5 changes: 5 additions & 0 deletions vendor/github.com/go-ini/ini/.github/ISSUE_TEMPLATE.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions vendor/github.com/go-ini/ini/.github/PULL_REQUEST_TEMPLATE.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions vendor/github.com/go-ini/ini/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions vendor/github.com/go-ini/ini/.travis.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 24abab1

Please sign in to comment.