Hadoop Security機(jī)制下跨集群日志分離認(rèn)證問題解決方案

概述:為解決臨時(shí)數(shù)據(jù)導(dǎo)致的集群資源爭(zhēng)用問題剃毒,我們采用了container日志分離方案病袄,但在Hadoop Security機(jī)制下,該方案存在跨集群的認(rèn)證問題赘阀。經(jīng)過對(duì)Hadoop Security機(jī)制及NodeMagager日志聚集功能源碼的分析益缠,探索了兩種解決方案:1)在各計(jì)算框架以個(gè)人用戶獨(dú)立認(rèn)證;2)在日志聚集功能模塊以Yarn用戶統(tǒng)一認(rèn)證基公,并對(duì)兩種解決方案的優(yōu)劣進(jìn)行了對(duì)比幅慌。

1 、概述

集群上的數(shù)據(jù)可以拆分為業(yè)務(wù)數(shù)據(jù)轰豆、臨時(shí)數(shù)據(jù)(日志胰伍、 app jars等),兩類數(shù)據(jù)(或其操作)共同爭(zhēng)用RPC, 存儲(chǔ)等資源酸休。經(jīng)統(tǒng)計(jì)骂租,每天NN RPC總量約為9.06億,其中斑司,存儲(chǔ)日志數(shù)據(jù)導(dǎo)致的RPC約占RPC總量的10%渗饮,為了降低計(jì)算集群的RPC壓力,我們結(jié)合YARN-3269提出了Container日志分離方案:將Container日志數(shù)據(jù)進(jìn)行聚集宿刮,然后存儲(chǔ)至獨(dú)立的用于存放冷數(shù)據(jù)的集群互站,從而消除日志存儲(chǔ)對(duì)計(jì)算集群的影響。

目前僵缺,集群采用了基于Kerberos的Hadoop Security機(jī)制云茸,而該安全機(jī)制會(huì)導(dǎo)致日志聚集功能中HDFSClient訪問冷數(shù)據(jù)集群NameNode認(rèn)證失敗,從而影響分離方案實(shí)施谤饭。

為了解決該問題标捺,保障分離方案順利實(shí)施懊纳,對(duì)Hadoop Security機(jī)制做了深入研究,并結(jié)合NodeManager日志聚集功能源碼分析亡容,探索了兩種解決方案:

1) 在各計(jì)算框架以個(gè)人用戶獨(dú)立認(rèn)證嗤疯。

2) 在日志聚集功能模塊以Yarn用戶統(tǒng)一認(rèn)證。

下文將對(duì)Hadoop Security 機(jī)制闺兢,日志分離功能遇到的問題的原因及解決方案進(jìn)行詳細(xì)分析茂缚,不足之處,也請(qǐng)批評(píng)指正屋谭。

2 脚囊、Hadoop Security

Hadoop Security機(jī)制采用Kerberos 與Delegation Tokens(代理Token)相結(jié)合的方案。

2.1 Kerberos

2.1.1 Kerberos 原理

為了更加形象的說明Kerberos的原理桐磁,我們采用舉例的方式進(jìn)行說明(官方示例)悔耘。

比如:用戶要去游樂場(chǎng),首先要在門口檢查用戶的身份(即 CHECK 用戶的 ID 和 PASS), 如果用戶通過驗(yàn)證我擂,游樂場(chǎng)的門衛(wèi) (AS) 即提供給用戶一張門卡 (TGT)衬以。

這張卡片的用處就是告訴游樂場(chǎng)的各個(gè)場(chǎng)所,用戶是通過正門進(jìn)來校摩,而不是后門偷爬進(jìn)來的看峻,并且也是獲取進(jìn)入場(chǎng)所一把鑰匙。

現(xiàn)在用戶有張卡衙吩,但是這對(duì)用戶來不重要互妓,因?yàn)橛脩魜碛螛穲?chǎng)不是為了拿這張卡的而是為了游覽游樂項(xiàng)目,這時(shí)用戶摩天樓坤塞,并想游玩车猬。

這時(shí)摩天輪的服務(wù)員 (client) 攔下用戶,向用戶要求摩天輪的 (ST) 票據(jù)尺锚,用戶說用戶只有一個(gè)門卡 (TGT), 那用戶只要把 TGT 放在一旁的票據(jù)授權(quán)機(jī) (TGS) 上刷一下珠闰。 票據(jù)授權(quán)機(jī) (TGS) 就根據(jù)用戶現(xiàn)在所在的摩天輪,給用戶一張摩天輪的票據(jù) (ST), 這樣用戶有了摩天輪的票據(jù)瘫辩,現(xiàn)在用戶可以暢通無阻的進(jìn)入摩天輪里游玩了伏嗜。

