cephx: ceph的认证和加密协议-阿里云开发者社区

开发者社区> 安全> 正文

cephx: ceph的认证和加密协议

简介: Ceph作为一个分布式存储系统,支持对象存储、块设备和文件系统。为了在网络传输中防止数据被篡改,做到较高程度的安全性,加入了Cephx加密认证协议。其目的是识别身份,加密、验证传输中的数据。 在ceph系统中,元数据保存在一个叫做ceph-mon的进程中,也可以称为monitor节点,系统可以有多个monitor副本节点,用paxos保持数据一致性。 这里不谈paxos,也不谈多个monitor

Ceph作为一个分布式存储系统,支持对象存储、块设备和文件系统。为了在网络传输中防止数据被篡改,做到较高程度的安全性,加入了Cephx加密认证协议。其目的是识别身份,加密、验证传输中的数据。

ceph系统中,元数据保存在一个叫做ceph-mon的进程中,也可以称为monitor节点,系统可以有多个monitor副本节点,用paxos保持数据一致性。 这里不谈paxos,也不谈多个monitor节点,我们只以单个monitor为例,重点说明cephx的实现。

monitor保存了系统中重要的元数据,例如每个用户的key以及权限,这也是我们要重点谈及的,至于其他osdmap, crush数据在这里不涉及。

一个ceph系统主要由monitor, osd, client 这几种类型的节点组成。monitor存放元数据,osd存放对象,client就是使用ceph系统的客户端。

一个monitor里存放着和认证相关的数据,简单可以用表结构来描述:

名称

key

Caps(权限)

client.admin

xxxxxxyyyyy

osd allow rw, mon allow rw

osd.1

aaaaaaaaaaaaa

osd allow rw

osd.2

bbbbbbbbbbbbb

osd allow rw

clientosd 必须先连接到monitor进行认证。clientosd都有一个叫做monclient的模块负责认证和密钥交换。而monitor上有一个AuthMonitorpaxos服务模块负责与monclient对话,cephx协议的实现则位于ceph源代码目录src/auth/cephx,有好几个模块负责。

 

Cephx是一种对称密钥加密协议,加密算法使用AES,并包含临时密钥生成和替换。每个clientosd都在本地有一个密钥,该密钥的副本同样存在于monitor上。

Ceph里面每一个节点都会使用一个名称,类型是EntityName, 例如:client.admin, osd.1等。而每一个节点连接到monitor以后,monitor都会为其生成一个global id,代表这个节点在整个ceph 系统中的全局id

monclient连接monitor的会话是为了获得session keyticket以及临时密钥(rotating key)

ceph 系统里面有auth认证服务,osdmap服务,还有文件系统的mds服务等。当节点之间彼此通讯时,刚连接开始时,使用ticket表明身份,使用rotating key加密加密解密ticketsession key, 验明身份后,后续通讯使用session key加、解密数据包。

monclient里,对每一种服务monclient都保存有从monitor获得的数据:与某类型节点网络连接时的session key,与某类型节点网络连接对应的ticket, 与该种服务对应的rotating key.


cephx
认证大致步骤如下:

monclientmonitor 发起连接,获得与monitor 通讯时的AUTH servicesession key以及ticket

认证信息是以后通讯中识别身份的凭据,其结构如下:

struct AuthTicket {

EntityName name;

uint64_t global_id; /* global instance id */

uint64_t auid;

utime_t created, renew_after, expires;

AuthCapsInfo caps;

__u32 flags;

};

内容依次是发起连接的实体名称,全局id、 用户id、一些和ticket生存周期有关的时间,以及权限。

struct CephXServiceTicketInfo {

AuthTicket ticket;

CryptoKey session_key;

};

 

我们在这里称CephxServiceTicketInfoticket,它包含认证信息以及session key

通过了与monitor的认证以后,client就可以从monitor获取与每一种service类型的相关的session key ticket,例如与osd服务和mds服务相关的session keyticket, ticketnameglobal_idauid是相同的,其他存活期时间信息和权限则是不同的,client在每一种service上可以有不同的权限。其中ticket数据对client是透明的,是被相关service的临时密钥加密的,其内容只有monitor和相关的service会去解密。


