Skip to content

Commit 1973726

Browse files
authored
Merge pull request #4977 from cs3org/fix/stat-as-daemon
Refactor EOSFS to use cbox / daemon instead of root Remove deprecated logic
2 parents 2089040 + e172a77 commit 1973726

File tree

8 files changed

+310
-681
lines changed

8 files changed

+310
-681
lines changed
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Enhancement: drop shadow namespaces
2+
3+
This comes as part of the effort to operate EOS without being root, see https://github.com/cs3org/reva/pull/4977
4+
5+
In this PR the post-home creation hook (and corresponding flag) is replaced by a create_home_hook, and the following configuration parameters are suppressed:
6+
7+
shadow_namespace
8+
share_folder
9+
default_quota_bytes
10+
default_secondary_quota_bytes
11+
default_quota_files
12+
uploads_namespace (unused)
13+
14+
https://github.com/cs3org/reva/pull/4984

changelog/unreleased/rootless-auth.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Enhancement: do not use root on EOS
2+
3+
Currently, the EOS drivers use root authentication for many different operations. This has now been changed to use one of the following:
4+
* cbox, which is a sudo'er
5+
* daemon, for read-only operations
6+
* the user himselft
7+
8+
Note that home creation is excluded here as this will be tackled in a different PR.
9+
10+
https://github.com/cs3org/reva/pull/4977/

pkg/eosclient/eosgrpc/eosgrpc.go

