mirror of https://github.com/kubernetes/kops.git
Support public buckets for serviceAccountIssuers on S3
S3 is also nudging towards bucket level permissions, so don't set an ACL when bucket is public.
This commit is contained in:
parent
994f7a17b4
commit
f4984dafab
|
@ -18,6 +18,7 @@ package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
@ -27,9 +28,11 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"gopkg.in/square/go-jose.v2"
|
"gopkg.in/square/go-jose.v2"
|
||||||
|
"k8s.io/klog/v2"
|
||||||
"k8s.io/kops/pkg/apis/kops"
|
"k8s.io/kops/pkg/apis/kops"
|
||||||
"k8s.io/kops/upup/pkg/fi"
|
"k8s.io/kops/upup/pkg/fi"
|
||||||
"k8s.io/kops/upup/pkg/fi/fitasks"
|
"k8s.io/kops/upup/pkg/fi/fitasks"
|
||||||
|
"k8s.io/kops/util/pkg/vfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IssuerDiscoveryModelBuilder publish OIDC issuer discovery metadata
|
// IssuerDiscoveryModelBuilder publish OIDC issuer discovery metadata
|
||||||
|
@ -51,6 +54,8 @@ type oidcDiscovery struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *IssuerDiscoveryModelBuilder) Build(c *fi.ModelBuilderContext) error {
|
func (b *IssuerDiscoveryModelBuilder) Build(c *fi.ModelBuilderContext) error {
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
said := b.Cluster.Spec.ServiceAccountIssuerDiscovery
|
said := b.Cluster.Spec.ServiceAccountIssuerDiscovery
|
||||||
if said == nil || said.DiscoveryStore == "" {
|
if said == nil || said.DiscoveryStore == "" {
|
||||||
return nil
|
return nil
|
||||||
|
@ -71,13 +76,41 @@ func (b *IssuerDiscoveryModelBuilder) Build(c *fi.ModelBuilderContext) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
publicFileACL := fi.PtrTo(true)
|
||||||
|
|
||||||
|
discoveryStorePath := b.Cluster.Spec.ServiceAccountIssuerDiscovery.DiscoveryStore
|
||||||
|
discoveryStore, err := vfs.Context.BuildVfsPath(discoveryStorePath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("building VFS path for %q: %w", discoveryStorePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch discoveryStore := discoveryStore.(type) {
|
||||||
|
case *vfs.S3Path:
|
||||||
|
isPublic, err := discoveryStore.IsBucketPublic(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("checking if bucket was public: %w", err)
|
||||||
|
}
|
||||||
|
if !isPublic {
|
||||||
|
klog.Infof("serviceAccountIssuers bucket %q is not public, will use object ACL", discoveryStore.Bucket())
|
||||||
|
} else {
|
||||||
|
publicFileACL = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
case *vfs.MemFSPath:
|
||||||
|
// ok
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unhandled type %T", discoveryStore)
|
||||||
|
}
|
||||||
|
|
||||||
keysFile := &fitasks.ManagedFile{
|
keysFile := &fitasks.ManagedFile{
|
||||||
Contents: keys,
|
Contents: keys,
|
||||||
Lifecycle: b.Lifecycle,
|
Lifecycle: b.Lifecycle,
|
||||||
Location: fi.PtrTo("openid/v1/jwks"),
|
Location: fi.PtrTo("openid/v1/jwks"),
|
||||||
Name: fi.PtrTo("keys.json"),
|
Name: fi.PtrTo("keys.json"),
|
||||||
Base: fi.PtrTo(b.Cluster.Spec.ServiceAccountIssuerDiscovery.DiscoveryStore),
|
Base: fi.PtrTo(discoveryStorePath),
|
||||||
PublicACL: fi.PtrTo(true),
|
PublicACL: publicFileACL,
|
||||||
}
|
}
|
||||||
c.AddTask(keysFile)
|
c.AddTask(keysFile)
|
||||||
|
|
||||||
|
@ -86,8 +119,8 @@ func (b *IssuerDiscoveryModelBuilder) Build(c *fi.ModelBuilderContext) error {
|
||||||
Lifecycle: b.Lifecycle,
|
Lifecycle: b.Lifecycle,
|
||||||
Location: fi.PtrTo(".well-known/openid-configuration"),
|
Location: fi.PtrTo(".well-known/openid-configuration"),
|
||||||
Name: fi.PtrTo("discovery.json"),
|
Name: fi.PtrTo("discovery.json"),
|
||||||
Base: fi.PtrTo(b.Cluster.Spec.ServiceAccountIssuerDiscovery.DiscoveryStore),
|
Base: fi.PtrTo(discoveryStorePath),
|
||||||
PublicACL: fi.PtrTo(true),
|
PublicACL: publicFileACL,
|
||||||
}
|
}
|
||||||
c.AddTask(discoveryFile)
|
c.AddTask(discoveryFile)
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ package vfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -503,6 +504,61 @@ func (p *S3Path) GetHTTPsUrl(dualstack bool) (string, error) {
|
||||||
return strings.TrimSuffix(url, "/"), nil
|
return strings.TrimSuffix(url, "/"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *S3Path) IsBucketPublic(ctx context.Context) (bool, error) {
|
||||||
|
client, err := p.client()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := client.GetBucketPolicyStatusWithContext(ctx, &s3.GetBucketPolicyStatusInput{})
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("from AWS S3 GetBucketPolicyStatusWithContext: %w", err)
|
||||||
|
}
|
||||||
|
if aws.BoolValue(result.PolicyStatus.IsPublic) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
|
||||||
|
// We could check bucket ACLs also...
|
||||||
|
|
||||||
|
// acl, err := client.GetBucketAclWithContext(ctx, &s3.GetBucketAclInput{
|
||||||
|
// Bucket: &p.bucket,
|
||||||
|
// })
|
||||||
|
// if err != nil {
|
||||||
|
// return false, fmt.Errorf("failed to get ACL for bucket %q: %w", p.bucket, err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// allowsAnonymousRead := false
|
||||||
|
// for _, grant := range acl.Grants {
|
||||||
|
// isAllUsers := false
|
||||||
|
|
||||||
|
// switch aws.StringValue(grant.Grantee.URI) {
|
||||||
|
// case "http://acs.amazonaws.com/groups/global/AllUsers":
|
||||||
|
// isAllUsers = true
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if isAllUsers {
|
||||||
|
// permission := aws.StringValue(grant.Permission)
|
||||||
|
// switch permission {
|
||||||
|
// case "FULL_CONTROL":
|
||||||
|
// klog.Warningf("bucket %q allows anonymous users full access", p.bucket)
|
||||||
|
// allowsAnonymousRead = true
|
||||||
|
// case "WRITE", "WRITE_ACP":
|
||||||
|
// klog.Warningf("bucket %q allows anonymous users write access", p.bucket)
|
||||||
|
// // it's not _read_ access
|
||||||
|
// case "READ":
|
||||||
|
// allowsAnonymousRead = true
|
||||||
|
// case "READ_ACP":
|
||||||
|
// // does not grant read
|
||||||
|
// default:
|
||||||
|
// klog.Warningf("bucket %q has unknown permission %q for anonymous access", p.bucket, permission)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return allowsAnonymousRead, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (p *S3Path) IsPublic() (bool, error) {
|
func (p *S3Path) IsPublic() (bool, error) {
|
||||||
client, err := p.client()
|
client, err := p.client()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in New Issue