Skip to content

Commit

Permalink
GCE: Use object-level permissions for files in GCS
Browse files Browse the repository at this point in the history
This lets us configure cross-project permissions while ourselves needing
minimal permissions, but also gives us a nice hook for future lockdown
of object-level permissions.
  • Loading branch information
justinsb committed Oct 29, 2017
1 parent d1ee802 commit 8ae266b
Show file tree
Hide file tree
Showing 42 changed files with 386 additions and 107 deletions.
2 changes: 1 addition & 1 deletion cmd/kops/create_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -996,7 +996,7 @@ func RunCreateCluster(f *util.Factory, out io.Writer, c *CreateClusterOptions) e
return err
}

err = registry.WriteConfigDeprecated(configBase.Join(registry.PathClusterCompleted), fullCluster)
err = registry.WriteConfigDeprecated(cluster, configBase.Join(registry.PathClusterCompleted), fullCluster)
if err != nil {
return fmt.Errorf("error writing completed cluster spec: %v", err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/kops/edit_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ func RunEditCluster(f *util.Factory, cmd *cobra.Command, args []string, out io.W
return preservedFile(err, file, out)
}

err = registry.WriteConfigDeprecated(configBase.Join(registry.PathClusterCompleted), fullCluster)
err = registry.WriteConfigDeprecated(newCluster, configBase.Join(registry.PathClusterCompleted), fullCluster)
if err != nil {
return preservedFile(fmt.Errorf("error writing completed cluster spec: %v", err), file, out)
}
Expand Down
1 change: 1 addition & 0 deletions cmd/kops/util/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go_library(
srcs = ["factory.go"],
visibility = ["//visibility:public"],
deps = [
"//pkg/acls/gce:go_default_library",
"//pkg/client/clientset_generated/clientset:go_default_library",
"//pkg/client/simple:go_default_library",
"//pkg/client/simple/api:go_default_library",
Expand Down
15 changes: 9 additions & 6 deletions cmd/kops/util/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,18 @@ package util

import (
"fmt"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/kops/pkg/client/simple"
"k8s.io/kops/pkg/client/simple/vfsclientset"
"k8s.io/kops/util/pkg/vfs"
"net/url"
"strings"

"github.com/golang/glog"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/client-go/rest"
gceacls "k8s.io/kops/pkg/acls/gce"
kopsclient "k8s.io/kops/pkg/client/clientset_generated/clientset"
"k8s.io/kops/pkg/client/simple"
"k8s.io/kops/pkg/client/simple/api"
"net/url"
"strings"
"k8s.io/kops/pkg/client/simple/vfsclientset"
"k8s.io/kops/util/pkg/vfs"
)

type FactoryOptions struct {
Expand All @@ -41,6 +42,8 @@ type Factory struct {
}

func NewFactory(options *FactoryOptions) *Factory {
gceacls.Register()

return &Factory{
options: options,
}
Expand Down
2 changes: 1 addition & 1 deletion federation/apply_federation.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ func (o *ApplyFederationOperation) federationContextForCluster(cluster *kopsapi.
federationKeystore := k8sapi.NewKubernetesKeystore(target.KubernetesClient, o.namespace)

checkExisting := true
context, err := fi.NewContext(target, nil, federationKeystore, nil, nil, checkExisting, nil)
context, err := fi.NewContext(target, nil, nil, federationKeystore, nil, nil, checkExisting, nil)
if err != nil {
return nil, err
}
Expand Down
14 changes: 14 additions & 0 deletions pkg/acls/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")

go_library(
name = "go_default_library",
srcs = [
"interface.go",
"plugins.go",
],
visibility = ["//visibility:public"],
deps = [
"//pkg/apis/kops:go_default_library",
"//util/pkg/vfs:go_default_library",
],
)
15 changes: 15 additions & 0 deletions pkg/acls/gce/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")

go_library(
name = "go_default_library",
srcs = ["storage.go"],
visibility = ["//visibility:public"],
deps = [
"//pkg/acls:go_default_library",
"//pkg/apis/kops:go_default_library",
"//upup/pkg/fi/cloudup:go_default_library",
"//upup/pkg/fi/cloudup/gce:go_default_library",
"//util/pkg/vfs:go_default_library",
"//vendor/google.golang.org/api/storage/v1:go_default_library",
],
)
68 changes: 68 additions & 0 deletions pkg/acls/gce/storage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package gce

import (
"fmt"

storage "google.golang.org/api/storage/v1"
"k8s.io/kops/pkg/acls"
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/upup/pkg/fi/cloudup"
"k8s.io/kops/upup/pkg/fi/cloudup/gce"
"k8s.io/kops/util/pkg/vfs"
)

// gcsAclStrategy is the AclStrategy for objects written to google cloud storage
type gcsAclStrategy struct {
}

var _ acls.ACLStrategy = &gcsAclStrategy{}

// GetACL returns the ACL to use if this is a google cloud storage path
func (s *gcsAclStrategy) GetACL(p vfs.Path, cluster *kops.Cluster) (vfs.ACL, error) {
if kops.CloudProviderID(cluster.Spec.CloudProvider) != kops.CloudProviderGCE {
return nil, nil
}
gcsPath, ok := p.(*vfs.GSPath)
if !ok {
return nil, nil
}

bucketName := gcsPath.Bucket()
client := gcsPath.Client()

// TODO: Cache?
bucket, err := client.Buckets.Get(bucketName).Do()
if err != nil {
return nil, fmt.Errorf("error querying bucket %q: %v", bucketName, err)
}

// TODO: Cache?
cloud, err := cloudup.BuildCloud(cluster)
if err != nil {
return nil, err
}

serviceAccount, err := cloud.(gce.GCECloud).ServiceAccount()
if err != nil {
return nil, err
}

var acls []*storage.ObjectAccessControl
for _, a := range bucket.DefaultObjectAcl {
acls = append(acls, a)
}

acls = append(acls, &storage.ObjectAccessControl{
Email: serviceAccount,
Entity: "user-" + serviceAccount,
Role: "READER",
})

return &vfs.GSAcl{
Acl: acls,
}, nil
}

func Register() {
acls.RegisterPlugin("k8s.io/kops/acl/gce", &gcsAclStrategy{})
}
12 changes: 12 additions & 0 deletions pkg/acls/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package acls

import (
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/util/pkg/vfs"
)

// ACLStrategy is the interface implemented by ACL strategy providers
type ACLStrategy interface {
// GetACL returns the ACL if this strategy handles the vfs.Path, when writing for the specified cluster
GetACL(p vfs.Path, cluster *kops.Cluster) (vfs.ACL, error)
}
41 changes: 41 additions & 0 deletions pkg/acls/plugins.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package acls

import (
"fmt"
"sync"

"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/util/pkg/vfs"
)

var strategies map[string]ACLStrategy
var strategiesMutex sync.Mutex

// GetACL returns the ACL for the vfs.Path, by consulting all registered strategies
func GetACL(p vfs.Path, cluster *kops.Cluster) (vfs.ACL, error) {
strategiesMutex.Lock()
defer strategiesMutex.Unlock()

for k, strategy := range strategies {
acl, err := strategy.GetACL(p, cluster)
if err != nil {
return nil, fmt.Errorf("error from acl provider %q: %v", k, err)
}
if acl != nil {
return acl, nil
}
}
return nil, nil
}

// RegisterPlugin adds the strategy to the registered strategies
func RegisterPlugin(key string, strategy ACLStrategy) {
strategiesMutex.Lock()
defer strategiesMutex.Unlock()

if strategies == nil {
strategies = make(map[string]ACLStrategy)
}

strategies[key] = strategy
}
1 change: 1 addition & 0 deletions pkg/apis/kops/registry/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ go_library(
],
visibility = ["//visibility:public"],
deps = [
"//pkg/acls:go_default_library",
"//pkg/apis/kops:go_default_library",
"//pkg/client/simple:go_default_library",
"//upup/pkg/fi/utils:go_default_library",
Expand Down
18 changes: 13 additions & 5 deletions pkg/apis/kops/registry/statestore.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ package registry

import (
"fmt"
"k8s.io/kops/upup/pkg/fi/utils"
"k8s.io/kops/util/pkg/vfs"
"os"
"strings"

"k8s.io/kops/pkg/acls"
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/upup/pkg/fi/utils"
"k8s.io/kops/util/pkg/vfs"
)

func ReadConfigDeprecated(configPath vfs.Path, config interface{}) error {
Expand Down Expand Up @@ -49,7 +52,7 @@ func ReadConfigDeprecated(configPath vfs.Path, config interface{}) error {

// WriteConfigDeprecated writes a config file as yaml.
// It is deprecated because it is unversioned, but it is still used, in particular for writing the completed config.
func WriteConfigDeprecated(configPath vfs.Path, config interface{}, writeOptions ...vfs.WriteOption) error {
func WriteConfigDeprecated(cluster *kops.Cluster, configPath vfs.Path, config interface{}, writeOptions ...vfs.WriteOption) error {
data, err := utils.YamlMarshal(config)
if err != nil {
return fmt.Errorf("error marshalling configuration: %v", err)
Expand All @@ -73,10 +76,15 @@ func WriteConfigDeprecated(configPath vfs.Path, config interface{}, writeOptions
}
}

acl, err := acls.GetACL(configPath, cluster)
if err != nil {
return err
}

if create {
err = configPath.CreateFile(data)
err = configPath.CreateFile(data, acl)
} else {
err = configPath.WriteFile(data)
err = configPath.WriteFile(data, acl)
}
if err != nil {
return fmt.Errorf("error writing configuration file %s: %v", configPath, err)
Expand Down
4 changes: 2 additions & 2 deletions pkg/client/simple/api/clientset.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,12 @@ func (c *RESTClientset) GetFederation(name string) (*kops.Federation, error) {

func (c *RESTClientset) SecretStore(cluster *kops.Cluster) (fi.SecretStore, error) {
namespace := restNamespaceForClusterName(cluster.Name)
return secrets.NewClientsetSecretStore(c.KopsClient, namespace), nil
return secrets.NewClientsetSecretStore(cluster, c.KopsClient, namespace), nil
}

func (c *RESTClientset) KeyStore(cluster *kops.Cluster) (fi.CAStore, error) {
namespace := restNamespaceForClusterName(cluster.Name)
return fi.NewClientsetCAStore(c.KopsClient, namespace), nil
return fi.NewClientsetCAStore(cluster, c.KopsClient, namespace), nil
}

func (c *RESTClientset) DeleteCluster(cluster *kops.Cluster) error {
Expand Down
1 change: 1 addition & 0 deletions pkg/client/simple/vfsclientset/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ go_library(
],
visibility = ["//visibility:public"],
deps = [
"//pkg/acls:go_default_library",
"//pkg/apis/kops:go_default_library",
"//pkg/apis/kops/registry:go_default_library",
"//pkg/apis/kops/v1alpha1:go_default_library",
Expand Down
7 changes: 3 additions & 4 deletions pkg/client/simple/vfsclientset/clientset.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ func (c *VFSClientset) ConfigBaseFor(cluster *kops.Cluster) (vfs.Path, error) {

// InstanceGroupsFor implements the InstanceGroupsFor method of simple.Clientset for a VFS-backed state store
func (c *VFSClientset) InstanceGroupsFor(cluster *kops.Cluster) kopsinternalversion.InstanceGroupInterface {
clusterName := cluster.Name
return newInstanceGroupVFS(c, clusterName)
return newInstanceGroupVFS(c, cluster)
}

func (c *VFSClientset) federations() kopsinternalversion.FederationInterface {
Expand Down Expand Up @@ -99,7 +98,7 @@ func (c *VFSClientset) SecretStore(cluster *kops.Cluster) (fi.SecretStore, error
return nil, err
}
basedir := configBase.Join("secrets")
return secrets.NewVFSSecretStore(basedir), nil
return secrets.NewVFSSecretStore(cluster, basedir), nil
}

func (c *VFSClientset) KeyStore(cluster *kops.Cluster) (fi.CAStore, error) {
Expand All @@ -108,7 +107,7 @@ func (c *VFSClientset) KeyStore(cluster *kops.Cluster) (fi.CAStore, error) {
return nil, err
}
basedir := configBase.Join("pki")
return fi.NewVFSCAStore(basedir), nil
return fi.NewVFSCAStore(cluster, basedir), nil
}

func DeleteAllClusterState(basePath vfs.Path) error {
Expand Down
4 changes: 2 additions & 2 deletions pkg/client/simple/vfsclientset/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func (r *ClusterVFS) Create(c *api.Cluster) (*api.Cluster, error) {
return nil, fmt.Errorf("clusterName is required")
}

if err := r.writeConfig(r.basePath.Join(clusterName, registry.PathCluster), c, vfs.WriteOptionCreate); err != nil {
if err := r.writeConfig(c, r.basePath.Join(clusterName, registry.PathCluster), c, vfs.WriteOptionCreate); err != nil {
if os.IsExist(err) {
return nil, err
}
Expand All @@ -126,7 +126,7 @@ func (r *ClusterVFS) Update(c *api.Cluster, status *api.ClusterStatus) (*api.Clu
return nil, err
}

if err := r.writeConfig(r.basePath.Join(clusterName, registry.PathCluster), c, vfs.WriteOptionOnlyIfExists); err != nil {
if err := r.writeConfig(c, r.basePath.Join(clusterName, registry.PathCluster), c, vfs.WriteOptionOnlyIfExists); err != nil {
if os.IsNotExist(err) {
return nil, err
}
Expand Down
Loading

0 comments on commit 8ae266b

Please sign in to comment.