Whamcloud - gitweb
LU-18581 sec: add hsm_ops rbac role 26/57526/6
authorSebastien Buisson <sbuisson@ddn.com>
Thu, 19 Dec 2024 14:08:11 +0000 (15:08 +0100)
committerOleg Drokin <green@whamcloud.com>
Sun, 2 Feb 2025 06:28:22 +0000 (06:28 +0000)
The purpose of the new hsm_ops rbac role is to control whether
clients can carry out HSM actions. When set, clients can call any
HSM primitive. When not set, clients get -EACCES when they try one of
the following:
- archive
- restore
- remove
- release
- set
- clear
- ct_register
- ct_unregister

sanity-hsm test_411 is added to exercise the new hsm_ops rbac role.

Signed-off-by: Sebastien Buisson <sbuisson@ddn.com>
Change-Id: I234f4e8e37ec359ac9f790e29bf0b91725f2ef16
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/57526
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Marc Vef <mvef@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
15 files changed:
lustre/doc/lctl-nodemap-modify.8
lustre/include/lustre_nodemap.h
lustre/include/md_object.h
lustre/include/uapi/linux/lustre/lustre_idl.h
lustre/mdt/mdt_coordinator.c
lustre/mdt/mdt_handler.c
lustre/mdt/mdt_hsm.c
lustre/mdt/mdt_lib.c
lustre/mdt/mdt_open.c
lustre/mdt/mdt_restripe.c
lustre/obdecho/echo_client.c
lustre/ptlrpc/wiretest.c
lustre/tests/sanity-hsm.sh
lustre/utils/wirecheck.c
lustre/utils/wiretest.c

index e382adc..e5bd903 100644 (file)
@@ -99,6 +99,8 @@ Defaults to all, which means all roles are allowed. Other possible values
 it is still possible to lock or unlock encrypted directories,
 as these operations only need read access to fscrypt metadata.
 .br
+- hsm_ops, to control whether clients can carry out HSM actions.
+.br
 - ignore_root_prjquota, so that project quota is not enforced for the root user.
 .br
 - quota_ops, to allow quota modifications.
index d6f65b4..001822d 100644 (file)
@@ -34,6 +34,7 @@ static const struct nodemap_rbac_name {
        { NODEMAP_RBAC_FSCRYPT_ADMIN,   "fscrypt_admin" },
        { NODEMAP_RBAC_SERVER_UPCALL,   "server_upcall" },
        { NODEMAP_RBAC_IGN_ROOT_PRJQUOTA,       "ignore_root_prjquota"  },
+       { NODEMAP_RBAC_HSM_OPS,         "hsm_ops"       },
 };
 
 struct nodemap_pde {
index 086476e..476b86c 100644 (file)
@@ -689,6 +689,7 @@ struct lu_ucred {
        int                      uc_rbac_fscrypt_admin:1;
        int                      uc_rbac_server_upcall:1;
        int                      uc_rbac_ignore_root_prjquota:1;
+       int                      uc_rbac_hsm_ops:1;
 };
 
 struct lu_ucred *lu_ucred(const struct lu_env *env);
index 83c881a..1c00ee3 100644 (file)
@@ -3830,6 +3830,7 @@ enum nodemap_rbac_roles {
        NODEMAP_RBAC_FSCRYPT_ADMIN      = 0x00000020,
        NODEMAP_RBAC_SERVER_UPCALL      = 0x00000040,
        NODEMAP_RBAC_IGN_ROOT_PRJQUOTA  = 0x00000080,
+       NODEMAP_RBAC_HSM_OPS            = 0x00000100,
        NODEMAP_RBAC_NONE       = (__u32)~(NODEMAP_RBAC_FILE_PERMS      |
                                           NODEMAP_RBAC_DNE_OPS |
                                           NODEMAP_RBAC_QUOTA_OPS       |
@@ -3837,7 +3838,8 @@ enum nodemap_rbac_roles {
                                           NODEMAP_RBAC_CHLG_OPS        |
                                           NODEMAP_RBAC_FSCRYPT_ADMIN   |
                                           NODEMAP_RBAC_SERVER_UPCALL   |
-                                          NODEMAP_RBAC_IGN_ROOT_PRJQUOTA),
+                                          NODEMAP_RBAC_IGN_ROOT_PRJQUOTA |
+                                          NODEMAP_RBAC_HSM_OPS),
        NODEMAP_RBAC_ALL        = 0xFFFFFFFF, /* future caps ON by default */
 };
 
index e9a465c..48c88ce 100644 (file)
@@ -1083,6 +1083,7 @@ int hsm_init_ucred(struct lu_ucred *uc)
        uc->uc_rbac_fscrypt_admin = 1;
        uc->uc_rbac_server_upcall = 1;
        uc->uc_rbac_ignore_root_prjquota = 1;
+       uc->uc_rbac_hsm_ops = 1;
 
        RETURN(0);
 }
