Skip to content

Commit

Permalink
Merge pull request #3726 from justinsb/storage_acls
Browse files Browse the repository at this point in the history
Automatic merge from submit-queue.

GCS: Use ACLs for GCE permissions

This needs less permissions, but also allows for more granular control over
access to files than whole-bucket permissions that IAM gives us.
  • Loading branch information
Kubernetes Submit Queue committed Oct 30, 2017
2 parents 6834fe0 + b2bcba4 commit b02c3a2
Show file tree
Hide file tree
Showing 56 changed files with 869 additions and 119 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
2 changes: 2 additions & 0 deletions hack/.packages
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ k8s.io/kops/nodeup/pkg/bootstrap
k8s.io/kops/nodeup/pkg/distros
k8s.io/kops/nodeup/pkg/model
k8s.io/kops/nodeup/pkg/model/resources
k8s.io/kops/pkg/acls
k8s.io/kops/pkg/acls/gce
k8s.io/kops/pkg/apis/kops
k8s.io/kops/pkg/apis/kops/install
k8s.io/kops/pkg/apis/kops/model
Expand Down
2 changes: 1 addition & 1 deletion nodeup/pkg/bootstrap/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (i *Installation) Run() error {
}

checkExisting := true
context, err := fi.NewContext(target, cloud, keyStore, secretStore, configBase, checkExisting, tasks)
context, err := fi.NewContext(target, nil, cloud, keyStore, secretStore, configBase, checkExisting, tasks)
if err != nil {
return fmt.Errorf("error building context: %v", 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",
],
)
84 changes: 84 additions & 0 deletions pkg/acls/gce/storage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

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{})
}
28 changes: 28 additions & 0 deletions pkg/acls/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

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)
}
57 changes: 57 additions & 0 deletions pkg/acls/plugins.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

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
Loading

0 comments on commit b02c3a2

Please sign in to comment.