閱讀328 返回首頁    go 技術社區[雲棲]


cephx: ceph的認證和加密協議

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來驗證簽名:




最後更新:2017-06-08 16:31:53

  上一篇:go  Everything@阿裏雲:隻有大象才能奔跑!
  下一篇:go  ceph monitor功能的兼容性管理