Skip to content
This repository has been archived by the owner on Jan 3, 2023. It is now read-only.

Add oauth authorization via an --access-token flag. #231

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ Follow the instructions as detailed [here](/examples/zone-printer/README.md).

To create an HTTPS ingress, follow the instructions [here](/examples/zone-printer/https.md).

## Authorization

By default, kubemci relies on the discovery of [Application Default Credentials](https://cloud.google.com/docs/authentication/production#finding_credentials_automatically) to authorize access to GCP resources.

As an alternative, kubemci accepts an `--access-token` argument that takes an oauth access token,
such as one generated for a service account via `gcloud --impersonate-service-account ... auth print-access-token`.
This method obviates the need to distribute private keys.

## More information

We have a [video](https://www.youtube.com/watch?v=0_Yt_1yICfk) explaining what
Expand Down
7 changes: 6 additions & 1 deletion app/kubemci/cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"k8s.io/api/extensions/v1beta1"
kubeclient "k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce"

// gcp is needed for GKE cluster auth to work.
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"

Expand All @@ -48,6 +49,8 @@ type createOptions struct {
KubeconfigFilename string
// Names of the contexts to use from the kubeconfig file.
KubeContexts []string
// Access token with which to access gpc resources.
AccessToken string
// Name of the load balancer.
// Required.
LBName string
Expand Down Expand Up @@ -98,6 +101,8 @@ func addCreateFlags(cmd *cobra.Command, options *createOptions) error {
cmd.Flags().StringVarP(&options.IngressFilename, "ingress", "i", options.IngressFilename, "[required] filename containing ingress spec")
cmd.Flags().StringVarP(&options.KubeconfigFilename, "kubeconfig", "k", options.KubeconfigFilename, "[required] path to kubeconfig file")
cmd.Flags().StringSliceVar(&options.KubeContexts, "kubecontexts", options.KubeContexts, "[optional] contexts in the kubeconfig file to install the ingress into")
cmd.Flags().StringVarP(&options.AccessToken, "access-token", "t", options.AccessToken, "[optional] access token for gcp resources (defaults to GOOGLE_APPLICATION_CREDENTIALS).")

// TODO(nikhiljindal): Add a short flag "-p" if it seems useful.
cmd.Flags().StringVarP(&options.GCPProject, "gcp-project", "", options.GCPProject, "[optional] name of the gcp project. Is fetched using gcloud config get-value project if unset here")
cmd.Flags().BoolVarP(&options.ForceUpdate, "force", "f", options.ForceUpdate, "[optional] overwrite existing settings if they are different")
Expand Down Expand Up @@ -140,7 +145,7 @@ func runCreate(options *createOptions, args []string) error {
return fmt.Errorf("error in verifying static IP for ingress %s, err: %s", options.IngressFilename, err)
}

cloudInterface, err := cloudinterface.NewGCECloudInterface(options.GCPProject)
cloudInterface, err := cloudinterface.NewGCECloudInterface(options.GCPProject, options.AccessToken)
if err != nil {
return fmt.Errorf("error in creating cloud interface: %s", err)
}
Expand Down
6 changes: 5 additions & 1 deletion app/kubemci/cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
multierror "github.com/hashicorp/go-multierror"
"github.com/spf13/cobra"
"k8s.io/api/extensions/v1beta1"

// gcp is needed for GKE cluster auth to work.
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"

Expand All @@ -46,6 +47,8 @@ type deleteOptions struct {
KubeconfigFilename string
// Names of the contexts to use from the kubeconfig file.
KubeContexts []string
// Access token with which to access gpc resources.
AccessToken string
// Name of the load balancer.
// Required.
LBName string
Expand Down Expand Up @@ -86,6 +89,7 @@ func addDeleteFlags(cmd *cobra.Command, options *deleteOptions) error {
cmd.Flags().StringVarP(&options.IngressFilename, "ingress", "i", options.IngressFilename, "[required] filename containing ingress spec")
cmd.Flags().StringVarP(&options.KubeconfigFilename, "kubeconfig", "k", options.KubeconfigFilename, "[required] path to kubeconfig file")
cmd.Flags().StringSliceVar(&options.KubeContexts, "kubecontexts", options.KubeContexts, "[optional] contexts in the kubeconfig file to delete the ingress from")
cmd.Flags().StringVarP(&options.AccessToken, "access-token", "t", options.AccessToken, "[optional] access token for gcp resources (defaults to GOOGLE_APPLICATION_CREDENTIALS).")
// TODO(nikhiljindal): Add a short flag "-p" if it seems useful.
cmd.Flags().StringVarP(&options.GCPProject, "gcp-project", "", options.GCPProject, "[optional] name of the gcp project. Is fetched using gcloud config get-value project if unset here")
cmd.Flags().BoolVarP(&options.ForceDelete, "force", "f", options.ForceDelete, "[optional] delete whatever can be deleted in case of errors. This should only be used in exceptional cases (for example: when the clusters are deleted before the ingress was deleted)")
Expand Down Expand Up @@ -124,7 +128,7 @@ func runDelete(options *deleteOptions, args []string) error {
if ingErr := ingress.UnmarshallAndApplyDefaults(options.IngressFilename, options.Namespace, &ing); ingErr != nil {
return fmt.Errorf("error in unmarshalling the yaml file %s, err: %s", options.IngressFilename, ingErr)
}
cloudInterface, ciErr := cloudinterface.NewGCECloudInterface(options.GCPProject)
cloudInterface, ciErr := cloudinterface.NewGCECloudInterface(options.GCPProject, options.AccessToken)
if ciErr != nil {
return fmt.Errorf("error in creating cloud interface: %s", ciErr)
}
Expand Down
5 changes: 4 additions & 1 deletion app/kubemci/cmd/getstatus.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ type getStatusOptions struct {
// Required
// TODO(nikhiljindal): This should be optional. Figure it out from gcloud settings.
GCPProject string
// Access token with which to access gpc resources.
AccessToken string
}

func newCmdGetStatus(out, err io.Writer) *cobra.Command {
Expand Down Expand Up @@ -69,6 +71,7 @@ func newCmdGetStatus(out, err io.Writer) *cobra.Command {
func addGetStatusFlags(cmd *cobra.Command, options *getStatusOptions) error {
// TODO(nikhiljindal): Add a short flag "-p" if it seems useful.
cmd.Flags().StringVarP(&options.GCPProject, "gcp-project", "", options.GCPProject, "[optional] name of the gcp project. Is fetched using gcloud config get-value project if unset here")
cmd.Flags().StringVarP(&options.AccessToken, "access-token", "t", options.AccessToken, "[optional] access token for gcp resources (defaults to GOOGLE_APPLICATION_CREDENTIALS).")
// TODO Add a verbose flag that turns on glog logging.
return nil
}
Expand All @@ -92,7 +95,7 @@ func validateGetStatusArgs(options *getStatusOptions, args []string) error {
func runGetStatus(options *getStatusOptions, args []string) error {
options.LBName = args[0]

cloudInterface, err := cloudinterface.NewGCECloudInterface(options.GCPProject)
cloudInterface, err := cloudinterface.NewGCECloudInterface(options.GCPProject, options.AccessToken)
if err != nil {
return fmt.Errorf("error in creating cloud interface: %s", err)
}
Expand Down
5 changes: 4 additions & 1 deletion app/kubemci/cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ type listOptions struct {
// Required
// TODO(nikhiljindal): This should be optional. Figure it out from gcloud settings.
GCPProject string
// Access token with which to access gpc resources.
AccessToken string
}

func newCmdList(out, err io.Writer) *cobra.Command {
Expand All @@ -63,6 +65,7 @@ func newCmdList(out, err io.Writer) *cobra.Command {

func addListFlags(cmd *cobra.Command, options *listOptions) error {
cmd.Flags().StringVarP(&options.GCPProject, "gcp-project", "", options.GCPProject, "[optional] name of the gcp project. Is fetched using 'gcloud config get-value project' if unset here")
cmd.Flags().StringVarP(&options.AccessToken, "access-token", "t", options.AccessToken, "[optional] access token for gcp resources (defaults to GOOGLE_APPLICATION_CREDENTIALS).")
return nil
}

Expand All @@ -84,7 +87,7 @@ func validateListArgs(options *listOptions, args []string) error {

// runList prints a list of all mcis that we've created.
func runList(options *listOptions, args []string) error {
cloudInterface, err := cloudinterface.NewGCECloudInterface(options.GCPProject)
cloudInterface, err := cloudinterface.NewGCECloudInterface(options.GCPProject, options.AccessToken)
if err != nil {
fmt.Println("error creating cloud interface:", err)
return fmt.Errorf("error in creating cloud interface: %s", err)
Expand Down
6 changes: 5 additions & 1 deletion app/kubemci/cmd/remove_clusters.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
multierror "github.com/hashicorp/go-multierror"
"github.com/spf13/cobra"
"k8s.io/api/extensions/v1beta1"

// gcp is needed for GKE cluster auth to work.
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"

Expand Down Expand Up @@ -51,6 +52,8 @@ type removeClustersOptions struct {
KubeconfigFilename string
// Names of the contexts to use from the kubeconfig file.
KubeContexts []string
// Access token with which to access gpc resources.
AccessToken string
// Name of the load balancer.
// Required.
LBName string
Expand Down Expand Up @@ -89,6 +92,7 @@ func addRemoveClustersFlags(cmd *cobra.Command, options *removeClustersOptions)
cmd.Flags().StringVarP(&options.IngressFilename, "ingress", "i", options.IngressFilename, "[required] filename containing ingress spec")
cmd.Flags().StringVarP(&options.KubeconfigFilename, "kubeconfig", "k", options.KubeconfigFilename, "[required] path to kubeconfig file")
cmd.Flags().StringSliceVar(&options.KubeContexts, "kubecontexts", options.KubeContexts, "[optional] contexts in the kubeconfig file to remove the ingress from")
cmd.Flags().StringVarP(&options.AccessToken, "access-token", "t", options.AccessToken, "[optional] access token for gcp resources (defaults to GOOGLE_APPLICATION_CREDENTIALS).")
cmd.Flags().StringVarP(&options.GCPProject, "gcp-project", "", options.GCPProject, "[required] name of the gcp project")
cmd.Flags().BoolVarP(&options.ForceUpdate, "force", "f", options.ForceUpdate, "[optional] overwrite existing settings if they are different")
cmd.Flags().StringVarP(&options.Namespace, "namespace", "n", options.Namespace, "[optional] namespace for the ingress only if left unspecified by ingress spec")
Expand Down Expand Up @@ -125,7 +129,7 @@ func runRemoveClusters(options *removeClustersOptions, args []string) error {
if err := ingress.UnmarshallAndApplyDefaults(options.IngressFilename, options.Namespace, &ing); err != nil {
return fmt.Errorf("error in unmarshalling the yaml file %s, err: %s", options.IngressFilename, err)
}
cloudInterface, err := cloudinterface.NewGCECloudInterface(options.GCPProject)
cloudInterface, err := cloudinterface.NewGCECloudInterface(options.GCPProject, options.AccessToken)
if err != nil {
err := fmt.Errorf("error in creating cloud interface: %s", err)
fmt.Println(err)
Expand Down
13 changes: 9 additions & 4 deletions app/kubemci/pkg/gcp/cloudinterface/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,27 @@
package cloudinterface

import (
"golang.org/x/oauth2"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce"
)

// NewGCECloudInterface returns a new GCECloud.
func NewGCECloudInterface(projectID string) (*gce.GCECloud, error) {
config := getCloudConfig(projectID)
func NewGCECloudInterface(projectID, accessToken string) (*gce.GCECloud, error) {
config := getCloudConfig(projectID, accessToken)
return gce.CreateGCECloud(&config)
}

func getCloudConfig(projectID string) gce.CloudConfig {
return gce.CloudConfig{
func getCloudConfig(projectID, accessToken string) gce.CloudConfig {
c := gce.CloudConfig{
ProjectID: projectID,
// TODO(nikhiljindal): Set the following properties.
// ApiEndpoint
// NetworkProjectID: Is different than project ID for projects with XPN enabled.
// Zone, Region
// NetworkName, SubnetworkName
}
if len(accessToken) > 0 {
c.TokenSource = oauth2.StaticTokenSource(&oauth2.Token{AccessToken: accessToken})
}
return c
}