.br
- ignore_root_prjquota, so that project quota is not enforced for the root user.
.br
+- local_admin, to allow mapped root to perform admin operations on files, unless
+it is squashed.
+.br
- quota_ops, to allow quota modifications.
.br
- server_upcall, to define which identity upcall to use. If set, identity upcall
{ NODEMAP_RBAC_SERVER_UPCALL, "server_upcall" },
{ NODEMAP_RBAC_IGN_ROOT_PRJQUOTA, "ignore_root_prjquota" },
{ NODEMAP_RBAC_HSM_OPS, "hsm_ops" },
+ { NODEMAP_RBAC_LOCAL_ADMIN, "local_admin" },
};
struct nodemap_pde {
int nodemap_get_config_req(struct obd_device *mgs_obd,
struct ptlrpc_request *req);
+
+/* Return true if id corresponds to local root */
+static inline bool is_local_root(__u32 id, struct lu_nodemap *nodemap)
+{
+ /* Plain root is also local root */
+ if (id == 0)
+ return true;
+
+ /* id is not mapped root (0):
+ * just a regular mapped user
+ */
+ if (id != nodemap_map_id(nodemap, NODEMAP_UID,
+ NODEMAP_CLIENT_TO_FS, 0))
+ return false;
+
+ /* id is mapped root, but root is squashed:
+ * not considered a local admin
+ */
+ if (id == nodemap_map_id(nodemap, NODEMAP_UID, NODEMAP_CLIENT_TO_FS,
+ nodemap->nm_squash_uid))
+ return false;
+
+ /* id is mapped root and not squashed:
+ * rely on the local_admin rbac role
+ */
+ return nodemap->nmf_rbac & NODEMAP_RBAC_LOCAL_ADMIN;
+}
+
#endif /* _LUSTRE_NODEMAP_H */
int uc_rbac_server_upcall:1;
int uc_rbac_ignore_root_prjquota:1;
int uc_rbac_hsm_ops:1;
+ int uc_rbac_local_admin:1;
};
struct lu_ucred *lu_ucred(const struct lu_env *env);
NODEMAP_RBAC_SERVER_UPCALL = 0x00000040,
NODEMAP_RBAC_IGN_ROOT_PRJQUOTA = 0x00000080,
NODEMAP_RBAC_HSM_OPS = 0x00000100,
+ NODEMAP_RBAC_LOCAL_ADMIN = 0x00000200,
NODEMAP_RBAC_NONE = (__u32)~(NODEMAP_RBAC_FILE_PERMS |
NODEMAP_RBAC_DNE_OPS |
NODEMAP_RBAC_QUOTA_OPS |
NODEMAP_RBAC_FSCRYPT_ADMIN |
NODEMAP_RBAC_SERVER_UPCALL |
NODEMAP_RBAC_IGN_ROOT_PRJQUOTA |
- NODEMAP_RBAC_HSM_OPS),
+ NODEMAP_RBAC_HSM_OPS |
+ NODEMAP_RBAC_LOCAL_ADMIN),
NODEMAP_RBAC_ALL = 0xFFFFFFFF, /* future caps ON by default */
};
uc->uc_rbac_server_upcall = 1;
uc->uc_rbac_ignore_root_prjquota = 1;
uc->uc_rbac_hsm_ops = 1;
+ uc->uc_rbac_local_admin = 1;
RETURN(0);
}
mdt_ucred(info)->uc_rbac_server_upcall = 1;
mdt_ucred(info)->uc_rbac_ignore_root_prjquota = 1;
mdt_ucred(info)->uc_rbac_hsm_ops = 1;
+ mdt_ucred(info)->uc_rbac_local_admin = 1;
rc = mdt_add_dirty_flag(info, mfd->mfd_object, &info->mti_attr);
lu_context_exit(&ses);
uc->uc_rbac_ignore_root_prjquota =
!!(rbac & NODEMAP_RBAC_IGN_ROOT_PRJQUOTA);
uc->uc_rbac_hsm_ops = !!(rbac & NODEMAP_RBAC_HSM_OPS);
+ uc->uc_rbac_local_admin = !!(rbac & NODEMAP_RBAC_LOCAL_ADMIN);
}
static int new_init_ucred(struct mdt_thread_info *info, ucred_init_type_t type,
mdt_root_squash(info, &peernid);
- if (ucred->uc_fsuid) {
+ if (!is_local_root(ucred->uc_fsuid, nodemap)) {
if (!cap_issubset(ucred->uc_cap, mdt->mdt_enable_cap_mask))
CDEBUG(D_SEC, "%s: drop capabilities %llx for NID %s\n",
mdt_obd_name(mdt),
mdt_root_squash(info,
&mdt_info_req(info)->rq_peer.nid);
- if (uc->uc_fsuid) {
+ if (!is_local_root(uc->uc_fsuid, nodemap)) {
if (!cap_issubset(uc->uc_cap, mdt->mdt_enable_cap_mask))
CDEBUG(D_SEC, "%s: drop capabilities %llx for NID %s\n",
mdt_obd_name(mdt),
uc->uc_rbac_server_upcall = 1;
uc->uc_rbac_ignore_root_prjquota = 1;
uc->uc_rbac_hsm_ops = 1;
+ uc->uc_rbac_local_admin = 1;
task = kthread_create(mdt_restriper_main, info, "mdt_restriper_%03d",
mdt_seq_site(mdt)->ss_node_id);
ucred->uc_rbac_server_upcall = 1;
ucred->uc_rbac_ignore_root_prjquota = 1;
ucred->uc_rbac_hsm_ops = 1;
+ ucred->uc_rbac_local_admin = 1;
}
static void echo_ucred_fini(struct lu_env *env)
*
* If nodemap is not active, always allow.
* For user and group quota, allow if the nodemap allows root access, unless
- * root is mapped.
+ * root does not have local admin role.
* For project quota, allow if project id is not squashed or deny_unknown
* is not set.
*
!(nodemap->nmf_rbac & NODEMAP_RBAC_QUOTA_OPS))
return false;
- if (nodemap_map_id(nodemap, NODEMAP_UID, NODEMAP_CLIENT_TO_FS, 0) != 0)
+ /* deny if local root has not local admin role */
+ if (!is_local_root(nodemap_map_id(nodemap, NODEMAP_UID,
+ NODEMAP_CLIENT_TO_FS, 0),
+ nodemap))
return false;
if (qc_type == PRJQUOTA) {
(unsigned)NODEMAP_RBAC_IGN_ROOT_PRJQUOTA);
LASSERTF(NODEMAP_RBAC_HSM_OPS == 0x00000100UL, "found 0x%.8xUL\n",
(unsigned)NODEMAP_RBAC_HSM_OPS);
- LASSERTF(NODEMAP_RBAC_NONE == 0xfffffe00UL, "found 0x%.8xUL\n",
+ LASSERTF(NODEMAP_RBAC_LOCAL_ADMIN == 0x00000200UL, "found 0x%.8xUL\n",
+ (unsigned)NODEMAP_RBAC_LOCAL_ADMIN);
+ LASSERTF(NODEMAP_RBAC_NONE == 0xfffffc00UL, "found 0x%.8xUL\n",
(unsigned)NODEMAP_RBAC_NONE);
LASSERTF(NODEMAP_RBAC_ALL == 0xffffffffUL, "found 0x%.8xUL\n",
(unsigned)NODEMAP_RBAC_ALL);
test_64a() {
local testfile=$DIR/$tdir/$tfile
local srv_uc=""
+ local local_admin=""
local rbac
(( MDS1_VERSION >= $(version_code 2.15.54) )) ||
(( MDS1_VERSION >= $(version_code 2.16.50) )) &&
srv_uc="server_upcall"
+ (( MDS1_VERSION >= $(version_code 2.16.52) )) &&
+ local_admin="local_admin"
+
stack_trap cleanup_local_client_nodemap EXIT
mkdir -p $DIR/$tdir || error "mkdir $DIR/$tdir failed"
setup_local_client_nodemap "c0" 1 1
chlg_ops \
fscrypt_admin \
$srv_uc \
+ $local_admin \
;
do
[[ "$rbac" =~ "$role" ]] ||
}
run_test 64g "Nodemap enforces server_upcall RBAC role"
+test_64h() {
+ local testfile=$DIR/$tdir/$tfile
+ local offset_start=100000
+ local offset_limit=200000
+ local projid=1001
+ local srv_uc=""
+ local rbac
+ local fid
+
+ (( MDS1_VERSION >= $(version_code 2.15.54) )) ||
+ skip "Need MDS >= 2.15.54 for role-based controls"
+
+ (( MDS1_VERSION >= $(version_code 2.16.50) )) &&
+ srv_uc="server_upcall"
+
+ do_nodes $(comma_list $(all_mdts_nodes)) \
+ $LCTL set_param mdt.*.identity_upcall=NONE
+
+ stack_trap \
+ "$LFS setquota -p $((projid+offset_start)) --delete $DIR/$tdir" EXIT
+ stack_trap cleanup_local_client_nodemap EXIT
+ mkdir -p $DIR/$tdir || error "mkdir $DIR/$tdir failed"
+ chmod 777 $DIR/$tdir
+ $LFS project -p $((projid+offset_start)) -s $DIR/$tdir
+ $LFS setquota -p $((projid+offset_start)) -b 1G -B 1G $DIR/$tdir
+ $LFS project -d $DIR/$tdir
+ $LFS quota -aph $DIR/$tdir
+ setup_local_client_nodemap "c0" 1 1
+
+ # skip test if server does not support local_admin rbac role
+ rbac=$(do_facet mds $LCTL get_param -n nodemap.c0.rbac)
+ [[ "$rbac" =~ "local_admin" ]] ||
+ skip "server does not support 'local_admin' rbac role"
+
+ # Let's offset ids. Even root is offset.
+ do_facet mgs $LCTL nodemap_add_offset --name c0 \
+ --offset $offset_start --limit $offset_limit ||
+ error "cannot set offset for c0"
+
+ rbac="file_perms,quota_ops"
+ [ -z "$srv_uc" ] || rbac="$rbac,$srv_uc"
+ do_facet mgs $LCTL nodemap_modify --name c0 --property rbac \
+ --value $rbac ||
+ error "setting rbac $rbac failed (1)"
+ wait_nm_sync c0 rbac
+
+ $RUNAS touch $testfile
+
+ # Without local_admin, root capabilities are dropped
+ chmod o+x $testfile && error "root chmod should fail (1)"
+ # and setquota/lfs project is not permitted
+ $LFS setquota -p $projid -b 4G -B 4G $DIR/$tdir &&
+ error "setquota should fail (1)"
+ $LFS project -p $((projid+1)) -s $DIR/$tdir &&
+ error "setting projid should fail (1)"
+
+ rbac="file_perms,quota_ops,local_admin"
+ [ -z "$srv_uc" ] || rbac="$rbac,$srv_uc"
+ do_facet mgs $LCTL nodemap_modify --name c0 \
+ --property rbac --value $rbac ||
+ error "setting rbac $rbac failed (2)"
+ wait_nm_sync c0 rbac
+ # squash root by setting admin=0
+ do_facet mgs $LCTL nodemap_modify --name c0 \
+ --property admin --value 0
+ wait_nm_sync c0 admin_nodemap
+
+ # Even with local_admin, capabilities are dropped if root is squashed
+ chmod o+x $testfile && error "root chmod should fail (2)"
+ # and setquota/lfs project is not permitted
+ $LFS setquota -p $projid -b 4G -B 4G $DIR/$tdir &&
+ error "setquota should fail (2)"
+ $LFS project -p $((projid+1)) -s $DIR/$tdir &&
+ error "setting projid should fail (2)"
+
+ do_facet mgs $LCTL nodemap_modify --name c0 \
+ --property admin --value 1
+ wait_nm_sync c0 admin_nodemap
+
+ # with local_admin and admin=1, capabilities are kept
+ chmod o+x $testfile || error "root chmod failed (1)"
+ # and setquota/lfs project is permitted
+ $LFS setquota -p $projid -b 4G -B 4G $DIR/$tdir ||
+ error "setquota failed (1)"
+ $LFS project -p $((projid+1)) -s $DIR/$tdir ||
+ error "setting projid failed (1)"
+
+ # remove offset and local_admin but keep admin, so that root
+ # on client is root on file system side
+ do_facet mgs $LCTL nodemap_del_offset --name c0 ||
+ error "cannot del offset for c0"
+ rbac="file_perms,quota_ops"
+ [ -z "$srv_uc" ] || rbac="$rbac,$srv_uc"
+ do_facet mgs $LCTL nodemap_modify --name c0 --property rbac \
+ --value $rbac ||
+ error "setting rbac $rbac failed (3)"
+ wait_nm_sync c0 rbac
+
+ # as root, capabilities are kept even without local_admin
+ chmod g+x $testfile || error "root chmod failed (2)"
+ # and setquota/lfs project is permitted
+ $LFS setquota -p $((projid+offset_start)) -b 3G -B 3G $DIR/$tdir ||
+ error "setquota failed (2)"
+ $LFS project -p $((projid+offset_start)) -s $DIR/$tdir ||
+ error "setting projid failed (2)"
+}
+run_test 64h "Nodemap enforces local_admin RBAC roles"
+
look_for_files() {
local pattern=$1
local neg=$2
CHECK_VALUE_X(NODEMAP_RBAC_SERVER_UPCALL);
CHECK_VALUE_X(NODEMAP_RBAC_IGN_ROOT_PRJQUOTA);
CHECK_VALUE_X(NODEMAP_RBAC_HSM_OPS);
+ CHECK_VALUE_X(NODEMAP_RBAC_LOCAL_ADMIN);
CHECK_VALUE_X(NODEMAP_RBAC_NONE);
CHECK_VALUE_X(NODEMAP_RBAC_ALL);
}
(unsigned)NODEMAP_RBAC_IGN_ROOT_PRJQUOTA);
LASSERTF(NODEMAP_RBAC_HSM_OPS == 0x00000100UL, "found 0x%.8xUL\n",
(unsigned)NODEMAP_RBAC_HSM_OPS);
- LASSERTF(NODEMAP_RBAC_NONE == 0xfffffe00UL, "found 0x%.8xUL\n",
+ LASSERTF(NODEMAP_RBAC_LOCAL_ADMIN == 0x00000200UL, "found 0x%.8xUL\n",
+ (unsigned)NODEMAP_RBAC_LOCAL_ADMIN);
+ LASSERTF(NODEMAP_RBAC_NONE == 0xfffffc00UL, "found 0x%.8xUL\n",
(unsigned)NODEMAP_RBAC_NONE);
LASSERTF(NODEMAP_RBAC_ALL == 0xffffffffUL, "found 0x%.8xUL\n",
(unsigned)NODEMAP_RBAC_ALL);