Ceph RGW的身份验证代码概览
Ceph 12.2.2中的RGW的身份认证由rgw::auth::StrategyRegistry的一个Singleton统一管理,在rgw_main.cc中,由如下代码进行了初始化:
1 | /* Initialize the registry of auth strategies which will coordinate |
在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 | class Strategy : public Engine { |
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 | process_request() |
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 | process_request() |
swift_strategy被RGWHandler_REST_SWIFT::authorize()所使用,调用栈大致如下
1 | process_request() |
RGW_Auth_S3::authorize()、RGWPostObj_ObjStore_S3::get_policy()和RGWHandler_REST_SWIFT::authorize()都是对相应的Strategy/Engine调用rgw::auth::Strategy::apply()来完成验证的。
rgw::auth::Strategy::apply()的主要步骤是
- 调用参数auth_strategy的authenticate(const req_state s)方法,其实就是AWSEngine::authenticate(const req_state const s)进行验证
- authenticate(s)会先使用所绑定的VersionAbstractor从HTTP Header和Query String的等处抽取并合成AccessKeyId、Signature和StringToSign,并构造signature_factory和completer_factory
- 对于AWS V2 Signature而言,signature_factory是rgw::auth::s3::get_v2_signature,completer_factory是null_completer_factory
- 对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
- 然后,authenticate(s)再调用各Engine重载的authenticate(access_key_id, signature, string_to_sign, signature_factory, completer_factory, s)
- 进行验证
- LocalEngine,使用signature_factor计算签名并与客户端的输入进行比较
- LDAPEngine和EC2Engine等有自定义逻辑,signature_factory未被使用
- 如果验证成功,使用apl_factory生成IdentityApplier,使用completer_factory生成Completer,然后封装在AuthResult中返回
- 对于LocalEngine,apl_factory的是AWSAuthStrategy
- 对于LDAPEngine和EC2Engine,apl_factory是ExternalAuthStrategy
- 进行验证
- authenticate(s)会先使用所绑定的VersionAbstractor从HTTP Header和Query String的等处抽取并合成AccessKeyId、Signature和StringToSign,并构造signature_factory和completer_factory
- 如果验证成功,调用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相关的逻辑
- 调用IdentityApplier的modify_request_state()方法,调整当前请求的req_state
- 如果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
- 两个较特殊的Completer
- 将IdentityApplier存放在s->auth.identity
- 将s->auth.completer存放在s->auth.completer
对于AWS Signature V4的情况,对于设置了非空的Completer的操作,还需要调用RGWOp::do_aws4_auth_completion()做最后的验证。主要调用栈如下:
1 | process_request() |
主要参考文献
- Amazon Simple Storage Service Developer Guide (API Version 2006-03-01): https://docs.aws.amazon.com/AmazonS3/latest/dev/s3-dg.pdf
- Amazon Simple Storage Service API Reference (API Version 2006-03-01): https://docs.aws.amazon.com/AmazonS3/latest/API/s3-api.pdf
- Ceph 12.2.2:https://github.com/ceph/ceph/releases/tag/v12.2.2
- RGW LDAP Authentication:http://docs.ceph.com/docs/master/radosgw/ldap-auth/