<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          云原生策略引擎 OPA 介紹

          共 10485字,需瀏覽 21分鐘

           ·

          2020-11-28 21:52

          OPA(發(fā)音為 “oh-pa”)是一個全場景通用的輕量策略引擎(Policy Engine),OPA 提供了聲明式表達的 Rego 語言來描述策略,并將策略的決策 offload 到 OPA,從而將策略的決策過程從策略的執(zhí)行中解耦。OPA 可適用于多種場景,比如 Kubernetes、Terraform、Envoy 等等,簡而言之,以前需要使用到 Policy 的場景理論上都可以用 OPA 來做一層抽象,如下所示:

          在大多數(shù)場景下,Policy 即是一系列用來控制服務的規(guī)則(可理解為就是 if-else 語句)。比如:

          • Authorization 場景:定義哪些用戶(Identity)可對哪些資源(Resource)進行什么類型的操作(Operation)的規(guī)則

          • 網(wǎng)絡防火墻規(guī)則:比如 iptables 中設定的規(guī)則,Kubernetes 中的 Network Policy 都可認為是與網(wǎng)絡相關的 Policy

          • 資源準入控制:符合某種 Policy 的資源(比如必須設置某些屬性或字段)才可以被業(yè)務層處理,否則將被拒絕服務,典型的有 Kubernetes 的 Admission Control 機制

          很多時候,Policy 的實現(xiàn)都與具體的服務耦合在一起,這導致 Policy 很難被單獨抽象描述和靈活變更(比如動態(tài)加載新的規(guī)則)。而且,不同的服務對 Policy 有著不同的描述,比如采用 JSON、YAML 或其他 DSL,這樣很難進一步將 Policy 以代碼的形式進行管理(Policy As Code)。因此,為了更靈活和一致的架構,我們必須將 Policy 的決策過程從具體的服務解耦出去,這也是 OPA 整體架構的核心理念。如上圖所示,在 OPA 的方案下我們對 Policy 的執(zhí)行可抽象成以下 3 個步驟

          • 用 Rego 語言描述具體的 Policy
          • 當某個服務需要執(zhí)行 Policy 時將這個動作(Query)交給 OPA 來完成(OPA 可以以 library 或者第三方服務的形式出現(xiàn))
          • OPA 在具體的輸入數(shù)據(jù)下(Data)執(zhí)行步驟 1 中的 Rego 代碼,并將 Policy 執(zhí)行后的結果返回給原始服務

          當采用 OPA 后,原來的服務架構可以簡化為如下圖所示:


          OPA 的出現(xiàn)本質(zhì)上還是為 Policy 相關邏輯增加一個抽象層,這樣一來我們可以用統(tǒng)一的 DSL 和處理流程來完成 Policy 相關的邏輯,從而達到一致的架構設計。

          Rego 語言的設計

          Rego 語言為 OPA 項目提供一種領域無關的描述策略的聲明式 DSL。Rego 的主要設計源于 Datalog,但是與 Datalog 不同的是,Rego 擴展了對 JSON 的支持,在 Rego 語言中,輸入輸出都是標準的 JSON 數(shù)據(jù)

          Rego 語言中最核心的一個功能的是對 Policy 的描述,即 Rules,Rules 是一個 if-then 的 logic statement。Rego 的 Rules 分為兩類:complete rulesincremental rules

          complete rules 只會產(chǎn)生一個單獨的值(即 true 或者 false):

          any_public_networks?=?true?{
          ?net?:=?data.networks[_]
          ?net.public
          }

          每一個 rule 都由 head 和 body 組成,比如上文 any_public_networks = true 就是 head,剩下的 {...} 則為 body。

          incremental rules 則將產(chǎn)生所有滿足 body 的 value 的集合,比如:

          public_network[net.id]?{
          ?net?:=?data.networks[_]
          ?net.public
          }

          當執(zhí)行 public_network 時,將返回一個 set,比如:

          [
          ??"net3",
          ??"net4"
          ]

          當 rule 內(nèi)部出現(xiàn)多個布爾判斷時,它們之間是邏輯與的關系,即多個布爾表達式的邏輯與構成整個 rule 最終的結果。

          當多個 rule 組合在一起,其表達的是邏輯或的關系:

          default?shell_accessible?=?false

          shell_accessible[server.id]?=?true?{
          ????input.servers[_].protocols[_]?==?"telnet"
          }

          shell_accessible[server.id]?=?true?{
          ????input.servers[_].protocols[_]?==?"ssh"
          }

          shell_accessible 是尋找 servers 中支持 telnetssh 的變量。

          使用 Rego 語言編寫 Policy 其實就是編寫一系列的 Rules

          除此之外,Rego 還支持一系列內(nèi)置函數(shù)來簡化日常使用,還可以采用單元測試的方式來輸入測試數(shù)據(jù)從而驗證 Rules 的正確性。

          OPA 的應用

          集成 OPA

          OPA 的運行依賴于 Rego 運行環(huán)境,因此 OPA 提供了兩種主要的使用方式:

          • Go Library:直接將 Rego 的運行環(huán)境以庫的形式整合到用戶服務中。當服務需要執(zhí)行 Rego 代碼時,只需要調(diào)用相關的 API 即可

          • REST API:如果服務不是用 Go 編寫,為了擁有 Rego 運行環(huán)境,此時則必須使用 REST API。OPA 提供了一個 HTTP 服務,當其他組件需要執(zhí)行 Rego 代碼時,則將代碼和數(shù)據(jù)一起以 REST API 的形式傳給 OPA HTTP 服務,待 Rego 執(zhí)行完后將相應的結果返回

          Kubernetes 中使用 OPA

          Kubernetes 中使用 OPA 需要使用 Kubernetes 準入控制器,拿最新發(fā)布的 Gatekeeper(https://github.com/open-policy-agent/gatekeeper) 項目為例,其主要的架構如下圖所示:

          Gatekeeper 在 Kubernetes 中以 Pod 形式啟動,啟動后將向 API Server 中注冊 Dynamic Admission Controller,本質(zhì)上就是讓 Gatekeeper 作為一個 Webhook Server。當用戶使用 kubectl 或者其他方式向 API Server 發(fā)出對資源的 CURD 請求時,其請求經(jīng)過 Authentication 和 Authorization 后,將發(fā)送給 Admission Controller,并最終以 AdmissionReview 請求的形式發(fā)送給 Gatekeeper。Gatekeeper 根據(jù)對應服務的 Policy(以 CRD 形式配置)對這個請求進行決策,并以 AdmissionReview 的響應返回給 API Server。

          Gatekeeper v3.0 的架構如下所示:

          在目前的設計中,Gatekeeper 有 4 類 CRD:

          • ConstraintTemplate:ConstraintTemplate 和 Constraint 可認為是類似于類和實例的關系。ConstraintTemplate 的 rego 字段用 Rego 語言具體描述了 Policy,但并沒有指定 Policy 中具體的參數(shù)。ConstraintTemplate 也描述了生成 Constraint CRD 的 schema

          • Constraint:Constraint 可認為是對某一個 ConstraintTemplate 的實例化,其中對 ConstraintTemplate 的未指定參數(shù)進行了具體的配置。因此,對同一個 ConstraintTemplate 不同的參數(shù)配置可以生成多個 Constraint。Constraint 是由 ConstraintTemplate 這個 CRD 的描述再次由 Gatekeeper 生成的 CRD(即通過 CRD 再生成 CRD)

          • Audit:Gatekeeper 的 Audit 功能可以定期對從 Kubernetes 集群復制對應的資源應用具體的 Contraint,從而發(fā)現(xiàn)當前已存在服務中是否有違反 Policy 的配置。Audit 即是用以控制和配置該功能的 CRD

          • Sync:Audit 功能在 Gatekeeper 執(zhí)行 Policy 時需要從 Kubernetes 集群中復制(replication)資源,Sync 即是用來控制和配置這一過程的 CRD

          目前 Gatekeeper 僅支持 Validating Webhook,Mutating Webhook 正在開發(fā)中。

          Gatekeeper 采用了 Kubebuilder 和 OPA Frameworks,其中 Contraint 相關的邏輯由 Frameworks 實現(xiàn),所以核心邏輯并不復雜。

          Gatekeeper 可認為獲得了 Kubernetes 場景下一個可編程的 Webhook,通過輸入不同的 DSL 就可獲得不同 Validating 能力的 Webhook。畢竟,在 K8s 里頭寫一個 Validating 的 Webhook 是一個相當無趣且重復的工作,用一個統(tǒng)一的 Webhook 框架將讓事情簡單非常多。

          OPA 的生態(tài)

          OPA 的開發(fā)歷史

          OPA 是由 Styra 公司于 2016 年左右發(fā)起并主導開發(fā),目前是 CNCF 孵化項目,仍處于開發(fā)階段(最新版本號為 0.19)。

          Styra 公司的產(chǎn)品就是基于 OPA 為 Kubernetes 提供 Policy 平臺,從而讓 Kubernetes 實現(xiàn)更細粒度的 Authorization,提升安全性。

          從 2019 年來看,個人感覺 Policy 方面受到越來越多的關注。從 Microsoft Azure 團隊和 OPA 團隊主導開發(fā) Gatekeeper 可看出,OPA 項目似乎越來越受到重視。比如在社區(qū)對 PodSecurityPolicy 的討論中,就有人覺得原來 PodSecurityPolicy 的設計不夠靈活且 API 不一致,且有人提議用 OPA 來替換這部分邏輯,但是因 Gatekeeper 仍不夠穩(wěn)定而暫時擱置。

          目前社區(qū)以 OPA 和 Gatekeeper 這兩個項目活躍程度最高。Gatekeeper 的進度可通過查看會議紀要了解,看起來已經(jīng)快到 GA 階段。

          Kubernetes 中的 Policy Working Group

          Kubernets 中有著各種各樣的 Policy:RBAC、Network Policy、Pod Security Policy、Scheduler Policy、ImagePull Policy 等等,為此,Kubernetes 社區(qū)成立 Policy Working Group 來跟蹤改進和 Kuberntes 相關的 Policy 提議。OPA 的最核心作者 Torin Sandall 為其中的 Organizer 之一。

          看了這個 Group 最近幾個月的會議記錄,目前有幾個正在進行的項目:

          • Guardian 項目:形式化驗證云原生項目(比如 Kubernetes 的 RBAC)的框架,目前仍處于 PoC 階段。感覺這個項目上手難度不小(涉及到形式化邏輯相關的知識),目前也沒太多人參與;
          • Container Policy Interface:類似于 CRI、CSI 等,為 Kubernetes 制定 Policy 相關的接口;
          • 與 OPA 項目相關項目的討論

          OPA 存在的問題

          OPA 的開銷

          OPA 的開銷簡單可分為兩方面:Policy 執(zhí)行性能開銷引入 OPA 層可能帶來的網(wǎng)絡開銷

          Policy 執(zhí)行性能開銷即 OPA 執(zhí)行 Rego 規(guī)則所帶來的開銷。從官方的數(shù)據(jù)來看,OPA 是一個高性能低延遲系統(tǒng),其 Policy 的執(zhí)行將控制在 1 毫秒以內(nèi)。且即使隨著 Policy 的增加,其開銷也是近似常數(shù)時間。但是,跟大多數(shù)系統(tǒng)一樣,如果沒有按照 best practice 編寫 Rego 規(guī)則同樣會給 OPA 帶來不正常的開銷。但是從官方給出的描述上看,OPA 在大多數(shù)場景下其開銷都是可被接受的。

          OPA 還引入了不少對執(zhí)行規(guī)則優(yōu)化算法,其中比較重要的有:

          • Rule Indexing:可將對規(guī)則執(zhí)行從 O(n) 的遍歷轉(zhuǎn)換成一個 Tire 樹查找,從而大大降低算法復雜度;
          • Partial Evaluation:將 Policy 和對應的數(shù)據(jù)一起結合編譯展開成符合 Rule Indexing 形式的 Rego 代碼。這在特定的 Policy Data 下有很好的性能表現(xiàn)。但實際測試下,對于復雜規(guī)則,Partial Evaluation 編譯出來的結果非常古怪,個人感覺這個算法還存在不少 Bug;

          如果 OPA 不是作為一個庫引入,而是整體作為一個 Web 服務引入(比如 OPA REST API、Kubernetes Admission Control 等),對某種資源的操作都需要經(jīng)過 OPA 執(zhí)行 Policy 后才進入下一步。除去 Policy 的執(zhí)行成本,其主要開銷即是網(wǎng)絡層帶來的多一跳。在 Kubernetes 的場景中,這個開銷其實不算大(就算沒有 OPA 的 Admission Control,還有其他各種原生的 Admission Controllers),跟資源 CURD 過程的其他環(huán)節(jié)比起來,這部分不是性能熱點。但是在其他業(yè)務場景中,這需要具體問題具體分析。

          Rego 語言的學習曲線

          個人覺得,OPA 項目中最難的環(huán)節(jié)即是對 Rego 語言的學習,此處的難主要是與其他一些描述 Policy 的 DSL 做比較,比如被廣泛使用的 JSON 或 YAML 這類配置型語言。Rego 語言與傳統(tǒng)的編程語言有一定的差異,從語法的學習到能夠熟練運用 Rego 寫出符合 OPA 最佳實踐的 Policy ,還是具備一定的學習曲線。雖然 Rego 語言為 OPA 帶來統(tǒng)一且強大的 DSL,但同樣也為 OPA 的落地增加了難度。

          Rego 語言的設計來自于 Datalog,用聲明式的方式去表達規(guī)則,沒有顯式的循環(huán)語句,整體風格比較類似于函數(shù)式編程,這對大部分熟悉命令式編程的用戶提高了入門門檻。

          業(yè)務改造成本

          一般來說,系統(tǒng)已存在的 Policy 想換成 OPA 架構,是很難做到業(yè)務不侵入。無論如何,當前業(yè)務代碼都必須進行相應程度的改造才能適用于 OPA,比如:

          • 在自身項目中集成 OPA
          • 將原來的 Policy 抽象用 Rego 來描述

          類似的項目

          社區(qū)中除了 OPA 之后,還有一些類似的與 Policy 相關的項目,比如:

          • Kyverno:Kyverno 是和 OPA 很像的項目,也是一個 Policy Engine,但是與 OPA 不同的是,Kyverno 采用 YAML 或 JSON 來描述 Policy。目前仍處于不穩(wěn)定開發(fā)階段,從 star 上看并沒有受到太大的關注;

          • Sentinel:Sentinel 是由 HashiCorp(開發(fā) Terraform 的公司)開發(fā)的一個內(nèi)嵌形式的 Policy As Code 的框架。Sentinel 同樣定義了用來描述 Policy 的 Sentinel Language(感覺和 OPA 使用的 Rego 語言還是挺類似的)。從官方的描述來看,Sentinel 的使用過程類似于以庫的形式集成以使用 Policy Engine。可惜這不是一個開源產(chǎn)品,很難看到更多的細節(jié);

          • Pulumi:與其他用 DSL 方式不同的是,Pulumi 走的是原生編程語言+SDK 的方式。這種做法可以讓用戶沒有 DSL 的學習成本,直接使用主流編程語言來進行 Policy 領域的編程;

          業(yè)界實踐

          備注:我在 2019 年 9 月左右的時候在互聯(lián)網(wǎng)上找了不少資料,發(fā)現(xiàn)真正將 OPA 運用于生產(chǎn)環(huán)境的用戶(除了 Styra 公司)還是及其稀少的,找了半天發(fā)現(xiàn)只有 Netflix 的工程師在 2017 年的 CloudNativeCon 上出來分享使用 OPA 的經(jīng)驗(后面發(fā)現(xiàn) Azure 用 Gatekeeper 啟動了一些服務)。所以 OPA 的穩(wěn)定性和可靠性還需要更多的案例去證明,目前還很難說方案已經(jīng)完全成熟。不過經(jīng)過后面大半年的宣傳和發(fā)展,近期的幾個 KubeCon 都能看到將 OPA 應用于生產(chǎn)環(huán)境的案例,所以感覺 OPA 正在被越來越多的開發(fā)者接受和使用。

          Netflix 采用 OPA 解決 Authorization 問題

          通過視頻(https://www.youtube.com/watch?v=R6tUNpRpdnY)可以了解到,Netflix 的工程師想解決這么一個問題

          Netflix 內(nèi)部有著相當多不同種類的微服務,服務之間有相互調(diào)用的需求,需要尋找一個較好的方式去解決服務間相互調(diào)用的 AuthZ 問題(即某一個服務可以以什么樣的權限去調(diào)用另一個服務)。

          Netflix 的工程師將 AuthZ 問題抽象為:

          Identity can/cannot perform Operation on Resource.

          即尋找一種簡單統(tǒng)一的方式去管理 I、O 和 R 三要素,其中在 Netflix 中,Identity 類型可以是 VM/Container 服務、某個員工等等;Resource 類型不僅僅是 REST Endpoints,也包括 gRPC Method、SSH、Kafka Topics 等等。各種服務也沒有統(tǒng)一的技術棧,每個服務可自由選擇合適的編程語言,比如有 Java、Node.JS、Python、Ruby 等。

          Netflix 的公司文化崇尚自由(Freedom)和責任(Responsiblity),每一個開發(fā)者需要對應用的整個生命周期負責(開發(fā)、測試、上線等等),所以開發(fā)者必須有自己定義 Policy 的自由,由開發(fā)者來決定一個服務具體可被誰使用。

          Netflix 最終設計了如下架構:

          • 上層用戶(通常是開發(fā)者)通過 Policy Portal 定義 Policy。Policy Portal 是一個 Web UI,幫助用戶設置具體的 Policy,并最終將 Policy 持久化到 Policy DB;

          • Aggregator 從多個渠道(比如 Application Ownership DB、Build Manifest、用戶管理系統(tǒng)等等)聚合 Policy 到 Policy DB。也就是說,除了用戶主動輸入 Policy 外,系統(tǒng)也會從其他地方加載其他的 Policy;

          • Distributor 從 Policy DB 和 Aggregator 拉取 Policy 并在內(nèi)存中維護(Keep it hot),從而可以保證 Service 可以盡快訪問 Policy。Distributor 采用可擴展設計,Service 的 AuthZ Agent 通過 Distributor 來拉取 Policy

          • 每個 Service 都內(nèi)嵌一個 AuthZ Agent(可能是以 sidecar 的形式部署,視頻中似乎沒有細說,但是可明確的是訪問這個 Agent 不需要額外的網(wǎng)絡訪問),通過調(diào)用 AuthZ Agent 來實現(xiàn) Policy decision

          而每一個 AuthZ Agent 的設計如下所示:

          API 層提供 OpenAPI 來供上層業(yè)務調(diào)用,Stager 承接 API 和 OPA,將具體的請求和 Policy 進行轉(zhuǎn)換并傳給 OPA 做具體的策略執(zhí)行。Updater 則是定期從 Distributor 中更新數(shù)據(jù)到 Stager 中。

          Microsoft Azure 使用 Gatekeeper 作為 Policy 控制

          Azure 團隊(主要是 Rita Zhang)作為 Gatekeeper 的主要開發(fā)者,將 Gatekeeper 應用到了 AKS。在 Slack 上和 Rita Zhang 簡單咨詢了一下,目前這個服務只是 private priview 階段(使用 Gatekeeper v2,后期會切換到 v3 版本),暫時應該還沒有推廣。

          對 WebAssembly 的支持

          OPA 在 v0.15.1 的時候支持了對 WebAssembly(下稱 WASM)的支持。OPA 對 WASM 的支持可如下架構所示:

          • Planner 將 Rego 的 AST 轉(zhuǎn)換成命令式的中間代碼 IR;

          • Compile 環(huán)節(jié)將 IR 翻譯成 wasm bytecode,其中 IR 使用到的底層函數(shù)都由 opa.wasm 提供;

          • opa.wasm 是由 C 語言寫的一系列不依賴于任何第三方庫的運行環(huán)境,主要是:

            在 Compile 階段,IR 所用到的外部函數(shù)都由 opa.wasm 提供。比如 Rego 代碼中的涉及到相關的比較最終都將轉(zhuǎn)換成 opa.wasm 的某個 compare 函數(shù)。

            • JSON 的序列化和反序列化;
            • 將 JSON 的對象轉(zhuǎn)換成 OPA 的內(nèi)置對象;
            • 操作對象的函數(shù)(Insert、Compare、Get 等);

          WASM 作為目前最為通用的字節(jié)碼標準,進一步提升了 OPA 的跨平臺特性:只要用 Rego 語句寫的 Policy,經(jīng)過編譯之后就能夠任何 WASM runtime 上運行。而且從性能上看,編譯成 WASM byetcode 執(zhí)行性能要遠遠比用 Go 解釋器執(zhí)行的性能要高非常多。

          但是 OPA WASM 目前同樣還有不少坑:planner 生成的 IR 并非緊湊高效;很多內(nèi)置函數(shù)到了 OPA WASM 上就沒有實現(xiàn);無法用上 Go 解釋器對規(guī)則的優(yōu)化算法等等。

          其中也可以將 OPA Go 解釋器編譯變成一個 WASM 程序,從而輸入依然是 Rego 代碼而無須進行編譯,也能充分利用起 Go 解釋器的所有邏輯。但是這種做法的復雜度要比直接將 Rego 代碼編譯成 WASM 的復雜度要高很多,比如:

          • 直接將 Go 代碼編譯成 WASM 并非完全成熟,Go 的 GC 是否能無縫切換為 WASM ?
          • 涉及到很多對操作系統(tǒng)接口的使用當前 WASM 也還未完全支持和統(tǒng)一(比如 WASI 標準);

          總而言之,將 Rego 編譯成 WASM 是一件更為 Pratical 的事情且收益更大。我還是非常看好 WASM 未來的發(fā)展的前景,也許在未來,編程語言不可以主動編譯成 WASM 將變成一件奇怪的事情。

          Policy As Code 帶來的價值

          OPA 提出的 Policy Engine 的圖景是很美好的,即 Policy As Code。如果能夠?qū)?Policy 以一種清晰統(tǒng)一的代碼形式管理,那么可以我們至少可以獲得以下幾個好處:

          • 全局統(tǒng)一的 Policy 描述方式:所有開發(fā)者對 Policy 的描述不再僅僅內(nèi)嵌于業(yè)務代碼中,也不再以多種不同的形式描述,而是以一種統(tǒng)一的語言進行表達;

          • 提升運維效率:雖然應用 Policy As Code 初期可能會帶來一定的學習成本,但是后期的價值是可以讓 Policy 擁有類似代碼的特性:

            • 不同的 Policy 代碼可以根據(jù)需求進行整合,比如每個業(yè)務必須先集成公司層面的 Policy 代碼才可以開發(fā)自身業(yè)務相關的 Policy;
            • 將安全準入的規(guī)則抽象成 Policy 代碼供業(yè)務直接使用,從而讓業(yè)務無需過多關注安全;
            • 不同的 Policy 可以像代碼一樣進行自動化檢查和測試(比如公司或組織層面的合規(guī)檢查等);
            • 更方便 Review;
          • 提升業(yè)務架構靈活度:類似于 OPA 的接耦架構可讓原來業(yè)務實現(xiàn)更為靈活的功能,比如:

            • 動態(tài)更新和加載新的 Policy;
            • 多個組件共享同一份 Policy 執(zhí)行相關邏輯(比如多個組件共享同一個 Authorization 組件);
            • 可編程能力:實現(xiàn)新的 Policy 只需要寫新的 Policy DSL 即可,無須改變業(yè)務和框架層邏輯,理論上編寫 DSL 代碼的復雜度要遠遠小于用原生語言開發(fā);
          • 更細粒度和高效的安全保障:代碼化后的 Policy 可以進行細粒度和靈活的描述,比如可以針對特定字段進行特殊判斷等。針對 Authorization,可很容易根據(jù)這一特點實現(xiàn)黑白名單功能,這比以往使用 RBAC 形式的授權要更為靈活。

            對安全的理解可抽象成 Policy 代碼強制自動執(zhí)行,這樣對安全的保障不再僅僅依靠開發(fā)者自身意識或運維人員的配置 Review,而是直接以統(tǒng)一的代碼形式體現(xiàn);

          總而言之,個人認為所謂 Policy As Code(或者 Infrastructure As CodeSecurity As Code 等等)本質(zhì)上都是讓原來對 Policy 描述抽象成代碼,從而擁有代碼的特性(復用性、抽象性、可執(zhí)行、可測試、版本化等),以此來獲得更高層次的抽象。但是,具體業(yè)務是否真正需要 Policy As Code,這種解耦是否真的有價值,還需要進行深入的思考。

          原文鏈接:https://zhengyinyong.com/post/opa-research/


          訓練營推薦





          ?點擊屏末?|??|?即刻學習

          瀏覽 61
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品欧美自拍 | 爱福利在线视频观看 | 日韩A片一级无码免费 | 超碰在线大香蕉 172.86.93.25 | www.三级 |