+96-94
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ import (
3434
"time"
3535

3636
erpc "github.com/cern-eos/go-eosgrpc"
37-
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
3837
"github.com/cs3org/reva/pkg/appctx"
3938
"github.com/cs3org/reva/pkg/eosclient"
4039
"github.com/cs3org/reva/pkg/errtypes"
4140
"github.com/cs3org/reva/pkg/storage/utils/acl"
41+
"github.com/cs3org/reva/pkg/utils"
4242
"github.com/google/uuid"
4343
"github.com/pkg/errors"
4444
"github.com/rs/zerolog"
@@ -58,27 +58,12 @@ const (
5858
UserAttr
5959
)
6060

61-
func serializeAttribute(a *eosclient.Attribute) string {
62-
return fmt.Sprintf("%s.%s=%s", attrTypeToString(a.Type), a.Key, a.Val)
63-
}
64-
65-
func attrTypeToString(at eosclient.AttrType) string {
66-
switch at {
67-
case eosclient.SystemAttr:
68-
return "sys"
69-
case eosclient.UserAttr:
70-
return "user"
71-
default:
72-
return "invalid"
73-
}
74-
}
75-
76-
func isValidAttribute(a *eosclient.Attribute) bool {
77-
// validate that an attribute is correct.
78-
if (a.Type != eosclient.SystemAttr && a.Type != eosclient.UserAttr) || a.Key == "" {
79-
return false
80-
}
81-
return true
61+
// Client performs actions against a EOS management node (MGM)
62+
// using the EOS GRPC interface.
63+
type Client struct {
64+
opt *Options
65+
httpcl *EOSHTTPClient
66+
cl erpc.EosClient
8267
}
8368

8469
// Options to configure the Client.
@@ -131,15 +116,6 @@ type Options struct {
131116
TokenExpiry int
132117
}
133118

134-
func getUser(ctx context.Context) (*userpb.User, error) {
135-
u, ok := appctx.ContextGetUser(ctx)
136-
if !ok {
137-
err := errors.Wrap(errtypes.UserRequired(""), "eosfs: error getting user from ctx")
138-
return nil, err
139-
}
140-
return u, nil
141-
}
142-
143119
func (opt *Options) init() {
144120
if opt.XrdcopyBinary == "" {
145121
opt.XrdcopyBinary = "/opt/eos/xrootd/bin/xrdcopy"
@@ -154,12 +130,27 @@ func (opt *Options) init() {
154130
}
155131
}
156132

157-
// Client performs actions against a EOS management node (MGM)
158-
// using the EOS GRPC interface.
159-
type Client struct {
160-
opt *Options
161-
httpcl *EOSHTTPClient
162-
cl erpc.EosClient
133+
func serializeAttribute(a *eosclient.Attribute) string {
134+
return fmt.Sprintf("%s.%s=%s", attrTypeToString(a.Type), a.Key, a.Val)
135+
}
136+
137+
func attrTypeToString(at eosclient.AttrType) string {
138+
switch at {
139+
case eosclient.SystemAttr:
140+
return "sys"
141+
case eosclient.UserAttr:
142+
return "user"
143+
default:
144+
return "invalid"
145+
}
146+
}
147+
148+
func isValidAttribute(a *eosclient.Attribute) bool {
149+
// validate that an attribute is correct.
150+
if (a.Type != eosclient.SystemAttr && a.Type != eosclient.UserAttr) || a.Key == "" {
151+
return false
152+
}
153+
return true
163154
}
164155

165156
// Create and connect a grpc eos Client.
@@ -230,28 +221,33 @@ func (c *Client) getRespError(rsp *erpc.NSResponse, err error) error {
230221

231222
// Common code to create and initialize a NSRequest.
232223
func (c *Client) initNSRequest(ctx context.Context, auth eosclient.Authorization, app string) (*erpc.NSRequest, error) {
233-
// Stuff filename, uid, gid into the MDRequest type
234-
235224
log := appctx.GetLogger(ctx)
236225
log.Debug().Str("(uid,gid)", "("+auth.Role.UID+","+auth.Role.GID+")").Msg("New grpcNS req")
237226

238227
rq := new(erpc.NSRequest)
239228
rq.Role = new(erpc.RoleId)
240229

241-
uidInt, err := strconv.ParseUint(auth.Role.UID, 10, 64)
242-
if err != nil {
243-
return nil, err
244-
}
245-
gidInt, err := strconv.ParseUint(auth.Role.GID, 10, 64)
246-
if err != nil {
247-
return nil, err
230+
// Let's put in the authentication info
231+
if auth.Token != "" {
232+
// Map to owner using EOSAUTHZ token
233+
// We do not become cbox
234+
rq.Authkey = auth.Token
235+
} else {
236+
// We take the secret key from the config, which maps on EOS to cbox
237+
// cbox is a sudo'er, so we become the user specified in UID/GID, if it is set
238+
rq.Authkey = c.opt.Authkey
239+
240+
uid, gid, err := utils.ExtractUidGid(auth)
241+
if err == nil {
242+
rq.Role.Uid = uid
243+
rq.Role.Gid = gid
244+
}
248245
}
249-
rq.Role.Uid = uidInt
250-
rq.Role.Gid = gidInt
246+
247+
// For NS operations, specifically for locking, we also need to provide the app
251248
if app != "" {
252249
rq.Role.App = app
253250
}
254-
rq.Authkey = c.opt.Authkey
255251

256252
return rq, nil
257253
}
@@ -263,23 +259,26 @@ func (c *Client) initMDRequest(ctx context.Context, auth eosclient.Authorization
263259
log := appctx.GetLogger(ctx)
264260
log.Debug().Str("(uid,gid)", "("+auth.Role.UID+","+auth.Role.GID+")").Msg("New grpcMD req")
265261

266-
mdrq := new(erpc.MDRequest)
267-
mdrq.Role = new(erpc.RoleId)
262+
rq := new(erpc.MDRequest)
263+
rq.Role = new(erpc.RoleId)
268264

269-
uidInt, err := strconv.ParseUint(auth.Role.UID, 10, 64)
270-
if err != nil {
271-
return nil, err
272-
}
273-
gidInt, err := strconv.ParseUint(auth.Role.GID, 10, 64)
274-
if err != nil {
275-
return nil, err
276-
}
277-
mdrq.Role.Uid = uidInt
278-
mdrq.Role.Gid = gidInt
265+
if auth.Token != "" {
266+
// Map to owner using EOSAUTHZ token
267+
// We do not become cbox
268+
rq.Authkey = auth.Token
269+
} else {
270+
// We take the secret key from the config, which maps on EOS to cbox
271+
// cbox is a sudo'er, so we become the user specified in UID/GID, if it is set
272+
rq.Authkey = c.opt.Authkey
279273

280-
mdrq.Authkey = c.opt.Authkey
274+
uid, gid, err := utils.ExtractUidGid(auth)
275+
if err == nil {
276+
rq.Role.Uid = uid
277+
rq.Role.Gid = gid
278+
}
279+
}
281280

282-
return mdrq, nil
281+
return rq, nil
283282
}
284283

285284
// AddACL adds an new acl to EOS with the given aclType.
@@ -711,9 +710,14 @@ func getAttribute(key, val string) (*eosclient.Attribute, error) {
711710
}
712711

713712
// GetFileInfoByPath returns the FilInfo at the given path.
714-
func (c *Client) GetFileInfoByPath(ctx context.Context, auth eosclient.Authorization, path string) (*eosclient.FileInfo, error) {
713+
func (c *Client) GetFileInfoByPath(ctx context.Context, userAuth eosclient.Authorization, path string) (*eosclient.FileInfo, error) {
715714
log := appctx.GetLogger(ctx)
716-
log.Debug().Str("func", "GetFileInfoByPath").Str("uid,gid", auth.Role.UID+","+auth.Role.GID).Str("path", path).Msg("entering")
715+
log.Debug().Str("func", "GetFileInfoByPath").Str("uid,gid", userAuth.Role.UID+","+userAuth.Role.GID).Str("path", path).Msg("entering")
716+
717+
// UserAuth may not be sufficient, because the user may not have access to the file
718+
// e.g. in the case of a guest account. So we check if a uid/gid is set, and if not,
719+
// revert to the daemon account
720+
auth := utils.GetUserOrDaemonAuth(userAuth)
717721

718722
// Initialize the common fields of the MDReq
719723
mdrq, err := c.initMDRequest(ctx, auth)
@@ -756,7 +760,16 @@ func (c *Client) GetFileInfoByPath(ctx context.Context, auth eosclient.Authoriza
756760
}
757761

758762
if c.opt.VersionInvariant && !isVersionFolder(path) && !info.IsDir {
759-
inode, err := c.getVersionFolderInode(ctx, auth, path)
763+
// Here we have to create a missing version folder, irrespective from the user (that could be a sharee, or a lw account, or...)
764+
// Therefore, we impersonate the owner of the file
765+
ownerAuth := eosclient.Authorization{
766+
Role: eosclient.Role{
767+
UID: strconv.FormatUint(info.UID, 10),
768+
GID: strconv.FormatUint(info.GID, 10),
769+
},
770+
}
771+
772+
inode, err := c.getOrCreateVersionFolderInode(ctx, ownerAuth, path)
760773
if err != nil {
761774
return nil, err
762775
}
@@ -817,13 +830,9 @@ func (c *Client) GetQuota(ctx context.Context, username string, rootAuth eosclie
817830
return nil, errtypes.InternalError(fmt.Sprintf("Quota error from eos. info: '%#v'", resp.Quota))
818831
}
819832

820-
qi := new(eosclient.QuotaInfo)
821-
if resp == nil {
822-
return nil, errtypes.InternalError("Out of memory")
823-
}
824-
825833
// Let's loop on all the quotas that match this uid (apparently there can be many)
826834
// If there are many for this node, we sum them up
835+
qi := new(eosclient.QuotaInfo)
827836
for i := 0; i < len(resp.Quota.Quotanode); i++ {
828837
log.Debug().Str("func", "GetQuota").Str("quotanode:", fmt.Sprintf("%d: %#v", i, resp.Quota.Quotanode[i])).Msg("")
829838

@@ -954,13 +963,11 @@ func (c *Client) Chown(ctx context.Context, auth, chownAuth eosclient.Authorizat
954963

955964
msg := new(erpc.NSRequest_ChownRequest)
956965
msg.Owner = new(erpc.RoleId)
957-
msg.Owner.Uid, err = strconv.ParseUint(chownAuth.Role.UID, 10, 64)
958-
if err != nil {
959-
return err
960-
}
961-
msg.Owner.Gid, err = strconv.ParseUint(chownAuth.Role.GID, 10, 64)
962-
if err != nil {
963-
return err
966+
967+
uid, gid, err := utils.ExtractUidGid(chownAuth)
968+
if err == nil {
969+
msg.Owner.Uid = uid
970+
msg.Owner.Gid = gid
964971
}
965972

966973
msg.Id = new(erpc.MDId)
@@ -1195,7 +1202,6 @@ func (c *Client) Rename(ctx context.Context, auth eosclient.Authorization, oldPa
11951202
// List the contents of the directory given by path.
11961203
func (c *Client) List(ctx context.Context, auth eosclient.Authorization, dpath string) ([]*eosclient.FileInfo, error) {
11971204
log := appctx.GetLogger(ctx)
1198-
log.Info().Str("func", "List").Str("uid,gid", auth.Role.UID+","+auth.Role.GID).Str("dpath", dpath).Msg("")
11991205

12001206
// Stuff filename, uid, gid into the FindRequest type
12011207
fdrq := new(erpc.FindRequest)
@@ -1206,16 +1212,12 @@ func (c *Client) List(ctx context.Context, auth eosclient.Authorization, dpath s
12061212

12071213
fdrq.Role = new(erpc.RoleId)
12081214

1209-
uidInt, err := strconv.ParseUint(auth.Role.UID, 10, 64)
1210-
if err != nil {
1211-
return nil, err
1212-
}
1213-
gidInt, err := strconv.ParseUint(auth.Role.GID, 10, 64)
1215+
uid, gid, err := utils.ExtractUidGid(auth)
12141216
if err != nil {
1215-
return nil, err
1217+
return nil, errors.Wrap(err, "Failed to extract uid/gid from auth")
12161218
}
1217-
fdrq.Role.Uid = uidInt
1218-
fdrq.Role.Gid = gidInt
1219+
fdrq.Role.Uid = uid
1220+
fdrq.Role.Gid = gid
12191221

12201222
fdrq.Authkey = c.opt.Authkey
12211223

@@ -1347,7 +1349,7 @@ func (c *Client) Read(ctx context.Context, auth eosclient.Authorization, path st
13471349
var localfile io.WriteCloser
13481350
localfile = nil
13491351

1350-
u, err := getUser(ctx)
1352+
u, err := utils.GetUser(ctx)
13511353
if err != nil {
13521354
return nil, errors.Wrap(err, "eos: no user in ctx")
13531355
}
@@ -1383,7 +1385,7 @@ func (c *Client) Write(ctx context.Context, auth eosclient.Authorization, path s
13831385
var length int64
13841386
length = -1
13851387

1386-
u, err := getUser(ctx)
1388+
u, err := utils.GetUser(ctx)
13871389
if err != nil {
13881390
return errors.Wrap(err, "eos: no user in ctx")
13891391
}
@@ -1661,17 +1663,17 @@ func (c *Client) GenerateToken(ctx context.Context, auth eosclient.Authorization
16611663
return "", err
16621664
}
16631665

1664-
func (c *Client) getVersionFolderInode(ctx context.Context, auth eosclient.Authorization, p string) (uint64, error) {
1666+
func (c *Client) getOrCreateVersionFolderInode(ctx context.Context, ownerAuth eosclient.Authorization, p string) (uint64, error) {
16651667
log := appctx.GetLogger(ctx)
1666-
log.Info().Str("func", "getVersionFolderInode").Str("uid,gid", auth.Role.UID+","+auth.Role.GID).Str("p", p).Msg("")
1668+
log.Info().Str("func", "getOrCreateVersionFolderInode").Str("uid,gid", ownerAuth.Role.UID+","+ownerAuth.Role.GID).Str("p", p).Msg("")
16671669

16681670
versionFolder := getVersionFolder(p)
1669-
md, err := c.GetFileInfoByPath(ctx, auth, versionFolder)
1671+
md, err := c.GetFileInfoByPath(ctx, ownerAuth, versionFolder)
16701672
if err != nil {
1671-
if err = c.CreateDir(ctx, auth, versionFolder); err != nil {
1673+
if err = c.CreateDir(ctx, ownerAuth, versionFolder); err != nil {
16721674
return 0, err
16731675
}
1674-
md, err = c.GetFileInfoByPath(ctx, auth, versionFolder)
1676+
md, err = c.GetFileInfoByPath(ctx, ownerAuth, versionFolder)
16751677
if err != nil {
16761678
return 0, err
16771679
}

pkg/eosclient/eosgrpc/eoshttp.go

-8
Original file line numberDiff line numberDiff line change
@@ -429,14 +429,6 @@ func (c *EOSHTTPClient) PUTFile(ctx context.Context, remoteuser string, auth eos
429429
log.Debug().Str("func", "PUTFile").Int64("Content-Length", length).Msg("setting header")
430430
req.Header.Set("Content-Length", strconv.FormatInt(length, 10))
431431
}
432-
if err != nil {
433-
log.Error().Str("func", "PUTFile").Str("url", loc.String()).Str("err", err.Error()).Msg("can't create redirected request")
434-
return err
435-
}
436-
if length >= 0 {
437-
log.Debug().Str("func", "PUTFile").Int64("Content-Length", length).Msg("setting header")
438-
req.Header.Set("Content-Length", strconv.FormatInt(length, 10))
439-
}
440432

441433
log.Debug().Str("func", "PUTFile").Str("location", loc.String()).Msg("redirection")
442434
nredirs++

0 commit comments

Comments
 (0)