當(dāng)然如果用戶玩完摩天輪后,想去游樂園的咖啡廳休息下伐厌,那用戶一樣只要帶著那張門卡 (TGT). 到相應(yīng)的咖啡廳的票據(jù)授權(quán)機(jī) (TGS) 刷一下承绸,得到咖啡廳的票據(jù) (ST) 就可以進(jìn)入咖啡廳。

當(dāng)用戶離開游樂場(chǎng)后挣轨,想用這張 TGT 去刷打的回家的費(fèi)用军熏,對(duì)不起,用戶的 TGT 已經(jīng)過期了卷扮,在用戶離開游樂場(chǎng)那刻開始荡澎,用戶的 TGT 就已經(jīng)銷毀了均践。

如圖1所示,Kerberos認(rèn)證的過程可以分為三步:1)Client獲取KDC訪問許可TGT(我是誰)摩幔,2)向TGS請(qǐng)求要訪問的目標(biāo)服務(wù)的票具(我要干什么)彤委,3)訪問目標(biāo)服務(wù)(干什么),圖中具體流程與舉例說明相仿或衡,下面我們結(jié)合HDFS的訪問過程對(duì)其進(jìn)行描述焦影。

圖1 Kerberos認(rèn)證過程分析

2.1.2 HDFS Client 的認(rèn)證流程

下面以大家常用的hdfs dfs – ls dir(或 hadoop fs –ls dir) 為例,描述Kerberos的認(rèn)證流程封断。

1) 首先使用kinit進(jìn)行登錄斯辰,輸入密碼后,Kerberos 客戶端收集user-principle(kinit時(shí)產(chǎn)生坡疼,可以使用Klist進(jìn)行查看) 和password彬呻,發(fā)送至KDC(AS)進(jìn)行認(rèn)證。

2) KDC認(rèn)證通過后回梧,下發(fā)TGT(user-kdc-ticket)給客戶端废岂∽娲辏客戶端收到TGT進(jìn)行校驗(yàn)通過后狱意,將TGT緩存在本地(用戶只讀)。

3) 將執(zhí)行hdfs dfs –ls dir時(shí)拯欧,首先從緩存中取出TGT, 然后向KDC(TGS)獲取連接NameNode(NN)訪問許可详囤。KDC收到請(qǐng)求,用戶身份校驗(yàn)通過后镐作,下發(fā)User-NN-Ticket.

4) HDFS客戶端使用得到的User-NN-Ticket連接NN藏姐。NN收到請(qǐng)求后,對(duì)Ticket進(jìn)行驗(yàn)證该贾,認(rèn)證通過后羔杨,使用加密數(shù)據(jù)回復(fù)客戶端,客戶端收到信任信息后杨蛋,發(fā)送listFiles(dir)請(qǐng)求兜材,并等待響應(yīng)。

以上為HDFS Client簡(jiǎn)要流程逞力,曙寡。

圖2 Kerberos認(rèn)證的局限性

2.2 Delegation Token

理論上,可以單獨(dú)使用Kerberos進(jìn)行身份認(rèn)證寇荧,然而举庶,在Hadoop這樣的分布式系統(tǒng)中使用時(shí),存在一個(gè)問題:對(duì)于每一個(gè)Job, 如果所有的工作任務(wù)者使用TGT通過Kerberos TGS進(jìn)行身份認(rèn)證揩抡,那么Kerberos將很快成為瓶頸户侥。圖2中的紅線說明了問題:一個(gè)作業(yè)可能有數(shù)千個(gè)節(jié)點(diǎn)到節(jié)點(diǎn)的通信镀琉,導(dǎo)致相同的KDC通信量。事實(shí)上添祸,在大集群中會(huì)不經(jīng)意地在KDC上執(zhí)行分布式拒絕服務(wù)攻擊滚粟。

因此,引入了Delegation Token作為一種輕量級(jí)的認(rèn)證方法來補(bǔ)充Kerberos身份驗(yàn)證刃泌。Kerberos是三方協(xié)議凡壤;相比之下,Delegation Token認(rèn)證是兩方認(rèn)證協(xié)議耙替。引入Delegation Token之后的認(rèn)證過程如圖3所示亚侠。