index 765f413..2b27bb1 100644 (file)
@@ -7126,6 +7126,7 @@ static int mdt_ctxt_add_dirty_flag(struct lu_env *env,
        mdt_ucred(info)->uc_rbac_fscrypt_admin = 1;
        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;
        rc = mdt_add_dirty_flag(info, mfd->mfd_object, &info->mti_attr);
 
        lu_context_exit(&ses);
index eb8b2c2..de1d926 100644 (file)
@@ -72,6 +72,8 @@ static inline bool mdt_hsm_is_admin(struct mdt_thread_info *info)
                return false;
 
        is_admin = cap_raised(mdt_ucred(info)->uc_cap, CAP_SYS_ADMIN);
+       if (!mdt_ucred(info)->uc_rbac_hsm_ops)
+               is_admin = false;
 
        mdt_exit_ucred(info);
 
@@ -278,6 +280,8 @@ int mdt_hsm_state_set(struct tgt_session_info *tsi)
        rc = mdt_init_ucred(info, (struct mdt_body *)info->mti_body);
        if (rc < 0)
                GOTO(out, rc = err_serious(rc));
+       if (!mdt_ucred(info)->uc_rbac_hsm_ops)
+               GOTO(out_ucred, rc = -EACCES);
 
        lh = &info->mti_lh[MDT_LH_CHILD];
        rc = mdt_object_lock(info, obj, lh, MDS_INODELOCK_LOOKUP |
@@ -581,6 +585,9 @@ int mdt_hsm_request(struct tgt_session_info *tsi)
        if (rc)
                GOTO(out, rc);
 
+       if (!mdt_ucred(info)->uc_rbac_hsm_ops)
+               GOTO(out_ucred, rc = -EACCES);
+
        switch (hr->hr_action) {
        /* code to be removed in hsm1_merge and final patch */
        case HUA_RELEASE:
index e8791f9..943d23d 100644 (file)
@@ -187,6 +187,7 @@ static void ucred_set_rbac_roles(struct mdt_thread_info *info,
        uc->uc_rbac_server_upcall = !!(rbac & NODEMAP_RBAC_SERVER_UPCALL);
        uc->uc_rbac_ignore_root_prjquota =
                !!(rbac & NODEMAP_RBAC_IGN_ROOT_PRJQUOTA);
+       uc->uc_rbac_hsm_ops = !!(rbac & NODEMAP_RBAC_HSM_OPS);
 }
 
 static int new_init_ucred(struct mdt_thread_info *info, ucred_init_type_t type,
index 0d81076..8dfc483 100644 (file)
@@ -1925,6 +1925,9 @@ static int mdt_hsm_release(struct mdt_thread_info *info, struct mdt_object *o,
        if (mdt_rdonly(info->mti_exp))
                RETURN(-EROFS);
 
+       if (!uc->uc_rbac_hsm_ops)
+               RETURN(-EACCES);
+
        data = req_capsule_client_get(info->mti_pill, &RMF_CLOSE_DATA);
        if (data == NULL)
                RETURN(-EPROTO);
index f1a2b4e..f1211a0 100644 (file)
@@ -909,6 +909,7 @@ int mdt_restriper_start(struct mdt_device *mdt)
        uc->uc_rbac_fscrypt_admin = 1;
        uc->uc_rbac_server_upcall = 1;
        uc->uc_rbac_ignore_root_prjquota = 1;
+       uc->uc_rbac_hsm_ops = 1;
 
        task = kthread_create(mdt_restriper_main, info, "mdt_restriper_%03d",
                              mdt_seq_site(mdt)->ss_node_id);
index 2286fd7..bd1e6b3 100644 (file)
@@ -1823,6 +1823,7 @@ static void echo_ucred_init(struct lu_env *env)
        ucred->uc_rbac_fscrypt_admin = 1;
        ucred->uc_rbac_server_upcall = 1;
        ucred->uc_rbac_ignore_root_prjquota = 1;
+       ucred->uc_rbac_hsm_ops = 1;
 }
 
 static void echo_ucred_fini(struct lu_env *env)
index e71f8de..a8b5667 100644 (file)
@@ -6525,7 +6525,9 @@ void lustre_assert_wire_constants(void)
                 (unsigned)NODEMAP_RBAC_SERVER_UPCALL);
        LASSERTF(NODEMAP_RBAC_IGN_ROOT_PRJQUOTA == 0x00000080UL, "found 0x%.8xUL\n",
                 (unsigned)NODEMAP_RBAC_IGN_ROOT_PRJQUOTA);
-       LASSERTF(NODEMAP_RBAC_NONE == 0xffffff00UL, "found 0x%.8xUL\n",
+       LASSERTF(NODEMAP_RBAC_HSM_OPS == 0x00000100UL, "found 0x%.8xUL\n",
+                (unsigned)NODEMAP_RBAC_HSM_OPS);
+       LASSERTF(NODEMAP_RBAC_NONE == 0xfffffe00UL, "found 0x%.8xUL\n",
                 (unsigned)NODEMAP_RBAC_NONE);
        LASSERTF(NODEMAP_RBAC_ALL == 0xffffffffUL, "found 0x%.8xUL\n",
                (unsigned)NODEMAP_RBAC_ALL);
index 89fe05d..162a2ad 100755 (executable)
@@ -5702,6 +5702,89 @@ test_410()
 }
 run_test 410 "lfs data_version -s allows release of force-archived file"
 
+cleanup_411() {
+       local nm=$1
+
+       do_facet mgs $LCTL nodemap_del $nm
+       do_facet mgs $LCTL nodemap_activate 0
+       wait_nm_sync active
+}
+
+test_411()
+{
+       local client_ip=$(host_nids_address $HOSTNAME $NETTYPE)
+       local client_nid=$(h2nettype $client_ip)
+       local tf1=$DIR/$tdir/fileA
+       local tf2=$DIR/$tdir/fileB
+       local nm=test_411
+       local roles
+       local fid
+
+       do_facet mgs $LCTL nodemap_modify --name default \
+               --property admin --value 1
+       do_facet mgs $LCTL nodemap_modify --name default \
+               --property trusted --value 1
+       do_facet mgs $LCTL nodemap_add $nm
+       do_facet mgs $LCTL nodemap_add_range    \
+               --name $nm --range $client_nid
+       do_facet mgs $LCTL nodemap_modify --name $nm \
+               --property admin --value 1
+       do_facet mgs $LCTL nodemap_modify --name $nm \
+               --property trusted --value 1
+       do_facet mgs $LCTL nodemap_activate 1
+       stack_trap "cleanup_411 $nm" EXIT
+       wait_nm_sync active
+       wait_nm_sync $nm trusted_nodemap
+
+       roles=$(do_facet mds $LCTL get_param -n nodemap.$nm.rbac)
+       [[ "$roles" =~ "hsm_ops" ]] ||
+               skip "role 'hsm_ops' not supported by server"
+
+       mkdir_on_mdt0 $DIR/$tdir || error "mkdir $DIR/$tdir failed"
+       echo hi > $tf1 || error "create $tf1 failed"
+       echo hi > $tf2 || error "create $tf2 failed"
+
+       copytool setup
+
+       # with hsm_ops role, all ops should pass
+       fid=$(path2fid $tf1)
+       $LFS hsm_state $tf1 || error "hsm_state $tf1 failed"
+       $LFS hsm_archive $tf1 || error "hsm_archive $tf1 failed"
+       wait_request_state $fid ARCHIVE SUCCEED
+       $LFS hsm_release $tf1 || error "hsm_release $tf1 failed"
+       check_hsm_flags $tf1 "0x0000000d"
+       $LFS hsm_restore $tf1 || error "hsm_restore $tf1 failed"
+       wait_request_state $fid RESTORE SUCCEED
+       $LFS hsm_remove $tf1 || error "hsm_remove $tf1 failed"
+       wait_request_state $fid REMOVE SUCCEED
+       check_hsm_flags $tf1 "0x00000000"
+       $LFS hsm_set --exists $tf1 || error "hsm_set $tf1 failed"
+       check_hsm_flags $tf1 "0x00000001"
+       $LFS hsm_clear --exists $tf1 || error "hsm_clear $tf1 failed"
+       check_hsm_flags $tf1 "0x00000000"
+
+       # remove hsm_ops from rbac roles
+       roles=$(echo "$roles" | sed 's/hsm_ops,//;s/,hsm_ops//;s/^hsm_ops,//')
+       do_facet mgs $LCTL nodemap_modify --name $nm \
+               --property rbac --value $roles
+       wait_nm_sync $nm rbac
+
+       # without hsm_ops role, all ops should fail
+       fid=$(path2fid $tf2)
+       $LFS hsm_state $tf2 || error "hsm_state $tf2 failed"
+       $LFS hsm_archive $tf2 && error "hsm_archive $tf2 succeeded"
+       $LFS hsm_release $tf2 && error "hsm_release $tf2 succeeded"
+       check_hsm_flags $tf2 "0x00000000"
+       $LFS hsm_restore $tf2 && error "hsm_restore $tf2 succeeded"
+       $LFS hsm_remove $tf2 && error "hsm_remove $tf2 succeeded"
+       check_hsm_flags $tf2 "0x00000000"
+       $LFS hsm_set --exists $tf2 && error "hsm_set $tf2 succeeded"
+       check_hsm_flags $tf2 "0x00000000"
+       $LFS hsm_clear --exists $tf2 && error "hsm_clear $tf2 succeeded"
+       check_hsm_flags $tf2 "0x00000000"
+}
+run_test 411 "hsm_ops rbac role"
+
 test_500()
 {
        [ "$MDS1_VERSION" -lt $(version_code 2.6.92) ] &&
index 87d509f..6e651ca 100644 (file)
@@ -3084,6 +3084,7 @@ static void check_nodemap_key(void)
        CHECK_VALUE_X(NODEMAP_RBAC_FSCRYPT_ADMIN);
        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_NONE);
        CHECK_VALUE_X(NODEMAP_RBAC_ALL);
 }
index 06757d0..045ab8d 100644 (file)
@@ -6568,7 +6568,9 @@ void lustre_assert_wire_constants(void)
                 (unsigned)NODEMAP_RBAC_SERVER_UPCALL);
        LASSERTF(NODEMAP_RBAC_IGN_ROOT_PRJQUOTA == 0x00000080UL, "found 0x%.8xUL\n",
                 (unsigned)NODEMAP_RBAC_IGN_ROOT_PRJQUOTA);
-       LASSERTF(NODEMAP_RBAC_NONE == 0xffffff00UL, "found 0x%.8xUL\n",
+       LASSERTF(NODEMAP_RBAC_HSM_OPS == 0x00000100UL, "found 0x%.8xUL\n",
+                (unsigned)NODEMAP_RBAC_HSM_OPS);
+       LASSERTF(NODEMAP_RBAC_NONE == 0xfffffe00UL, "found 0x%.8xUL\n",
                 (unsigned)NODEMAP_RBAC_NONE);
        LASSERTF(NODEMAP_RBAC_ALL == 0xffffffffUL, "found 0x%.8xUL\n",
                (unsigned)NODEMAP_RBAC_ALL);