Browse Source
Merge pull request #1596 from kmlebedev/store_s3cred
Merge pull request #1596 from kmlebedev/store_s3cred
S3 credentials store in filerpull/1650/head
Chris Lu
4 years ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 374 additions and 15 deletions
-
44weed/s3api/auth_credentials.go
-
6weed/s3api/auto_signature_v4_test.go
-
3weed/s3api/filer_util.go
-
2weed/s3api/s3api_server.go
-
95weed/s3iam/s3iam_filer_store.go
-
65weed/s3iam/s3iam_filer_store_test.go
-
174weed/shell/command_s3_configure.go
@ -0,0 +1,95 @@ |
|||||
|
package s3iam |
||||
|
|
||||
|
import ( |
||||
|
"github.com/chrislusf/seaweedfs/weed/pb/filer_pb" |
||||
|
"github.com/chrislusf/seaweedfs/weed/pb/iam_pb" |
||||
|
"time" |
||||
|
|
||||
|
proto "github.com/golang/protobuf/proto" |
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
iamConfigPrefix = "/etc/iam" |
||||
|
iamIdentityFile = "identity.json" |
||||
|
) |
||||
|
|
||||
|
type IAMFilerStore struct { |
||||
|
client *filer_pb.SeaweedFilerClient |
||||
|
} |
||||
|
|
||||
|
func NewIAMFilerStore(client *filer_pb.SeaweedFilerClient) *IAMFilerStore { |
||||
|
return &IAMFilerStore{client: client} |
||||
|
} |
||||
|
|
||||
|
func (ifs *IAMFilerStore) getIAMConfigRequest() *filer_pb.LookupDirectoryEntryRequest { |
||||
|
return &filer_pb.LookupDirectoryEntryRequest{ |
||||
|
Directory: iamConfigPrefix, |
||||
|
Name: iamIdentityFile, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (ifs *IAMFilerStore) LoadIAMConfig(config *iam_pb.S3ApiConfiguration) error { |
||||
|
resp, err := filer_pb.LookupEntry(*ifs.client, ifs.getIAMConfigRequest()) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
err = ifs.loadIAMConfigFromEntry(resp.Entry, config) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func (ifs *IAMFilerStore) SaveIAMConfig(config *iam_pb.S3ApiConfiguration) error { |
||||
|
entry := &filer_pb.Entry{ |
||||
|
Name: iamIdentityFile, |
||||
|
IsDirectory: false, |
||||
|
Attributes: &filer_pb.FuseAttributes{ |
||||
|
Mtime: time.Now().Unix(), |
||||
|
Crtime: time.Now().Unix(), |
||||
|
FileMode: uint32(0644), |
||||
|
Collection: "", |
||||
|
Replication: "", |
||||
|
}, |
||||
|
Content: []byte{}, |
||||
|
} |
||||
|
err := ifs.saveIAMConfigToEntry(entry, config) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
_, err = filer_pb.LookupEntry(*ifs.client, ifs.getIAMConfigRequest()) |
||||
|
if err == filer_pb.ErrNotFound { |
||||
|
err = filer_pb.CreateEntry(*ifs.client, &filer_pb.CreateEntryRequest{ |
||||
|
Directory: iamConfigPrefix, |
||||
|
Entry: entry, |
||||
|
IsFromOtherCluster: false, |
||||
|
Signatures: nil, |
||||
|
}) |
||||
|
} else { |
||||
|
err = filer_pb.UpdateEntry(*ifs.client, &filer_pb.UpdateEntryRequest{ |
||||
|
Directory: iamConfigPrefix, |
||||
|
Entry: entry, |
||||
|
IsFromOtherCluster: false, |
||||
|
Signatures: nil, |
||||
|
}) |
||||
|
} |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func (ifs *IAMFilerStore) loadIAMConfigFromEntry(entry *filer_pb.Entry, config *iam_pb.S3ApiConfiguration) error { |
||||
|
if err := proto.Unmarshal(entry.Content, config); err != nil { |
||||
|
return err |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func (ifs *IAMFilerStore) saveIAMConfigToEntry(entry *filer_pb.Entry, config *iam_pb.S3ApiConfiguration) (err error) { |
||||
|
entry.Content, err = proto.Marshal(config) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
return nil |
||||
|
} |
@ -0,0 +1,65 @@ |
|||||
|
package s3iam |
||||
|
|
||||
|
import ( |
||||
|
"testing" |
||||
|
|
||||
|
"github.com/chrislusf/seaweedfs/weed/pb/filer_pb" |
||||
|
"github.com/chrislusf/seaweedfs/weed/pb/iam_pb" |
||||
|
|
||||
|
"github.com/stretchr/testify/assert" |
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
ACTION_READ = "Read" |
||||
|
ACTION_WRITE = "Write" |
||||
|
ACTION_ADMIN = "Admin" |
||||
|
ACTION_TAGGING = "Tagging" |
||||
|
ACTION_LIST = "List" |
||||
|
) |
||||
|
|
||||
|
func TestS3Conf(t *testing.T) { |
||||
|
ifs := &IAMFilerStore{} |
||||
|
s3Conf := &iam_pb.S3ApiConfiguration{ |
||||
|
Identities: []*iam_pb.Identity{ |
||||
|
{ |
||||
|
Name: "some_name", |
||||
|
Credentials: []*iam_pb.Credential{ |
||||
|
{ |
||||
|
AccessKey: "some_access_key1", |
||||
|
SecretKey: "some_secret_key1", |
||||
|
}, |
||||
|
}, |
||||
|
Actions: []string{ |
||||
|
ACTION_ADMIN, |
||||
|
ACTION_READ, |
||||
|
ACTION_WRITE, |
||||
|
}, |
||||
|
}, |
||||
|
{ |
||||
|
Name: "some_read_only_user", |
||||
|
Credentials: []*iam_pb.Credential{ |
||||
|
{ |
||||
|
AccessKey: "some_access_key2", |
||||
|
SecretKey: "some_secret_key2", |
||||
|
}, |
||||
|
}, |
||||
|
Actions: []string{ |
||||
|
ACTION_READ, |
||||
|
ACTION_TAGGING, |
||||
|
ACTION_LIST, |
||||
|
}, |
||||
|
}, |
||||
|
}, |
||||
|
} |
||||
|
entry := filer_pb.Entry{} |
||||
|
err := ifs.saveIAMConfigToEntry(&entry, s3Conf) |
||||
|
assert.Equal(t, err, nil) |
||||
|
s3ConfSaved := &iam_pb.S3ApiConfiguration{} |
||||
|
err = ifs.loadIAMConfigFromEntry(&entry, s3ConfSaved) |
||||
|
assert.Equal(t, err, nil) |
||||
|
|
||||
|
assert.Equal(t, "some_name", s3ConfSaved.Identities[0].Name) |
||||
|
assert.Equal(t, "some_read_only_user", s3ConfSaved.Identities[1].Name) |
||||
|
assert.Equal(t, "some_access_key1", s3ConfSaved.Identities[0].Credentials[0].AccessKey) |
||||
|
assert.Equal(t, "some_secret_key2", s3ConfSaved.Identities[1].Credentials[0].SecretKey) |
||||
|
} |
@ -0,0 +1,174 @@ |
|||||
|
package shell |
||||
|
|
||||
|
import ( |
||||
|
"flag" |
||||
|
"fmt" |
||||
|
"io" |
||||
|
"sort" |
||||
|
"strings" |
||||
|
|
||||
|
"github.com/chrislusf/seaweedfs/weed/pb/filer_pb" |
||||
|
"github.com/chrislusf/seaweedfs/weed/pb/iam_pb" |
||||
|
"github.com/chrislusf/seaweedfs/weed/s3iam" |
||||
|
) |
||||
|
|
||||
|
func init() { |
||||
|
Commands = append(Commands, &commandS3Configure{}) |
||||
|
} |
||||
|
|
||||
|
type commandS3Configure struct { |
||||
|
} |
||||
|
|
||||
|
func (c *commandS3Configure) Name() string { |
||||
|
return "s3.configure" |
||||
|
} |
||||
|
|
||||
|
func (c *commandS3Configure) Help() string { |
||||
|
return `configure and apply s3 options for each bucket |
||||
|
# see the current configuration file content |
||||
|
s3.configure |
||||
|
` |
||||
|
} |
||||
|
|
||||
|
func (c *commandS3Configure) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) { |
||||
|
s3ConfigureCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError) |
||||
|
actions := s3ConfigureCommand.String("actions", "", "actions names") |
||||
|
user := s3ConfigureCommand.String("user", "", "user name") |
||||
|
buckets := s3ConfigureCommand.String("buckets", "", "bucket name") |
||||
|
accessKey := s3ConfigureCommand.String("access_key", "", "specify the access key") |
||||
|
secretKey := s3ConfigureCommand.String("secret_key", "", "specify the secret key") |
||||
|
isDelete := s3ConfigureCommand.Bool("delete", false, "delete users, actions or access keys") |
||||
|
apply := s3ConfigureCommand.Bool("apply", false, "update and apply s3 configuration") |
||||
|
|
||||
|
if err = s3ConfigureCommand.Parse(args); err != nil { |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
s3cfg := &iam_pb.S3ApiConfiguration{} |
||||
|
ifs := &s3iam.IAMFilerStore{} |
||||
|
if err = commandEnv.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error { |
||||
|
ifs = s3iam.NewIAMFilerStore(&client) |
||||
|
if err := ifs.LoadIAMConfig(s3cfg); err != nil { |
||||
|
return nil |
||||
|
} |
||||
|
return nil |
||||
|
}); err != nil { |
||||
|
return err |
||||
|
} |
||||
|
|
||||
|
idx := 0 |
||||
|
changed := false |
||||
|
if *user != "" { |
||||
|
for i, identity := range s3cfg.Identities { |
||||
|
if *user == identity.Name { |
||||
|
idx = i |
||||
|
changed = true |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
var cmdActions []string |
||||
|
for _, action := range strings.Split(*actions, ",") { |
||||
|
if *buckets == "" { |
||||
|
cmdActions = append(cmdActions, action) |
||||
|
} else { |
||||
|
for _, bucket := range strings.Split(*buckets, ",") { |
||||
|
cmdActions = append(cmdActions, fmt.Sprintf("%s:%s", action, bucket)) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
if changed { |
||||
|
if *isDelete { |
||||
|
var exists []int |
||||
|
for _, cmdAction := range cmdActions { |
||||
|
for i, currentAction := range s3cfg.Identities[idx].Actions { |
||||
|
if cmdAction == currentAction { |
||||
|
exists = append(exists, i) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
sort.Sort(sort.Reverse(sort.IntSlice(exists))) |
||||
|
for _, i := range exists { |
||||
|
s3cfg.Identities[idx].Actions = append( |
||||
|
s3cfg.Identities[idx].Actions[:i], |
||||
|
s3cfg.Identities[idx].Actions[i+1:]..., |
||||
|
) |
||||
|
} |
||||
|
if *accessKey != "" { |
||||
|
exists = []int{} |
||||
|
for i, credential := range s3cfg.Identities[idx].Credentials { |
||||
|
if credential.AccessKey == *accessKey { |
||||
|
exists = append(exists, i) |
||||
|
} |
||||
|
} |
||||
|
sort.Sort(sort.Reverse(sort.IntSlice(exists))) |
||||
|
for _, i := range exists { |
||||
|
s3cfg.Identities[idx].Credentials = append( |
||||
|
s3cfg.Identities[idx].Credentials[:i], |
||||
|
s3cfg.Identities[idx].Credentials[:i+1]..., |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
if *actions == "" && *accessKey == "" && *buckets == "" { |
||||
|
s3cfg.Identities = append(s3cfg.Identities[:idx], s3cfg.Identities[idx+1:]...) |
||||
|
} |
||||
|
} else { |
||||
|
if *actions != "" { |
||||
|
for _, cmdAction := range cmdActions { |
||||
|
found := false |
||||
|
for _, action := range s3cfg.Identities[idx].Actions { |
||||
|
if cmdAction == action { |
||||
|
found = true |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
if !found { |
||||
|
s3cfg.Identities[idx].Actions = append(s3cfg.Identities[idx].Actions, cmdAction) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
if *accessKey != "" && *user != "anonymous" { |
||||
|
found := false |
||||
|
for _, credential := range s3cfg.Identities[idx].Credentials { |
||||
|
if credential.AccessKey == *accessKey { |
||||
|
found = true |
||||
|
credential.SecretKey = *secretKey |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
if !found { |
||||
|
s3cfg.Identities[idx].Credentials = append(s3cfg.Identities[idx].Credentials, &iam_pb.Credential{ |
||||
|
AccessKey: *accessKey, |
||||
|
SecretKey: *secretKey, |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} else if *user != "" && *actions != "" { |
||||
|
identity := iam_pb.Identity{ |
||||
|
Name: *user, |
||||
|
Actions: cmdActions, |
||||
|
Credentials: []*iam_pb.Credential{}, |
||||
|
} |
||||
|
if *user != "anonymous" { |
||||
|
identity.Credentials = append(identity.Credentials, |
||||
|
&iam_pb.Credential{AccessKey: *accessKey, SecretKey: *secretKey}) |
||||
|
} |
||||
|
s3cfg.Identities = append(s3cfg.Identities, &identity) |
||||
|
} |
||||
|
|
||||
|
for _, identity := range s3cfg.Identities { |
||||
|
fmt.Fprintf(writer, fmt.Sprintf("%+v\n", identity)) |
||||
|
} |
||||
|
|
||||
|
fmt.Fprintln(writer) |
||||
|
|
||||
|
if *apply { |
||||
|
if err := ifs.SaveIAMConfig(s3cfg); err != nil { |
||||
|
return err |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return nil |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue