Ceph RGW的身份验证代码概览

Ceph RGW的身份验证代码概览

Ceph 12.2.2中的RGW的身份认证由rgw::auth::StrategyRegistry的一个Singleton统一管理,在rgw_main.cc中,由如下代码进行了初始化:

1
2
3
4
/* Initialize the registry of auth strategies which will coordinate
* the dynamic reconfiguration. */
auto auth_registry = \
rgw::auth::StrategyRegistry::create(g_ceph_context, store);

在RGW处理请求时,将在不同场景下相应调用rgw::auth::StrategyRegistry::get_swift()、rgw::auth::StrategyRegistry::get_s3_main()或rgw::auth::StrategyRegistry::get_s3_post(),取得Strategy/Engine对象,并调用Strategy/Engine上的authenticate()方法进行身份验证。

StrategyRegistry的构成

StrategyRegistry中包含了3个成员变量:

  • s3_main_strategy
  • s3_post_strategy
  • swift_strategy

如名字所述,swift_strategy 是针对Swift接口的(不做展开),而s3_main_strategy和s3_post_strategy是针对S3接口。
对于S3的身份验证,s3_main_strategy覆盖了绝大多数应用场景,s3_post_strategy只负责处理Browser-Based Uploads Using POST(详见Browser-Based Uploads Using POST (AWS Signature Version 2)Authenticating Requests in Browser-Based Uploads Using POST (AWS Signature Version 4))。

s3_main_strategy

s3_main_strategy的类型是s3_main_strategy_t,其中包含了两个验证引擎:

s3_main_strategy_plain,类型为s3_strategy_t<rgw::auth::s3::AWSGeneralAbstractor, true>
s3_main_strategy_boto2,类型为s3_strategy_t<rgw::auth::s3::AWSGeneralBoto2Abstractor, true>

从类型定义可,见s3_main_strategy_plain和s3_main_strategy_boto2的区别,就是他们使用了AWSEngine::VersionAbstractor的不同子类rgw::auth::s3::AWSGeneralAbstractor和rgw::auth::s3::AWSGeneralBoto2Abstractor。
AWSEngine::VersionAbstractor的主要接口是get_auth_data_v2和get_auth_data_v4(Versioned),不同的VersionAbstractor子类通过实现这些方法从不同的客户端格式中抽取(Abstract)抽取所需信息以及特定的回调类。
s3_main_strategy_t对s3_main_strategy_plain和s3_main_strategy_boto2以不同的策略SUFFICIENT和FALLBACK加入自己的auth_stack中。这些策略决定了两个策略的主次关系。比如,后面s3_main_strategy_t的authenticat()会先使用s3_main_strategy_plain的authenticate(),如果s3_main_strategy_plain的authenticate()返回的是DENIED,才会尝试s3_main_strategy_boto2的authenticate(),否则将以s3_main_strategy_plain的结果为准。以下是几种策略和结果在源代码中的定义和注释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Strategy : public Engine {

enum class Control {
/* Failure of an engine injected with the REQUISITE specifier aborts
* the strategy's authentication process immediately. No other engine
* will be tried. */
REQUISITE,

/* Success of an engine injected with the SUFFICIENT specifier ends
* strategy's authentication process successfully. However, denying
* doesn't abort it -- there will be fall-back to following engine
* if the one that failed wasn't the last one. */
SUFFICIENT,

/* Like SUFFICIENT with the exception that on failure the reason code
* is not overridden. Instead, it's taken directly from the last tried
* non-FALLBACK engine. If there was no previous non-FALLBACK engine
* in a Strategy, then the result_t::deny(reason = -EACCES) is used. */
FALLBACK,
};

};

class Engine {

class AuthResult {

enum class Status {
/* Engine doesn't grant the access but also doesn't reject it. */
DENIED,

/* Engine successfully authenticated requester. */
GRANTED,

/* Engine strictly indicates that a request should be rejected
* without trying any further engine. */
REJECTED
};

};
};

s3_main_strategy_plain

s3_main_strategy_plain的定义本质上是rgw::auth::s3::AWSAuthStrategy< rgw::auth::s3::AWSGeneralAbstractor, true>,里面又递归包含了3种Strategy/Engine(Strategy是Engine的子类)

  • anonymous_engine:类型rgw::auth::S3AnonymousEngine,主要逻辑在rgw::auth::AnonymousEngine::authenticate()中实现,允许匿名请求验证成功(后面的鉴权逻辑会负责挡住越权的匿名访问)
  • external_engines:类型为ExternalAuthStrategy,目前的作用是递归地引入外部身份验证Engine的支持
  • local_engine:类型为LocalEngine,使用保存在RGW的meta pool中的AccessKey和SecretKey对请求中的AccessKeyId和Signature进行验证,可由bool配置项rgw_s3_auth_use_rados控制是否启用。
external_engines

ExternalAuthStrategy中目前可根据配置递归包含两种外部Engine的支持:

  • keystone_engine:类型为EC2Engine,由配置项rgw_s3_auth_use_keystone和rgw_keystone_url控制是否启用
  • ldap_engine:类型为LDAPEngine,由配置项rgw_s3_auth_use_ldap和rgw_ldap_uri控制是否启用
ldap_engine

ldap_engine的类型LDAPEngine主要封装了以下功能:

  • 读取配置文件中rgw_ldap_uri、rgw_ldap_binddn,rgw_ldap_searchdn,rgw_ldap_searchfilter和rgw_ldap_dnattr这些详细描述LDAP服务的的配置信息(LDAPEngine::init())
  • 将S3签名格式映射成LDAP的用户名和密码,然后向LDAP服务器验证(LDAPEngine::authenticate())
s3_main_strategy_boto2

类似s3_main_strategy_plain,不再累述。

s3_post_strategy

3_post_strategy的类型是s3_post_strategy_t,即rgw::auth::s3::AWSAuthStrategy< rgw::auth::s3::AWSBrowserUploadAbstractor, false>。虽然在包含级别上,s3_post_strategy以与s3_main_strategy一样都直属与StrategyRegistry,但从实现上它与s3_main_strategy下的s3_main_strategy_plain更类似,当然VersionAbstractor是不一样的,也不支持匿名验证。

主要验证过程

s3_main_strategy被RGW_Auth_S3::authorize()所使用,相对与每个请求的派发函数process_request()函数的调用栈大致为

1
2
3
4
5
6
process_request()
rest->get_handler()
op = handler->get_op(store)
op->verify_requester(auth_registry)
RGWHandler_REST_S3::authorize()
RGW_Auth_S3::authorize()

s3_post_strategy被RGWPostObj_ObjStore_S3::get_policy()所使用。调用栈大致如下。POST请求之所以在很后期才验证,主要是因为其验证过程需要request的body中的表单数据(详见Browser-Based Uploads Using POST (AWS Signature Version 2)和Authenticating Requests in Browser-Based Uploads Using POST (AWS Signature Version 4)),而这些数据在之前的步骤中还未被解析。同时也由于这样的特殊情况,许多对于其他请求而言的已经在process_request中被自动调用的处理逻辑,在POST请求中,则要在RGWPostObj::execute()显示地调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
process_request()
rest->get_handler()
op = handler->get_op(store)
op->verify_requester(auth_registry)
handler->postauth_init()
rgw_process_authenticated(handler, op, req, s)
handler->init_permissions(op)
handler->read_permissions(op)
op->init_processing()
op->verify_op_mask()
op->verify_permission()
ret = op->verify_params()
op->pre_exec()
RGWPostObj::execute()
RGWPostObj_ObjStore_S3::get_params()
RGWPostObj_ObjStore_S3::get_policy()

swift_strategy被RGWHandler_REST_SWIFT::authorize()所使用,调用栈大致如下

1
2
3
4
5
process_request()
rest->get_handler()
op = handler->get_op(store)
op->verify_requester(auth_registry)
RGWHandler_REST_SWIFT::authorize()

RGW_Auth_S3::authorize()、RGWPostObj_ObjStore_S3::get_policy()和RGWHandler_REST_SWIFT::authorize()都是对相应的Strategy/Engine调用rgw::auth::Strategy::apply()来完成验证的。
rgw::auth::Strategy::apply()的主要步骤是

  1. 调用参数auth_strategy的authenticate(const req_state s)方法,其实就是AWSEngine::authenticate(const req_state const s)进行验证
    1. authenticate(s)会先使用所绑定的VersionAbstractor从HTTP Header和Query String的等处抽取并合成AccessKeyId、Signature和StringToSign,并构造signature_factory和completer_factory
      1. 对于AWS V2 Signature而言,signature_factory是rgw::auth::s3::get_v2_signature,completer_factory是null_completer_factory
      2. 对AWS V2 Signature而言,signature_factory是rgw::auth::s3::get_v4_signature,而completer_factory视情况而定
        • 对于Browser-Based Uploads Using POST,是null_completer_factory
        • 对于Payload Unsigned,是null_completer_factory
        • 对于Single Signed Chunk,是AWSv4ComplSingle::create,支持的操作是
          • RGW_OP_CREATE_BUCKET
          • RGW_OP_PUT_OBJ
          • RGW_OP_PUT_ACLS
          • RGW_OP_PUT_CORS
          • RGW_OP_COMPLETE_MULTIPART
          • RGW_OP_SET_BUCKET_VERSIONING
          • RGW_OP_DELETE_MULTI_OBJ
          • RGW_OP_ADMIN_SET_METADATA
          • RGW_OP_SET_BUCKET_WEBSITE
          • RGW_OP_PUT_BUCKET_POLICY
          • RGW_OP_PUT_OBJ_TAGGING
          • RGW_OP_PUT_LC
        • 对于Multiple Signed Chunk,是AWSv4ComplMulti::create,支持的操作是
          • RGW_OP_PUT_OBJ
    2. 然后,authenticate(s)再调用各Engine重载的authenticate(access_key_id, signature, string_to_sign, signature_factory, completer_factory, s)
      1. 进行验证
        • LocalEngine,使用signature_factor计算签名并与客户端的输入进行比较
        • LDAPEngine和EC2Engine等有自定义逻辑,signature_factory未被使用
      2. 如果验证成功,使用apl_factory生成IdentityApplier,使用completer_factory生成Completer,然后封装在AuthResult中返回
        • 对于LocalEngine,apl_factory的是AWSAuthStrategy
        • 对于LDAPEngine和EC2Engine,apl_factory是ExternalAuthStrategy
  2. 如果验证成功,调用IdentityApplier的load_acct_info()方法(根据所用的子类不同调用不同的方法)
    • rgw::auth::LocalApplier::load_acct_info(user_info)只是简单地将来自RGW的meta pool中的用户信息复制到user_info
    • rgw::auth::RemoteApplier::load_acct_info(user_info)则是根据外部Engine返回的用户账号信息查询RGW的meta pool,如果RGW的meta pool还未存在相应用户,则调用rgw::auth::RemoteApplier::create_account()进行创建
    • 另外还有SysReqApplier和ThirdPartyAccountApplier两种IdentityApplier,它们一般作为其他IdentityApplier的Decorator,而不是独立使用
      • SysReqApplier用于识别system账号(LocalEngine的LocalApplier和LDAPEngine/EC2Engine的RemoteApplier都使用它做Decorator)
      • ThirdPartyAccountApplier主要用于处理Swift的Tenant相关的逻辑
  3. 调用IdentityApplier的modify_request_state()方法,调整当前请求的req_state
  4. 如果Completer非空,调用Completer的modify_request_state()方法,调整本当前请求的req_state
    • 两个较特殊的Completer
      • AWSv4ComplSingle::modify_request_state()把Completer做为filter添加到s->cio中,以便在接收数据过程中计算signature
      • AWSv4ComplMulti::modify_request_state()除了在req_state中调整一些信息,还把Completer做为filter添加到s->cio中,以便在接收数据过程中计算signature
  5. 将IdentityApplier存放在s->auth.identity
  6. 将s->auth.completer存放在s->auth.completer

对于AWS Signature V4的情况,对于设置了非空的Completer的操作,还需要调用RGWOp::do_aws4_auth_completion()做最后的验证。主要调用栈如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
process_request()
rest->get_handler()
op = handler->get_op(store)
...
rgw_process_authenticated(handler, op, req, s)
handler->init_permissions(op)
handler->read_permissions(op)
op->init_processing()
op->verify_op_mask()
op->verify_permission()
ret = op->verify_params()
op->pre_exec()
op->execute()
RGWXXX_ObjStore_S3::get_params()
RGWOp::do_aws4_auth_completion()
// 例外: RGW_OP_PUT_OBJ只直接在execute()中调用而不是在get_params()中

主要参考文献