From b162043239646ca7a26da2146b56578b5ed0d3af Mon Sep 17 00:00:00 2001 From: Sebastien Buisson Date: Wed, 22 Jan 2025 15:20:11 +0100 Subject: [PATCH] LU-18109 nodemap: fix idmap offset for root root must be mapped and offset just like other ids, so that all ids on the client in the range [0, offset_limit-1] are mapped to the filesystem id range [offset, offset+offset_limit-1]. This means that as soon as an offset is defined on a nodemap, root is necessarily mapped: - if admin=1, root id is offset - if admin=0, root is squashed then offset. Similarly: - if trusted=1, ids are mapped (with any explicit mapping) and offset - if trusted=0, ids are squashed then offset. Moreover, add_offset and del_offset operations are only allowed on the MGS or for a dynamic nodemap. Finally, enhance sanity-sec test_27ab to test id mappings with offset. Fixes: e3051ad0f1 ("LU-18109 utils: adding nodemap offset capability") Signed-off-by: Sebastien Buisson Change-Id: I7a20c59496174d9d1fc040c5b3b43d8dad3447f9 Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/57856 Tested-by: jenkins Tested-by: Maloo Reviewed-by: Oleg Drokin Reviewed-by: Andreas Dilger Reviewed-by: Marc Vef --- lustre/mdt/mdt_lib.c | 12 ++- lustre/ptlrpc/nodemap_handler.c | 44 ++++++-- lustre/tests/sanity-sec.sh | 232 +++++++++++++++++++++++++++++++++------- 3 files changed, 237 insertions(+), 51 deletions(-) diff --git a/lustre/mdt/mdt_lib.c b/lustre/mdt/mdt_lib.c index 943d23d..d4cb5a9 100644 --- a/lustre/mdt/mdt_lib.c +++ b/lustre/mdt/mdt_lib.c @@ -258,7 +258,10 @@ static int new_init_ucred(struct mdt_thread_info *info, ucred_init_type_t type, GOTO(out_nodemap, rc = -EACCES); } - if (nodemap && ucred->uc_o_uid == nodemap->nm_squash_uid && + if (nodemap && + ucred->uc_o_uid == nodemap_map_id(nodemap, NODEMAP_UID, + NODEMAP_CLIENT_TO_FS, + nodemap->nm_squash_uid) && nodemap->nmf_deny_unknown) /* deny access before we get identity ref */ GOTO(out, rc = -EACCES); @@ -528,8 +531,11 @@ static int old_init_ucred_common(struct mdt_thread_info *info, struct mdt_device *mdt = info->mti_mdt; struct md_identity *identity = NULL; - if (nodemap && uc->uc_o_uid == nodemap->nm_squash_uid - && nodemap->nmf_deny_unknown) + if (nodemap && + uc->uc_o_uid == nodemap_map_id(nodemap, NODEMAP_UID, + NODEMAP_CLIENT_TO_FS, + nodemap->nm_squash_uid) && + nodemap->nmf_deny_unknown) /* deny access before we get identity ref */ RETURN(-EACCES); diff --git a/lustre/ptlrpc/nodemap_handler.c b/lustre/ptlrpc/nodemap_handler.c index 5c04e3d..cdd5572 100644 --- a/lustre/ptlrpc/nodemap_handler.c +++ b/lustre/ptlrpc/nodemap_handler.c @@ -780,9 +780,12 @@ __u32 nodemap_map_id(struct lu_nodemap *nodemap, if (unlikely(nodemap == NULL)) goto out; - if (id == 0) { + if (id_type != NODEMAP_PROJID && id == 0) { + /* root id is mapped and offset just as the other ids. This + * means root cannot remain root as soon as offset is defined. + */ if (nodemap->nmf_allow_root_access) - goto out; + goto offset; goto map; } @@ -847,13 +850,30 @@ offset: GOTO(out, found_id); if (tree_type == NODEMAP_FS_TO_CLIENT) { + if (found_id < offset_start) { + /* If we are outside boundaries, try to squash before + * offsetting, and return unmapped otherwise. + */ + if (!attempted_squash) + GOTO(squash, found_id); + + CDEBUG(D_SEC, + "%s: squash_id for type %u is below nodemap start %u, use unmapped value %u\n", + nodemap->nm_name, id_type, offset_start, + found_id); + GOTO(out, found_id); + } found_id -= offset_start; } else { - if (found_id >= offset_limit && !attempted_squash) - GOTO(squash, found_id); - - if (attempted_squash) { - CERROR("%s: squash_id for type %u is outside nodemap limit %u, use unmapped value %u\n", + if (found_id >= offset_limit) { + /* If we are outside boundaries, try to squash before + * offsetting, and return unmapped otherwise. + */ + if (!attempted_squash) + GOTO(squash, found_id); + + CDEBUG(D_SEC, + "%s: squash_id for type %u is outside nodemap limit %u, use unmapped value %u\n", nodemap->nm_name, id_type, offset_limit, found_id); GOTO(out, found_id); @@ -1681,7 +1701,8 @@ EXPORT_SYMBOL(nodemap_set_squash_projid); * Check if nodemap allows setting quota. * * If nodemap is not active, always allow. - * For user and group quota, allow if the nodemap allows root access. + * For user and group quota, allow if the nodemap allows root access, unless + * root is mapped. * For project quota, allow if project id is not squashed or deny_unknown * is not set. * @@ -1699,6 +1720,9 @@ bool nodemap_can_setquota(struct lu_nodemap *nodemap, __u32 qc_type, __u32 id) !(nodemap->nmf_rbac & NODEMAP_RBAC_QUOTA_OPS)) return false; + if (nodemap_map_id(nodemap, NODEMAP_UID, NODEMAP_CLIENT_TO_FS, 0) != 0) + return false; + if (qc_type == PRJQUOTA) { id = nodemap_map_id(nodemap, NODEMAP_PROJID, NODEMAP_CLIENT_TO_FS, id); @@ -2011,6 +2035,8 @@ int nodemap_add_offset(const char *nodemap_name, char *offset) if (is_default_nodemap(nodemap)) GOTO(out_putref, rc = -EINVAL); + if (!allow_op_on_nm(nodemap)) + GOTO(out_putref, rc = -EPERM); if (nodemap->nm_offset_start_uid) { /* nodemap has already offset */ @@ -2099,6 +2125,8 @@ int nodemap_del_offset(const char *nodemap_name) if (is_default_nodemap(nodemap)) GOTO(out_putref, rc = -EINVAL); + if (!allow_op_on_nm(nodemap)) + GOTO(out_putref, rc = -EPERM); rc = nodemap_del_offset_helper(nodemap); if (rc == 0) diff --git a/lustre/tests/sanity-sec.sh b/lustre/tests/sanity-sec.sh index ad6aa82..536e628 100755 --- a/lustre/tests/sanity-sec.sh +++ b/lustre/tests/sanity-sec.sh @@ -2360,35 +2360,44 @@ test_27aa() { #LU-17922 run_test 27aa "test nodemap idmap range" test_27ab() { #LU-18109 - local idmap + local offset_start=100000 + local offset_limit=200000 + local nid=1.1.1.1@tcp777 + local activedefault + local nm1=Test18109 + local nm2=OffsetTest + local squash=65534 + local id_start=500 + local expected local id=500 + local idmap local offset (( MDS1_VERSION > $(version_code 2.16.50.170) )) || skip "need MDS > 2.16.50.170 for nodemap range offset" - do_facet mgs $LCTL nodemap_add Test18109 || - error "unable to add Test18109 as nodemap" - stack_trap "do_facet mgs $LCTL nodemap_del Test18109 || true" + do_facet mgs $LCTL nodemap_add $nm1 || + error "unable to add $nm1 as nodemap" + stack_trap "do_facet mgs $LCTL nodemap_del $nm1 || true" - do_facet mgs $LCTL nodemap_add OffsetTest || - error "unable to add OffsetTest as nodemap" - stack_trap "do_facet mgs $LCTL nodemap_del OffsetTest || true" + do_facet mgs $LCTL nodemap_add $nm2 || + error "unable to add $nm2 as nodemap" + stack_trap "do_facet mgs $LCTL nodemap_del $nm2 || true" - do_facet mgs $LCTL nodemap_add_offset --name Test18109 \ - --offset 100000 --limit 200000 || - error "cannot set offset 100000-299999 for Test18109" + do_facet mgs $LCTL nodemap_add_offset --name $nm1 \ + --offset $offset_start --limit $offset_limit || + error "cannot set offset $offset_start-$((offset_start+offset_limit-1)) for $nm1" #expected error, invalid offset range supplied - do_facet mgs $LCTL nodemap_add_offset --name OffsetTest \ - --offset 150000 --limit 100000 && - error "cannot set offset 150000-249999 for OffsetTest" + do_facet mgs $LCTL nodemap_add_offset --name $nm2 \ + --offset $((offset_start+50000)) --limit 100000 && + error "setting offset $((offset_start+50000))-249999 on $nm2 should fail" - do_facet mgs $LCTL nodemap_add_idmap --name Test18109 \ + do_facet mgs $LCTL nodemap_add_idmap --name $nm1 \ --idtype uid --idmap 500-509:0-9 || error "unable to add idmap range 500-509:0-9" - idmap=$(do_facet mgs $LCTL get_param nodemap.Test18109.idmap | + idmap=$(do_facet mgs $LCTL get_param nodemap.$nm1.idmap | grep idtype) while IFS= read -r idmap; do if (( $id <= 509 )); then @@ -2398,51 +2407,194 @@ test_27ab() { #LU-18109 ((id++)) done < <(echo "$idmap") - do_facet mgs $LCTL nodemap_del_idmap --name Test18109 \ + do_facet mgs $LCTL nodemap_add_range --name $nm1 --range $nid || + error "Add range $nid to $nm1 failed" + do_facet mgs $LCTL nodemap_modify --name $nm1 \ + --property admin --value 1 || + error "Setting admin=1 on $nm1 failed" + do_facet mgs $LCTL nodemap_modify --name $nm1 \ + --property trusted --value 1 || + error "Setting trusted=1 on $nm1 failed" + do_facet mgs $LCTL nodemap_modify --name $nm1 \ + --property squash_uid --value $squash || + error "Setting squash_uid=$squash on $nm1 failed" + do_facet mgs $LCTL nodemap_modify --name $nm1 \ + --property squash_gid --value $squash || + error "Setting squash_gid=$squash on $nm1 failed" + + activedefault=$(do_facet mgs $LCTL get_param -n nodemap.active) + if ((activedefault != 1)); then + do_facet mgs $LCTL nodemap_activate 1 + wait_nm_sync active + stack_trap cleanup_active EXIT + fi + + if (( MDS1_VERSION >= $(version_code 2.16.51.45) )); then + # with admin=1, we expect root to be offset + id=0 + expected=$offset_start + idmap=$(do_facet mgs $LCTL nodemap_test_id --nid $nid \ + --idtype uid --id $id) + ((idmap == expected)) || + error "uid $id should be mapped to $expected" + idmap=$(do_facet mgs $LCTL nodemap_test_id --nid $nid \ + --idtype gid --id $id) + ((idmap == expected)) || + error "gid $id should be mapped to $expected" + # with trusted=1, we expect ids to be offset + id=$((id_start+1)) + expected=$((offset_start+id_start+1)) + idmap=$(do_facet mgs $LCTL nodemap_test_id --nid $nid \ + --idtype uid --id $id) + ((idmap == expected)) || + error "uid $id should be mapped to $expected" + idmap=$(do_facet mgs $LCTL nodemap_test_id --nid $nid \ + --idtype gid --id $id) + ((idmap == expected)) || + error "gid $id should be mapped to $expected" + + do_facet mgs $LCTL nodemap_modify --name $nm1 \ + --property trusted --value 0 || + error "Setting trusted=0 on $nm1 failed" + + # with trusted=0, we expect uid to be mapped+offset, + # gid to be squashed+offset + expected=$((offset_start+1)) + idmap=$(do_facet mgs $LCTL nodemap_test_id --nid $nid \ + --idtype uid --id $id) + ((idmap == expected)) || + error "uid $id should be mapped to $expected" + expected=$((offset_start+squash)) + idmap=$(do_facet mgs $LCTL nodemap_test_id --nid $nid \ + --idtype gid --id $id) + ((idmap == expected)) || + error "gid $id should be mapped to $expected" + + do_facet mgs $LCTL nodemap_modify --name $nm1 \ + --property admin --value 0 || + error "Setting admin=0 on $nm1 failed" + + # with admin=0, we expect root to be squashed+offset + id=0 + expected=$((offset_start+squash)) + idmap=$(do_facet mgs $LCTL nodemap_test_id --nid $nid \ + --idtype uid --id $id) + ((idmap == expected)) || + error "uid $id should be mapped to $expected" + idmap=$(do_facet mgs $LCTL nodemap_test_id --nid $nid \ + --idtype gid --id $id) + ((idmap == expected)) || + error "gid $id should be mapped to $expected" + + do_facet mgs $LCTL nodemap_modify --name $nm1 \ + --property admin --value 1 || + error "Setting admin=1 on $nm1 failed" + do_facet mgs $LCTL nodemap_modify --name $nm1 \ + --property trusted --value 1 || + error "Setting trusted=1 on $nm1 failed" + fi + + do_facet mgs $LCTL nodemap_del_idmap --name $nm1 \ --idtype uid --idmap 500-509:0 || error "cannot delete idmap range 500-509:0" #expected error, invalid secondary range supplied - do_facet mgs $LCTL nodemap_add --name Test18109 \ + do_facet mgs $LCTL nodemap_add --name $nm1 \ --idtype uid --idmap 500-509:200000-200010 && error "Invalid range 200000-200010 was supplied" - (( $(do_facet mgs $LCTL get_param nodemap.Test18109.idmap | + (( $(do_facet mgs $LCTL get_param nodemap.$nm1.idmap | grep -c idtype) == 0 )) || error "invalid range 200000-200010 supplied and passed" - offset=$(do_facet mgs $LCTL get_param nodemap.Test18109.offset | + offset=$(do_facet mgs $LCTL get_param nodemap.$nm1.offset | grep start_uid) - [[ "$offset" == *"start_uid: 100000"* ]] || - error "expected start_uid of 100000 not found before remounting" + [[ "$offset" == *"start_uid: $offset_start"* ]] || + error "expected start_uid of $offset_start not found before remounting" - offset=$(do_facet mgs $LCTL get_param nodemap.Test18109.offset | + offset=$(do_facet mgs $LCTL get_param nodemap.$nm1.offset | grep limit_uid) - [[ "$offset" == *"limit_uid: 200000"* ]] || - error "expected limit_uid of 200000 not found before remounting" + [[ "$offset" == *"limit_uid: $offset_limit"* ]] || + error "expected limit_uid of $offset_limit not found before remounting" + + if (( MDS1_VERSION >= $(version_code 2.16.51.45) )); then + # with admin=1, we expect root to be offset + id=0 + expected=$offset_start + idmap=$(do_facet mgs $LCTL nodemap_test_id --nid $nid \ + --idtype uid --id $id) + ((idmap == expected)) || + error "uid $id should be mapped to $expected" + idmap=$(do_facet mgs $LCTL nodemap_test_id --nid $nid \ + --idtype gid --id $id) + ((idmap == expected)) || + error "gid $id should be mapped to $expected" + # with trusted=1, we expect ids to be offset + id=$((id_start+1)) + expected=$((offset_start+id_start+1)) + idmap=$(do_facet mgs $LCTL nodemap_test_id --nid $nid \ + --idtype uid --id $id) + ((idmap == expected)) || + error "uid $id should be mapped to $expected" + idmap=$(do_facet mgs $LCTL nodemap_test_id --nid $nid \ + --idtype gid --id $id) + ((idmap == expected)) || + error "gid $id should be mapped to $expected" + + do_facet mgs $LCTL nodemap_modify --name $nm1 \ + --property trusted --value 0 || + error "Setting trusted=0 on $nm1 failed" + + # with trusted=0, we expect uid to be squashed+offset + expected=$((offset_start+squash)) + idmap=$(do_facet mgs $LCTL nodemap_test_id --nid $nid \ + --idtype uid --id $id) + ((idmap == expected)) || + error "uid $id should be mapped to $expected" + idmap=$(do_facet mgs $LCTL nodemap_test_id --nid $nid \ + --idtype gid --id $id) + ((idmap == expected)) || + error "gid $id should be mapped to $expected" + + do_facet mgs $LCTL nodemap_modify --name $nm1 \ + --property admin --value 0 || + error "Setting admin=0 on $nm1 failed" + + # with admin=0, we expect root to be squashed+offset + id=0 + expected=$((offset_start+squash)) + idmap=$(do_facet mgs $LCTL nodemap_test_id --nid $nid \ + --idtype uid --id $id) + ((idmap == expected)) || + error "uid $id should be mapped to $expected" + idmap=$(do_facet mgs $LCTL nodemap_test_id --nid $nid \ + --idtype gid --id $id) + ((idmap == expected)) || + error "gid $id should be mapped to $expected" + fi stopall || error "failed to unmount servers" setupall || error "failed to remount servers" - offset=$(do_facet mgs $LCTL get_param nodemap.Test18109.offset | + offset=$(do_facet mgs $LCTL get_param nodemap.$nm1.offset | grep start_uid) - [[ "$offset" == *"start_uid: 100000"* ]] || - error "expected start_uid of 100000 not found after remounting" + [[ "$offset" == *"start_uid: $offset_start"* ]] || + error "expected start_uid of $offset_start not found after remounting" - offset=$(do_facet mgs $LCTL get_param nodemap.Test18109.offset | + offset=$(do_facet mgs $LCTL get_param nodemap.$nm1.offset | grep limit_uid) - [[ "$offset" == *"limit_uid: 200000"* ]] || - error "expected limit_uid of 200000 not found after remounting" + [[ "$offset" == *"limit_uid: $offset_limit"* ]] || + error "expected limit_uid of $offset_limit not found after remounting" - do_facet mgs $LCTL nodemap_del_offset --name Test18109 || - error "cannot del offset from Test18109" + do_facet mgs $LCTL nodemap_del_offset --name $nm1 || + error "cannot del offset from $nm1" - offset=$(do_facet mgs $LCTL get_param nodemap.Test18109.offset | + offset=$(do_facet mgs $LCTL get_param nodemap.$nm1.offset | grep start_uid) [[ "$offset" == *"start_uid: 0"* ]] || error "expected start_uid 0, found $offset" - offset=$(do_facet mgs $LCTL get_param nodemap.Test18109.offset | + offset=$(do_facet mgs $LCTL get_param nodemap.$nm1.offset | grep limit_uid) [[ "$offset" == *"limit_uid: 0"* ]] || error "expected limit_uid 0, found $offset" @@ -2450,21 +2602,21 @@ test_27ab() { #LU-18109 stopall || error "failed to unmount servers" setupall || error "failed to remount servers" - offset=$(do_facet mgs $LCTL get_param nodemap.Test18109.offset | + offset=$(do_facet mgs $LCTL get_param nodemap.$nm1.offset | grep start_uid) [[ "$offset" == *"start_uid: 0"* ]] || error "expected start_uid 0, found $offset after remounting" - offset=$(do_facet mgs $LCTL get_param nodemap.Test18109.offset | + offset=$(do_facet mgs $LCTL get_param nodemap.$nm1.offset | grep limit_uid) [[ "$offset" == *"limit_uid: 0"* ]] || error "expected limit_uid 0, found $offset after remounting" - do_facet mgs $LCTL nodemap_del Test18109 || - error "failed to remove nodemap Test18109" + do_facet mgs $LCTL nodemap_del $nm1 || + error "failed to remove nodemap $nm1" - do_facet mgs $LCTL nodemap_del OffsetTest || - error "failed to remove nodemap OffsetTest" + do_facet mgs $LCTL nodemap_del $nm2 || + error "failed to remove nodemap $nm2" } run_test 27ab "test nodemap idmap offset" -- 1.8.3.1