為了簡(jiǎn)潔起見,圖3省略了Kerberos身份驗(yàn)證的步驟和任務(wù)分配的細(xì)節(jié)俗扇。假設(shè)硝烂,現(xiàn)在已經(jīng)完成了Kerberos的三步式認(rèn)證,后續(xù)流程如下(KMS Delegation與HDFS Delegation協(xié)同铜幽,下面統(tǒng)一以HDFS的角度進(jìn)行說明):

1)Client在進(jìn)行完Kerberos的三步式認(rèn)證后滞谢,獲得NameNode產(chǎn)生的HDFS Delegation Token,并緩存于UGI.

2)Client 向RM(ResourceManager)提交App時(shí)除抛,會(huì)攜帶該Token信息狮杨。

3)RM接到Token之后, 會(huì)馬上對(duì)Token進(jìn)行Renew操作已驗(yàn)證其合法性,并將其持久化到要啟動(dòng)ApplicationMaster的Worker(NodeManager)到忽,Worker在啟動(dòng)ApplicationMaster加載該Token(后續(xù)Worker類似)橄教。

4)Worker 通過Token 對(duì)HDFS進(jìn)行訪問。

5)運(yùn)行結(jié)束喘漏,RM撤銷Token.

圖3 Delegation Token 補(bǔ)充方案認(rèn)證流程

圖3 Delegation Token 補(bǔ)充方案認(rèn)證流程

值得注意的是护蝶,Token具有超時(shí)時(shí)間,默認(rèn)為24小時(shí)翩迈。在不對(duì)Token更新的情況下持灰,超過24小時(shí)的App將會(huì)失敗。因此负饲,存在Renewer對(duì)Token進(jìn)行更新以保證長(zhǎng)任務(wù)執(zhí)行(token最終超時(shí)時(shí)間由yarn參數(shù)delegation.token.max-lifetime決定)堤魁。

3 、日志聚集功能

3.1 日志分離失敗case

在原有配制基礎(chǔ)上绽族,開啟日志分離功能(跨集群日志聚集)后姨涡,發(fā)現(xiàn)未按預(yù)期進(jìn)行日志分離,且NodeManager節(jié)點(diǎn)存在以下異常信息:

2018-05-09 17:36:02,539 ERROR logaggregation.LogAggregationService (LogAggregationService.java:run(340)) - Failed to

 setup application log directory for application_1525801833018_0004

java.io.IOException: Failed on local exception: java.io.IOException: org.apache.hadoop.security.AccessControlExcepti

on: Client cannot authenticate via:[TOKEN, KERBEROS]; Host Details : local host is: "*"; destination host is: "*";

通過觀察日志吧慢,可以清晰的發(fā)現(xiàn)涛漂,該異常系權(quán)限認(rèn)證失敗所致。通過分析源碼,該異常發(fā)生的位置進(jìn)行的操作為:通過userUGI.doAs創(chuàng)建AppLogDir匈仗。日志顯示的結(jié)果可能為userUGI中沒有訪問遠(yuǎn)程集群的Token瓢剿,導(dǎo)致失敗。

3.1.1 UGI 追蹤(UGI****從哪里來)

分析userUGI中是否具有訪問冷數(shù)據(jù)集群的Token, 我們需要對(duì)UGI的來源進(jìn)行跟蹤悠轩。通過分析源碼间狂,我們發(fā)現(xiàn)UGI關(guān)聯(lián)的User及Token(圖中Credentials為工具類,用于讀寫存儲(chǔ)在內(nèi)存或磁盤中密鑰和令牌)是通過解析LogAggregationServicer接收的APPLICATION_STARTED Event 得到的火架,具體跟蹤流程如圖4所示鉴象,其中Hander, Initializer為方便說明,抽象出來的對(duì)象何鸡。

根據(jù)時(shí)序圖中訪問流程纺弊,結(jié)合異常日志信息,可以確定異常原因的確userUGI沒有訪問遠(yuǎn)程集群的Toket(Credentials)

圖4 UGI初始化流程分析(錯(cuò)誤Case流程)

3.1.2 Credentials ( 或Tokens) 追蹤(Creadential 從哪里來)