对于每一种service,它们的monclient除了上面的内容,还会定时从monitor获取它们自身这种service相关的临时密钥,主要数据结构如下:

一个会过期的密钥:

struct ExpiringCryptoKey {

CryptoKey key;

utime_t expiration

};

 

一个对过期密钥的管理器:

struct RotatingSecrets {

map<uint64_t, ExpiringCryptoKey> secrets;

version_t max_ver;

};

管理器包含3个密钥,依次是过去的、现在的和将来的,mapkey是一个临时密钥id (称为secret id), 每次往里面添加一个密钥,max_ver增加1,并且把最前面个一密钥删除,始终保持3个密钥,所以每次增加一个密钥,他们的secret id都比以前的大,这样就不存在secret id重复。

client准备发起对osd的访问时,就用对应的osd serviceticket去访问osd, osd服务则用从monitor得到的临时密钥解密ticket,验证其身份,然后从ticket取出session key, 因为客户端已经有session key, 这样双方就有了相同的session key,以后通讯时clientosd就用这个密钥加密和解密数据包,来验证数据的正确性。只要连接没有断开,session key就保持不变。

 

cephx认证具体步骤如下:

monclientmonitor的会话:

 

Step 1:

monclient: 发送 { protocol: 0, entity name, global id: 0 }

monintor: 保存entity name, 生成并保存64bit server challenge,

并把 server challenge 发送给对方。

 

Step 2:

monclient: 生成一个64bit client challenge, 用本地盘上的密钥把server challengeclient challenge加密后,再用64比特为单位混淆生成一个64bit key, 并发送请求:

{ CEPHX_GET_AUTH_SESSION_KEY, client challenge, key, old ticket }

注意(第一次连接开始old ticket为无效数据)

 

monitor 接收到CEPHX_GET_AUTH_SESSION_KEY请求, 把得到的client challenge和存在自己内存里的server challengeclient对应的key加密后混淆生成一个64bit key, 并与传过来的key比较,如果不相等,则认证不通过。 (注意,monitor在自身数据库存有对方的key) ,解密传过来的old ticket, 得到 CephXServiceTicketInfo结构。从密钥库取出client对应的身份信息(密钥,权限)。 生成新的ticket: (创建的时间,存活时间,global id, auid) ,生成与本monserver对话的session key, keystore获取对应于AUTH service 的临时密钥rotating key(secret 密钥, secret id密钥id).

 生成CephXServiceTicket结构(session key, ticket的存活时间), client在密钥库中的密钥加密。生成 CephXTicketBlob, 其中包含临时密钥secret id CephXServiceTicketInfo, CephxServiceTicketInfo包含 : (session key, ticket, 权限),其中CephxServiceTicketInfo用对应的临时密钥secret加密。注意Client不在乎ticket是什么内容,因为这个作为一张票子是给通讯对方的,自己并不需要解释什么。如果上次成功解密 old ticket info,则使用old tick info中的session key加密整个CephXTicketBlob结构。

 

把经过上述处理后的CephxServiceTicketCephxTicketBlob一起发送给monclient

 

Step 3:

monclient 用自身本地盘上的密钥解密 CephxServciceTicket,在获得CephxTicketBlob时,如果CephxTicketBlob是经过加密的,则用客户端当前的session key解密CephxTicketBlob

保存CephxServiceTicket中的session key为最新的session key,保存CephXServiceTicketsession key的存活时间。

lient 如果需要其他服务的密钥,则发起CEPHX_GET_PRINCIPAL_SESSION_KEY请求。

 首先生成内容HEADER, 包括(global id, service id, CephxServiceTicketBlob),其中service id就是AUTH认证服务的id,然后生成CephXServiceTicketRequest结构, 内容包括bitmask,每一位代表一种服务类型,例如位掩码中可以包含OSD, MDS

