feature/372-drop_eacl_related_code #397

Merged
alexvanin merged 3 commits from dkirillov/frostfs-s3-gw:feature/372-drop_eacl_related_code into master 2024-07-01 14:18:59 +00:00
32 changed files with 137 additions and 3767 deletions

View File

@ -31,7 +31,6 @@ type (
LocationConstraint string
ObjectLockEnabled bool
HomomorphicHashDisabled bool
APEEnabled bool
}
// ObjectInfo holds S3 object data.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -38,7 +38,6 @@ type (
IsResolveListAllow() bool
BypassContentEncodingInChunks() bool
MD5Enabled() bool
ACLEnabled() bool
RetryMaxAttempts() int
RetryMaxBackoff() time.Duration
RetryStrategy() RetryStrategy

View File

@ -13,7 +13,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/layer"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
"go.uber.org/zap"
)
@ -42,11 +41,10 @@ func path2BucketObject(path string) (string, string, error) {
func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
var (
err error
versionID string
metadata map[string]string
tagSet map[string]string
sessionTokenEACL *session.Container
err error
versionID string
metadata map[string]string
tagSet map[string]string
ctx = r.Context()
reqInfo = middleware.GetReqInfo(ctx)
@ -93,20 +91,11 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
return
}
apeEnabled := dstBktInfo.APEEnabled || settings.CannedACL != ""
if apeEnabled && cannedACLStatus == aclStatusYes {
if cannedACLStatus == aclStatusYes {
h.logAndSendError(w, "acl not supported for this bucket", reqInfo, errors.GetAPIError(errors.ErrAccessControlListNotSupported))
return
}
needUpdateEACLTable := !(apeEnabled || cannedACLStatus == aclStatusNo)
if needUpdateEACLTable {
if sessionTokenEACL, err = getSessionTokenSetEACL(ctx); err != nil {
h.logAndSendError(w, "could not get eacl session token from a box", reqInfo, err)
return
}
}
extendedSrcObjInfo, err := h.obj.GetExtendedObjectInfo(ctx, srcObjPrm)
if err != nil {
h.logAndSendError(w, "could not find object", reqInfo, err)
@ -239,25 +228,6 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
return
}
if needUpdateEACLTable {
newEaclTable, err := h.getNewEAclTable(r, dstBktInfo, dstObjInfo)
if err != nil {
h.logAndSendError(w, "could not get new eacl table", reqInfo, err)
return
}
p := &layer.PutBucketACLParams{
BktInfo: dstBktInfo,
EACL: newEaclTable,
SessionToken: sessionTokenEACL,
}
if err = h.obj.PutBucketACL(ctx, p); err != nil {
h.logAndSendError(w, "could not put bucket acl", reqInfo, err)
return
}
}
if tagSet != nil {
tagPrm := &data.PutObjectTaggingParams{
ObjectVersion: &data.ObjectVersion{

View File

@ -72,7 +72,6 @@ type configMock struct {
defaultCopiesNumbers []uint32
bypassContentEncodingInChunks bool
md5Enabled bool
aclEnabled bool
}
func (c *configMock) DefaultPlacementPolicy(_ string) netmap.PlacementPolicy {
@ -120,10 +119,6 @@ func (c *configMock) MD5Enabled() bool {
return c.md5Enabled
}
func (c *configMock) ACLEnabled() bool {
return c.aclEnabled
}
func (c *configMock) ResolveNamespaceAlias(ns string) string {
return ns
}

View File

@ -7,12 +7,9 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
s3errors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/errors"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/stretchr/testify/require"
)
@ -84,31 +81,6 @@ func headObject(t *testing.T, tc *handlerContext, bktName, objName string, heade
assertStatus(t, w, status)
}
func TestInvalidAccessThroughCache(t *testing.T) {
hc := prepareHandlerContext(t)
bktName, objName := "bucket-for-cache", "obj-for-cache"
bktInfo, _ := createBucketAndObject(hc, bktName, objName)
setContainerEACL(hc, bktInfo.CID)
headObject(t, hc, bktName, objName, nil, http.StatusOK)
w, r := prepareTestRequest(hc, bktName, objName, nil)
hc.Handler().HeadObjectHandler(w, r.WithContext(middleware.SetBox(r.Context(), &middleware.Box{AccessBox: newTestAccessBox(t, nil)})))
assertStatus(t, w, http.StatusForbidden)
}
func setContainerEACL(hc *handlerContext, cnrID cid.ID) {
table := eacl.NewTable()
table.SetCID(cnrID)
for _, op := range fullOps {
table.AddRecord(getOthersRecord(op, eacl.ActionDeny))
}
err := hc.MockedPool().SetContainerEACL(hc.Context(), *table, nil)
require.NoError(hc.t, err)
}
func TestHeadObject(t *testing.T) {
hc := prepareHandlerContextWithMinCache(t)
bktName, objName := "bucket", "obj"
@ -155,7 +127,7 @@ func newTestAccessBox(t *testing.T, key *keys.PrivateKey) *accessbox.Box {
}
var btoken bearer.Token
btoken.SetEACLTable(*eacl.NewTable())
btoken.SetImpersonate(true)
err = btoken.Sign(key.PrivateKey)
require.NoError(t, err)

View File

@ -112,14 +112,7 @@ func (h *handler) CreateMultipartUploadHandler(w http.ResponseWriter, r *http.Re
return
}
settings, err := h.obj.GetBucketSettings(r.Context(), bktInfo)
if err != nil {
h.logAndSendError(w, "couldn't get bucket settings", reqInfo, err)
return
}
apeEnabled := bktInfo.APEEnabled || settings.CannedACL != ""
if apeEnabled && cannedACLStatus == aclStatusYes {
if cannedACLStatus == aclStatusYes {
h.logAndSendError(w, "acl not supported for this bucket", reqInfo, errors.GetAPIError(errors.ErrAccessControlListNotSupported))
return
}
@ -133,20 +126,6 @@ func (h *handler) CreateMultipartUploadHandler(w http.ResponseWriter, r *http.Re
Data: &layer.UploadData{},
}
needUpdateEACLTable := !(apeEnabled || cannedACLStatus == aclStatusNo)
if needUpdateEACLTable {
key, err := h.bearerTokenIssuerKey(r.Context())
if err != nil {
h.logAndSendError(w, "couldn't get gate key", reqInfo, err, additional...)
return
}
if _, err = parseACLHeaders(r.Header, key); err != nil {
h.logAndSendError(w, "could not parse acl", reqInfo, err, additional...)
return
}
p.Data.ACLHeaders = formACLHeadersForMultipart(r.Header)
}
if len(r.Header.Get(api.AmzTagging)) > 0 {
p.Data.TagSet, err = parseTaggingHeader(r.Header)
if err != nil {
@ -196,25 +175,6 @@ func (h *handler) CreateMultipartUploadHandler(w http.ResponseWriter, r *http.Re
}
}
func formACLHeadersForMultipart(header http.Header) map[string]string {
result := make(map[string]string)
if value := header.Get(api.AmzACL); value != "" {
result[api.AmzACL] = value
}
if value := header.Get(api.AmzGrantRead); value != "" {
result[api.AmzGrantRead] = value
}
if value := header.Get(api.AmzGrantFullControl); value != "" {
result[api.AmzGrantFullControl] = value
}
if value := header.Get(api.AmzGrantWrite); value != "" {
result[api.AmzGrantWrite] = value
}
return result
}
func (h *handler) UploadPartHandler(w http.ResponseWriter, r *http.Request) {
reqInfo := middleware.GetReqInfo(r.Context())
@ -500,33 +460,6 @@ func (h *handler) completeMultipartUpload(r *http.Request, c *layer.CompleteMult
}
}
if len(uploadData.ACLHeaders) != 0 {
sessionTokenSetEACL, err := getSessionTokenSetEACL(ctx)
if err != nil {
return nil, fmt.Errorf("couldn't get eacl token: %w", err)
}
key, err := h.bearerTokenIssuerKey(ctx)
if err != nil {
return nil, fmt.Errorf("couldn't get gate key: %w", err)
}
acl, err := parseACLHeaders(r.Header, key)
if err != nil {
return nil, fmt.Errorf("could not parse acl: %w", err)
}
resInfo := &resourceInfo{
Bucket: objInfo.Bucket,
Object: objInfo.Name,
}
astObject, err := aclToAst(acl, resInfo)
if err != nil {
return nil, fmt.Errorf("could not translate acl of completed multipart upload to ast: %w", err)
}
if _, err = h.updateBucketACL(r, astObject, bktInfo, sessionTokenSetEACL); err != nil {
return nil, fmt.Errorf("could not update bucket acl while completing multipart upload: %w", err)
}
}
return objInfo, nil
}

View File

@ -28,8 +28,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/pkg/retryer"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/pkg/service/tree"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
"git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
"git.frostfs.info/TrueCloudLab/policy-engine/schema/s3"
@ -173,10 +171,9 @@ func (p *policyCondition) UnmarshalJSON(data []byte) error {
// keywords of predefined basic ACL values.
const (
basicACLPrivate = "private"
basicACLReadOnly = "public-read"
basicACLPublic = "public-read-write"
cannedACLAuthRead = "authenticated-read"
basicACLPrivate = "private"
basicACLReadOnly = "public-read"
basicACLPublic = "public-read-write"
)
type createBucketParams struct {
@ -186,12 +183,10 @@ type createBucketParams struct {
func (h *handler) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
var (
err error
newEaclTable *eacl.Table
sessionTokenEACL *session.Container
cannedACLStatus = aclHeadersStatus(r)
ctx = r.Context()
reqInfo = middleware.GetReqInfo(ctx)
err error
cannedACLStatus = aclHeadersStatus(r)
ctx = r.Context()
reqInfo = middleware.GetReqInfo(ctx)
)
bktInfo, err := h.getBucketAndCheckOwner(r, reqInfo.BucketName)
@ -206,20 +201,11 @@ func (h *handler) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
return
}
apeEnabled := bktInfo.APEEnabled || settings.CannedACL != ""
if apeEnabled && cannedACLStatus == aclStatusYes {
if cannedACLStatus == aclStatusYes {
h.logAndSendError(w, "acl not supported for this bucket", reqInfo, errors.GetAPIError(errors.ErrAccessControlListNotSupported))
return
}
needUpdateEACLTable := !(apeEnabled || cannedACLStatus == aclStatusNo)
if needUpdateEACLTable {
if sessionTokenEACL, err = getSessionTokenSetEACL(r.Context()); err != nil {
h.logAndSendError(w, "could not get eacl session token from a box", reqInfo, err)
return
}
}
tagSet, err := parseTaggingHeader(r.Header)
if err != nil {
h.logAndSendError(w, "could not parse tagging header", reqInfo, err)
@ -292,13 +278,6 @@ func (h *handler) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
}
objInfo := extendedObjInfo.ObjectInfo
if needUpdateEACLTable {
if newEaclTable, err = h.getNewEAclTable(r, bktInfo, objInfo); err != nil {
h.logAndSendError(w, "could not get new eacl table", reqInfo, err)
return
}
}
if tagSet != nil {
tagPrm := &data.PutObjectTaggingParams{
ObjectVersion: &data.ObjectVersion{
@ -315,19 +294,6 @@ func (h *handler) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
}
}
if newEaclTable != nil {
p := &layer.PutBucketACLParams{
BktInfo: bktInfo,
EACL: newEaclTable,
SessionToken: sessionTokenEACL,
}
if err = h.obj.PutBucketACL(r.Context(), p); err != nil {
h.logAndSendError(w, "could not put bucket acl", reqInfo, err)
return
}
}
if settings.VersioningEnabled() {
w.Header().Set(api.AmzVersionID, objInfo.VersionID())
}
@ -459,13 +425,10 @@ func formEncryptionParamsBase(r *http.Request, isCopySource bool) (enc encryptio
func (h *handler) PostObject(w http.ResponseWriter, r *http.Request) {
var (
newEaclTable *eacl.Table
tagSet map[string]string
sessionTokenEACL *session.Container
ctx = r.Context()
reqInfo = middleware.GetReqInfo(ctx)
metadata = make(map[string]string)
cannedACLStatus = aclHeadersStatus(r)
tagSet map[string]string
ctx = r.Context()
reqInfo = middleware.GetReqInfo(ctx)
metadata = make(map[string]string)
)
policy, err := checkPostPolicy(r, reqInfo, metadata)
@ -501,20 +464,11 @@ func (h *handler) PostObject(w http.ResponseWriter, r *http.Request) {
return
}
apeEnabled := bktInfo.APEEnabled || settings.CannedACL != ""
if apeEnabled && cannedACLStatus == aclStatusYes {
if acl := auth.MultipartFormValue(r, "acl"); acl != "" && acl != basicACLPrivate {
h.logAndSendError(w, "acl not supported for this bucket", reqInfo, errors.GetAPIError(errors.ErrAccessControlListNotSupported))
return
}
needUpdateEACLTable := !(apeEnabled || cannedACLStatus == aclStatusNo)
if needUpdateEACLTable {
if sessionTokenEACL, err = getSessionTokenSetEACL(ctx); err != nil {
h.logAndSendError(w, "could not get eacl session token from a box", reqInfo, err)
return
}
}
var contentReader io.Reader
var size uint64
if content, ok := r.MultipartForm.Value["file"]; ok {
@ -550,18 +504,6 @@ func (h *handler) PostObject(w http.ResponseWriter, r *http.Request) {
}
objInfo := extendedObjInfo.ObjectInfo
if acl := auth.MultipartFormValue(r, "acl"); acl != "" {
r.Header.Set(api.AmzACL, acl)
r.Header.Set(api.AmzGrantFullControl, "")
r.Header.Set(api.AmzGrantWrite, "")
r.Header.Set(api.AmzGrantRead, "")
if newEaclTable, err = h.getNewEAclTable(r, bktInfo, objInfo); err != nil {
h.logAndSendError(w, "could not get new eacl table", reqInfo, err)
return
}
}
if tagSet != nil {
tagPrm := &data.PutObjectTaggingParams{
ObjectVersion: &data.ObjectVersion{
@ -578,19 +520,6 @@ func (h *handler) PostObject(w http.ResponseWriter, r *http.Request) {
}
}
if newEaclTable != nil {
p := &layer.PutBucketACLParams{
BktInfo: bktInfo,
EACL: newEaclTable,
SessionToken: sessionTokenEACL,
}
if err = h.obj.PutBucketACL(ctx, p); err != nil {
h.logAndSendError(w, "could not put bucket acl", reqInfo, err)
return
}
}
if settings.VersioningEnabled() {
w.Header().Set(api.AmzVersionID, objInfo.VersionID())
}
@ -716,56 +645,6 @@ func aclHeadersStatus(r *http.Request) aclStatus {
return aclStatusNo
}
func (h *handler) getNewEAclTable(r *http.Request, bktInfo *data.BucketInfo, objInfo *data.ObjectInfo) (*eacl.Table, error) {
var newEaclTable *eacl.Table
key, err := h.bearerTokenIssuerKey(r.Context())
if err != nil {
return nil, fmt.Errorf("get bearer token issuer: %w", err)
}
objectACL, err := parseACLHeaders(r.Header, key)
if err != nil {
return nil, fmt.Errorf("could not parse object acl: %w", err)
}
resInfo := &resourceInfo{
Bucket: objInfo.Bucket,
Object: objInfo.Name,
Version: objInfo.VersionID(),
}
bktPolicy, err := aclToPolicy(objectACL, resInfo)
if err != nil {
return nil, fmt.Errorf("could not translate object acl to bucket policy: %w", err)
}
astChild, err := policyToAst(bktPolicy)
if err != nil {
return nil, fmt.Errorf("could not translate policy to ast: %w", err)
}
bacl, err := h.obj.GetBucketACL(r.Context(), bktInfo)
if err != nil {
return nil, fmt.Errorf("could not get bucket eacl: %w", err)
}
parentAst := tableToAst(bacl.EACL, objInfo.Bucket)
strCID := bacl.Info.CID.EncodeToString()
for _, resource := range parentAst.Resources {
if resource.Bucket == strCID {
resource.Bucket = objInfo.Bucket
}
}
if resAst, updated := mergeAst(parentAst, astChild); updated {
if newEaclTable, err = astToTable(resAst); err != nil {
return nil, fmt.Errorf("could not translate ast to table: %w", err)
}
}
return newEaclTable, nil
}
func parseTaggingHeader(header http.Header) (map[string]string, error) {
var tagSet map[string]string
if tagging := header.Get(api.AmzTagging); len(tagging) > 0 {
@ -805,8 +684,7 @@ func parseCannedACL(header http.Header) (string, error) {
return basicACLPrivate, nil
}
if acl == basicACLPrivate || acl == basicACLPublic ||
acl == cannedACLAuthRead || acl == basicACLReadOnly {
if acl == basicACLPrivate || acl == basicACLPublic || acl == basicACLReadOnly {
return acl, nil
}
@ -814,11 +692,6 @@ func parseCannedACL(header http.Header) (string, error) {
}
func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) {
if h.cfg.ACLEnabled() {
h.createBucketHandlerACL(w, r)
return
}
h.createBucketHandlerPolicy(w, r)
}
@ -878,7 +751,6 @@ func (h *handler) createBucketHandlerPolicy(w http.ResponseWriter, r *http.Reque
return
}
p.APEEnabled = true
bktInfo, err := h.obj.CreateBucket(ctx, p)
if err != nil {
h.logAndSendError(w, "could not create bucket", reqInfo, err)
@ -941,78 +813,6 @@ func (h *handler) putBucketSettingsRetryer() aws.RetryerV2 {
})
}
func (h *handler) createBucketHandlerACL(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
reqInfo := middleware.GetReqInfo(ctx)
boxData, err := middleware.GetBoxData(ctx)
if err != nil {
h.logAndSendError(w, "get access box from request", reqInfo, err)
return
}
key, p, err := h.parseCommonCreateBucketParams(reqInfo, boxData, r)
if err != nil {
h.logAndSendError(w, "parse create bucket params", reqInfo, err)
return
}
aclPrm := &layer.PutBucketACLParams{SessionToken: boxData.Gate.SessionTokenForSetEACL()}
if aclPrm.SessionToken == nil {
h.logAndSendError(w, "couldn't find session token for setEACL", reqInfo, errors.GetAPIError(errors.ErrAccessDenied))
return
}
bktACL, err := parseACLHeaders(r.Header, key)
if err != nil {
h.logAndSendError(w, "could not parse bucket acl", reqInfo, err)
return
}
resInfo := &resourceInfo{Bucket: reqInfo.BucketName}
aclPrm.EACL, err = bucketACLToTable(bktACL, resInfo)
if err != nil {
h.logAndSendError(w, "could translate bucket acl to eacl", reqInfo, err)
return
}
bktInfo, err := h.obj.CreateBucket(ctx, p)
if err != nil {
h.logAndSendError(w, "could not create bucket", reqInfo, err)
return
}
h.reqLogger(ctx).Info(logs.BucketIsCreated, zap.Stringer("container_id", bktInfo.CID))
aclPrm.BktInfo = bktInfo
if err = h.obj.PutBucketACL(r.Context(), aclPrm); err != nil {
h.logAndSendError(w, "could not put bucket e/ACL", reqInfo, err)
return
}
sp := &layer.PutSettingsParams{
BktInfo: bktInfo,
Settings: &data.BucketSettings{
OwnerKey: key,
Versioning: data.VersioningUnversioned,
},
}
if p.ObjectLockEnabled {
sp.Settings.Versioning = data.VersioningEnabled
}
if err = h.obj.PutBucketSettings(ctx, sp); err != nil {
h.logAndSendError(w, "couldn't save bucket settings", reqInfo, err,
zap.String("container_id", bktInfo.CID.EncodeToString()))
return
}
if err = middleware.WriteSuccessResponseHeadersOnly(w); err != nil {
h.logAndSendError(w, "write response", reqInfo, err)
return
}
}
const s3ActionPrefix = "s3:"
var (
@ -1067,8 +867,6 @@ func bucketCannedACLToAPERules(cannedACL string, reqInfo *middleware.ReqInfo, cn
switch cannedACL {
case basicACLPrivate:
case cannedACLAuthRead:
fallthrough
case basicACLReadOnly:
chains[0].Rules = append(chains[0].Rules, chain.Rule{
Status: chain.Allow,

View File

@ -381,21 +381,6 @@ func TestCreateBucket(t *testing.T) {
createBucketAssertS3Error(hc, bktName, box2, s3errors.ErrBucketAlreadyExists)
}
func TestCreateOldBucketPutVersioning(t *testing.T) {
hc := prepareHandlerContext(t)
hc.config.aclEnabled = true
bktName := "bkt-name"
info := createBucket(hc, bktName)
settings, err := hc.tree.GetSettingsNode(hc.Context(), info.BktInfo)
require.NoError(t, err)
settings.OwnerKey = nil
err = hc.tree.PutSettingsNode(hc.Context(), info.BktInfo, settings)
require.NoError(t, err)
putBucketVersioning(t, hc, bktName, true)
}
func TestCreateNamespacedBucket(t *testing.T) {
hc := prepareHandlerContext(t)
bktName := "bkt-name"

View File

@ -16,7 +16,6 @@ import (
frosterrors "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/errors"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
)
@ -142,16 +141,3 @@ func parseRange(s string) (*layer.RangeParams, error) {
End: values[1],
}, nil
}
func getSessionTokenSetEACL(ctx context.Context) (*session.Container, error) {
boxData, err := middleware.GetBoxData(ctx)
if err != nil {
return nil, err
}
sessionToken := boxData.Gate.SessionTokenForSetEACL()
if sessionToken == nil {
return nil, s3errors.GetAPIError(s3errors.ErrAccessDenied)
}
return sessionToken, nil
}

View File

@ -12,21 +12,9 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
"go.uber.org/zap"
)
type (
// BucketACL extends BucketInfo by eacl.Table.
BucketACL struct {
Info *data.BucketInfo
EACL *eacl.Table
}
)
const (
attributeLocationConstraint = ".s3-location-constraint"
AttributeLockEnabled = "LockEnabled"
@ -64,7 +52,6 @@ func (n *Layer) containerInfo(ctx context.Context, prm PrmContainer) (*data.Buck
info.Created = container.CreatedAt(cnr)
info.LocationConstraint = cnr.Attribute(attributeLocationConstraint)
info.HomomorphicHashDisabled = container.IsHomomorphicHashingDisabled(cnr)
info.APEEnabled = cnr.BasicACL().Bits() == 0
attrLockEnabled := cnr.Attribute(AttributeLockEnabled)
if len(attrLockEnabled) > 0 {
@ -133,7 +120,6 @@ func (n *Layer) createContainer(ctx context.Context, p *CreateBucketParams) (*da
Created: TimeNow(ctx),
LocationConstraint: p.LocationConstraint,
ObjectLockEnabled: p.ObjectLockEnabled,
APEEnabled: p.APEEnabled,
}
attributes := [][2]string{
@ -146,11 +132,6 @@ func (n *Layer) createContainer(ctx context.Context, p *CreateBucketParams) (*da
})
}
basicACL := acl.PublicRWExtended
if p.APEEnabled {
basicACL = 0
}
res, err := n.frostFS.CreateContainer(ctx, PrmContainerCreate{
Creator: bktInfo.Owner,
Policy: p.Policy,
@ -159,7 +140,7 @@ func (n *Layer) createContainer(ctx context.Context, p *CreateBucketParams) (*da
SessionToken: p.SessionContainerCreation,
CreationTime: bktInfo.Created,
AdditionalAttributes: attributes,
BasicACL: basicACL,
BasicACL: 0, // means APE
})
if err != nil {
return nil, fmt.Errorf("create container: %w", err)
@ -172,17 +153,3 @@ func (n *Layer) createContainer(ctx context.Context, p *CreateBucketParams) (*da
return bktInfo, nil
}
func (n *Layer) setContainerEACLTable(ctx context.Context, idCnr cid.ID, table *eacl.Table, sessionToken *session.Container) error {
table.SetCID(idCnr)
return n.frostFS.SetContainerEACL(ctx, *table, sessionToken)
}
func (n *Layer) GetContainerEACL(ctx context.Context, cnrID cid.ID) (*eacl.Table, error) {
prm := PrmContainerEACL{
ContainerID: cnrID,
SessionToken: n.SessionTokenForRead(ctx),
}
return n.frostFS.ContainerEACL(ctx, prm)
}

View File

@ -11,7 +11,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
@ -64,15 +63,6 @@ type PrmUserContainers struct {
SessionToken *session.Container
}
// PrmContainerEACL groups parameters of FrostFS.ContainerEACL operation.
type PrmContainerEACL struct {
// Container identifier.
ContainerID cid.ID
// Token of the container's creation session. Nil means session absence.
SessionToken *session.Container
}
// ContainerCreateResult is a result parameter of FrostFS.CreateContainer operation.
type ContainerCreateResult struct {
ContainerID cid.ID
@ -216,18 +206,6 @@ type FrostFS interface {
// prevented the containers from being listed.
UserContainers(context.Context, PrmUserContainers) ([]cid.ID, error)
// SetContainerEACL saves the eACL table of the container in FrostFS. The
// extended ACL is modified within session if session token is not nil.
//
// It returns any error encountered which prevented the eACL from being saved.
SetContainerEACL(context.Context, eacl.Table, *session.Container) error
// ContainerEACL reads the container eACL from FrostFS by the container ID.
//
// It returns exactly one non-nil value. It returns any error encountered which
// prevented the eACL from being read.
ContainerEACL(context.Context, PrmContainerEACL) (*eacl.Table, error)
// DeleteContainer marks the container to be removed from FrostFS by ID.
// Request is sent within session if the session token is specified.
// Successful return does not guarantee actual removal.

View File

@ -5,13 +5,11 @@ import (
"context"
"crypto/rand"
"crypto/sha256"
"errors"
"fmt"
"io"
"strings"
"time"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
v2container "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/container"
objectv2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
@ -20,7 +18,6 @@ import (
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
@ -68,7 +65,6 @@ type TestFrostFS struct {
objectErrors map[string]error
objectPutErrors map[string]error
containers map[string]*container.Container
eaclTables map[string]*eacl.Table
currentEpoch uint64
key *keys.PrivateKey
}
@ -79,7 +75,6 @@ func NewTestFrostFS(key *keys.PrivateKey) *TestFrostFS {
objectErrors: make(map[string]error),
objectPutErrors: make(map[string]error),
containers: make(map[string]*container.Container),
eaclTables: make(map[string]*eacl.Table),
key: key,
}
}
@ -222,7 +217,7 @@ func (t *TestFrostFS) ReadObject(ctx context.Context, prm PrmObjectRead) (*Objec
if obj, ok := t.objects[sAddr]; ok {
owner := getBearerOwner(ctx)
if !t.checkAccess(prm.Container, owner, eacl.OperationGet, obj) {
if !t.checkAccess(prm.Container, owner) {
return nil, ErrAccessDenied
}
@ -324,9 +319,9 @@ func (t *TestFrostFS) DeleteObject(ctx context.Context, prm PrmObjectDelete) err
return err
}
if obj, ok := t.objects[addr.EncodeToString()]; ok {
if _, ok := t.objects[addr.EncodeToString()]; ok {
owner := getBearerOwner(ctx)
if !t.checkAccess(prm.Container, owner, eacl.OperationDelete, obj) {
if !t.checkAccess(prm.Container, owner) {
return ErrAccessDenied
}
@ -354,30 +349,6 @@ func (t *TestFrostFS) AllObjects(cnrID cid.ID) []oid.ID {
return result
}
func (t *TestFrostFS) SetContainerEACL(_ context.Context, table eacl.Table, _ *session.Container) error {
cnrID, ok := table.CID()
if !ok {
return errors.New("invalid cid")
}
if _, ok = t.containers[cnrID.EncodeToString()]; !ok {
return errors.New("not found")
}
t.eaclTables[cnrID.EncodeToString()] = &table
return nil
}
func (t *TestFrostFS) ContainerEACL(_ context.Context, prm PrmContainerEACL) (*eacl.Table, error) {
table, ok := t.eaclTables[prm.ContainerID.EncodeToString()]
if !ok {
return nil, errors.New("not found")
}
return table, nil
}
func (t *TestFrostFS) SearchObjects(_ context.Context, prm PrmObjectSearch) ([]oid.ID, error) {
filters := object.NewSearchFilters()
filters.AddRootFilter()
@ -415,7 +386,7 @@ func (t *TestFrostFS) SearchObjects(_ context.Context, prm PrmObjectSearch) ([]o
return res, nil
}
func (t *TestFrostFS) checkAccess(cnrID cid.ID, owner user.ID, op eacl.Operation, obj *object.Object) bool {
func (t *TestFrostFS) checkAccess(cnrID cid.ID, owner user.ID) bool {
cnr, ok := t.containers[cnrID.EncodeToString()]
if !ok {
return false
@ -425,57 +396,6 @@ func (t *TestFrostFS) checkAccess(cnrID cid.ID, owner user.ID, op eacl.Operation
return cnr.Owner().Equals(owner)
}
table, ok := t.eaclTables[cnrID.EncodeToString()]
if !ok {
return true
}
for _, rec := range table.Records() {
if rec.Operation() != op {
continue
}
if !matchTarget(rec, owner) {
continue
}
if matchFilter(rec.Filters(), obj) {
return rec.Action() == eacl.ActionAllow
}
}
return true
}
func matchTarget(rec eacl.Record, owner user.ID) bool {
for _, trgt := range rec.Targets() {
if trgt.Role() == eacl.RoleOthers {
return true
}
var targetOwner user.ID
for _, pk := range eacl.TargetECDSAKeys(&trgt) {
user.IDFromKey(&targetOwner, *pk)
if targetOwner.Equals(owner) {
return true
}
}
}
return false
}
func matchFilter(filters []eacl.Filter, obj *object.Object) bool {
objID, _ := obj.ID()
for _, f := range filters {
fv2 := f.ToV2()
if fv2.GetMatchType() != acl.MatchTypeStringEqual ||
fv2.GetHeaderType() != acl.HeaderTypeObject ||
fv2.GetKey() != acl.FilterObjectID ||
fv2.GetValue() != objID.EncodeToString() {
return false
}
}
return true
}

View File

@ -22,7 +22,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
@ -159,13 +158,6 @@ type (
SessionContainerCreation *session.Container
LocationConstraint string
ObjectLockEnabled bool
APEEnabled bool
}
// PutBucketACLParams stores put bucket acl request parameters.
PutBucketACLParams struct {
BktInfo *data.BucketInfo
EACL *eacl.Table
SessionToken *session.Container
}
// DeleteBucketParams stores delete bucket request parameters.
DeleteBucketParams struct {
@ -350,24 +342,6 @@ func (n *Layer) ResolveCID(ctx context.Context, name string) (cid.ID, error) {
return n.ResolveBucket(ctx, name)
}
// GetBucketACL returns bucket acl info by name.
func (n *Layer) GetBucketACL(ctx context.Context, bktInfo *data.BucketInfo) (*BucketACL, error) {
eACL, err := n.GetContainerEACL(ctx, bktInfo.CID)
if err != nil {
return nil, fmt.Errorf("get container eacl: %w", err)
}
return &BucketACL{
Info: bktInfo,
EACL: eACL,
}, nil
}
// PutBucketACL puts bucket acl by name.
func (n *Layer) PutBucketACL(ctx context.Context, param *PutBucketACLParams) error {
return n.setContainerEACLTable(ctx, param.BktInfo.CID, param.EACL, param.SessionToken)
}
// ListBuckets returns all user containers. The name of the bucket is a container
// id. Timestamp is omitted since it is not saved in frostfs container.
func (n *Layer) ListBuckets(ctx context.Context) ([]*data.BucketInfo, error) {

View File

@ -36,7 +36,6 @@ const (
MultipartObjectSize = "S3-Multipart-Object-Size"
metaPrefix = "meta-"
aclPrefix = "acl-"
MaxSizeUploadsList = 1000
MaxSizePartsList = 1000
@ -62,8 +61,7 @@ type (
}
UploadData struct {
TagSet map[string]string
ACLHeaders map[string]string
TagSet map[string]string
}
UploadPartParams struct {
@ -149,7 +147,6 @@ type (
func (n *Layer) CreateMultipartUpload(ctx context.Context, p *CreateMultipartParams) error {
metaSize := len(p.Header)
if p.Data != nil {
metaSize += len(p.Data.ACLHeaders)
metaSize += len(p.Data.TagSet)
}
@ -167,10 +164,6 @@ func (n *Layer) CreateMultipartUpload(ctx context.Context, p *CreateMultipartPar
}
if p.Data != nil {
for key, val := range p.Data.ACLHeaders {
info.Meta[aclPrefix+key] = val
}
for key, val := range p.Data.TagSet {
info.Meta[tagPrefix+key] = val
}
@ -432,16 +425,13 @@ func (n *Layer) CompleteMultipartUpload(ctx context.Context, p *CompleteMultipar
initMetadata[MultipartObjectSize] = strconv.FormatUint(multipartObjetSize, 10)
uploadData := &UploadData{
TagSet: make(map[string]string),
ACLHeaders: make(map[string]string),
TagSet: make(map[string]string),
}
for key, val := range multipartInfo.Meta {
if strings.HasPrefix(key, metaPrefix) {
initMetadata[strings.TrimPrefix(key, metaPrefix)] = val
} else if strings.HasPrefix(key, tagPrefix) {
uploadData.TagSet[strings.TrimPrefix(key, tagPrefix)] = val
} else if strings.HasPrefix(key, aclPrefix) {
uploadData.ACLHeaders[strings.TrimPrefix(key, aclPrefix)] = val
}
}

View File

@ -51,7 +51,6 @@ var withoutResourceOps = []string{
type PolicySettings interface {
PolicyDenyByDefault() bool
ACLEnabled() bool
}
type FrostFSIDInformer interface {
@ -150,12 +149,7 @@ func policyCheck(r *http.Request, cfg PolicyConfig) error {
return apiErr.GetAPIErrorWithError(apiErr.ErrAccessDenied, fmt.Errorf("policy check: %s", st.String()))
}
isAPE := !cfg.Settings.ACLEnabled()
if bktInfo != nil {
isAPE = bktInfo.APEEnabled
}
if isAPE && cfg.Settings.PolicyDenyByDefault() {
if cfg.Settings.PolicyDenyByDefault() {
return apiErr.GetAPIErrorWithError(apiErr.ErrAccessDenied, fmt.Errorf("policy check: %s", st.String()))
}

View File

@ -72,7 +72,6 @@ func (c *centerMock) Authenticate(*http.Request) (*middleware.Box, error) {
type middlewareSettingsMock struct {
denyByDefault bool
aclEnabled bool
sourceIPHeader string
}
@ -92,10 +91,6 @@ func (r *middlewareSettingsMock) PolicyDenyByDefault() bool {
return r.denyByDefault
}
func (r *middlewareSettingsMock) ACLEnabled() bool {
return r.aclEnabled
}
type frostFSIDMock struct {
tags map[string]string
validateError bool
@ -429,8 +424,7 @@ func (h *handlerMock) CreateBucketHandler(w http.ResponseWriter, r *http.Request
reqInfo := middleware.GetReqInfo(r.Context())
h.buckets[reqInfo.Namespace+reqInfo.BucketName] = &data.BucketInfo{
Name: reqInfo.BucketName,
APEEnabled: !h.cfg.ACLEnabled(),
Name: reqInfo.BucketName,
}
res := &handlerResult{

View File

@ -37,6 +37,7 @@ type routerMock struct {
cfg Config
middlewareSettings *middlewareSettingsMock
policyChecker engine.LocalOverrideEngine
handler *handlerMock
}
func (m *routerMock) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@ -64,12 +65,14 @@ func prepareRouter(t *testing.T, opts ...option) *routerMock {
Enabled: true,
}
handlerTestMock := &handlerMock{t: t, cfg: middlewareSettings, buckets: map[string]*data.BucketInfo{}}
cfg := Config{
Throttle: middleware.ThrottleOpts{
Limit: 10,
BacklogTimeout: 30 * time.Second,
},
Handler: &handlerMock{t: t, cfg: middlewareSettings, buckets: map[string]*data.BucketInfo{}},
Handler: handlerTestMock,
Center: &centerMock{t: t},
Log: logger,
Metrics: metrics.NewAppMetrics(metricsConfig),
@ -91,6 +94,7 @@ func prepareRouter(t *testing.T, opts ...option) *routerMock {
cfg: cfg,
middlewareSettings: middlewareSettings,
policyChecker: policyChecker,
handler: handlerTestMock,
}
}
@ -299,102 +303,6 @@ func TestDefaultPolicyCheckerWithUserTags(t *testing.T) {
createBucket(router, ns, bktName)
}
func TestACLAPE(t *testing.T) {
t.Run("acl disabled, ape deny by default", func(t *testing.T) {
router := prepareRouter(t)
ns, bktName, objName := "", "bucket", "object"
bktNameOld, bktNameNew := "old-bucket", "new-bucket"
createOldBucket(router, bktNameOld)
createNewBucket(router, bktNameNew)
router.middlewareSettings.aclEnabled = false
router.middlewareSettings.denyByDefault = true
// Allow because of using old bucket
putObject(router, ns, bktNameOld, objName, nil)
// Deny because of deny by default
putObjectErr(router, ns, bktNameNew, objName, nil, apiErrors.ErrAccessDenied)
// Deny because of deny by default
createBucketErr(router, ns, bktName, nil, apiErrors.ErrAccessDenied)
listBucketsErr(router, ns, apiErrors.ErrAccessDenied)
// Allow operations and check
allowOperations(router, ns, []string{"s3:CreateBucket", "s3:ListAllMyBuckets"}, nil)
createBucket(router, ns, bktName)
listBuckets(router, ns)
})
t.Run("acl disabled, ape allow by default", func(t *testing.T) {
router := prepareRouter(t)
ns, bktName, objName := "", "bucket", "object"
bktNameOld, bktNameNew := "old-bucket", "new-bucket"
createOldBucket(router, bktNameOld)
createNewBucket(router, bktNameNew)
router.middlewareSettings.aclEnabled = false
router.middlewareSettings.denyByDefault = false
// Allow because of using old bucket
putObject(router, ns, bktNameOld, objName, nil)
// Allow because of allow by default
putObject(router, ns, bktNameNew, objName, nil)
// Allow because of deny by default
createBucket(router, ns, bktName)
listBuckets(router, ns)
// Deny operations and check
denyOperations(router, ns, []string{"s3:CreateBucket", "s3:ListAllMyBuckets"}, nil)
createBucketErr(router, ns, bktName, nil, apiErrors.ErrAccessDenied)
listBucketsErr(router, ns, apiErrors.ErrAccessDenied)
})
t.Run("acl enabled, ape deny by default", func(t *testing.T) {
router := prepareRouter(t)
ns, bktName, objName := "", "bucket", "object"
bktNameOld, bktNameNew := "old-bucket", "new-bucket"
createOldBucket(router, bktNameOld)
createNewBucket(router, bktNameNew)
router.middlewareSettings.aclEnabled = true
router.middlewareSettings.denyByDefault = true
// Allow because of using old bucket
putObject(router, ns, bktNameOld, objName, nil)
// Deny because of deny by default
putObjectErr(router, ns, bktNameNew, objName, nil, apiErrors.ErrAccessDenied)
// Allow because of old behavior
createBucket(router, ns, bktName)
listBuckets(router, ns)
})
t.Run("acl enabled, ape allow by default", func(t *testing.T) {
router := prepareRouter(t)
ns, bktName, objName := "", "bucket", "object"
bktNameOld, bktNameNew := "old-bucket", "new-bucket"
createOldBucket(router, bktNameOld)
createNewBucket(router, bktNameNew)
router.middlewareSettings.aclEnabled = true
router.middlewareSettings.denyByDefault = false
// Allow because of using old bucket
putObject(router, ns, bktNameOld, objName, nil)
// Allow because of allow by default
putObject(router, ns, bktNameNew, objName, nil)
// Allow because of old behavior
createBucket(router, ns, bktName)
listBuckets(router, ns)
})
}
func TestRequestParametersCheck(t *testing.T) {
t.Run("prefix parameter, allow specific value", func(t *testing.T) {
router := prepareRouter(t)
@ -717,21 +625,6 @@ func addPolicy(router *routerMock, ns string, id string, effect engineiam.Effect
require.NoError(router.t, err)
}
func createOldBucket(router *routerMock, bktName string) {
createSpecificBucket(router, bktName, true)
}
func createNewBucket(router *routerMock, bktName string) {
createSpecificBucket(router, bktName, false)
}
func createSpecificBucket(router *routerMock, bktName string, old bool) {
aclEnabled := router.middlewareSettings.ACLEnabled()
router.middlewareSettings.aclEnabled = old
createBucket(router, "", bktName)
router.middlewareSettings.aclEnabled = aclEnabled
}
func createBucket(router *routerMock, namespace, bktName string) {
w := createBucketBase(router, namespace, bktName, nil)
resp := readResponse(router.t, w)
@ -754,24 +647,6 @@ func createBucketBase(router *routerMock, namespace, bktName string, header http
return w
}
func listBuckets(router *routerMock, namespace string) {
w := listBucketsBase(router, namespace)
resp := readResponse(router.t, w)
require.Equal(router.t, s3middleware.ListBucketsOperation, resp.Method)
}
func listBucketsErr(router *routerMock, namespace string, errCode apiErrors.ErrorCode) {
w := listBucketsBase(router, namespace)
assertAPIError(router.t, w, errCode)
}
func listBucketsBase(router *routerMock, namespace string) *httptest.ResponseRecorder {
w, r := httptest.NewRecorder(), httptest.NewRequest(http.MethodGet, "/", nil)
r.Header.Set(FrostfsNamespaceHeader, namespace)
router.ServeHTTP(w, r)
return w
}
func getBucketErr(router *routerMock, namespace, bktName string, errCode apiErrors.ErrorCode) {
w := getBucketBase(router, namespace, bktName)
assertAPIError(router.t, w, errCode)

View File

@ -20,7 +20,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
@ -102,7 +101,6 @@ type (
Container ContainerOptions
FrostFSKey *keys.PrivateKey
GatesPublicKeys []*keys.PublicKey
EACLRules []byte
Impersonate bool
SessionTokenRules []byte
SkipSessionRules bool
@ -441,47 +439,11 @@ func (a *Agent) ObtainSecret(ctx context.Context, w io.Writer, options *ObtainSe
return enc.Encode(or)
}
func buildEACLTable(eaclTable []byte) (*eacl.Table, error) {
table := eacl.NewTable()
if len(eaclTable) != 0 {
return table, table.UnmarshalJSON(eaclTable)
}
record := eacl.NewRecord()
record.SetOperation(eacl.OperationGet)
record.SetAction(eacl.ActionAllow)
eacl.AddFormedTarget(record, eacl.RoleOthers)
table.AddRecord(record)
for _, rec := range restrictedRecords() {
table.AddRecord(rec)
}
return table, nil
}
func restrictedRecords() (records []*eacl.Record) {
for op := eacl.OperationGet; op <= eacl.OperationRangeHash; op++ {
record := eacl.NewRecord()
record.SetOperation(op)
record.SetAction(eacl.ActionDeny)
eacl.AddFormedTarget(record, eacl.RoleOthers)
records = append(records, record)
}
return
}
func buildBearerToken(key *keys.PrivateKey, impersonate bool, table *eacl.Table, lifetime lifetimeOptions, gateKey *keys.PublicKey) (*bearer.Token, error) {
func buildBearerToken(key *keys.PrivateKey, impersonate bool, lifetime lifetimeOptions, gateKey *keys.PublicKey) (*bearer.Token, error) {
var ownerID user.ID
user.IDFromKey(&ownerID, (ecdsa.PublicKey)(*gateKey))
var bearerToken bearer.Token
if !impersonate {
bearerToken.SetEACLTable(*table)
}
bearerToken.ForUser(ownerID)
bearerToken.SetExp(lifetime.Exp)
bearerToken.SetIat(lifetime.Iat)
@ -496,10 +458,10 @@ func buildBearerToken(key *keys.PrivateKey, impersonate bool, table *eacl.Table,
return &bearerToken, nil
}
func buildBearerTokens(key *keys.PrivateKey, impersonate bool, table *eacl.Table, lifetime lifetimeOptions, gatesKeys []*keys.PublicKey) ([]*bearer.Token, error) {
func buildBearerTokens(key *keys.PrivateKey, impersonate bool, lifetime lifetimeOptions, gatesKeys []*keys.PublicKey) ([]*bearer.Token, error) {
bearerTokens := make([]*bearer.Token, 0, len(gatesKeys))
for _, gateKey := range gatesKeys {
tkn, err := buildBearerToken(key, impersonate, table, lifetime, gateKey)
tkn, err := buildBearerToken(key, impersonate, lifetime, gateKey)
if err != nil {
return nil, fmt.Errorf("build bearer token: %w", err)
}
@ -544,12 +506,7 @@ func buildSessionTokens(key *keys.PrivateKey, lifetime lifetimeOptions, ctxs []s
func createTokens(options *IssueSecretOptions, lifetime lifetimeOptions) ([]*accessbox.GateData, error) {
gates := make([]*accessbox.GateData, len(options.GatesPublicKeys))
table, err := buildEACLTable(options.EACLRules)
if err != nil {
return nil, fmt.Errorf("failed to build eacl table: %w", err)
}
bearerTokens, err := buildBearerTokens(options.FrostFSKey, options.Impersonate, table, lifetime, options.GatesPublicKeys)
bearerTokens, err := buildBearerTokens(options.FrostFSKey, options.Impersonate, lifetime, options.GatesPublicKeys)
if err != nil {
return nil, fmt.Errorf("failed to build bearer tokens: %w", err)
}
@ -577,9 +534,14 @@ func createTokens(options *IssueSecretOptions, lifetime lifetimeOptions) ([]*acc
func formTokensToUpdate(options tokenUpdateOptions) ([]*accessbox.GateData, error) {
btoken := options.box.Gate.BearerToken
table := btoken.EACLTable()
bearerTokens, err := buildBearerTokens(options.frostFSKey, btoken.Impersonate(), &table, options.lifetime, options.gatesPublicKeys)
btokenv2 := new(acl.BearerToken)
btoken.WriteToV2(btokenv2)
if btokenv2.GetBody().GetEACL() != nil {
return nil, errors.New("EACL table in bearer token isn't supported")
}
bearerTokens, err := buildBearerTokens(options.frostFSKey, btoken.Impersonate(), options.lifetime, options.gatesPublicKeys)
if err != nil {
return nil, fmt.Errorf("failed to build bearer tokens: %w", err)
}

View File

@ -2,6 +2,7 @@ package authmate
import (
"encoding/json"
"errors"
"fmt"
apisession "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
@ -55,22 +56,11 @@ func buildContext(rules []byte) ([]sessionTokenContext, error) {
return nil, fmt.Errorf("failed to unmarshal rules for session token: %w", err)
}
var (
containsPut = false
containsSetEACL = false
)
for _, d := range sessionCtxs {
if d.verb == session.VerbContainerPut {
containsPut = true
} else if d.verb == session.VerbContainerSetEACL {
containsSetEACL = true
if d.verb == session.VerbContainerSetEACL {
return nil, errors.New("verb container SetEACL isn't supported")
}
}
if containsPut && !containsSetEACL {
sessionCtxs = append(sessionCtxs, sessionTokenContext{
verb: session.VerbContainerSetEACL,
})
}
return sessionCtxs, nil
}
@ -78,6 +68,5 @@ func buildContext(rules []byte) ([]sessionTokenContext, error) {
return []sessionTokenContext{
{verb: session.VerbContainerPut},
{verb: session.VerbContainerDelete},
{verb: session.VerbContainerSetEACL},
}, nil
}

View File

@ -17,20 +17,15 @@ func TestContainerSessionRules(t *testing.T) {
{
"verb": "DELETE",
"containerID": "6CcWg8LkcbfMUC8pt7wiy5zM1fyS3psNoxgfppcCgig1"
},
{
"verb": "SETEACL"
}
]`)
sessionContext, err := buildContext(jsonRules)
require.NoError(t, err)
require.Len(t, sessionContext, 3)
require.Len(t, sessionContext, 2)
require.Equal(t, sessionContext[0].verb, session.VerbContainerPut)
require.Zero(t, sessionContext[0].containerID)
require.Equal(t, sessionContext[1].verb, session.VerbContainerDelete)
require.NotNil(t, sessionContext[1].containerID)
require.Equal(t, sessionContext[2].verb, session.VerbContainerSetEACL)
require.Zero(t, sessionContext[2].containerID)
}

View File

@ -2,7 +2,6 @@ package modules
import (
"context"
"errors"
"fmt"
"os"
"time"
@ -29,8 +28,6 @@ const (
walletFlag = "wallet"
addressFlag = "address"
peerFlag = "peer"
bearerRulesFlag = "bearer-rules"
disableImpersonateFlag = "disable-impersonate"
gatePublicKeyFlag = "gate-public-key"
containerIDFlag = "container-id"
containerFriendlyNameFlag = "container-friendly-name"
@ -70,8 +67,6 @@ func initIssueSecretCmd() {
issueSecretCmd.Flags().String(walletFlag, "", "Path to the wallet that will be owner of the credentials")
issueSecretCmd.Flags().String(addressFlag, "", "Address of the wallet account")
issueSecretCmd.Flags().String(peerFlag, "", "Address of a frostfs peer to connect to")
issueSecretCmd.Flags().String(bearerRulesFlag, "", "Rules for bearer token (filepath or a plain json string are allowed, can be used only with --disable-impersonate)")
issueSecretCmd.Flags().Bool(disableImpersonateFlag, false, "Mark token as not impersonate to don't consider token signer as request owner (must be provided to use --bearer-rules flag)")
issueSecretCmd.Flags().StringSlice(gatePublicKeyFlag, nil, "Public 256r1 key of a gate (use flags repeatedly for multiple gates or separate them by comma)")
issueSecretCmd.Flags().String(containerIDFlag, "", "Auth container id to put the secret into (if not provided new container will be created)")
issueSecretCmd.Flags().String(containerFriendlyNameFlag, "", "Friendly name of auth container to put the secret into (flag value will be used only if --container-id is missed)")
@ -134,17 +129,6 @@ func runIssueSecretCmd(cmd *cobra.Command, _ []string) error {
return wrapPreparationError(fmt.Errorf("couldn't parse container policy: %s", err.Error()))
}
disableImpersonate := viper.GetBool(disableImpersonateFlag)
eaclRules := viper.GetString(bearerRulesFlag)
if !disableImpersonate && eaclRules != "" {
return wrapPreparationError(errors.New("--bearer-rules flag can be used only with --disable-impersonate"))
}
bearerRules, err := getJSONRules(eaclRules)
if err != nil {
return wrapPreparationError(fmt.Errorf("couldn't parse 'bearer-rules' flag: %s", err.Error()))
}
sessionRules, skipSessionRules, err := getSessionRules(viper.GetString(sessionTokensFlag))
if err != nil {
return wrapPreparationError(fmt.Errorf("couldn't parse 'session-tokens' flag: %s", err.Error()))
@ -200,8 +184,7 @@ func runIssueSecretCmd(cmd *cobra.Command, _ []string) error {
},
FrostFSKey: key,
GatesPublicKeys: gatesPublicKeys,
EACLRules: bearerRules,
Impersonate: !disableImpersonate,
Impersonate: true,
SessionTokenRules: sessionRules,
SkipSessionRules: skipSessionRules,
ContainerPolicies: policies,

View File

@ -97,7 +97,6 @@ type (
clientCut bool
maxBufferSizeForPut uint64
md5Enabled bool
aclEnabled bool
namespaceHeader string
defaultNamespaces []string
policyDenyByDefault bool
@ -210,7 +209,6 @@ func newAppSettings(log *Logger, v *viper.Viper) *appSettings {
func (s *appSettings) update(v *viper.Viper, log *zap.Logger) {
s.updateNamespacesSettings(v, log)
s.useDefaultXMLNamespace(v.GetBool(cfgKludgeUseDefaultXMLNS))
s.setACLEnabled(v.GetBool(cfgKludgeACLEnabled))
s.setBypassContentEncodingInChunks(v.GetBool(cfgKludgeBypassContentEncodingCheckInChunks))
s.setClientCut(v.GetBool(cfgClientCut))
s.setBufferMaxSizeForPut(v.GetUint64(cfgBufferMaxSizeForPut))
@ -347,18 +345,6 @@ func (s *appSettings) setMD5Enabled(md5Enabled bool) {
s.mu.Unlock()
}
func (s *appSettings) setACLEnabled(enableACL bool) {
s.mu.Lock()
s.aclEnabled = enableACL
s.mu.Unlock()
}
func (s *appSettings) ACLEnabled() bool {
s.mu.RLock()
defer s.mu.RUnlock()
return s.aclEnabled
}
func (s *appSettings) NamespaceHeader() string {
s.mu.RLock()
defer s.mu.RUnlock()

View File

@ -160,8 +160,6 @@ const ( // Settings.
cfgKludgeUseDefaultXMLNS = "kludge.use_default_xmlns"
cfgKludgeBypassContentEncodingCheckInChunks = "kludge.bypass_content_encoding_check_in_chunks"
cfgKludgeDefaultNamespaces = "kludge.default_namespaces"
cfgKludgeACLEnabled = "kludge.acl_enabled"
// Web.
cfgWebReadTimeout = "web.read_timeout"
cfgWebReadHeaderTimeout = "web.read_header_timeout"
@ -731,7 +729,6 @@ func newSettings() *viper.Viper {
v.SetDefault(cfgKludgeUseDefaultXMLNS, false)
v.SetDefault(cfgKludgeBypassContentEncodingCheckInChunks, false)
v.SetDefault(cfgKludgeDefaultNamespaces, defaultDefaultNamespaces)
v.SetDefault(cfgKludgeACLEnabled, false)
// web
v.SetDefault(cfgWebReadHeaderTimeout, defaultReadHeaderTimeout)

View File

@ -154,8 +154,6 @@ S3_GW_KLUDGE_USE_DEFAULT_XMLNS=false
S3_GW_KLUDGE_BYPASS_CONTENT_ENCODING_CHECK_IN_CHUNKS=false
# Namespaces that should be handled as default
S3_GW_KLUDGE_DEFAULT_NAMESPACES="" "root"
# Enable bucket/object ACL support for newly created buckets.
S3_GW_KLUDGE_ACL_ENABLED=false
S3_GW_TRACING_ENABLED=false
S3_GW_TRACING_ENDPOINT="localhost:4318"

View File

@ -182,8 +182,6 @@ kludge:
bypass_content_encoding_check_in_chunks: false
# Namespaces that should be handled as default
default_namespaces: [ "", "root" ]
# Enable bucket/object ACL support for newly created buckets.
acl_enabled: false
runtime:
soft_memory_limit: 1gb

View File

@ -54,11 +54,6 @@ func (g *GateData) SessionTokenForDelete() *session.Container {
return g.containerSessionToken(session.VerbContainerDelete)
}
// SessionTokenForSetEACL returns the first suitable container session context for SetEACL operation.
func (g *GateData) SessionTokenForSetEACL() *session.Container {
return g.containerSessionToken(session.VerbContainerSetEACL)
}
// SessionToken returns the first container session context.
func (g *GateData) SessionToken() *session.Container {
if len(g.SessionTokens) != 0 {
@ -78,7 +73,7 @@ func (g *GateData) containerSessionToken(verb session.ContainerVerb) *session.Co
func isAppropriateContainerContext(tok *session.Container, verb session.ContainerVerb) bool {
switch verb {
case session.VerbContainerSetEACL, session.VerbContainerDelete, session.VerbContainerPut:
case session.VerbContainerDelete, session.VerbContainerPut:
return tok.AssertVerb(verb)
default:
return false

View File

@ -205,18 +205,6 @@ func TestGateDataSessionToken(t *testing.T) {
require.Equal(t, sessionTknDelete, sessionTkn)
})
t.Run("session token for set eACL", func(t *testing.T) {
gate.SessionTokens = []*session.Container{}
sessionTkn := gate.SessionTokenForSetEACL()
require.Nil(t, sessionTkn)
sessionTknSetEACL := new(session.Container)
sessionTknSetEACL.ForVerb(session.VerbContainerSetEACL)
gate.SessionTokens = []*session.Container{sessionTknSetEACL}
sessionTkn = gate.SessionTokenForSetEACL()
require.Equal(t, sessionTknSetEACL, sessionTkn)
})
t.Run("session token", func(t *testing.T) {
gate.SessionTokens = []*session.Container{}
sessionTkn := gate.SessionToken()

View File

@ -20,10 +20,10 @@ potentially).
1. [Generation of wallet](#generation-of-wallet)
2. [Issuance of a secret](#issuance-of-a-secret)
1. [CLI parameters](#cli-parameters)
2. [Bearer tokens](#bearer-tokens)
3. [Session tokens](#session-tokens)
4. [Containers policy](#containers-policy)
1. [CLI parameters](#cli-parameters)
2. [Bearer tokens](#bearer-tokens)
3. [Session tokens](#session-tokens)
4. [Containers policy](#containers-policy)
3. [Obtainment of a secret](#obtaining-credential-secrets)
4. [Generate presigned url](#generate-presigned-url)
5. [Update secrets](#update-secret)
@ -75,6 +75,7 @@ wallet is successfully created, the file location is wallet.json
```
To get the public key from the wallet:
```shell
$ ./bin/neo-go wallet dump-keys -w wallet.json
@ -90,22 +91,25 @@ put them as an object into a container on the FrostFS network.
### CLI parameters
**Required parameters:**
* `--wallet` is a path to a wallet `.json` file. You can provide a passphrase to decrypt
a wallet via environment variable `AUTHMATE_WALLET_PASSPHRASE`, or you will be asked to enter a passphrase
interactively. You can also specify an account address to use from a wallet using the `--address` parameter.
a wallet via environment variable `AUTHMATE_WALLET_PASSPHRASE`, or you will be asked to enter a passphrase
interactively. You can also specify an account address to use from a wallet using the `--address` parameter.
* `--peer` is an address of a FrostFS peer to connect to
* `--gate-public-key` is a public `secp256r1` 33-byte short key of a gate (use flags repeatedly for multiple gates). The tokens are encrypted
by a set of gateway keys, so you need to pass them as well.
* `--gate-public-key` is a public `secp256r1` 33-byte short key of a gate (use flags repeatedly for multiple gates). The
tokens are encrypted by a set of gateway keys, so you need to pass them as well.
You can issue a secret using the parameters above only. The tool will
1. create a new container
1. without a friendly name
2. with ACL `0x3c8c8cce` -- all operations are forbidden for `OTHERS` and `BEARER` user groups, except for `GET`
3. with policy `REP 2 IN X CBF 3 SELECT 2 FROM * AS X`
2. put bearer and session tokens with default rules (details in [Bearer tokens](#Bearer tokens) and
[Session tokens](#Session tokens))
1. create a new container
1. without a friendly name
2. with ACL `0x3c8c8cce` -- all operations are forbidden for `OTHERS` and `BEARER` user groups, except for `GET`
3. with policy `REP 2 IN X CBF 3 SELECT 2 FROM * AS X`
2. put bearer and session tokens with default rules (details in [Bearer tokens](#bearer-tokens) and
[Session tokens](#session-tokens))
E.g.:
```shell
$ frostfs-s3-authmate issue-secret --wallet wallet.json \
--peer 192.168.130.71:8080 \
@ -128,134 +132,78 @@ $ frostfs-s3-authmate issue-secret --wallet wallet.json \
`initial_access_key_id` contains the first credentials in the chain of credentials versions
(can be useful when you update your credentials).
`access_key_id` consists of Base58 encoded containerID(cid) and objectID(oid) stored on the FrostFS network and containing
`access_key_id` consists of Base58 encoded containerID(cid) and objectID(oid) stored on the FrostFS network and
containing
the secret. Format of `access_key_id`: `%cid0%oid`, where 0(zero) is a delimiter.
**Optional parameters:**
* `--container-id` - you can put the tokens into an existing container, but this way is ***not recommended***.
* `--container-friendly-name` -- name of a container with tokens, by default container will not have a friendly name
* `--container-placement-policy` - placement policy of auth container to put the secret into. Default value is
`REP 2 IN X CBF 3 SELECT 2 FROM * AS X`
* `--lifetime`-- lifetime of tokens. For example 50h30m (note: max time unit is an hour so to set a day you should use
24h). Default value is `720h` (30 days). It will be ceil rounded to the nearest amount of epoch
* `--container-placement-policy` - placement policy of auth container to put the secret into. Default value is
`REP 2 IN X CBF 3 SELECT 2 FROM * AS X`
* `--lifetime`-- lifetime of tokens. For example 50h30m (note: max time unit is an hour so to set a day you should use
24h). Default value is `720h` (30 days). It will be ceil rounded to the nearest amount of epoch
* `--aws-cli-credentials` - path to the aws cli credentials file, where authmate will write `access_key_id` and
`secret_access_key` to
`secret_access_key` to
* `--access-key-id` -- credentials that you want to update (e.g. to add more gates that can use your creds)
without changing values of `aws_access_key_id` and `aws_secret_access_key`. If you want to update credential you MUST
provide also secret key using `AUTHMATE_SECRET_ACCESS_KEY` env variable.
without changing values of `aws_access_key_id` and `aws_secret_access_key`. If you want to update credential you MUST
provide also secret key using `AUTHMATE_SECRET_ACCESS_KEY` env variable.
* `--frostfsid` -- FrostfsID contract hash (LE) or name in NNS to register public key in contract
(`--rpc-endpoint` flag also must be provided).
(`--rpc-endpoint` flag also must be provided).
* `--rpc-endpoint` -- NEO node RPC address.
### Bearer tokens
Creation of bearer tokens is mandatory.
By default, bearer token will be created with `impersonate` flag and won't have eACL table. It means that gate which will use such token
to interact with node can have access to your private containers or to containers in which eACL grants access to you
by public key.
Rules for a bearer token can be set via parameter `--bearer-rules` (json-string and file path allowed).
But you must provide `--disable-impersonate` flag:
```shell
$ frostfs-s3-authmate issue-secret --wallet wallet.json \
--peer 192.168.130.71:8080 \
--gate-public-key 0313b1ac3a8076e155a7e797b24f0b650cccad5941ea59d7cfd51a024a8b2a06bf \
--bearer-rules bearer-rules.json \
--disable-impersonate
```
where content of `bearer-rules.json`:
```json
{
"records": [
{"operation": "PUT", "action": "ALLOW", "filters": [], "targets": [{"role": "OTHERS", "keys": []}]},
{"operation": "GET", "action": "ALLOW", "filters": [], "targets": [{"role": "OTHERS", "keys": []}]},
{"operation": "HEAD", "action": "ALLOW", "filters": [], "targets": [{"role": "OTHERS", "keys": []}]},
{"operation": "DELETE", "action": "ALLOW", "filters": [], "targets": [{"role": "OTHERS", "keys": []}]},
{"operation": "SEARCH", "action": "ALLOW", "filters": [], "targets": [{"role": "OTHERS", "keys": []}]},
{"operation": "GETRANGE", "action": "ALLOW", "filters": [], "targets": [{"role": "OTHERS", "keys": []}]},
{"operation": "GETRANGEHASH", "action": "ALLOW", "filters": [], "targets": [{"role": "OTHERS", "keys": []}]}
]
}
```
**Note:** such rules allow all operations for all users (the same behavior when records are empty).
To restrict access you MUST provide records with `DENY` action. That's why we recommend always place such records
at the end of records (see default rules below) to prevent undesirable access violation.
Since the rules are applied from top to bottom, they do not override what was previously allowed.
If bearer rules are not set, a token will be auto-generated with a value:
```json
{
"version": {
"major": 2,
"minor": 11
},
"containerID": {
"value": null
},
"records": [
{"operation": "GET", "action": "ALLOW", "filters": [], "targets": [{"role": "OTHERS", "keys": []}]},
{"operation": "GET", "action": "DENY", "filters": [], "targets": [{"role": "OTHERS", "keys": []}]},
{"operation": "HEAD", "action": "DENY", "filters": [], "targets": [{"role": "OTHERS", "keys": []}]},
{"operation": "PUT", "action": "DENY", "filters": [], "targets": [{"role": "OTHERS", "keys": []}]},
{"operation": "DELETE", "action": "DENY", "filters": [], "targets": [{"role": "OTHERS", "keys": []}]},
{"operation": "SEARCH", "action": "DENY", "filters": [], "targets": [{"role": "OTHERS", "keys": []}]},
{"operation": "GETRANGE", "action": "DENY", "filters": [], "targets": [{"role": "OTHERS", "keys": []}]},
{"operation": "GETRANGEHASH", "action": "DENY", "filters": [], "targets": [{"role": "OTHERS", "keys": []}]}
]
}
```
Bearer token will be created with `impersonate` flag. It means that gate (which will use such token to interact with
node) can have access to your private containers or to containers in which eACL grants access to you by public key.
### Session tokens
With a session token, there are 3 options:
1. append `--session-tokens` parameter with your custom rules in json format (as a string or file path). E.g.:
```shell
$ frostfs-s3-authmate issue-secret --wallet wallet.json \
--peer 192.168.130.71:8080 \
--gate-public-key 0313b1ac3a8076e155a7e797b24f0b650cccad5941ea59d7cfd51a024a8b2a06bf \
--session-tokens session.json
```
where content of `session.json`:
```json
[
{
"verb": "PUT",
"containerID": null
},
{
"verb": "DELETE",
"containerID": null
},
{
"verb": "SETEACL",
"containerID": null
}
]
```
```shell
$ frostfs-s3-authmate issue-secret --wallet wallet.json \
--peer 192.168.130.71:8080 \
--gate-public-key 0313b1ac3a8076e155a7e797b24f0b650cccad5941ea59d7cfd51a024a8b2a06bf \
--session-tokens session.json
```
where content of `session.json`:
```json
[
{
"verb": "PUT",
"containerID": null
},
{
"verb": "DELETE",
"containerID": null
}
]
```
Available `verb` values: `PUT`, `DELETE`, `SETEACL`.
Available `verb` values: `PUT`, `DELETE`.
If `containerID` is `null` or omitted, then session token rule will be applied
to all containers. Otherwise, specify `containerID` value in human-redabale
format (base58 encoded string).
If `containerID` is `null` or omitted, then session token rule will be applied
to all containers. Otherwise, specify `containerID` value in human-readable
format (base58 encoded string).
> **_NB!_** To create buckets in FrostFS it's necessary to have session tokens with `PUT` and `SETEACL` permissions, that's why
the authmate creates a `SETEACL` session token automatically in case when a user specified the token rule with `PUT` and
forgot about the rule with `SETEACL`.
> **_NB!_** To create buckets in FrostFS it's necessary to have session tokens with `PUT` permissions.
2. append `--session-tokens` parameter with the value `none` -- no session token will be created
3. skip the parameter, and `authmate` will create session tokens with default rules (the same as in `session.json`
in example above)
in example above)
### Containers policy
Rules for mapping of `LocationConstraint` ([aws spec](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html#API_CreateBucket_RequestBody))
Rules for mapping
of `LocationConstraint` ([aws spec](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html#API_CreateBucket_RequestBody))
to `PlacementPolicy`
can be set via parameter `--container-policy` (json-string and file path allowed):
```json
{
"rep-3": "REP 3",
@ -302,7 +250,6 @@ Enter password for gate-wallet.json >
}
```
## Generate presigned URL
You can generate [presigned url](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html)
@ -332,6 +279,7 @@ $ frostfs-s3-authmate generate-presigned-url --endpoint http://localhost:8084 \
```
### AWS CLI
You can also can get the presigned URL (only for GET) using aws cli v2:
```shell
@ -341,19 +289,22 @@ http://localhost:8084/pregigned/obj?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Crede
```
## Update secret
You can extend list of s3 gates that can accept already issued credentials.
To do this use `frostfs-s3-authmate update-secret` command:
**Required parameters:**
* `--wallet` is a path to a user wallet `.json` file. You can provide a passphrase to decrypt
a wallet via environment variable `AUTHMATE_WALLET_PASSPHRASE`, or you will be asked to enter a passphrase
interactively. You can also specify an account address to use from a wallet using the `--address` parameter.
* `--gate-wallet` is a path to a gate wallet `.json` file (need to decrypt current access box version). You can provide a passphrase to decrypt
* `--gate-wallet` is a path to a gate wallet `.json` file (need to decrypt current access box version). You can provide
a passphrase to decrypt
a wallet via environment variable `AUTHMATE_WALLET_GATE_PASSPHRASE`, or you will be asked to enter a passphrase
interactively. You can also specify an account address to use from a wallet using the `--gate-address` parameter.
* `--peer` is an address of a FrostFS peer to connect to
* `--gate-public-key` is a public `secp256r1` 33-byte short key of a gate (use flags repeatedly for multiple gates).
* `--access-key-id` is a credential id to update.
* `--gate-public-key` is a public `secp256r1` 33-byte short key of a gate (use flags repeatedly for multiple gates).
* `--access-key-id` is a credential id to update.
```shell
$ frostfs-s3-authmate update-secret --wallet wallet.json --gate-wallet s3-wallet.json \
@ -380,9 +331,9 @@ Enter password for s3-wallet.json >
There are several non-zero exit codes added at the moment.
| Code | Description |
|-------|--------------------------------------------------------------------------------------------|
| 1 | Any unknown errors, or errors generated by the parser of command line parameters. |
| 2 | Preparation errors: malformed configuration, issues with input data parsing. |
| 3 | FrostFS errors: connectivity problems, misconfiguration. |
| 4 | Business logic errors: `authmate` could not execute its task because of some restrictions. |
| Code | Description |
|------|--------------------------------------------------------------------------------------------|
| 1 | Any unknown errors, or errors generated by the parser of command line parameters. |
| 2 | Preparation errors: malformed configuration, issues with input data parsing. |
| 3 | FrostFS errors: connectivity problems, misconfiguration. |
| 4 | Business logic errors: `authmate` could not execute its task because of some restrictions. |

View File

@ -558,7 +558,6 @@ kludge:
use_default_xmlns: false
bypass_content_encoding_check_in_chunks: false
default_namespaces: [ "", "root" ]
acl_enabled: false
```
| Parameter | Type | SIGHUP reload | Default value | Description |
@ -566,7 +565,6 @@ kludge:
| `use_default_xmlns` | `bool` | yes | `false` | Enable using default xml namespace `http://s3.amazonaws.com/doc/2006-03-01/` when parse xml bodies. |
| `bypass_content_encoding_check_in_chunks` | `bool` | yes | `false` | Use this flag to be able to use [chunked upload approach](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html) without having `aws-chunked` value in `Content-Encoding` header. |
| `default_namespaces` | `[]string` | yes | `["","root"]` | Namespaces that should be handled as default. |
| `acl_enabled` | `bool` | yes | `false` | Enable bucket/object ACL support for newly created buckets. |
# `runtime` section
Contains runtime parameters.

View File

@ -15,7 +15,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
@ -162,29 +161,6 @@ func (x *FrostFS) UserContainers(ctx context.Context, layerPrm layer.PrmUserCont
return r, handleObjectError("list user containers via connection pool", err)
}
// SetContainerEACL implements frostfs.FrostFS interface method.
func (x *FrostFS) SetContainerEACL(ctx context.Context, table eacl.Table, sessionToken *session.Container) error {
prm := pool.PrmContainerSetEACL{Table: table, Session: sessionToken, WaitParams: &x.await}
err := x.pool.SetEACL(ctx, prm)
return handleObjectError("save eACL via connection pool", err)
}
// ContainerEACL implements frostfs.FrostFS interface method.
func (x *FrostFS) ContainerEACL(ctx context.Context, layerPrm layer.PrmContainerEACL) (*eacl.Table, error) {
prm := pool.PrmContainerEACL{
ContainerID: layerPrm.ContainerID,
Session: layerPrm.SessionToken,
}
res, err := x.pool.GetEACL(ctx, prm)
if err != nil {
return nil, handleObjectError("read eACL via connection pool", err)
}
return &res, nil
}
// DeleteContainer implements frostfs.FrostFS interface method.
func (x *FrostFS) DeleteContainer(ctx context.Context, id cid.ID, token *session.Container) error {
prm := pool.PrmContainerDelete{ContainerID: id, Session: token, WaitParams: &x.await}