本節(jié)從Spark計(jì)算引擎的角度骡男,對(duì)Credentials(或Tokens)來源進(jìn)行追蹤淆游。通過分析yarn/Client源碼,Client在啟動(dòng)AM (ApplicationMaster)前隔盛,會(huì)進(jìn)行一系列準(zhǔn)備工作犹菱。準(zhǔn)備工作過程中存在與其它組件的通信,其中包括準(zhǔn)備本地資源時(shí)(prepareLocalResources)與NN(NameNode)的通信:1)通過TGT 獲取user-nn-ticket(Client啟動(dòng)在客戶機(jī)吮炕,可以使用TGT) 腊脱;2)使用user-nn-ticket 訪問NN,并獲取Delegation Tokens. 獲取到Tokens后會(huì)通過Credentials將Tokens(不含TGT)存儲(chǔ)在ContainerLaunchContext中来屠。并隨同ApplicationSubmissionContext一起提交至Yarn虑椎,請(qǐng)求啟動(dòng)AM震鹉;Yarn收到請(qǐng)求后俱笛,會(huì)為其選擇NodeManager,使用ContainerLaunchContext 拉起AM.


圖5 Credentials 追蹤

從上圖可知,最后LogAggregationServicer可使用的Tokens是客戶端(Agent)初始化時(shí)传趾,獲取的迎膜。換句話說,客戶端獲取了訪問某NN的Token時(shí)浆兰,LogAggregationServicer才具有訪問該NN的Token. 而默認(rèn)情況下磕仅,客戶端僅會(huì)獲取fs.defaultFS(HADOOP_CONF:core-site.xml中配置),因此簸呈,跨集群訪問時(shí)無訪問日志集群的權(quán)限榕订。

3.2 解決方案

通過上述分析可知,若想訪問某服務(wù)蜕便,需具備以下一種條件:

1) 擁有該服務(wù)授予的合法Token.

2) 角色持用TGT(password認(rèn)證或keytabs),可以通過Kerberos完成完整的服務(wù)認(rèn)證劫恒。

基于以上分析,我們對(duì)日志分離認(rèn)證問題提出了兩種方案:

1)各計(jì)算框架以個(gè)人用戶獨(dú)立認(rèn)證

該方案的核心思想是向Yarn提交應(yīng)用前,使客戶端(Agent)獲取所有必要的Token两嘴〈猿客戶端啟動(dòng)在使用kinit進(jìn)行登錄的客戶機(jī),因此其可使用TGT 完成Kerberos認(rèn)證憔辫,并可以獲取到任務(wù)想訪問的服務(wù)(類2.1.1節(jié)流程)趣些。

因此,針對(duì)日志分離跨集群認(rèn)證問題贰您,應(yīng)使客戶端在向Yarn提交應(yīng)用前坏平,獲取到所有NN 的Token,以便傳遞到NM以用戶身份進(jìn)行日志聚集操作锦亦。

該方案需要在各計(jì)算引擎進(jìn)行配置或修改功茴,以使在提交應(yīng)用前,獲取到所需的Tokens.目前孽亲,Spark(“spark.yarn.access.namenodes”)及MR(“ mapreduce.job.hdfs-servers”)引擎坎穿,自帶配制參數(shù),用于指定額外的NN返劲,以獲取Tokens玲昧。其它引擎目前未進(jìn)行調(diào)研。

2 )日志聚集功能模塊以Yarn****用戶統(tǒng)一認(rèn)證

該方案的核心思想是使用NodeManager的啟動(dòng)用戶Yarn進(jìn)行日志聚集篮绿,從而使用Yarn統(tǒng)一進(jìn)行認(rèn)證孵延。

NodeManager使用KeyTabs方式進(jìn)行登錄,其可以通過Kerberos認(rèn)證訪問所有服務(wù)(包括NN)亲配;另外尘应,日志聚集功能,以AbstractService方式運(yùn)行于NodeManger吼虎。因此犬钢,理論上可以使用NodeManager獲取的Tokens 訪問遠(yuǎn)程N(yùn)N,創(chuàng)建日志目錄或上傳日志等思灰。

日志聚集不僅包括日志上傳等工作玷犹,還包括container本地日志清理工作,而Container日志的管理是以應(yīng)用提交用戶的名義進(jìn)行的管理洒疚,若直接將UserUGI簡(jiǎn)單的更換成NodeManager LoginUGI歹颓,則日志后處理工作將無法進(jìn)行,因此油湖,我們采用Token劫持方案進(jìn)行實(shí)現(xiàn)(若集群支持ProxyUser巍扛,可使用ProxyUser),即:使用用戶的UGI + NodeManager 獲取的Token方式進(jìn)行實(shí)現(xiàn)乏德,具體如下:

    //Get the cerdentials that NM connects to remote NN and use it replace user's credentials.
    try {
      credentials = new Credentials();
      FileSystem remoteFs = getFileSystem(getConfig());
      remoteFs.addDelegationTokens(UserGroupInformation.getLoginUser().getShortUserName(), credentials);
    } catch (IOException e) {
      LOG.warn("Get credential failed for application: " + appId);
      throw new YarnRuntimeException(e);
    }

    // Get user's FileSystem credentials
    final UserGroupInformation userUgi =
        UserGroupInformation.createRemoteUser(user);
    if (credentials != null) {
      userUgi.addCredentials(credentials);
    }