生成CephXAuthorize结构, 内容包含随机数nonc,并用session key 加密CephxAuthorize.

 

monclient发送:Header CephxAuthorize CephxServiceTicketRequest

 

Monitor读取Header, CephxServiceTickBlob,用CephxServiceTicketBlob中指定的secret id,从内存中获得临时密钥secret,secret解密CephxServiceTicketBlob, 获得CephXServiceTicketInfo, 检验Header中的global id是否和CephxServiceTickeInfo中的global id相同,不相同就失败。

CephxServiceTicketInfo中的session key解密CephxAuthorize.

生成CephXAuthorizeReply , 内容包含CephxAuthoriznonce +1.

解码 CephxServiceTicketRequest, 对其中的bitmask对应的每一种service,过程如下:

 

生成与该服务对话的session key

keystore获取对应于该service的临时密钥(secret, secret id).

生成 CephXServiceTicketsession key, ticket的存活时间),

用刚才解密的CephxServiceTicketInfo中的session key加密之。

 

生成 CephXTicketBlob, 其中包含 (secret id CephXServiceTicketInfo

CephXServiceTicketInfo代表代表一张票子(ticket), 其中包含 : (刚才生成的与该service对话的session key, ticket, 权限),其中CephxServiceTicketInfo用服务对应的临时密钥secret加密. ticket继承了step 2中的大部分内容,但是权限项目是对应的service中该client的权限。使用解密的CephxServiceTickt中的session key加密整个CephXTicketBlob.

把经过上述处理后的CephxServiceTicketCephxTicketBlob一起发送给client.

 

monclient:对每一种service服务,过程如下:

用与monitor对话的session key解密CephxServciceTicket,如果CephxTicketBlob时经过密钥

加密的,则用这个session key解密CephxTicketBlob。保存CephxServiceTicket中的session key为最新的session key,保存CephXServiceTicketsession key的存活时间。

client osd会话的建立:

ceph 使用消息通讯机制,其中messenger模块负责通讯,以simple messenger为例:

起实现通讯的代码位于src/msg/simple/Pipe.cc中,例如当发起一个连接时,Pipe::connect被调用,执行开始就区获得一个认证信息:




get_authorizer最终会执行到Client的代码中:

build_authorizer最后会执行到CephxTicketManager中,该代码取出对应于某service的认证处理代码:

 

而该处理程序则提供认证信息:


我们看到它主要是打包global_id, service id 以及ticket和一个随机数,然后用session key加密。

以上时连接方发起的,下面看接收连接方时如何工作的:
接收连接方的入口在src/msg/simple/Pipe.cc的accept()函数:




而vierfy_authorizer函数最终会执行到:

我们看到这个函数最终获得对方的caps_info也即权限信息,对方的名称,对方的全局id, 对方的session_key,以及uid.




而cephx_verify_authorizer做了什么? 它用get_service_secret(service_id, ticket.secret_id, service_secret)得到临时密钥,然后decode_decrypt_enc_bl(cct, ticket_info, service_secret, ticket.blob, error)把票子解密。检验票子中的global id是否和明文发送过来的global id相同,不相同就会返回失败。

最后它做一些nonce的解密,这不太重要,没有什么信息量。


双方握手成功后,都会创建一个消息处理器,主要起到数据报的加密和检验。


以后双方发送数据都用sign_message签名:



而当前cephx 的sign_message依赖crc数据,只使用头尾几个crc字段来计算签名:


而接收数据方调用check_message_signature来验证签名:




版权声明:本文中所有内容均属于阿里云开发者社区所有,任何媒体、网站或个人未经阿里云开发者社区协议授权不得转载、链接、转贴或以其他方式复制发布/发表。申请授权请邮件developerteam@list.alibaba-inc.com,已获得阿里云开发者社区协议授权的媒体、网站,在转载使用时必须注明"稿件来源:阿里云开发者社区,原文作者姓名",违者本社区将依法追究责任。 如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:developer2020@service.aliyun.com 进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
+ 订阅

云安全开发者的大本营

其他文章