3.3 方案對(duì)比

表1 跨集群日志分離認(rèn)證問題解決方案對(duì)比

對(duì)比項(xiàng) 獨(dú)立認(rèn)證 統(tǒng)一認(rèn)證
方案 向Yarn提交應(yīng)用前撤奸,客戶端(Agent)以登錄用戶身份獲取所有必要的Token 劫持NodeManager的Token ,使用Yarn用戶進(jìn)行統(tǒng)一認(rèn)證
日志Owner 使用用戶獲取NN Token,日志文件Owner為用戶 使用劫持NM獲取的NN Token寂呛,日志文件Owner為NM啟動(dòng)用戶Yarn怎诫, 因此需要借助Ranger進(jìn)行授權(quán)
擴(kuò)展性 需要各計(jì)算引擎分別適配,以便 Agent中獲取到日志集群NN的Token 計(jì)算引擎無需關(guān)心日志聚集所需NN Token
魯棒性 初始化時(shí)贷痪,需要連接的多個(gè)NN以獲取所有Tokens, 當(dāng)日志NN不可達(dá)時(shí)幻妓,應(yīng)用將因連接異常而無法提交 日志NN不可達(dá)時(shí),僅影響日志聚集劫拢,不影響應(yīng)用提交

綜上肉津,我們采用日志聚集功能模塊以Yarn用戶統(tǒng)一認(rèn)證的方式來解決跨集群日志分離認(rèn)證問題。

4 結(jié)論

本文分析了Hadoop Security的原理舱沧,提出了兩種跨集群日志分離認(rèn)證問題解決方案妹沙。并對(duì)比了兩種方案的優(yōu)劣,最終選用日志聚集功能模塊以Yarn用戶統(tǒng)一認(rèn)證方案解決跨集群日志分離認(rèn)證問題熟吏,現(xiàn)該方案已上線驗(yàn)證距糖,截止目前運(yùn)行良好。

參考文獻(xiàn)
[1] Hadoop Delegation Tokens Explained :https://blog.cloudera.com/blog/2017/12/hadoop-delegation-tokens-explained/
[2] https://zh.wikipedia.org/wiki/Kerberos
[3]https://github.com/Tianny/Bloggy/issues/4

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末牵寺,一起剝皮案震驚了整個(gè)濱河市悍引,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌帽氓,老刑警劉巖趣斤,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異黎休,居然都是意外死亡浓领,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門势腮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來联贩,“玉大人,你說我怎么就攤上這事嫉鲸〕潘猓” “怎么了歹啼?”我有些...
    開封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵玄渗,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我狸眼,道長(zhǎng)藤树,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任拓萌,我火速辦了婚禮岁钓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己屡限,他們只是感情好品嚣,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著钧大,像睡著了一般翰撑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上啊央,一...
    開封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天眶诈,我揣著相機(jī)與錄音,去河邊找鬼瓜饥。 笑死逝撬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的乓土。 我是一名探鬼主播宪潮,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼趣苏!你這毒婦竟也來了坎炼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤拦键,失蹤者是張志新(化名)和其女友劉穎谣光,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芬为,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡萄金,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了媚朦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氧敢。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖询张,靈堂內(nèi)的尸體忽然破棺而出孙乖,到底是詐尸還是另有隱情,我是刑警寧澤份氧,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布唯袄,位于F島的核電站,受9級(jí)特大地震影響蜗帜,放射性物質(zhì)發(fā)生泄漏恋拷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一厅缺、第九天 我趴在偏房一處隱蔽的房頂上張望蔬顾。 院中可真熱鬧宴偿,春花似錦、人聲如沸诀豁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽舷胜。三九已至都哭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逞带,已是汗流浹背欺矫。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留展氓,地道東北人穆趴。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像遇汞,于是被迫代替她去往敵國(guó)和親未妹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容